@triedotdev/mcp 1.0.156 → 1.0.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/{chunk-EJKVKVWM.js → chunk-62JD7MIS.js} +4 -4
  2. package/dist/{chunk-SSNOHUHY.js → chunk-ACU3IXZG.js} +1 -1
  3. package/dist/{chunk-OMZSQAQ2.js → chunk-E2DRW5XC.js} +10 -10
  4. package/dist/{chunk-FNZPMR62.js → chunk-IW7VZ2XD.js} +2 -5
  5. package/dist/chunk-IW7VZ2XD.js.map +1 -0
  6. package/dist/chunk-LANXFFR2.js +3983 -0
  7. package/dist/chunk-LANXFFR2.js.map +1 -0
  8. package/dist/{chunk-LGDZXKC5.js → chunk-NYYVYJPJ.js} +21 -21
  9. package/dist/{chunk-CYKJIQNG.js → chunk-UAQXKEMS.js} +5 -5
  10. package/dist/cli/main.js +15 -15
  11. package/dist/cli/yolo-daemon.js +8 -8
  12. package/dist/{fast-analyzer-CRWPDN6C.js → fast-analyzer-LLZ6FLP5.js} +3 -3
  13. package/dist/{goal-validator-4E5ZLCDH.js → goal-validator-4DDL7NBP.js} +2 -2
  14. package/dist/index.js +19 -3980
  15. package/dist/index.js.map +1 -1
  16. package/dist/server/mcp-server.d.ts +22 -0
  17. package/dist/server/mcp-server.js +41 -0
  18. package/dist/{tiered-storage-SUYPBYJL.js → tiered-storage-WWVVHGFR.js} +2 -2
  19. package/dist/{trie-agent-GL3VQPCD.js → trie-agent-ZLKBEWPW.js} +6 -6
  20. package/dist/trie-agent-ZLKBEWPW.js.map +1 -0
  21. package/package.json +1 -1
  22. package/dist/chunk-FNZPMR62.js.map +0 -1
  23. /package/dist/{chunk-EJKVKVWM.js.map → chunk-62JD7MIS.js.map} +0 -0
  24. /package/dist/{chunk-SSNOHUHY.js.map → chunk-ACU3IXZG.js.map} +0 -0
  25. /package/dist/{chunk-OMZSQAQ2.js.map → chunk-E2DRW5XC.js.map} +0 -0
  26. /package/dist/{chunk-LGDZXKC5.js.map → chunk-NYYVYJPJ.js.map} +0 -0
  27. /package/dist/{chunk-CYKJIQNG.js.map → chunk-UAQXKEMS.js.map} +0 -0
  28. /package/dist/{fast-analyzer-CRWPDN6C.js.map → fast-analyzer-LLZ6FLP5.js.map} +0 -0
  29. /package/dist/{goal-validator-4E5ZLCDH.js.map → goal-validator-4DDL7NBP.js.map} +0 -0
  30. /package/dist/{tiered-storage-SUYPBYJL.js.map → server/mcp-server.js.map} +0 -0
  31. /package/dist/{trie-agent-GL3VQPCD.js.map → tiered-storage-WWVVHGFR.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,4001 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- GitHubIngester
4
- } from "./chunk-ACU2RJUJ.js";
5
- import {
6
- appendToSection,
7
- completeBootstrap,
8
- getContextForAI,
9
- getProjectInfoStructured,
10
- getProjectSection,
11
- getProjectSections,
12
- initProjectInfo,
13
- initializeBootstrapFiles,
14
- loadBootstrapContext,
15
- loadContextState,
16
- loadProjectInfo,
17
- loadRules,
18
- loadTeamInfo,
19
- needsBootstrap,
20
- projectInfoExists,
21
- updateProjectSection
22
- } from "./chunk-ZBXW244P.js";
23
- import {
24
- LinearIngester
25
- } from "./chunk-OLNZJ3XV.js";
26
- import {
27
- GitHubBranchesTool,
28
- TrieCheckTool,
29
- TrieCloudFixTool,
30
- TrieExplainTool,
31
- TrieFeedbackTool,
32
- TrieFixTool,
33
- TrieGetBlockersTool,
34
- TrieGetDecisionsTool,
35
- TrieGetGovernanceTool,
36
- TrieGetRelatedDecisionsTool,
37
- TrieGetRelatedGovernanceTool,
38
- TriePipelineTool,
39
- TrieQueryContextTool,
40
- TrieTellTool,
41
- TrieWatchTool,
42
- getPrompt,
43
- getSystemPrompt,
44
- handleCheckpointTool
45
- } from "./chunk-LGDZXKC5.js";
3
+ startServer
4
+ } from "./chunk-LANXFFR2.js";
5
+ import "./chunk-ACU2RJUJ.js";
6
+ import "./chunk-ZBXW244P.js";
7
+ import "./chunk-OLNZJ3XV.js";
8
+ import "./chunk-NYYVYJPJ.js";
46
9
  import "./chunk-JVMBCWKS.js";
47
- import "./chunk-CYKJIQNG.js";
48
- import {
49
- CodebaseIndex
50
- } from "./chunk-Q5EKA5YA.js";
10
+ import "./chunk-UAQXKEMS.js";
11
+ import "./chunk-Q5EKA5YA.js";
51
12
  import "./chunk-VR4VWXXU.js";
52
13
  import "./chunk-5TRCQAOE.js";
53
14
  import "./chunk-LR46VMIE.js";
54
15
  import "./chunk-T63OHG4Q.js";
55
- import {
56
- exportToJson,
57
- formatFriendlyError,
58
- importFromJson,
59
- isTrieInitialized
60
- } from "./chunk-EJKVKVWM.js";
61
- import {
62
- loadConfig
63
- } from "./chunk-GDWA3CH3.js";
16
+ import "./chunk-62JD7MIS.js";
17
+ import "./chunk-GDWA3CH3.js";
64
18
  import "./chunk-TN5WEKWI.js";
65
19
  import "./chunk-ZV2K6M7T.js";
66
- import {
67
- findCrossProjectPatterns,
68
- getGlobalMemoryStats,
69
- listTrackedProjects,
70
- searchGlobalPatterns
71
- } from "./chunk-OMZSQAQ2.js";
72
- import {
73
- ContextGraph
74
- } from "./chunk-VUL52BQL.js";
75
- import "./chunk-SSNOHUHY.js";
20
+ import "./chunk-E2DRW5XC.js";
21
+ import "./chunk-IW7VZ2XD.js";
22
+ import "./chunk-VUL52BQL.js";
23
+ import "./chunk-ACU3IXZG.js";
76
24
  import "./chunk-FQ45QP5A.js";
77
25
  import "./chunk-5BYSJ7XT.js";
26
+ import "./chunk-IRZXBQVQ.js";
27
+ import "./chunk-EFWVF6TI.js";
78
28
  import "./chunk-SY6KQG44.js";
79
29
  import "./chunk-OMR4YCBS.js";
80
30
  import "./chunk-6NLHFIYA.js";
81
- import "./chunk-FNZPMR62.js";
82
31
  import "./chunk-ME2OERF5.js";
83
- import {
84
- findSimilarIssues,
85
- getMemoryStats,
86
- getRecentIssues,
87
- markIssueResolved,
88
- purgeIssues,
89
- searchIssues
90
- } from "./chunk-IRZXBQVQ.js";
91
- import "./chunk-EFWVF6TI.js";
92
- import {
93
- runShellCommandSync
94
- } from "./chunk-Y4B3VEL7.js";
32
+ import "./chunk-Y4B3VEL7.js";
95
33
  import "./chunk-43X6JBEM.js";
96
- import {
97
- getTrieDirectory,
98
- getWorkingDirectory
99
- } from "./chunk-VVITXIHN.js";
100
- import {
101
- isInteractiveMode
102
- } from "./chunk-KDHN2ZQE.js";
34
+ import "./chunk-VVITXIHN.js";
35
+ import "./chunk-KDHN2ZQE.js";
103
36
  import "./chunk-DGUM43GV.js";
104
37
 
105
- // src/server/mcp-server.ts
106
- import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
107
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
108
- import {
109
- CallToolRequestSchema,
110
- ListToolsRequestSchema,
111
- ListResourcesRequestSchema,
112
- ReadResourceRequestSchema
113
- } from "@modelcontextprotocol/sdk/types.js";
114
-
115
- // src/utils/ai-tool-detector.ts
116
- function detectAITool() {
117
- if (process.env.CLAUDE_CODE_VERSION || process.env.CLAUDE_CODE) {
118
- return {
119
- name: "Claude Code",
120
- preferredOutputFormat: "markdown",
121
- supportsDiffs: true,
122
- supportsInlineActions: true
123
- };
124
- }
125
- if (process.env.CURSOR_IDE || process.env.CURSOR_TRACE_ID) {
126
- return {
127
- name: "Cursor",
128
- preferredOutputFormat: "rich-text",
129
- supportsDiffs: true,
130
- supportsInlineActions: false
131
- };
132
- }
133
- if (process.env.OPENCODE_INSTANCE) {
134
- return {
135
- name: "OpenCode",
136
- preferredOutputFormat: "plain-text",
137
- supportsDiffs: false,
138
- supportsInlineActions: false
139
- };
140
- }
141
- if (process.env.VSCODE_PID || process.env.VSCODE_IPC_HOOK) {
142
- return {
143
- name: "VS Code",
144
- preferredOutputFormat: "markdown",
145
- supportsDiffs: true,
146
- supportsInlineActions: false
147
- };
148
- }
149
- const parentProcess = process.env.PARENT_PROCESS_NAME || process.env._ || "";
150
- if (parentProcess.toLowerCase().includes("claude")) {
151
- return {
152
- name: "Claude Code",
153
- preferredOutputFormat: "markdown",
154
- supportsDiffs: true,
155
- supportsInlineActions: true
156
- };
157
- }
158
- if (parentProcess.toLowerCase().includes("cursor")) {
159
- return {
160
- name: "Cursor",
161
- preferredOutputFormat: "rich-text",
162
- supportsDiffs: true,
163
- supportsInlineActions: false
164
- };
165
- }
166
- if (process.env.npm_execpath || process.argv[1]?.includes("npx")) {
167
- return {
168
- name: "MCP Client",
169
- preferredOutputFormat: "markdown",
170
- supportsDiffs: true,
171
- supportsInlineActions: false
172
- };
173
- }
174
- return {
175
- name: "MCP Client",
176
- preferredOutputFormat: "markdown",
177
- supportsDiffs: true,
178
- supportsInlineActions: false
179
- };
180
- }
181
-
182
- // src/tools/test.ts
183
- import { readFile } from "fs/promises";
184
- import { existsSync } from "fs";
185
- import { extname, relative, resolve, isAbsolute, dirname, basename, join } from "path";
186
- var TrieTestTool = class {
187
- async execute(args) {
188
- const { action, files, framework, style = "unit" } = args || {};
189
- if (!action || !files || files.length === 0) {
190
- return {
191
- content: [{
192
- type: "text",
193
- text: this.getHelpText()
194
- }]
195
- };
196
- }
197
- switch (action) {
198
- case "generate":
199
- return this.generateTests(files, framework, style);
200
- case "coverage":
201
- return this.analyzeCoverage(files);
202
- case "suggest":
203
- return this.suggestTests(files);
204
- case "run":
205
- return this.runTestsInfo(files);
206
- default:
207
- return {
208
- content: [{
209
- type: "text",
210
- text: `Unknown action: ${action}`
211
- }]
212
- };
213
- }
214
- }
215
- async generateTests(files, framework, style) {
216
- const detectedFramework = framework || await this.detectTestFramework();
217
- let output = `
218
- ${"\u2501".repeat(60)}
219
- `;
220
- output += `\u{1F9EA} TEST GENERATION
221
- `;
222
- output += `${"\u2501".repeat(60)}
223
-
224
- `;
225
- output += `## \u2699\uFE0F Configuration
226
-
227
- `;
228
- output += `- **Framework:** ${detectedFramework}
229
- `;
230
- output += `- **Style:** ${style}
231
- `;
232
- output += `- **Files:** ${files.length}
233
-
234
- `;
235
- const workDir = getWorkingDirectory(void 0, true);
236
- const allUnits = [];
237
- for (const file of files) {
238
- const resolvedPath = isAbsolute(file) ? file : resolve(workDir, file);
239
- if (!existsSync(resolvedPath)) {
240
- output += `[!] File not found: ${file}
241
- `;
242
- continue;
243
- }
244
- const code = await readFile(resolvedPath, "utf-8");
245
- const language = this.detectLanguage(resolvedPath);
246
- const relativePath = relative(workDir, resolvedPath);
247
- const units = this.extractTestableUnits(code, language);
248
- allUnits.push({ file: relativePath, units });
249
- output += `### \u{1F4C4} ${relativePath}
250
-
251
- `;
252
- output += `Found **${units.length}** testable units:
253
-
254
- `;
255
- for (const unit of units) {
256
- const complexityIcon = unit.complexity > 10 ? "\u{1F534}" : unit.complexity > 5 ? "\u{1F7E1}" : "\u{1F7E2}";
257
- output += `- ${complexityIcon} \`${unit.name}\` (${unit.type}, complexity: ${unit.complexity})
258
- `;
259
- if (unit.dependencies.length > 0) {
260
- output += ` - Dependencies: ${unit.dependencies.join(", ")}
261
- `;
262
- }
263
- }
264
- output += "\n";
265
- }
266
- output += `${"\u2500".repeat(60)}
267
- `;
268
- output += `## \u{1F9E0} Test Generation Request
269
-
270
- `;
271
- const systemPrompt = getSystemPrompt("test");
272
- output += `**Role:** ${systemPrompt.split("\n")[0]}
273
-
274
- `;
275
- for (const { file, units } of allUnits) {
276
- if (units.length === 0) continue;
277
- const code = await readFile(resolve(workDir, file), "utf-8");
278
- const language = this.detectLanguage(file);
279
- const prompt = getPrompt("test", "generate", {
280
- code,
281
- language,
282
- filePath: file,
283
- framework: detectedFramework
284
- });
285
- output += `### Tests for ${file}
286
-
287
- `;
288
- output += prompt;
289
- output += "\n\n";
290
- }
291
- output += `${"\u2500".repeat(60)}
292
- `;
293
- output += `
294
- ## \u{1F4DD} Expected Test Output
295
-
296
- `;
297
- output += `Generate complete test files with:
298
- `;
299
- output += `- All imports and setup
300
- `;
301
- output += `- Tests for each function/method
302
- `;
303
- output += `- Edge cases and error scenarios
304
- `;
305
- output += `- Mock requirements clearly stated
306
- `;
307
- output += `- Ready to copy and run
308
- `;
309
- return { content: [{ type: "text", text: output }] };
310
- }
311
- async analyzeCoverage(files) {
312
- let output = `
313
- ${"\u2501".repeat(60)}
314
- `;
315
- output += `\u{1F4CA} COVERAGE ANALYSIS
316
- `;
317
- output += `${"\u2501".repeat(60)}
318
-
319
- `;
320
- const workDir = getWorkingDirectory(void 0, true);
321
- for (const file of files) {
322
- const resolvedPath = isAbsolute(file) ? file : resolve(workDir, file);
323
- if (!existsSync(resolvedPath)) {
324
- output += `[!] File not found: ${file}
325
- `;
326
- continue;
327
- }
328
- const code = await readFile(resolvedPath, "utf-8");
329
- const language = this.detectLanguage(resolvedPath);
330
- const relativePath = relative(workDir, resolvedPath);
331
- const units = this.extractTestableUnits(code, language);
332
- const testFile = await this.findTestFile(resolvedPath);
333
- let testCode = "";
334
- let testedUnits = [];
335
- if (testFile) {
336
- testCode = await readFile(testFile, "utf-8");
337
- testedUnits = this.findTestedUnits(testCode, units.map((u) => u.name));
338
- }
339
- const coverage = units.length > 0 ? Math.round(testedUnits.length / units.length * 100) : 0;
340
- const coverageIcon = coverage >= 80 ? "\u{1F7E2}" : coverage >= 50 ? "\u{1F7E1}" : "\u{1F534}";
341
- output += `### \u{1F4C4} ${relativePath}
342
-
343
- `;
344
- output += `**Coverage:** ${coverageIcon} ${coverage}% (${testedUnits.length}/${units.length} units)
345
- `;
346
- if (testFile) {
347
- output += `**Test file:** \`${relative(workDir, testFile)}\`
348
- `;
349
- } else {
350
- output += `**Test file:** \u274C Not found
351
- `;
352
- }
353
- output += "\n";
354
- const untested = units.filter((u) => !testedUnits.includes(u.name));
355
- if (untested.length > 0) {
356
- output += `**Missing Tests:**
357
- `;
358
- for (const unit of untested) {
359
- const priority = unit.complexity > 5 ? "\u{1F534} HIGH" : "\u{1F7E1} MEDIUM";
360
- output += `- ${priority}: \`${unit.name}\` (${unit.type})
361
- `;
362
- }
363
- output += "\n";
364
- }
365
- if (testedUnits.length > 0) {
366
- output += `**Tested:**
367
- `;
368
- for (const name of testedUnits) {
369
- output += `- \u2705 \`${name}\`
370
- `;
371
- }
372
- output += "\n";
373
- }
374
- }
375
- output += `${"\u2500".repeat(60)}
376
- `;
377
- output += `## \u{1F4CB} Recommendations
378
-
379
- `;
380
- output += `To improve coverage:
381
- `;
382
- output += `1. Focus on high-complexity untested functions first
383
- `;
384
- output += `2. Add edge case tests for existing coverage
385
- `;
386
- output += `3. Consider integration tests for complex interactions
387
- `;
388
- output += `
389
- Run \`trie_test action:generate files:["file.ts"]\` to generate missing tests.
390
- `;
391
- return { content: [{ type: "text", text: output }] };
392
- }
393
- async suggestTests(files) {
394
- let output = `
395
- ${"\u2501".repeat(60)}
396
- `;
397
- output += `\u{1F4A1} TEST SUGGESTIONS
398
- `;
399
- output += `${"\u2501".repeat(60)}
400
-
401
- `;
402
- const workDir = getWorkingDirectory(void 0, true);
403
- for (const file of files) {
404
- const resolvedPath = isAbsolute(file) ? file : resolve(workDir, file);
405
- if (!existsSync(resolvedPath)) {
406
- output += `[!] File not found: ${file}
407
- `;
408
- continue;
409
- }
410
- const code = await readFile(resolvedPath, "utf-8");
411
- const relativePath = relative(workDir, resolvedPath);
412
- const patterns = this.detectTestablePatterns(code);
413
- output += `### \u{1F4C4} ${relativePath}
414
-
415
- `;
416
- if (patterns.length === 0) {
417
- output += `No specific test suggestions for this file.
418
-
419
- `;
420
- continue;
421
- }
422
- output += `| Priority | Pattern | Suggested Test |
423
- `;
424
- output += `|----------|---------|----------------|
425
- `;
426
- for (const pattern of patterns) {
427
- output += `| ${pattern.priority} | ${pattern.name} | ${pattern.suggestion} |
428
- `;
429
- }
430
- output += "\n";
431
- }
432
- return { content: [{ type: "text", text: output }] };
433
- }
434
- async runTestsInfo(files) {
435
- const framework = await this.detectTestFramework();
436
- let output = `
437
- ${"\u2501".repeat(60)}
438
- `;
439
- output += `RUN TESTS
440
- `;
441
- output += `${"\u2501".repeat(60)}
442
-
443
- `;
444
- output += `## Detected Configuration
445
-
446
- `;
447
- output += `- **Framework:** ${framework}
448
- `;
449
- output += `- **Files:** ${files.join(", ")}
450
-
451
- `;
452
- output += `## Commands
453
-
454
- `;
455
- switch (framework) {
456
- case "jest":
457
- output += `\`\`\`bash
458
- `;
459
- output += `# Run all tests
460
- `;
461
- output += `npx jest
462
-
463
- `;
464
- output += `# Run specific files
465
- `;
466
- output += `npx jest ${files.join(" ")}
467
-
468
- `;
469
- output += `# Run with coverage
470
- `;
471
- output += `npx jest --coverage
472
- `;
473
- output += `\`\`\`
474
- `;
475
- break;
476
- case "vitest":
477
- output += `\`\`\`bash
478
- `;
479
- output += `# Run all tests
480
- `;
481
- output += `npx vitest run
482
-
483
- `;
484
- output += `# Run specific files
485
- `;
486
- output += `npx vitest run ${files.join(" ")}
487
-
488
- `;
489
- output += `# Run with coverage
490
- `;
491
- output += `npx vitest run --coverage
492
- `;
493
- output += `\`\`\`
494
- `;
495
- break;
496
- case "pytest":
497
- output += `\`\`\`bash
498
- `;
499
- output += `# Run all tests
500
- `;
501
- output += `pytest
502
-
503
- `;
504
- output += `# Run specific files
505
- `;
506
- output += `pytest ${files.join(" ")}
507
-
508
- `;
509
- output += `# Run with coverage
510
- `;
511
- output += `pytest --cov
512
- `;
513
- output += `\`\`\`
514
- `;
515
- break;
516
- default:
517
- output += `Run your test framework with the specified files.
518
- `;
519
- }
520
- output += `
521
- *Note: This tool provides test commands but doesn't execute them directly.*
522
- `;
523
- return { content: [{ type: "text", text: output }] };
524
- }
525
- extractTestableUnits(code, language) {
526
- const units = [];
527
- const lines = code.split("\n");
528
- for (let i = 0; i < lines.length; i++) {
529
- const line = lines[i] || "";
530
- const funcMatch = line.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/);
531
- if (funcMatch) {
532
- const endLine = this.findBlockEnd(lines, i);
533
- const body = lines.slice(i, endLine + 1).join("\n");
534
- units.push({
535
- name: funcMatch[1],
536
- type: "function",
537
- startLine: i + 1,
538
- endLine: endLine + 1,
539
- signature: `${funcMatch[1]}(${funcMatch[2]})`,
540
- complexity: this.calculateComplexity(body),
541
- dependencies: this.extractDependencies(body)
542
- });
543
- }
544
- const arrowMatch = line.match(/(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*(?:=>|:)/);
545
- if (arrowMatch) {
546
- const endLine = this.findBlockEnd(lines, i);
547
- const body = lines.slice(i, endLine + 1).join("\n");
548
- units.push({
549
- name: arrowMatch[1],
550
- type: "function",
551
- startLine: i + 1,
552
- endLine: endLine + 1,
553
- signature: arrowMatch[1],
554
- complexity: this.calculateComplexity(body),
555
- dependencies: this.extractDependencies(body)
556
- });
557
- }
558
- const classMatch = line.match(/(?:export\s+)?class\s+(\w+)/);
559
- if (classMatch) {
560
- const endLine = this.findBlockEnd(lines, i);
561
- units.push({
562
- name: classMatch[1],
563
- type: "class",
564
- startLine: i + 1,
565
- endLine: endLine + 1,
566
- signature: `class ${classMatch[1]}`,
567
- complexity: this.calculateComplexity(lines.slice(i, endLine + 1).join("\n")),
568
- dependencies: []
569
- });
570
- }
571
- const componentMatch = line.match(/(?:export\s+)?(?:const|function)\s+([A-Z]\w+)\s*[=:]/);
572
- if (componentMatch && /jsx|tsx/.test(language)) {
573
- const endLine = this.findBlockEnd(lines, i);
574
- units.push({
575
- name: componentMatch[1],
576
- type: "component",
577
- startLine: i + 1,
578
- endLine: endLine + 1,
579
- signature: componentMatch[1],
580
- complexity: this.calculateComplexity(lines.slice(i, endLine + 1).join("\n")),
581
- dependencies: this.extractDependencies(lines.slice(i, endLine + 1).join("\n"))
582
- });
583
- }
584
- }
585
- return units;
586
- }
587
- findBlockEnd(lines, startLine) {
588
- let braceCount = 0;
589
- let started = false;
590
- for (let i = startLine; i < lines.length; i++) {
591
- const line = lines[i] || "";
592
- for (const char of line) {
593
- if (char === "{") {
594
- braceCount++;
595
- started = true;
596
- } else if (char === "}") {
597
- braceCount--;
598
- }
599
- }
600
- if (started && braceCount === 0) {
601
- return i;
602
- }
603
- }
604
- return lines.length - 1;
605
- }
606
- calculateComplexity(code) {
607
- let complexity = 1;
608
- const patterns = [
609
- /if\s*\(/g,
610
- /else\s+if/g,
611
- /\?\s*[^:]/g,
612
- // Conditionals
613
- /for\s*\(/g,
614
- /while\s*\(/g,
615
- /do\s*\{/g,
616
- // Loops
617
- /catch\s*\(/g,
618
- // Error handling
619
- /&&/g,
620
- /\|\|/g,
621
- // Logical operators
622
- /case\s+/g
623
- // Switch cases
624
- ];
625
- for (const pattern of patterns) {
626
- const matches = code.match(pattern);
627
- if (matches) {
628
- complexity += matches.length;
629
- }
630
- }
631
- return complexity;
632
- }
633
- extractDependencies(code) {
634
- const deps = [];
635
- const calls = code.match(/\b([a-z]\w+)\s*\(/gi) || [];
636
- const builtins = ["if", "for", "while", "switch", "catch", "function", "return", "throw", "new", "await", "async"];
637
- for (const call of calls) {
638
- const name = call.replace(/\s*\($/, "");
639
- if (!builtins.includes(name.toLowerCase()) && !deps.includes(name)) {
640
- deps.push(name);
641
- }
642
- }
643
- return deps.slice(0, 10);
644
- }
645
- async findTestFile(sourcePath) {
646
- const dir = dirname(sourcePath);
647
- const base = basename(sourcePath, extname(sourcePath));
648
- const ext = extname(sourcePath);
649
- const patterns = [
650
- `${base}.test${ext}`,
651
- `${base}.spec${ext}`,
652
- `${base}_test${ext}`,
653
- `test_${base}${ext}`
654
- ];
655
- for (const pattern of patterns) {
656
- const testPath = join(dir, pattern);
657
- if (existsSync(testPath)) {
658
- return testPath;
659
- }
660
- }
661
- const testsDir = join(dir, "__tests__");
662
- if (existsSync(testsDir)) {
663
- for (const pattern of patterns) {
664
- const testPath = join(testsDir, pattern);
665
- if (existsSync(testPath)) {
666
- return testPath;
667
- }
668
- }
669
- }
670
- return null;
671
- }
672
- findTestedUnits(testCode, unitNames) {
673
- const tested = [];
674
- for (const name of unitNames) {
675
- const patterns = [
676
- new RegExp(`describe\\s*\\([^)]*${name}`, "i"),
677
- new RegExp(`it\\s*\\([^)]*${name}`, "i"),
678
- new RegExp(`test\\s*\\([^)]*${name}`, "i"),
679
- new RegExp(`expect\\s*\\([^)]*${name}`, "i")
680
- ];
681
- for (const pattern of patterns) {
682
- if (pattern.test(testCode)) {
683
- tested.push(name);
684
- break;
685
- }
686
- }
687
- }
688
- return tested;
689
- }
690
- detectTestablePatterns(code) {
691
- const patterns = [];
692
- const checks = [
693
- { pattern: /async\s+function|await\s+/i, priority: "\u{1F534} HIGH", name: "Async code", suggestion: "Test async flows and error handling" },
694
- { pattern: /try\s*{[^}]*catch/i, priority: "\u{1F534} HIGH", name: "Error handling", suggestion: "Test both success and error paths" },
695
- { pattern: /if\s*\([^)]*&&[^)]*\)/i, priority: "\u{1F7E1} MED", name: "Complex conditionals", suggestion: "Test all condition branches" },
696
- { pattern: /\.map\(|\.filter\(|\.reduce\(/i, priority: "\u{1F7E1} MED", name: "Array operations", suggestion: "Test with empty, single, multiple items" },
697
- { pattern: /fetch\(|axios\.|request\(/i, priority: "\u{1F534} HIGH", name: "HTTP requests", suggestion: "Mock API calls, test error responses" },
698
- { pattern: /localStorage|sessionStorage/i, priority: "\u{1F7E1} MED", name: "Storage usage", suggestion: "Mock storage, test read/write" },
699
- { pattern: /setTimeout|setInterval/i, priority: "\u{1F7E1} MED", name: "Timers", suggestion: "Use fake timers in tests" },
700
- { pattern: /useState|useEffect|useCallback/i, priority: "\u{1F534} HIGH", name: "React hooks", suggestion: "Test hook behavior and updates" },
701
- { pattern: /form|input|submit/i, priority: "\u{1F7E1} MED", name: "Form handling", suggestion: "Test validation and submission" }
702
- ];
703
- for (const { pattern, priority, name, suggestion } of checks) {
704
- if (pattern.test(code)) {
705
- patterns.push({ priority, name, suggestion });
706
- }
707
- }
708
- return patterns;
709
- }
710
- async detectTestFramework() {
711
- const workDir = getWorkingDirectory(void 0, true);
712
- const packagePath = resolve(workDir, "package.json");
713
- if (existsSync(packagePath)) {
714
- try {
715
- const pkg = JSON.parse(await readFile(packagePath, "utf-8"));
716
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
717
- if (deps.vitest) return "vitest";
718
- if (deps.jest) return "jest";
719
- if (deps.mocha) return "mocha";
720
- } catch {
721
- }
722
- }
723
- if (existsSync(resolve(workDir, "pytest.ini")) || existsSync(resolve(workDir, "pyproject.toml"))) {
724
- return "pytest";
725
- }
726
- return "jest";
727
- }
728
- detectLanguage(filePath) {
729
- const ext = extname(filePath).toLowerCase();
730
- const langMap = {
731
- ".ts": "typescript",
732
- ".tsx": "tsx",
733
- ".js": "javascript",
734
- ".jsx": "jsx",
735
- ".py": "python",
736
- ".go": "go",
737
- ".rs": "rust"
738
- };
739
- return langMap[ext] || "javascript";
740
- }
741
- getHelpText() {
742
- return `
743
- ${"\u2501".repeat(60)}
744
- \u{1F9EA} TRIE TEST - AI-POWERED TEST GENERATION
745
- ${"\u2501".repeat(60)}
746
-
747
- ## Usage
748
-
749
- ### Generate tests:
750
- \`\`\`
751
- trie_test action:"generate" files:["src/utils.ts"]
752
- \`\`\`
753
-
754
- ### Analyze coverage:
755
- \`\`\`
756
- trie_test action:"coverage" files:["src/utils.ts"]
757
- \`\`\`
758
-
759
- ### Get test suggestions:
760
- \`\`\`
761
- trie_test action:"suggest" files:["src/app.ts"]
762
- \`\`\`
763
-
764
- ### Get run commands:
765
- \`\`\`
766
- trie_test action:"run" files:["src/utils.test.ts"]
767
- \`\`\`
768
-
769
- ## Options
770
-
771
- | Option | Values | Description |
772
- |--------|--------|-------------|
773
- | action | generate, coverage, suggest, run | What to do |
774
- | files | Array of paths | Files to analyze |
775
- | framework | jest, vitest, mocha, pytest | Test framework |
776
- | style | unit, integration, e2e, all | Test style |
777
- `;
778
- }
779
- };
780
-
781
- // src/tools/pr-review.ts
782
- import { readFile as readFile2 } from "fs/promises";
783
- import { existsSync as existsSync2 } from "fs";
784
- import { join as join2, basename as basename2, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
785
- var TriePRReviewTool = class {
786
- // Review workflow is now ledger-driven; keep a lightweight built-in checklist.
787
- CRITICAL_REVIEW_CHECKLIST = {
788
- stateAndLifecycle: [
789
- "Uninitialized state accessed before setup",
790
- "Missing cleanup on unmount/dispose",
791
- "State mutations in wrong lifecycle phase"
792
- ],
793
- edgeCasesAndRaces: [
794
- "Race conditions in async operations",
795
- "Missing error handling for edge cases",
796
- "Unhandled promise rejections"
797
- ],
798
- missingPieces: [
799
- "Missing input validation",
800
- "Missing error handling",
801
- "Missing logging/monitoring"
802
- ]
803
- };
804
- exec(command, cwd, maxBuffer) {
805
- const opts = {
806
- captureOutput: false,
807
- redactOutput: true,
808
- ...cwd ? { cwd } : {},
809
- ...maxBuffer ? { maxBuffer } : {}
810
- };
811
- const { stdout } = runShellCommandSync(
812
- command,
813
- { actor: "tool:pr-review", triggeredBy: "manual", targetPath: cwd ?? getWorkingDirectory(void 0, true) },
814
- opts
815
- );
816
- return stdout.trimEnd();
817
- }
818
- async execute(args) {
819
- const {
820
- pr,
821
- worktree,
822
- mode,
823
- files: _specificFiles
824
- // Reserved for future file filtering
825
- } = args;
826
- try {
827
- const prInfo = await this.getPRInfo(pr, worktree);
828
- if (!prInfo.success) {
829
- return {
830
- content: [{
831
- type: "text",
832
- text: `${prInfo.error}
833
-
834
- Usage:
835
- - \`pr_review 12345\` \u2014 review specific PR
836
- - \`pr_review\` \u2014 review PR for current branch
837
- - \`pr_review ../worktree\` \u2014 review worktree changes`
838
- }]
839
- };
840
- }
841
- const changes = await this.getChanges(prInfo);
842
- if (changes.files.length === 0) {
843
- return {
844
- content: [{
845
- type: "text",
846
- text: `No changes found to review.`
847
- }]
848
- };
849
- }
850
- const designDocs = await this.findDesignDocs(changes.files, prInfo);
851
- const workflow = this.buildReviewWorkflow(changes.files, {
852
- ...prInfo.number ? { prNumber: prInfo.number } : {},
853
- ...prInfo.title ? { prTitle: prInfo.title } : {},
854
- ...prInfo.author ? { prAuthor: prInfo.author } : {},
855
- ...prInfo.baseBranch ? { baseBranch: prInfo.baseBranch } : {},
856
- ...prInfo.headBranch ? { headBranch: prInfo.headBranch } : {},
857
- mode: mode || "own",
858
- designDocs
859
- });
860
- const fileContents = await this.preloadFiles(changes.files.map((f) => f.path));
861
- const reviewPrompt = this.generateReviewPrompt(workflow, changes, fileContents);
862
- return {
863
- content: [{
864
- type: "text",
865
- text: reviewPrompt
866
- }]
867
- };
868
- } catch (error) {
869
- return {
870
- content: [{
871
- type: "text",
872
- text: `Error: ${error instanceof Error ? error.message : String(error)}`
873
- }]
874
- };
875
- }
876
- }
877
- /**
878
- * Get PR information from GitHub or local worktree
879
- */
880
- async getPRInfo(pr, worktree) {
881
- if (worktree) {
882
- const worktreePath = isAbsolute2(worktree) ? worktree : resolve2(getWorkingDirectory(void 0, true), worktree);
883
- if (!existsSync2(worktreePath)) {
884
- return { success: false, error: `Worktree not found: ${worktreePath}` };
885
- }
886
- return {
887
- success: true,
888
- type: "worktree",
889
- path: worktreePath,
890
- title: `Local changes in ${basename2(worktreePath)}`,
891
- author: this.getGitUser(),
892
- baseBranch: "HEAD~1",
893
- headBranch: "HEAD"
894
- };
895
- }
896
- if (pr) {
897
- return this.getPRFromGitHub(pr);
898
- }
899
- try {
900
- this.exec("git branch --show-current");
901
- const prJson = this.exec(
902
- `gh pr view --json number,title,author,headRefName,baseRefName`
903
- );
904
- const prData = JSON.parse(prJson);
905
- return {
906
- success: true,
907
- type: "github",
908
- number: String(prData.number),
909
- title: prData.title,
910
- author: prData.author?.login || "unknown",
911
- baseBranch: prData.baseRefName,
912
- headBranch: prData.headRefName
913
- };
914
- } catch {
915
- return {
916
- success: true,
917
- type: "local",
918
- title: "Local uncommitted changes",
919
- author: this.getGitUser(),
920
- baseBranch: "HEAD",
921
- headBranch: "working tree"
922
- };
923
- }
924
- }
925
- /**
926
- * Get PR info from GitHub CLI
927
- */
928
- async getPRFromGitHub(prNumber) {
929
- try {
930
- const prJson = this.exec(
931
- `gh pr view ${prNumber} --json number,title,author,headRefName,baseRefName`
932
- );
933
- const prData = JSON.parse(prJson);
934
- return {
935
- success: true,
936
- type: "github",
937
- number: String(prData.number),
938
- title: prData.title,
939
- author: prData.author?.login || "unknown",
940
- baseBranch: prData.baseRefName,
941
- headBranch: prData.headRefName
942
- };
943
- } catch (error) {
944
- return {
945
- success: false,
946
- error: `Failed to get PR #${prNumber}. Is \`gh\` CLI installed and authenticated?`
947
- };
948
- }
949
- }
950
- /**
951
- * Get changes (diff) for the PR or local changes
952
- */
953
- async getChanges(prInfo) {
954
- let diffOutput;
955
- try {
956
- if (prInfo.type === "github" && prInfo.number) {
957
- diffOutput = this.exec(`gh pr diff ${prInfo.number}`, void 0, 50 * 1024 * 1024);
958
- } else if (prInfo.type === "worktree" && prInfo.path) {
959
- diffOutput = this.exec(`git diff HEAD`, prInfo.path, 50 * 1024 * 1024);
960
- } else {
961
- diffOutput = this.exec(`git diff HEAD`, void 0, 50 * 1024 * 1024);
962
- if (!diffOutput.trim()) {
963
- diffOutput = this.exec(`git diff --cached`, void 0, 50 * 1024 * 1024);
964
- }
965
- }
966
- } catch {
967
- diffOutput = "";
968
- }
969
- const files = this.parseDiff(diffOutput);
970
- return { files, fullDiff: diffOutput };
971
- }
972
- /**
973
- * Parse git diff output into structured file changes
974
- */
975
- parseDiff(diffOutput) {
976
- const files = [];
977
- const fileDiffs = diffOutput.split(/^diff --git /m).slice(1);
978
- for (const fileDiff of fileDiffs) {
979
- const lines = fileDiff.split("\n");
980
- const headerLine = lines[0] || "";
981
- const pathMatch = headerLine.match(/a\/(.+?) b\/(.+)/);
982
- if (!pathMatch || !pathMatch[2]) continue;
983
- const path2 = pathMatch[2];
984
- const diff = `diff --git ${fileDiff}`;
985
- let additions = 0;
986
- let deletions = 0;
987
- for (const line of lines) {
988
- if (line.startsWith("+") && !line.startsWith("+++")) {
989
- additions++;
990
- } else if (line.startsWith("-") && !line.startsWith("---")) {
991
- deletions++;
992
- }
993
- }
994
- files.push({ path: path2, diff, additions, deletions, status: "modified" });
995
- }
996
- return files;
997
- }
998
- buildReviewWorkflow(files, context) {
999
- const totalAdditions = files.reduce((sum, f) => sum + (f.additions || 0), 0);
1000
- const totalDeletions = files.reduce((sum, f) => sum + (f.deletions || 0), 0);
1001
- const reviewMode = context.mode === "others" ? "others" : "own";
1002
- const reviewInstructions = {
1003
- mode: reviewMode,
1004
- description: reviewMode === "own" ? "Your PR \u2014 focus on footguns, missing tests, and production risks." : "Someone else's PR \u2014 focus on correctness, clarity, and maintainability."
1005
- };
1006
- const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
1007
- const fileOrder = sorted.map((f, i) => ({
1008
- index: i + 1,
1009
- path: f.path,
1010
- reason: i === 0 ? "Start here to establish baseline context" : "Proceed in filename order"
1011
- }));
1012
- return {
1013
- metadata: {
1014
- ...context.prNumber ? { prNumber: context.prNumber } : {},
1015
- prTitle: context.prTitle || "PR Review",
1016
- prAuthor: context.prAuthor || "unknown",
1017
- baseBranch: context.baseBranch || "unknown",
1018
- headBranch: context.headBranch || "unknown",
1019
- totalFiles: files.length,
1020
- totalAdditions,
1021
- totalDeletions,
1022
- designDocs: context.designDocs
1023
- },
1024
- fileOrder,
1025
- reviewInstructions
1026
- };
1027
- }
1028
- /**
1029
- * Find related design docs
1030
- */
1031
- async findDesignDocs(files, prInfo) {
1032
- const designDocs = [];
1033
- const cwd = prInfo.path || getWorkingDirectory(void 0, true);
1034
- for (const file of files) {
1035
- if (file.path.includes("design_docs/") || file.path.includes("docs/design/") || file.path.includes("rfcs/")) {
1036
- designDocs.push(file.path);
1037
- }
1038
- }
1039
- const designDocPaths = [
1040
- "design_docs",
1041
- "docs/design",
1042
- "docs/rfcs",
1043
- "rfcs"
1044
- ];
1045
- for (const docPath of designDocPaths) {
1046
- const fullPath = join2(cwd, docPath);
1047
- if (existsSync2(fullPath)) {
1048
- }
1049
- }
1050
- return designDocs;
1051
- }
1052
- /**
1053
- * Pre-load all changed files for instant responses during review
1054
- */
1055
- async preloadFiles(filePaths) {
1056
- const contents = /* @__PURE__ */ new Map();
1057
- const cwd = getWorkingDirectory(void 0, true);
1058
- await Promise.all(filePaths.map(async (filePath) => {
1059
- try {
1060
- const fullPath = isAbsolute2(filePath) ? filePath : join2(cwd, filePath);
1061
- if (existsSync2(fullPath)) {
1062
- const content = await readFile2(fullPath, "utf-8");
1063
- contents.set(filePath, content);
1064
- }
1065
- } catch {
1066
- }
1067
- }));
1068
- return contents;
1069
- }
1070
- /**
1071
- * Get git user name
1072
- */
1073
- getGitUser() {
1074
- try {
1075
- return this.exec("git config user.name");
1076
- } catch {
1077
- return "unknown";
1078
- }
1079
- }
1080
- /**
1081
- * Generate the full review prompt for Claude to execute
1082
- */
1083
- generateReviewPrompt(workflow, changes, _fileContents) {
1084
- const { metadata, fileOrder, reviewInstructions } = workflow;
1085
- let prompt = `# \u{1F50D} Super Reviewer \u2014 Interactive PR Review
1086
-
1087
- `;
1088
- if (metadata.prNumber) {
1089
- prompt += `## PR #${metadata.prNumber}: ${metadata.prTitle}
1090
-
1091
- `;
1092
- } else {
1093
- prompt += `## ${metadata.prTitle}
1094
-
1095
- `;
1096
- }
1097
- prompt += `**Author:** ${metadata.prAuthor}
1098
- `;
1099
- prompt += `**Branch:** \`${metadata.headBranch}\` \u2192 \`${metadata.baseBranch}\`
1100
- `;
1101
- prompt += `**Scope:** ${metadata.totalFiles} files, +${metadata.totalAdditions}/-${metadata.totalDeletions} lines
1102
-
1103
- `;
1104
- prompt += `---
1105
-
1106
- `;
1107
- prompt += `## Review Mode
1108
-
1109
- `;
1110
- prompt += `**Current Mode:** ${reviewInstructions.mode === "own" ? "1" : "2"}. ${reviewInstructions.description}
1111
-
1112
- `;
1113
- prompt += `> To switch modes, say "mode 1" for your own PR or "mode 2" for someone else's
1114
-
1115
- `;
1116
- prompt += `---
1117
-
1118
- `;
1119
- prompt += `## Files to Review (ordered for understanding)
1120
-
1121
- `;
1122
- prompt += `| # | File | Why this order |
1123
- `;
1124
- prompt += `|---|------|----------------|
1125
- `;
1126
- for (const file of fileOrder) {
1127
- const stats = `+${changes.files.find((f) => f.path === file.path)?.additions || 0}/-${changes.files.find((f) => f.path === file.path)?.deletions || 0}`;
1128
- prompt += `| ${file.index} | \`${file.path}\` (${stats}) | ${file.reason} |
1129
- `;
1130
- }
1131
- prompt += `
1132
- ---
1133
-
1134
- `;
1135
- prompt += `## Critical Review Mindset
1136
-
1137
- `;
1138
- prompt += `As we go through each file, I'll actively look for:
1139
-
1140
- `;
1141
- prompt += `**State & Lifecycle:**
1142
- `;
1143
- for (const check of this.CRITICAL_REVIEW_CHECKLIST.stateAndLifecycle) {
1144
- prompt += `- ${check}
1145
- `;
1146
- }
1147
- prompt += `
1148
- **Edge Cases & Races:**
1149
- `;
1150
- for (const check of this.CRITICAL_REVIEW_CHECKLIST.edgeCasesAndRaces) {
1151
- prompt += `- ${check}
1152
- `;
1153
- }
1154
- prompt += `
1155
- **Missing Pieces:**
1156
- `;
1157
- for (const check of this.CRITICAL_REVIEW_CHECKLIST.missingPieces) {
1158
- prompt += `- ${check}
1159
- `;
1160
- }
1161
- prompt += `
1162
- ---
1163
-
1164
- `;
1165
- prompt += `## File Diffs (pre-loaded for instant review)
1166
-
1167
- `;
1168
- prompt += `<details>
1169
- <summary>\u{1F4C1} All file diffs (click to expand)</summary>
1170
-
1171
- `;
1172
- for (const file of fileOrder) {
1173
- const fileChange = changes.files.find((f) => f.path === file.path);
1174
- if (fileChange) {
1175
- prompt += `### ${file.path}
1176
-
1177
- `;
1178
- prompt += `\`\`\`diff
1179
- ${fileChange.diff}
1180
- \`\`\`
1181
-
1182
- `;
1183
- }
1184
- }
1185
- prompt += `</details>
1186
-
1187
- `;
1188
- prompt += `---
1189
-
1190
- `;
1191
- prompt += `## Ready to Begin
1192
-
1193
- `;
1194
- prompt += `I'll walk you through each file, explaining what changed and why. After each file, I'll pause for your questions.
1195
-
1196
- `;
1197
- prompt += `**Commands during review:**
1198
- `;
1199
- prompt += `- \`yes\` or \`y\` \u2014 continue to next file
1200
- `;
1201
- prompt += `- \`skip to [filename]\` \u2014 jump to specific file
1202
- `;
1203
- prompt += `- \`questions?\` \u2014 ask about current file
1204
- `;
1205
- prompt += `- \`reorder\` \u2014 change the file order
1206
- `;
1207
- prompt += `- \`done\` \u2014 end review and show summary
1208
-
1209
- `;
1210
- prompt += `**Ready for File 1?** (\`${fileOrder[0]?.path || "no files"}\`)
1211
-
1212
- `;
1213
- prompt += `*(say "yes" to start, or ask me anything)*
1214
- `;
1215
- return prompt;
1216
- }
1217
- };
1218
-
1219
- // src/tools/project-info.ts
1220
- var TrieProjectInfoTool = class {
1221
- async execute(args) {
1222
- const workDir = args.directory || getWorkingDirectory(void 0, true);
1223
- const action = args.action || "view";
1224
- try {
1225
- switch (action) {
1226
- case "view":
1227
- return await this.handleView(workDir, args.section);
1228
- case "init":
1229
- return await this.handleInit(workDir);
1230
- case "update":
1231
- return await this.handleUpdate(workDir, args.section, args.content);
1232
- case "append":
1233
- return await this.handleAppend(workDir, args.section, args.content);
1234
- case "sections":
1235
- return await this.handleSections(workDir);
1236
- case "raw":
1237
- return await this.handleRaw(workDir);
1238
- default:
1239
- return this.error(`Unknown action: ${action}. Use: view, init, update, append, sections, raw`);
1240
- }
1241
- } catch (error) {
1242
- return this.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1243
- }
1244
- }
1245
- async handleView(workDir, section) {
1246
- if (!projectInfoExists(workDir)) {
1247
- return this.formatResponse(`## Project Info Not Found
1248
-
1249
- No \`.trie/PROJECT.md\` file exists in this project.
1250
-
1251
- **To create one, use:**
1252
- \`\`\`
1253
- trie_project action="init"
1254
- \`\`\`
1255
-
1256
- Or run: \`trie project init\`
1257
-
1258
- This will create a template with sections for:
1259
- - Project Overview
1260
- - Technology Stack
1261
- - Architecture
1262
- - Coding Conventions
1263
- - Environment
1264
- - Team
1265
- - Compliance
1266
- - AI Instructions`);
1267
- }
1268
- if (section) {
1269
- const sectionContent = await getProjectSection(section, workDir);
1270
- if (sectionContent === null) {
1271
- const sections = await getProjectSections(workDir);
1272
- return this.formatResponse(`## Section Not Found: "${section}"
1273
-
1274
- Available sections:
1275
- ${sections.map((s) => `- ${s}`).join("\n")}`);
1276
- }
1277
- return this.formatResponse(`## ${section}
1278
-
1279
- ${sectionContent}`);
1280
- }
1281
- const info = await getProjectInfoStructured(workDir);
1282
- let output = `## Project Information
1283
-
1284
- **Path:** \`${info.path}\`
1285
- **Sections:** ${Object.keys(info.sections).length}
1286
-
1287
- ---
1288
-
1289
- `;
1290
- for (const [name, content] of Object.entries(info.sections)) {
1291
- output += `### ${name}
1292
-
1293
- ${content}
1294
-
1295
- ---
1296
-
1297
- `;
1298
- }
1299
- return this.formatResponse(output);
1300
- }
1301
- async handleInit(workDir) {
1302
- const result = await initProjectInfo(workDir);
1303
- if (result.created) {
1304
- return this.formatResponse(`## PROJECT.md Created
1305
-
1306
- **Path:** \`${result.path}\`
1307
-
1308
- A new PROJECT.md file has been created with a template including sections for:
1309
- - Project Overview
1310
- - Technology Stack
1311
- - Architecture
1312
- - Coding Conventions
1313
- - Environment
1314
- - Team
1315
- - Compliance
1316
- - AI Instructions
1317
-
1318
- **Next steps:**
1319
- 1. Open the file and fill in your project details
1320
- 2. The content will be available via \`trie://project\` resource
1321
- 3. AI assistants will use this context when working on your project
1322
-
1323
- **View the template:**
1324
- \`\`\`
1325
- trie_project action="view"
1326
- \`\`\``);
1327
- }
1328
- return this.formatResponse(`## PROJECT.md Already Exists
1329
-
1330
- **Path:** \`${result.path}\`
1331
-
1332
- Use \`trie_project action="view"\` to see the current content.`);
1333
- }
1334
- async handleUpdate(workDir, section, content) {
1335
- if (!section) {
1336
- return this.error("Missing required parameter: section");
1337
- }
1338
- if (!content) {
1339
- return this.error("Missing required parameter: content");
1340
- }
1341
- const success = await updateProjectSection(section, content, workDir);
1342
- if (success) {
1343
- return this.formatResponse(`## Section Updated: "${section}"
1344
-
1345
- The "${section}" section has been updated with your new content.
1346
-
1347
- **View updated section:**
1348
- \`\`\`
1349
- trie_project action="view" section="${section}"
1350
- \`\`\``);
1351
- }
1352
- const sections = await getProjectSections(workDir);
1353
- return this.error(`Could not update section "${section}".
1354
-
1355
- Available sections:
1356
- ${sections.map((s) => `- ${s}`).join("\n")}`);
1357
- }
1358
- async handleAppend(workDir, section, content) {
1359
- if (!section) {
1360
- return this.error("Missing required parameter: section");
1361
- }
1362
- if (!content) {
1363
- return this.error("Missing required parameter: content");
1364
- }
1365
- const success = await appendToSection(section, content, workDir);
1366
- if (success) {
1367
- return this.formatResponse(`## Content Appended to: "${section}"
1368
-
1369
- Your content has been added to the "${section}" section.
1370
-
1371
- **View updated section:**
1372
- \`\`\`
1373
- trie_project action="view" section="${section}"
1374
- \`\`\``);
1375
- }
1376
- return this.error(`Could not append to section "${section}". Make sure the section exists.`);
1377
- }
1378
- async handleSections(workDir) {
1379
- if (!projectInfoExists(workDir)) {
1380
- return this.formatResponse(`## No PROJECT.md Found
1381
-
1382
- Run \`trie_project action="init"\` to create one.`);
1383
- }
1384
- const sections = await getProjectSections(workDir);
1385
- return this.formatResponse(`## Available Sections
1386
-
1387
- ${sections.map((s, i) => `${i + 1}. **${s}**`).join("\n")}
1388
-
1389
- **View a section:**
1390
- \`\`\`
1391
- trie_project action="view" section="Section Name"
1392
- \`\`\`
1393
-
1394
- **Update a section:**
1395
- \`\`\`
1396
- trie_project action="update" section="Section Name" content="New content..."
1397
- \`\`\``);
1398
- }
1399
- async handleRaw(workDir) {
1400
- const content = await loadProjectInfo(workDir);
1401
- if (!content) {
1402
- return this.formatResponse(`No PROJECT.md found. Run \`trie_project action="init"\` to create one.`);
1403
- }
1404
- return this.formatResponse(content);
1405
- }
1406
- formatResponse(text) {
1407
- return {
1408
- content: [{ type: "text", text }]
1409
- };
1410
- }
1411
- error(message) {
1412
- return {
1413
- content: [{ type: "text", text: `**Error:** ${message}` }]
1414
- };
1415
- }
1416
- };
1417
-
1418
- // src/tools/init.ts
1419
- var TrieInitTool = class {
1420
- async execute(params) {
1421
- const action = params.action || "init";
1422
- const workDir = params.directory || getWorkingDirectory(void 0, true);
1423
- if (action === "status") {
1424
- const needs = needsBootstrap(workDir);
1425
- const context = await loadBootstrapContext(workDir);
1426
- const existingFiles = context.files.filter((f) => f.exists).map((f) => f.name);
1427
- const missingFiles = context.files.filter((f) => !f.exists).map((f) => f.name);
1428
- return [
1429
- "# Bootstrap Status",
1430
- "",
1431
- `**Bootstrap pending:** ${needs ? "Yes" : "No"}`,
1432
- "",
1433
- "## Files",
1434
- "",
1435
- "**Existing:**",
1436
- existingFiles.length > 0 ? existingFiles.map((f) => `- .trie/${f}`).join("\n") : "- None",
1437
- "",
1438
- "**Missing:**",
1439
- missingFiles.length > 0 ? missingFiles.map((f) => `- .trie/${f}`).join("\n") : "- None"
1440
- ].join("\n");
1441
- }
1442
- if (action === "complete") {
1443
- const result2 = await completeBootstrap(workDir);
1444
- if (result2) {
1445
- return "Bootstrap completed. BOOTSTRAP.md has been deleted.";
1446
- }
1447
- return "No BOOTSTRAP.md file found.";
1448
- }
1449
- if (action === "context") {
1450
- const context = await loadBootstrapContext(workDir);
1451
- if (!context.injectedContent) {
1452
- return 'No bootstrap context available. Run trie_init with action="init" first.';
1453
- }
1454
- return context.injectedContent;
1455
- }
1456
- const initOptions = {
1457
- workDir
1458
- };
1459
- if (params.force !== void 0) {
1460
- initOptions.force = params.force;
1461
- }
1462
- if (params.skipBootstrap !== void 0) {
1463
- initOptions.skipBootstrap = params.skipBootstrap;
1464
- }
1465
- const result = await initializeBootstrapFiles(initOptions);
1466
- const lines = [
1467
- "# Trie Workspace Initialized",
1468
- ""
1469
- ];
1470
- if (result.created.length > 0) {
1471
- lines.push("## Created Files", "");
1472
- for (const file of result.created) {
1473
- lines.push(`- .trie/${file}`);
1474
- }
1475
- lines.push("");
1476
- }
1477
- if (result.skipped.length > 0) {
1478
- lines.push("## Skipped (already exist)", "");
1479
- for (const file of result.skipped) {
1480
- lines.push(`- .trie/${file}`);
1481
- }
1482
- lines.push("");
1483
- }
1484
- lines.push("## Detected Stack", "");
1485
- if (result.stack.framework) lines.push(`- **Framework:** ${result.stack.framework}`);
1486
- if (result.stack.language) lines.push(`- **Language:** ${result.stack.language}`);
1487
- if (result.stack.database) lines.push(`- **Database:** ${result.stack.database}`);
1488
- if (result.stack.auth) lines.push(`- **Auth:** ${result.stack.auth}`);
1489
- if (result.stack.packageManager) lines.push(`- **Package Manager:** ${result.stack.packageManager}`);
1490
- lines.push("");
1491
- if (result.stack.suggestedSkills.length > 0) {
1492
- lines.push("## Suggested Skills", "");
1493
- for (const skill of result.stack.suggestedSkills) {
1494
- lines.push(`\`\`\`bash`);
1495
- lines.push(`trie skills add ${skill}`);
1496
- lines.push(`\`\`\``);
1497
- }
1498
- lines.push("");
1499
- }
1500
- lines.push(
1501
- "## Next Steps",
1502
- "",
1503
- "1. Edit `.trie/PROJECT.md` with your project description",
1504
- "2. Define coding standards in `.trie/RULES.md`",
1505
- "3. Run `trie watch` to analyze your codebase",
1506
- '4. Run `trie_init` with action="complete" when setup is done'
1507
- );
1508
- return lines.join("\n");
1509
- }
1510
- };
1511
-
1512
- // src/tools/memory-search.ts
1513
- var TrieMemorySearchTool = class {
1514
- async execute(params) {
1515
- const action = params.action || "search";
1516
- const workDir = params.directory || getWorkingDirectory(void 0, true);
1517
- let resultText;
1518
- switch (action) {
1519
- case "search":
1520
- resultText = await this.handleSearch(params, workDir);
1521
- break;
1522
- case "stats":
1523
- resultText = await this.handleStats(workDir);
1524
- break;
1525
- case "recent":
1526
- resultText = await this.handleRecent(params, workDir);
1527
- break;
1528
- case "similar":
1529
- resultText = await this.handleSimilar(params, workDir);
1530
- break;
1531
- case "resolve":
1532
- resultText = await this.handleResolve(params, workDir);
1533
- break;
1534
- case "global":
1535
- resultText = await this.handleGlobal(params);
1536
- break;
1537
- case "purge":
1538
- resultText = await this.handlePurge(params, workDir);
1539
- break;
1540
- default:
1541
- resultText = "Unknown action. Use: search, stats, recent, similar, resolve, global, or purge";
1542
- }
1543
- return {
1544
- content: [{
1545
- type: "text",
1546
- text: resultText
1547
- }]
1548
- };
1549
- }
1550
- async handleSearch(params, workDir) {
1551
- if (!params.query) {
1552
- return "Missing required parameter: query";
1553
- }
1554
- const searchOptions = {
1555
- workDir,
1556
- limit: params.limit || 10
1557
- };
1558
- if (params.severity !== void 0) {
1559
- searchOptions.severity = params.severity;
1560
- }
1561
- if (params.agent !== void 0) {
1562
- searchOptions.agent = params.agent;
1563
- }
1564
- if (params.includeResolved !== void 0) {
1565
- searchOptions.includeResolved = params.includeResolved;
1566
- }
1567
- const results = await searchIssues(params.query, searchOptions);
1568
- if (results.length === 0) {
1569
- return `No issues found matching "${params.query}"`;
1570
- }
1571
- const lines = [
1572
- `# Search Results: "${params.query}"`,
1573
- "",
1574
- `Found ${results.length} matching issue(s):`,
1575
- ""
1576
- ];
1577
- for (const result of results) {
1578
- const i = result.issue;
1579
- const status = i.resolved ? " [RESOLVED]" : "";
1580
- lines.push(
1581
- `## [${i.severity.toUpperCase()}]${status} ${i.issue.slice(0, 80)}`,
1582
- "",
1583
- `- **File:** \`${i.file}\`${i.line ? `:${i.line}` : ""}`,
1584
- `- **Agent:** ${i.agent}`,
1585
- `- **Score:** ${(result.score * 100).toFixed(0)}%`,
1586
- `- **Date:** ${new Date(i.timestamp).toLocaleDateString()}`,
1587
- `- **Fix:** ${i.fix.slice(0, 150)}${i.fix.length > 150 ? "..." : ""}`,
1588
- ""
1589
- );
1590
- }
1591
- return lines.join("\n");
1592
- }
1593
- async handleStats(workDir) {
1594
- const stats = await getMemoryStats(workDir);
1595
- const globalStats = await getGlobalMemoryStats();
1596
- const lines = [
1597
- "# Memory Statistics",
1598
- "",
1599
- "## Local Issue Memory",
1600
- "",
1601
- `- **Active Issues:** ${stats.activeIssues}`,
1602
- `- **Resolved:** ${stats.resolvedCount}`,
1603
- `- **Total (all-time):** ${stats.totalIssues}`
1604
- ];
1605
- const cap = stats.capacityInfo;
1606
- if (cap.isAtCap) {
1607
- lines.push(
1608
- "",
1609
- "### \u26A0\uFE0F CAPACITY WARNING",
1610
- "",
1611
- `**Memory is at maximum capacity (${cap.current}/${cap.max} issues, ${cap.percentFull}%)**`,
1612
- "",
1613
- "Trie caps issue storage at 10,000 for performance. New repeats are deduplicated.",
1614
- "Older issues are compacted into summaries, and if it still exceeds the cap the oldest/lowest-value issues are pruned.",
1615
- "",
1616
- "**Options to free up space:**",
1617
- "- `trie memory purge smart` - Remove resolved and old low-priority issues (recommended)",
1618
- "- `trie memory purge resolved` - Remove all resolved issues",
1619
- "- `trie memory purge old` - Remove issues older than 90 days",
1620
- "- `trie memory purge all` - Clear all issues (keeps summaries)"
1621
- );
1622
- } else if (cap.percentFull >= 80) {
1623
- lines.push(
1624
- "",
1625
- `### \u2139\uFE0F Memory Usage: ${cap.percentFull}% (${cap.current}/${cap.max})`,
1626
- "",
1627
- `You're approaching the storage cap. Consider running \`trie memory purge smart\` to free up space.`
1628
- );
1629
- } else {
1630
- lines.push(`- **Memory Usage:** ${cap.percentFull}% (${cap.current}/${cap.max})`);
1631
- }
1632
- if (stats.deduplicationStats.duplicatesAvoided > 0) {
1633
- lines.push(
1634
- "",
1635
- "### Deduplication",
1636
- "",
1637
- `- **Unique Patterns:** ${stats.deduplicationStats.uniquePatterns}`,
1638
- `- **Duplicates Avoided:** ${stats.deduplicationStats.duplicatesAvoided}`
1639
- );
1640
- }
1641
- if (stats.oldestIssue) {
1642
- lines.push("", `- **Date Range:** ${stats.oldestIssue.split("T")[0]} to ${stats.newestIssue?.split("T")[0]}`);
1643
- }
1644
- lines.push("", "### By Severity", "");
1645
- for (const [severity, count] of Object.entries(stats.issuesBySeverity)) {
1646
- lines.push(`- ${severity}: ${count}`);
1647
- }
1648
- lines.push("", "### By Agent", "");
1649
- const sortedAgents = Object.entries(stats.issuesByAgent).sort((a, b) => b[1] - a[1]);
1650
- for (const [agent, count] of sortedAgents.slice(0, 10)) {
1651
- lines.push(`- ${agent}: ${count}`);
1652
- }
1653
- lines.push(
1654
- "",
1655
- "## Global Cross-Project Memory",
1656
- "",
1657
- `- **Tracked Projects:** ${globalStats.trackedProjects}`,
1658
- `- **Total Patterns:** ${globalStats.totalPatterns}`,
1659
- `- **Cross-Project Patterns:** ${globalStats.crossProjectPatterns}`,
1660
- `- **Fixed Patterns:** ${globalStats.fixedPatterns}`
1661
- );
1662
- return lines.join("\n");
1663
- }
1664
- async handleRecent(params, workDir) {
1665
- const issues = await getRecentIssues({
1666
- workDir,
1667
- limit: params.limit || 10
1668
- });
1669
- if (issues.length === 0) {
1670
- return "No recent issues found.";
1671
- }
1672
- const lines = [
1673
- "# Recent Issues (Last 7 Days)",
1674
- ""
1675
- ];
1676
- for (const i of issues) {
1677
- const status = i.resolved ? " [RESOLVED]" : "";
1678
- lines.push(
1679
- `## [${i.severity.toUpperCase()}]${status} ${i.issue.slice(0, 70)}`,
1680
- "",
1681
- `- \`${i.file}\`${i.line ? `:${i.line}` : ""}`,
1682
- `- Agent: ${i.agent} | ${new Date(i.timestamp).toLocaleDateString()}`,
1683
- ""
1684
- );
1685
- }
1686
- return lines.join("\n");
1687
- }
1688
- async handleSimilar(params, workDir) {
1689
- if (!params.query) {
1690
- return "Missing required parameter: query (issue description to find similar issues for)";
1691
- }
1692
- const mockIssue = {
1693
- id: "search",
1694
- severity: "moderate",
1695
- issue: params.query,
1696
- fix: "",
1697
- file: "",
1698
- agent: "search",
1699
- confidence: 1,
1700
- autoFixable: false
1701
- };
1702
- const results = await findSimilarIssues(mockIssue, {
1703
- workDir,
1704
- limit: params.limit || 5
1705
- });
1706
- if (results.length === 0) {
1707
- return "No similar issues found.";
1708
- }
1709
- const lines = [
1710
- `# Similar Issues`,
1711
- "",
1712
- `Found ${results.length} similar issue(s):`,
1713
- ""
1714
- ];
1715
- for (const result of results) {
1716
- const i = result.issue;
1717
- lines.push(
1718
- `## [${i.severity.toUpperCase()}] ${i.issue.slice(0, 70)}`,
1719
- "",
1720
- `- **File:** \`${i.file}\``,
1721
- `- **Similarity:** ${(result.score * 100).toFixed(0)}%`,
1722
- ""
1723
- );
1724
- }
1725
- return lines.join("\n");
1726
- }
1727
- async handleResolve(params, workDir) {
1728
- if (!params.issueId) {
1729
- return "Missing required parameter: issueId";
1730
- }
1731
- const result = await markIssueResolved(params.issueId, workDir);
1732
- if (result) {
1733
- return `Issue ${params.issueId} marked as resolved.`;
1734
- }
1735
- return `Issue ${params.issueId} not found.`;
1736
- }
1737
- async handleGlobal(params) {
1738
- if (params.query) {
1739
- const patterns2 = await searchGlobalPatterns(params.query, {
1740
- limit: params.limit || 10
1741
- });
1742
- if (patterns2.length === 0) {
1743
- return `No global patterns found matching "${params.query}"`;
1744
- }
1745
- const lines2 = [
1746
- `# Global Pattern Search: "${params.query}"`,
1747
- ""
1748
- ];
1749
- for (const p of patterns2) {
1750
- lines2.push(
1751
- `## [${p.severity.toUpperCase()}] ${p.pattern.slice(0, 60)}`,
1752
- "",
1753
- `- **Occurrences:** ${p.occurrences} across ${p.projects.length} projects`,
1754
- `- **Agent:** ${p.agent}`,
1755
- `- **Projects:** ${p.projects.slice(0, 3).join(", ")}`,
1756
- p.fixApplied ? `- **Fixed in:** ${p.fixApplied.project}` : "",
1757
- ""
1758
- );
1759
- }
1760
- return lines2.join("\n");
1761
- }
1762
- const patterns = await findCrossProjectPatterns(2);
1763
- const projects = await listTrackedProjects();
1764
- const lines = [
1765
- "# Global Cross-Project Memory",
1766
- "",
1767
- `**Tracked Projects:** ${projects.length}`,
1768
- `**Cross-Project Patterns:** ${patterns.length}`,
1769
- ""
1770
- ];
1771
- if (patterns.length > 0) {
1772
- lines.push("## Top Cross-Project Patterns", "");
1773
- for (const p of patterns.slice(0, 5)) {
1774
- lines.push(
1775
- `### [${p.severity.toUpperCase()}] ${p.pattern.slice(0, 50)}`,
1776
- "",
1777
- `- Seen ${p.occurrences}x across ${p.projects.length} projects`,
1778
- p.fixApplied ? `- Fixed in ${p.fixApplied.project}` : "- Not yet fixed",
1779
- ""
1780
- );
1781
- }
1782
- }
1783
- if (projects.length > 0) {
1784
- lines.push("## Recent Projects", "", "| Project | Issues |", "|---------|--------|");
1785
- for (const p of projects.slice(0, 5)) {
1786
- lines.push(`| ${p.name} | ${p.totalIssues} |`);
1787
- }
1788
- }
1789
- return lines.join("\n");
1790
- }
1791
- async handlePurge(params, workDir) {
1792
- const strategy = params.purgeStrategy || "smart";
1793
- const options = { workDir };
1794
- if (params.daysOld !== void 0) {
1795
- options.daysOld = params.daysOld;
1796
- }
1797
- const result = await purgeIssues(strategy, options);
1798
- const lines = [
1799
- "# Memory Purge Complete",
1800
- "",
1801
- `**Strategy:** ${strategy}`,
1802
- `**Removed:** ${result.removed} issues`,
1803
- `**Remaining:** ${result.remaining} issues`,
1804
- ""
1805
- ];
1806
- switch (strategy) {
1807
- case "smart":
1808
- lines.push(
1809
- "**What was removed:**",
1810
- "- Resolved issues older than 30 days",
1811
- "- Low-priority issues (info/low severity) older than 30 days",
1812
- "",
1813
- "**What was kept:**",
1814
- "- All critical and high severity issues",
1815
- "- All unresolved issues",
1816
- "- All issues from the last 30 days"
1817
- );
1818
- break;
1819
- case "resolved":
1820
- lines.push("**Removed all resolved issues.**", "**Kept all unresolved issues.**");
1821
- break;
1822
- case "old":
1823
- lines.push(
1824
- `**Removed all issues older than ${params.daysOld || 90} days.**`,
1825
- `**Kept all recent issues.**`
1826
- );
1827
- break;
1828
- case "all":
1829
- lines.push(
1830
- "**Cleared all issues from active memory.**",
1831
- "",
1832
- "Historical summaries are preserved in compacted-summaries.json."
1833
- );
1834
- break;
1835
- }
1836
- lines.push("", "**Note:** Compacted historical summaries were preserved for trend analysis.");
1837
- return lines.join("\n");
1838
- }
1839
- };
1840
-
1841
- // src/tools/reconcile.ts
1842
- import path from "path";
1843
- async function removeOrphanEdges(graph) {
1844
- const nodes = await graph.listNodes();
1845
- const ids = new Set(nodes.map((n) => n.id));
1846
- const edges = await graph.listEdges();
1847
- let removed = 0;
1848
- for (const edge of edges) {
1849
- if (!ids.has(edge.from_id) || !ids.has(edge.to_id)) {
1850
- await graph.deleteEdge(edge.id);
1851
- removed++;
1852
- }
1853
- }
1854
- return removed;
1855
- }
1856
- var TrieReconcileTool = class {
1857
- async execute(input = {}) {
1858
- try {
1859
- const projectPath = input.directory || getWorkingDirectory(void 0, true);
1860
- const sourcePath = input.source ?? path.join(getTrieDirectory(projectPath), "context.json");
1861
- const graph = new ContextGraph(projectPath);
1862
- await importFromJson(graph, "", sourcePath);
1863
- const removed = await removeOrphanEdges(graph);
1864
- return {
1865
- content: [{
1866
- type: "text",
1867
- text: `Reconciled context from ${sourcePath}. Removed ${removed} orphaned edges.`
1868
- }]
1869
- };
1870
- } catch (error) {
1871
- const friendly = formatFriendlyError(error);
1872
- return { content: [{ type: "text", text: friendly.userMessage }] };
1873
- }
1874
- }
1875
- };
1876
-
1877
- // src/tools/context.ts
1878
- var TrieContextTool = class {
1879
- async execute(input = {}) {
1880
- try {
1881
- const workDir = input.directory || getWorkingDirectory(void 0, true);
1882
- const graph = new ContextGraph(workDir);
1883
- const snapshot = await graph.getSnapshot();
1884
- await exportToJson(graph);
1885
- const summary = `Nodes: ${snapshot.nodes.length}, Edges: ${snapshot.edges.length}, Exported: ${snapshot.exported_at}`;
1886
- return {
1887
- content: [{
1888
- type: "text",
1889
- text: summary
1890
- }],
1891
- data: snapshot
1892
- };
1893
- } catch (error) {
1894
- const friendly = formatFriendlyError(error);
1895
- return { content: [{ type: "text", text: friendly.userMessage }] };
1896
- }
1897
- }
1898
- };
1899
-
1900
- // src/tools/linear-sync.ts
1901
- var LinearSyncTool = class {
1902
- async execute(input) {
1903
- try {
1904
- const workDir = input.directory || getWorkingDirectory(void 0, true);
1905
- const graph = new ContextGraph(workDir);
1906
- const ingester = new LinearIngester(workDir, graph);
1907
- await ingester.syncTickets();
1908
- return {
1909
- content: [{
1910
- type: "text",
1911
- text: "\u2705 Linear tickets synced successfully. Trie now has context on your active tasks."
1912
- }]
1913
- };
1914
- } catch (error) {
1915
- return {
1916
- isError: true,
1917
- content: [{
1918
- type: "text",
1919
- text: `Failed to sync Linear tickets: ${error.message}`
1920
- }]
1921
- };
1922
- }
1923
- }
1924
- };
1925
-
1926
- // src/tools/github-sync.ts
1927
- var GitHubSyncTool = class {
1928
- async execute(input) {
1929
- try {
1930
- const workDir = input.directory || getWorkingDirectory(void 0, true);
1931
- const graph = new ContextGraph(workDir);
1932
- const ingester = new GitHubIngester(graph);
1933
- const token = await ingester.getApiToken();
1934
- if (!token) {
1935
- return {
1936
- isError: true,
1937
- content: [{
1938
- type: "text",
1939
- text: 'GitHub token not configured.\n\nSet one of:\n \u2022 Environment variable: GITHUB_TOKEN=ghp_...\n \u2022 Config dialog (C key) \u2192 API Keys \u2192 GitHub\n \u2022 .trie/config.json: { "apiKeys": { "github": "ghp_..." } }\n\nToken needs `repo` scope for private repos, `public_repo` for public.'
1940
- }]
1941
- };
1942
- }
1943
- const repoInfo = ingester.getRepoInfo(workDir);
1944
- if (!repoInfo) {
1945
- return {
1946
- isError: true,
1947
- content: [{
1948
- type: "text",
1949
- text: "Could not detect GitHub repository.\n\nMake sure this directory is a git repo with a GitHub remote:\n git remote get-url origin"
1950
- }]
1951
- };
1952
- }
1953
- const prResult = await ingester.syncPullRequests(repoInfo.owner, repoInfo.name, token);
1954
- const issueResult = await ingester.syncIssues(repoInfo.owner, repoInfo.name, token);
1955
- const LINE = "\u2500".repeat(45);
1956
- const lines = [
1957
- "GITHUB SYNC",
1958
- LINE,
1959
- `Repo: ${repoInfo.owner}/${repoInfo.name}`,
1960
- `Pulled ${prResult.prs} open PR${prResult.prs !== 1 ? "s" : ""}, ${issueResult.issues} open issue${issueResult.issues !== 1 ? "s" : ""}`,
1961
- `Linked ${prResult.linkedTickets} PR${prResult.linkedTickets !== 1 ? "s" : ""} to Linear tickets`,
1962
- `Linked ${prResult.linkedFiles} PR${prResult.linkedFiles !== 1 ? "s" : ""} to changed files in context graph`,
1963
- LINE,
1964
- "Next sync: trie_github_sync",
1965
- "Or use trie_pipeline for consolidated view."
1966
- ];
1967
- return {
1968
- content: [{
1969
- type: "text",
1970
- text: lines.join("\n")
1971
- }]
1972
- };
1973
- } catch (error) {
1974
- return {
1975
- isError: true,
1976
- content: [{
1977
- type: "text",
1978
- text: `GitHub sync failed: ${error.message}`
1979
- }]
1980
- };
1981
- }
1982
- }
1983
- };
1984
-
1985
- // src/tools/index-codebase.ts
1986
- import { glob } from "glob";
1987
- var INDEXABLE_EXTENSIONS = [
1988
- "ts",
1989
- "tsx",
1990
- "js",
1991
- "jsx",
1992
- "mjs",
1993
- "vue",
1994
- "svelte",
1995
- "astro",
1996
- "py",
1997
- "go",
1998
- "rs",
1999
- "java",
2000
- "c",
2001
- "cpp",
2002
- "h",
2003
- "hpp",
2004
- "cs",
2005
- "rb",
2006
- "php",
2007
- "css",
2008
- "scss",
2009
- "html"
2010
- ];
2011
- var TrieIndexTool = class {
2012
- async execute(params) {
2013
- const action = params.action || "full";
2014
- const workDir = getWorkingDirectory(params.directory, true);
2015
- if (!isTrieInitialized(workDir)) {
2016
- return "Trie is not initialized for this project. Run `trie init` first.";
2017
- }
2018
- const codebaseIndex = new CodebaseIndex(workDir);
2019
- if (action === "status") {
2020
- const stats2 = codebaseIndex.getStats();
2021
- let lastUpdatedStr = "Never";
2022
- if (stats2.lastUpdated) {
2023
- try {
2024
- const date = new Date(stats2.lastUpdated);
2025
- lastUpdatedStr = date.toLocaleString("en-US", {
2026
- year: "numeric",
2027
- month: "short",
2028
- day: "numeric",
2029
- hour: "numeric",
2030
- minute: "2-digit"
2031
- });
2032
- } catch {
2033
- lastUpdatedStr = "Unknown";
2034
- }
2035
- }
2036
- return [
2037
- "# Codebase Index Status",
2038
- "",
2039
- `**Total files indexed:** ${stats2.totalFiles}`,
2040
- `**Total size:** ${(stats2.totalSize / 1024 / 1024).toFixed(2)} MB`,
2041
- `**Files scanned for goals:** ${stats2.scannedFiles}`,
2042
- `**Files with violations:** ${stats2.filesWithViolations}`,
2043
- `**Last updated:** ${lastUpdatedStr}`,
2044
- "",
2045
- "## Files by Type",
2046
- "",
2047
- ...Object.entries(stats2.filesByType).sort(([, a], [, b]) => b - a).slice(0, 10).map(([type, count]) => `- .${type}: ${count} files`),
2048
- "",
2049
- "---",
2050
- "",
2051
- 'Run `trie_index` with action="full" to re-index the codebase.',
2052
- 'Run `trie_index` with action="clear" to clear stale cache entries.'
2053
- ].join("\n");
2054
- }
2055
- if (action === "clear") {
2056
- const cleared = codebaseIndex.clearStaleCache();
2057
- await codebaseIndex.save();
2058
- return `Cleared ${cleared} stale cache entries.`;
2059
- }
2060
- const pattern = `${workDir}/**/*.{${INDEXABLE_EXTENSIONS.join(",")}}`;
2061
- const allFiles = await glob(pattern, {
2062
- ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**", "**/.trie/**", "**/coverage/**"],
2063
- nodir: true
2064
- });
2065
- let indexed = 0;
2066
- let failed = 0;
2067
- const startTime = Date.now();
2068
- const BATCH_SIZE = 50;
2069
- for (let i = 0; i < allFiles.length; i += BATCH_SIZE) {
2070
- const batch = allFiles.slice(i, i + BATCH_SIZE);
2071
- await Promise.all(batch.map(async (filePath) => {
2072
- try {
2073
- let relativePath = filePath;
2074
- if (filePath.toLowerCase().startsWith(workDir.toLowerCase() + "/")) {
2075
- relativePath = filePath.slice(workDir.length + 1);
2076
- }
2077
- const result = await codebaseIndex.indexFile(relativePath);
2078
- if (result) {
2079
- indexed++;
2080
- } else {
2081
- failed++;
2082
- }
2083
- } catch {
2084
- failed++;
2085
- }
2086
- }));
2087
- }
2088
- await codebaseIndex.save();
2089
- const duration = ((Date.now() - startTime) / 1e3).toFixed(1);
2090
- const stats = codebaseIndex.getStats();
2091
- return [
2092
- "# Codebase Indexing Complete",
2093
- "",
2094
- `**Files indexed:** ${indexed}`,
2095
- `**Files failed:** ${failed}`,
2096
- `**Duration:** ${duration}s`,
2097
- "",
2098
- "## Index Statistics",
2099
- "",
2100
- `- Total files: ${stats.totalFiles}`,
2101
- `- Total size: ${(stats.totalSize / 1024 / 1024).toFixed(2)} MB`,
2102
- "",
2103
- "## Files by Type",
2104
- "",
2105
- ...Object.entries(stats.filesByType).sort(([, a], [, b]) => b - a).slice(0, 10).map(([type, count]) => `- .${type}: ${count} files`),
2106
- "",
2107
- "---",
2108
- "",
2109
- "The index will be automatically updated when files change during watch mode.",
2110
- "Goal checks will now use cached results for unchanged files."
2111
- ].join("\n");
2112
- }
2113
- };
2114
-
2115
- // src/server/tool-registry.ts
2116
- var TrieCheckpointTool = class {
2117
- async execute(input) {
2118
- const result = await handleCheckpointTool(input);
2119
- return {
2120
- content: [{
2121
- type: "text",
2122
- text: result
2123
- }]
2124
- };
2125
- }
2126
- };
2127
- var ToolRegistry = class {
2128
- tools = /* @__PURE__ */ new Map();
2129
- definitions = [];
2130
- constructor() {
2131
- this.initializeTools();
2132
- this.defineToolSchemas();
2133
- }
2134
- initializeTools() {
2135
- this.tools.set("fix", new TrieFixTool());
2136
- this.tools.set("cloud_fix", new TrieCloudFixTool());
2137
- this.tools.set("explain", new TrieExplainTool());
2138
- this.tools.set("test", new TrieTestTool());
2139
- this.tools.set("watch", new TrieWatchTool());
2140
- this.tools.set("pr_review", new TriePRReviewTool());
2141
- this.tools.set("project", new TrieProjectInfoTool());
2142
- this.tools.set("init", new TrieInitTool());
2143
- this.tools.set("memory", new TrieMemorySearchTool());
2144
- this.tools.set("checkpoint", new TrieCheckpointTool());
2145
- this.tools.set("check", new TrieCheckTool());
2146
- this.tools.set("tell", new TrieTellTool());
2147
- this.tools.set("reconcile", new TrieReconcileTool());
2148
- this.tools.set("context", new TrieContextTool());
2149
- this.tools.set("feedback", new TrieFeedbackTool());
2150
- this.tools.set("ok", new TrieFeedbackTool());
2151
- this.tools.set("bad", new TrieFeedbackTool());
2152
- this.tools.set("linear_sync", new LinearSyncTool());
2153
- this.tools.set("github_sync", new GitHubSyncTool());
2154
- this.tools.set("github_branches", new GitHubBranchesTool());
2155
- this.tools.set("pipeline", new TriePipelineTool());
2156
- this.tools.set("index", new TrieIndexTool());
2157
- this.tools.set("get_governance", new TrieGetGovernanceTool());
2158
- this.tools.set("get_decisions", new TrieGetDecisionsTool());
2159
- this.tools.set("get_blockers", new TrieGetBlockersTool());
2160
- this.tools.set("get_related_governance", new TrieGetRelatedGovernanceTool());
2161
- this.tools.set("get_related_decisions", new TrieGetRelatedDecisionsTool());
2162
- this.tools.set("query_context", new TrieQueryContextTool());
2163
- }
2164
- defineToolSchemas() {
2165
- this.definitions = [
2166
- {
2167
- name: "trie",
2168
- description: "Quick menu of available Trie commands. TIP: Read trie://context first for project state and priorities. Call with `action` to run directly.",
2169
- inputSchema: {
2170
- type: "object",
2171
- properties: {
2172
- action: {
2173
- type: "string",
2174
- description: "Optional quick action: pr_review, watch, fix, explain, test, visual_qa_browser"
2175
- },
2176
- files: {
2177
- type: "array",
2178
- items: { type: "string" },
2179
- description: "Files to scan (absolute or relative)"
2180
- },
2181
- directory: {
2182
- type: "string",
2183
- description: "Directory to scan (defaults to detected workspace)"
2184
- }
2185
- }
2186
- }
2187
- },
2188
- {
2189
- name: "trie_fix",
2190
- description: "Apply high-confidence fixes to code. Use action:route to see triage plan first. Alias: fix",
2191
- inputSchema: {
2192
- type: "object",
2193
- properties: {
2194
- action: {
2195
- type: "string",
2196
- enum: ["route"],
2197
- description: "route: show fix routing plan without applying fixes"
2198
- },
2199
- issueIds: {
2200
- type: "array",
2201
- items: { type: "string" },
2202
- description: "Specific issues to fix (empty = all high-confidence)"
2203
- },
2204
- autoApprove: {
2205
- type: "boolean",
2206
- description: "Skip human review for high-confidence fixes"
2207
- }
2208
- }
2209
- }
2210
- },
2211
- {
2212
- name: "trie_cloud_fix",
2213
- description: "Dispatch issues to Cursor cloud agents for verified, test-passing fixes. The cloud agent runs in an isolated VM, applies the fix, runs tests, screenshots the result, and opens a PR. CRITICAL: Ad-hoc mode (file+issue+fix) dispatches ONLY that single issue. Use trie_fix action:route first to see which issues qualify.",
2214
- inputSchema: {
2215
- type: "object",
2216
- properties: {
2217
- action: {
2218
- type: "string",
2219
- enum: ["configure", "dispatch", "status", "artifacts", "cancel"],
2220
- description: "configure: save API key | dispatch: send to cloud | status: poll jobs | artifacts: get screenshots + PR links | cancel: abort a job"
2221
- },
2222
- issueIds: {
2223
- type: "array",
2224
- items: { type: "string" },
2225
- description: "Issue IDs to dispatch (from trie_fix/watch). If omitted, dispatches all cloud-eligible pending issues. IGNORED if file+issue+fix are provided (ad-hoc mode)."
2226
- },
2227
- forceCloud: {
2228
- type: "boolean",
2229
- description: "When true, bypass triage and dispatch ALL resolved issues to cloud agents. Use when user explicitly requests cloud fix. In ad-hoc mode, this is implied."
2230
- },
2231
- file: {
2232
- type: "string",
2233
- description: "For ad-hoc single-incident dispatch: file path. When provided with issue+fix, ONLY this single issue is dispatched (memory issues are ignored)."
2234
- },
2235
- issue: {
2236
- type: "string",
2237
- description: "For ad-hoc dispatch: issue description (use with file and fix)"
2238
- },
2239
- fix: {
2240
- type: "string",
2241
- description: "For ad-hoc dispatch: suggested fix (use with file and issue)"
2242
- },
2243
- line: { type: "number", description: "For ad-hoc dispatch: line number" },
2244
- severity: { type: "string", description: "For ad-hoc dispatch: severity (critical, serious, moderate, low)" },
2245
- effort: { type: "string", description: "For ad-hoc dispatch: effort (trivial, easy, medium, hard)" },
2246
- apiKey: {
2247
- type: "string",
2248
- description: "Cursor API key (configure action only)"
2249
- },
2250
- jobId: {
2251
- type: "string",
2252
- description: "Job ID for cancel or artifacts actions"
2253
- }
2254
- },
2255
- required: ["action"]
2256
- }
2257
- },
2258
- {
2259
- name: "trie_explain",
2260
- description: "Explain code, issues, or changes in plain language. Alias: explain",
2261
- inputSchema: {
2262
- type: "object",
2263
- properties: {
2264
- type: {
2265
- type: "string",
2266
- enum: ["code", "issue", "change", "risk"]
2267
- },
2268
- target: {
2269
- type: "string",
2270
- description: "What to explain (file path, issue ID, etc.)"
2271
- }
2272
- },
2273
- required: ["type", "target"]
2274
- }
2275
- },
2276
- {
2277
- name: "trie_feedback",
2278
- description: "Record quick feedback about a warning or suggestion (thumbs up/down). Alias: trie_ok, trie_bad",
2279
- inputSchema: {
2280
- type: "object",
2281
- properties: {
2282
- helpful: { type: "boolean", description: "true for \u{1F44D}, false for \u{1F44E}" },
2283
- target: { type: "string", description: "Optional file or item being rated" },
2284
- note: { type: "string", description: "Optional short note about why" },
2285
- files: {
2286
- type: "array",
2287
- items: { type: "string" },
2288
- description: "Optional related files (absolute or relative)"
2289
- },
2290
- directory: { type: "string", description: "Working directory (defaults to auto-detected)" }
2291
- },
2292
- required: ["helpful"]
2293
- }
2294
- },
2295
- {
2296
- name: "trie_check",
2297
- description: "Run Trie risk check on current changes. Modes: quick, full, offline.",
2298
- inputSchema: {
2299
- type: "object",
2300
- properties: {
2301
- directory: { type: "string" },
2302
- files: { type: "array", items: { type: "string" } },
2303
- mode: { type: "string", enum: ["quick", "full", "offline"] }
2304
- }
2305
- }
2306
- },
2307
- {
2308
- name: "trie_tell",
2309
- description: "Report an incident so Trie can learn.",
2310
- inputSchema: {
2311
- type: "object",
2312
- properties: {
2313
- description: { type: "string" },
2314
- directory: { type: "string" }
2315
- },
2316
- required: ["description"]
2317
- }
2318
- },
2319
- {
2320
- name: "trie_reconcile",
2321
- description: "Re-sync context graph from context.json and clean orphaned edges.",
2322
- inputSchema: {
2323
- type: "object",
2324
- properties: {
2325
- directory: { type: "string" },
2326
- source: { type: "string" }
2327
- }
2328
- }
2329
- },
2330
- {
2331
- name: "trie_context",
2332
- description: "Return current context snapshot (nodes/edges) and export context.json.",
2333
- inputSchema: {
2334
- type: "object",
2335
- properties: {
2336
- directory: { type: "string" }
2337
- }
2338
- }
2339
- },
2340
- {
2341
- name: "trie_test",
2342
- description: "Generate or reason about tests. Alias: test",
2343
- inputSchema: {
2344
- type: "object",
2345
- properties: {
2346
- action: {
2347
- type: "string",
2348
- enum: ["generate", "coverage", "suggest", "run"],
2349
- description: "Test action to perform"
2350
- },
2351
- files: {
2352
- type: "array",
2353
- items: { type: "string" },
2354
- description: "Target source files"
2355
- },
2356
- framework: {
2357
- type: "string",
2358
- description: "Optional framework hint (jest, vitest, playwright, etc.)"
2359
- },
2360
- style: {
2361
- type: "string",
2362
- description: "Test style (unit, integration, e2e). Defaults to unit."
2363
- }
2364
- },
2365
- required: ["action", "files"]
2366
- }
2367
- },
2368
- {
2369
- name: "trie_watch",
2370
- description: "Autonomous watch mode. Alias: watch",
2371
- inputSchema: {
2372
- type: "object",
2373
- properties: {
2374
- action: {
2375
- type: "string",
2376
- enum: ["start", "stop", "status", "issues"],
2377
- description: "Watch action"
2378
- },
2379
- directory: {
2380
- type: "string",
2381
- description: "Workspace directory to watch (optional, auto-detected)"
2382
- },
2383
- debounceMs: {
2384
- type: "number",
2385
- description: "Debounce time for scans (ms)"
2386
- }
2387
- },
2388
- required: ["action"]
2389
- }
2390
- },
2391
- {
2392
- name: "trie_project",
2393
- description: "View and manage project information (.trie/PROJECT.md). Store project context for AI assistants. Alias: project",
2394
- inputSchema: {
2395
- type: "object",
2396
- properties: {
2397
- action: {
2398
- type: "string",
2399
- enum: ["view", "init", "update", "append", "sections", "raw"],
2400
- description: "Action: view (default), init (create template), update (replace section), append (add to section), sections (list), raw (full file)"
2401
- },
2402
- section: {
2403
- type: "string",
2404
- description: 'Section name (e.g., "Project Overview", "Technology Stack", "AI Instructions")'
2405
- },
2406
- content: {
2407
- type: "string",
2408
- description: "Content for update/append actions"
2409
- },
2410
- directory: {
2411
- type: "string",
2412
- description: "Project directory (defaults to current workspace)"
2413
- }
2414
- }
2415
- }
2416
- },
2417
- {
2418
- name: "trie_init",
2419
- description: "Initialize bootstrap files (.trie/RULES.md, .trie/TEAM.md, .trie/BOOTSTRAP.md). Detects stack.",
2420
- inputSchema: {
2421
- type: "object",
2422
- properties: {
2423
- action: {
2424
- type: "string",
2425
- enum: ["init", "status", "complete", "context"],
2426
- description: "Action: init (create files), status (check state), complete (finish bootstrap), context (get injected content)"
2427
- },
2428
- directory: {
2429
- type: "string",
2430
- description: "Project directory (defaults to current workspace)"
2431
- },
2432
- force: {
2433
- type: "boolean",
2434
- description: "Overwrite existing files"
2435
- },
2436
- skipBootstrap: {
2437
- type: "boolean",
2438
- description: "Skip creating BOOTSTRAP.md"
2439
- }
2440
- }
2441
- }
2442
- },
2443
- {
2444
- name: "trie_memory",
2445
- description: "Search and manage issue memory. Find similar issues, view stats, search across projects, and purge old issues.",
2446
- inputSchema: {
2447
- type: "object",
2448
- properties: {
2449
- action: {
2450
- type: "string",
2451
- enum: ["search", "stats", "recent", "similar", "resolve", "global", "purge"],
2452
- description: "Action: search (find issues), stats (show statistics), recent (recent issues), similar (find similar), resolve (mark resolved), global (cross-project), purge (free up memory)"
2453
- },
2454
- query: {
2455
- type: "string",
2456
- description: "Search query for search/similar/global actions"
2457
- },
2458
- issueId: {
2459
- type: "string",
2460
- description: "Issue ID for resolve action"
2461
- },
2462
- limit: {
2463
- type: "number",
2464
- description: "Maximum results to return"
2465
- },
2466
- severity: {
2467
- type: "array",
2468
- items: { type: "string" },
2469
- description: "Filter by severity levels"
2470
- },
2471
- agent: {
2472
- type: "string",
2473
- description: "Filter by agent name"
2474
- },
2475
- includeResolved: {
2476
- type: "boolean",
2477
- description: "Include resolved issues in search"
2478
- },
2479
- directory: {
2480
- type: "string",
2481
- description: "Project directory (defaults to current workspace)"
2482
- },
2483
- purgeStrategy: {
2484
- type: "string",
2485
- enum: ["smart", "resolved", "old", "all"],
2486
- description: "Purge strategy: smart (remove resolved & old low-priority), resolved (all resolved), old (older than daysOld), all (clear all)"
2487
- },
2488
- daysOld: {
2489
- type: "number",
2490
- description: "Days threshold for old purge strategy (default 90)"
2491
- }
2492
- }
2493
- },
2494
- // MCP Apps: Interactive memory viewer
2495
- _meta: {
2496
- ui: { resourceUri: "ui://trie/memory-viewer" }
2497
- }
2498
- },
2499
- {
2500
- name: "trie_checkpoint",
2501
- description: "Save a context checkpoint without running a full scan. Quick save for your current work state.",
2502
- inputSchema: {
2503
- type: "object",
2504
- properties: {
2505
- action: {
2506
- type: "string",
2507
- enum: ["save", "list", "last"],
2508
- description: "Action: save (create checkpoint), list (show recent), last (show last checkpoint)"
2509
- },
2510
- message: {
2511
- type: "string",
2512
- description: "Checkpoint message (what you were working on)"
2513
- },
2514
- notes: {
2515
- type: "string",
2516
- description: "Additional notes"
2517
- },
2518
- files: {
2519
- type: "array",
2520
- items: { type: "string" },
2521
- description: "Files to associate with this checkpoint"
2522
- }
2523
- },
2524
- required: ["action"]
2525
- }
2526
- },
2527
- {
2528
- name: "trie_index",
2529
- description: "Index codebase for fast goal checking and file lookups. Index is auto-updated during watch mode. Use this for initial indexing or to rebuild the index.",
2530
- inputSchema: {
2531
- type: "object",
2532
- properties: {
2533
- action: {
2534
- type: "string",
2535
- enum: ["full", "status", "clear"],
2536
- description: "Action: full (index all files), status (show index stats), clear (remove stale cache)"
2537
- },
2538
- directory: {
2539
- type: "string",
2540
- description: "Project directory (defaults to current workspace)"
2541
- }
2542
- }
2543
- }
2544
- },
2545
- // Add remaining tool definitions...
2546
- this.createSpecialAgentDefinitions()
2547
- ].flat();
2548
- }
2549
- createSpecialAgentDefinitions() {
2550
- return [
2551
- {
2552
- name: "trie_visual_qa_browser",
2553
- description: "Capture screenshots at mobile/tablet/desktop viewports for visual QA. Alias: visual_qa_browser",
2554
- inputSchema: {
2555
- type: "object",
2556
- properties: {
2557
- url: { type: "string", description: "URL to screenshot" },
2558
- port: { type: "number", description: "Specific port to check" },
2559
- waitForSelector: { type: "string", description: "CSS selector to wait for" },
2560
- waitMs: { type: "number", description: "Additional wait time" }
2561
- }
2562
- },
2563
- // MCP Apps: Interactive visual QA gallery
2564
- _meta: {
2565
- ui: { resourceUri: "ui://trie/visual-qa" }
2566
- }
2567
- },
2568
- {
2569
- name: "trie_pr_review",
2570
- description: "\u{1F50D} Interactive PR review: walks through changes file-by-file. Alias: pr_review",
2571
- inputSchema: {
2572
- type: "object",
2573
- properties: {
2574
- pr: { type: "string", description: "PR number to review" },
2575
- worktree: { type: "string", description: "Path to worktree directory" },
2576
- mode: {
2577
- type: "string",
2578
- enum: ["own", "others"],
2579
- description: "Review mode"
2580
- },
2581
- files: {
2582
- type: "array",
2583
- items: { type: "string" },
2584
- description: "Specific files to review"
2585
- }
2586
- }
2587
- },
2588
- // MCP Apps: Interactive PR review UI
2589
- _meta: {
2590
- ui: { resourceUri: "ui://trie/pr-review" }
2591
- }
2592
- },
2593
- {
2594
- name: "trie_linear_sync",
2595
- description: "Sync active Linear tickets to build context for JIT defect prediction. Alias: linear_sync",
2596
- inputSchema: {
2597
- type: "object",
2598
- properties: {
2599
- directory: { type: "string", description: "Project directory" }
2600
- }
2601
- }
2602
- },
2603
- {
2604
- name: "trie_github_sync",
2605
- description: "Sync open PRs and issues from GitHub into the Trie context graph. Links PRs to files they touch and to Linear tickets mentioned in PR descriptions. Run once to initialize, or periodically to stay current.",
2606
- inputSchema: {
2607
- type: "object",
2608
- properties: {
2609
- directory: { type: "string", description: "Project directory (defaults to current workspace)" }
2610
- }
2611
- }
2612
- },
2613
- {
2614
- name: "trie_github_branches",
2615
- description: "Fetch GitHub branches with latest commit info. Use when the user asks which branch has the latest updates, what branches exist, or when branches were last updated. Requires GitHub API key.",
2616
- inputSchema: {
2617
- type: "object",
2618
- properties: {
2619
- directory: { type: "string", description: "Project directory (defaults to current workspace)" },
2620
- limit: { type: "number", description: "Max branches to return (default 15, max 30)" }
2621
- }
2622
- }
2623
- },
2624
- {
2625
- name: "trie_pipeline",
2626
- description: "Get consolidated pipeline status: open PRs, active Linear tickets, open GitHub issues, and Trie scan issues not yet in any ticket or PR (coverage gaps). Use to understand where work stands and what is falling through the cracks.",
2627
- inputSchema: {
2628
- type: "object",
2629
- properties: {
2630
- action: {
2631
- type: "string",
2632
- enum: ["status", "coverage", "create_tickets"],
2633
- description: "status: full pipeline view | coverage: only show Trie issues with no ticket/PR | create_tickets: open Linear tickets for uncovered issues"
2634
- },
2635
- focus: {
2636
- type: "string",
2637
- description: "Optional file path or Linear ticket ID to narrow the view"
2638
- },
2639
- issueIds: {
2640
- type: "array",
2641
- items: { type: "string" },
2642
- description: "Issue IDs to create tickets for (create_tickets action only)"
2643
- },
2644
- directory: { type: "string", description: "Project directory" }
2645
- }
2646
- }
2647
- },
2648
- {
2649
- name: "trie_get_governance",
2650
- description: "Query governance records (architectural decisions, standards, team agreements) from governance ledger with targeted retrieval. Prevents context pollution by returning only relevant records.",
2651
- inputSchema: {
2652
- type: "object",
2653
- properties: {
2654
- relatedTo: { type: "string", description: "File path or topic to find related governance" },
2655
- tags: { type: "array", items: { type: "string" }, description: "Filter by tags" },
2656
- since: { type: "string", description: 'Time filter: ISO date or "7d", "30d", "90d"' },
2657
- limit: { type: "number", description: "Max results (default 10)" },
2658
- directory: { type: "string", description: "Working directory" }
2659
- }
2660
- }
2661
- },
2662
- {
2663
- name: "trie_get_decisions",
2664
- description: "[DEPRECATED - use trie_get_governance] Query governance records from ledger. Backward compatibility alias.",
2665
- inputSchema: {
2666
- type: "object",
2667
- properties: {
2668
- relatedTo: { type: "string", description: "File path or topic to find related governance" },
2669
- tags: { type: "array", items: { type: "string" }, description: "Filter by tags" },
2670
- since: { type: "string", description: 'Time filter: ISO date or "7d", "30d", "90d"' },
2671
- limit: { type: "number", description: "Max results (default 10)" },
2672
- directory: { type: "string", description: "Working directory" }
2673
- }
2674
- }
2675
- },
2676
- {
2677
- name: "trie_get_blockers",
2678
- description: "Get active blockers from governance ledger. Returns only unresolved blockers to avoid noise.",
2679
- inputSchema: {
2680
- type: "object",
2681
- properties: {
2682
- tags: { type: "array", items: { type: "string" }, description: "Filter by tags" },
2683
- limit: { type: "number", description: "Max results (default 5)" },
2684
- directory: { type: "string", description: "Working directory" }
2685
- }
2686
- }
2687
- },
2688
- {
2689
- name: "trie_get_related_governance",
2690
- description: "Find governance records related to a specific governance record, file, or topic. Targeted context retrieval.",
2691
- inputSchema: {
2692
- type: "object",
2693
- properties: {
2694
- governanceId: { type: "string", description: "Governance ID to find related records" },
2695
- file: { type: "string", description: "File path to find related governance" },
2696
- topic: { type: "string", description: "Topic to find related governance" },
2697
- limit: { type: "number", description: "Max results (default 5)" },
2698
- directory: { type: "string", description: "Working directory" }
2699
- }
2700
- }
2701
- },
2702
- {
2703
- name: "trie_get_related_decisions",
2704
- description: "[DEPRECATED - use trie_get_related_governance] Find related governance records. Backward compatibility alias.",
2705
- inputSchema: {
2706
- type: "object",
2707
- properties: {
2708
- decisionId: { type: "string", description: "Decision ID to find related records" },
2709
- file: { type: "string", description: "File path to find related governance" },
2710
- topic: { type: "string", description: "Topic to find related governance" },
2711
- limit: { type: "number", description: "Max results (default 5)" },
2712
- directory: { type: "string", description: "Working directory" }
2713
- }
2714
- }
2715
- },
2716
- {
2717
- name: "trie_query_context",
2718
- description: 'Natural-language search across ALL Trie context: goals, hypotheses, nudges (goal violations), incidents, governance, blockers. Use for "what are my goals", "show hypotheses", "any nudges", "show incidents", "recent governance", etc.',
2719
- inputSchema: {
2720
- type: "object",
2721
- properties: {
2722
- query: { type: "string", description: "Natural language query (e.g. goals, hypotheses, nudges, incidents, governance)" },
2723
- type: {
2724
- type: "string",
2725
- enum: ["goals", "hypotheses", "nudges", "incidents", "governance", "decisions", "blockers", "facts", "questions", "all"],
2726
- description: 'Type of context to query (default: all). "decisions" is deprecated, use "governance"'
2727
- },
2728
- limit: { type: "number", description: "Max results per type (default 10)" },
2729
- directory: { type: "string", description: "Working directory" }
2730
- },
2731
- required: ["query"]
2732
- }
2733
- }
2734
- ];
2735
- }
2736
- getTool(name) {
2737
- return this.tools.get(name);
2738
- }
2739
- getAllTools() {
2740
- return this.definitions;
2741
- }
2742
- getToolNames() {
2743
- return Array.from(this.tools.keys());
2744
- }
2745
- hasTool(name) {
2746
- return this.tools.has(name);
2747
- }
2748
- };
2749
-
2750
- // src/server/resource-manager.ts
2751
- import { readdir, readFile as readFile3 } from "fs/promises";
2752
- import { existsSync as existsSync3 } from "fs";
2753
- import { join as join3, dirname as dirname2 } from "path";
2754
- import { fileURLToPath } from "url";
2755
- var UI_APPS = {
2756
- "ledger": {
2757
- name: "Decision Ledger",
2758
- description: "Track decisions, blockers, facts, and questions across your project"
2759
- },
2760
- "goals": {
2761
- name: "Goals & Progress",
2762
- description: "Monitor code quality goals and track progress with visual indicators"
2763
- },
2764
- "hypotheses": {
2765
- name: "Hypotheses",
2766
- description: "Test and validate development theories with evidence-based reasoning"
2767
- },
2768
- "nudges": {
2769
- name: "Nudges & Insights",
2770
- description: "AI-powered code quality insights, warnings, and suggestions"
2771
- },
2772
- "chat": {
2773
- name: "AI Chat",
2774
- description: "Interactive chat with Trie assistant for code analysis and guidance"
2775
- }
2776
- };
2777
- var ResourceManager = class {
2778
- /**
2779
- * Get all available resources dynamically
2780
- */
2781
- async getAvailableResources() {
2782
- const resources = [];
2783
- resources.push(
2784
- {
2785
- uri: "trie://context",
2786
- name: "AI Context (Consolidated)",
2787
- description: "Single source of truth: project state, coding rules, recent issues, and cross-project patterns. Read this first.",
2788
- mimeType: "text/markdown"
2789
- },
2790
- {
2791
- uri: "trie://project",
2792
- name: "Project Information",
2793
- description: "User-defined project context (description, conventions, architecture, AI instructions)",
2794
- mimeType: "text/markdown"
2795
- },
2796
- {
2797
- uri: "trie://context/state",
2798
- name: "Context State",
2799
- description: "Detailed context state with scan history and priorities",
2800
- mimeType: "application/json"
2801
- },
2802
- {
2803
- uri: "trie://config",
2804
- name: "Trie Configuration",
2805
- description: "Current Trie configuration settings",
2806
- mimeType: "application/json"
2807
- },
2808
- {
2809
- uri: "trie://cache/stats",
2810
- name: "Cache Statistics",
2811
- description: "Trie scan cache statistics and performance metrics",
2812
- mimeType: "application/json"
2813
- },
2814
- {
2815
- uri: "trie://signatures",
2816
- name: "Vulnerability Signatures",
2817
- description: "Summary of loaded vulnerability detection signatures",
2818
- mimeType: "application/json"
2819
- },
2820
- {
2821
- uri: "trie://bootstrap",
2822
- name: "Bootstrap Status",
2823
- description: "Bootstrap file status and project setup context",
2824
- mimeType: "application/json"
2825
- },
2826
- {
2827
- uri: "trie://rules",
2828
- name: "Project Rules",
2829
- description: "User-defined coding standards from .trie/RULES.md",
2830
- mimeType: "text/markdown"
2831
- },
2832
- {
2833
- uri: "trie://team",
2834
- name: "Team Info",
2835
- description: "Team ownership and escalation from .trie/TEAM.md",
2836
- mimeType: "text/markdown"
2837
- },
2838
- {
2839
- uri: "trie://memory",
2840
- name: "Issue Memory",
2841
- description: "Issue memory statistics and recent issues",
2842
- mimeType: "application/json"
2843
- },
2844
- {
2845
- uri: "trie://memory/global",
2846
- name: "Global Memory",
2847
- description: "Cross-project patterns and statistics",
2848
- mimeType: "application/json"
2849
- }
2850
- );
2851
- resources.push(...await this.getScanReportResources());
2852
- resources.push(...this.getUIAppResources());
2853
- return resources;
2854
- }
2855
- async getScanReportResources() {
2856
- try {
2857
- const reportsDir = join3(getWorkingDirectory(void 0, true), "trie-reports");
2858
- const files = await readdir(reportsDir);
2859
- const reportFiles = files.filter((f) => f.endsWith(".txt") || f.endsWith(".json"));
2860
- return reportFiles.slice(0, 10).map((file) => ({
2861
- uri: `trie://reports/${file}`,
2862
- name: `Scan Report: ${file}`,
2863
- description: `Trie scan report from ${file}`,
2864
- mimeType: file.endsWith(".json") ? "application/json" : "text/plain"
2865
- }));
2866
- } catch {
2867
- return [];
2868
- }
2869
- }
2870
- /**
2871
- * Read content for a specific resource
2872
- */
2873
- async readResourceContent(uri) {
2874
- if (uri.startsWith("ui://trie/")) {
2875
- const appId = uri.replace("ui://trie/", "");
2876
- return await this.getUIAppResource(uri, appId);
2877
- }
2878
- const parsedUri = uri.replace("trie://", "");
2879
- if (parsedUri === "context") {
2880
- return await this.getContextResource(uri);
2881
- }
2882
- if (parsedUri === "project") {
2883
- return await this.getProjectResource(uri);
2884
- }
2885
- if (parsedUri === "context/state") {
2886
- return await this.getContextStateResource(uri);
2887
- }
2888
- if (parsedUri === "config") {
2889
- return await this.getConfigResource(uri);
2890
- }
2891
- if (parsedUri === "cache/stats") {
2892
- return await this.getCacheStatsResource(uri);
2893
- }
2894
- if (parsedUri === "signatures") {
2895
- return await this.getSignaturesResource(uri);
2896
- }
2897
- if (parsedUri === "bootstrap") {
2898
- return await this.getBootstrapResource(uri);
2899
- }
2900
- if (parsedUri === "rules") {
2901
- return await this.getRulesResource(uri);
2902
- }
2903
- if (parsedUri === "team") {
2904
- return await this.getTeamResource(uri);
2905
- }
2906
- if (parsedUri === "memory") {
2907
- return await this.getMemoryResource(uri);
2908
- }
2909
- if (parsedUri === "memory/global") {
2910
- return await this.getGlobalMemoryResource(uri);
2911
- }
2912
- if (parsedUri.startsWith("reports/")) {
2913
- return await this.getScanReportResource(uri, parsedUri);
2914
- }
2915
- throw new Error(`Unknown resource: ${uri}`);
2916
- }
2917
- /**
2918
- * Get UI App resources for MCP Apps
2919
- */
2920
- getUIAppResources() {
2921
- return Object.entries(UI_APPS).map(([id, app]) => ({
2922
- uri: `ui://trie/${id}`,
2923
- name: app.name,
2924
- description: app.description,
2925
- mimeType: "text/html;profile=mcp-app"
2926
- }));
2927
- }
2928
- /**
2929
- * Read UI App resource content (bundled HTML)
2930
- */
2931
- async getUIAppResource(uri, appId) {
2932
- const currentFile = fileURLToPath(import.meta.url);
2933
- const distDir = dirname2(dirname2(currentFile));
2934
- const uiDir = join3(distDir, "ui");
2935
- const htmlPath = join3(uiDir, `${appId}.html`);
2936
- try {
2937
- if (!existsSync3(htmlPath)) {
2938
- return {
2939
- contents: [{
2940
- uri,
2941
- mimeType: "text/html;profile=mcp-app",
2942
- text: this.getFallbackUIHtml(appId)
2943
- }]
2944
- };
2945
- }
2946
- const content = await readFile3(htmlPath, "utf-8");
2947
- return {
2948
- contents: [{
2949
- uri,
2950
- mimeType: "text/html;profile=mcp-app",
2951
- text: content
2952
- }]
2953
- };
2954
- } catch (error) {
2955
- return {
2956
- contents: [{
2957
- uri,
2958
- mimeType: "text/html;profile=mcp-app",
2959
- text: this.getFallbackUIHtml(appId)
2960
- }]
2961
- };
2962
- }
2963
- }
2964
- /**
2965
- * Generate fallback HTML for UI apps that haven't been built yet
2966
- */
2967
- getFallbackUIHtml(appId) {
2968
- const app = UI_APPS[appId];
2969
- const title = app?.name || "Trie UI";
2970
- const description = app?.description || "Loading...";
2971
- return `<!DOCTYPE html>
2972
- <html lang="en">
2973
- <head>
2974
- <meta charset="UTF-8">
2975
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
2976
- <title>${title} - Trie</title>
2977
- <style>
2978
- :root {
2979
- --bg: #0d1117;
2980
- --surface: #161b22;
2981
- --border: #30363d;
2982
- --text: #e6edf3;
2983
- --text-muted: #8b949e;
2984
- --primary: #58a6ff;
2985
- }
2986
- * { box-sizing: border-box; margin: 0; padding: 0; }
2987
- body {
2988
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
2989
- background: var(--bg);
2990
- color: var(--text);
2991
- display: flex;
2992
- align-items: center;
2993
- justify-content: center;
2994
- min-height: 100vh;
2995
- padding: 24px;
2996
- }
2997
- .container {
2998
- text-align: center;
2999
- max-width: 400px;
3000
- }
3001
- h1 {
3002
- font-size: 24px;
3003
- margin-bottom: 8px;
3004
- }
3005
- p {
3006
- color: var(--text-muted);
3007
- margin-bottom: 24px;
3008
- }
3009
- .status {
3010
- display: inline-flex;
3011
- align-items: center;
3012
- gap: 8px;
3013
- padding: 8px 16px;
3014
- background: var(--surface);
3015
- border: 1px solid var(--border);
3016
- border-radius: 8px;
3017
- color: var(--primary);
3018
- }
3019
- .spinner {
3020
- width: 16px;
3021
- height: 16px;
3022
- border: 2px solid var(--border);
3023
- border-top-color: var(--primary);
3024
- border-radius: 50%;
3025
- animation: spin 0.8s linear infinite;
3026
- }
3027
- @keyframes spin { to { transform: rotate(360deg); } }
3028
- </style>
3029
- </head>
3030
- <body>
3031
- <div class="container">
3032
- <h1>${title}</h1>
3033
- <p>${description}</p>
3034
- <div class="status">
3035
- <div class="spinner"></div>
3036
- <span>Connecting to Trie...</span>
3037
- </div>
3038
- </div>
3039
- <script type="module">
3040
- // MCP Apps SDK connection
3041
- import { App } from "@modelcontextprotocol/ext-apps";
3042
-
3043
- const app = new App();
3044
- await app.connect();
3045
-
3046
- app.ontoolresult = (result) => {
3047
- document.querySelector('.status span').textContent = 'Connected! Waiting for data...';
3048
- console.log('Received tool result:', result);
3049
- };
3050
- </script>
3051
- </body>
3052
- </html>`;
3053
- }
3054
- async getContextResource(uri) {
3055
- const workDir = getWorkingDirectory(void 0, true);
3056
- const state = await loadContextState();
3057
- const summary = [
3058
- "# Trie Context",
3059
- "",
3060
- "> Quick reference for AI assistants. Detailed sections below.",
3061
- "",
3062
- "## Quick Status",
3063
- "",
3064
- `| Metric | Value |`,
3065
- `|--------|-------|`,
3066
- `| Last Scan | ${state.lastScan ? new Date(state.lastScan.timestamp).toLocaleDateString() : "Never"} |`,
3067
- `| Critical Issues | ${state.lastScan?.issues.critical ?? 0} |`,
3068
- `| Total Issues | ${state.lastScan?.issues.total ?? 0} |`,
3069
- ""
3070
- ];
3071
- if (state.activePriorities.length > 0) {
3072
- summary.push("## Top Priorities", "");
3073
- state.activePriorities.slice(0, 3).forEach((p, i) => {
3074
- summary.push(`${i + 1}. ${p}`);
3075
- });
3076
- summary.push("");
3077
- }
3078
- try {
3079
- const recentIssues = await getRecentIssues({ workDir, limit: 3 });
3080
- if (recentIssues.length > 0) {
3081
- summary.push("## Recent Issues", "");
3082
- recentIssues.forEach((i) => {
3083
- summary.push(`- [${i.severity.toUpperCase()}] ${i.issue.slice(0, 50)}... (\`${i.file.split("/").pop()}\`)`);
3084
- });
3085
- summary.push("");
3086
- }
3087
- } catch {
3088
- }
3089
- try {
3090
- const patterns = await findCrossProjectPatterns(2);
3091
- if (patterns.length > 0) {
3092
- summary.push("## Watch For (seen in other projects)", "");
3093
- patterns.slice(0, 2).forEach((p) => {
3094
- summary.push(`- ${p.pattern.slice(0, 40)}... (${p.occurrences}x across ${p.projects.length} projects)`);
3095
- });
3096
- summary.push("");
3097
- }
3098
- } catch {
3099
- }
3100
- const rules = await loadRules(workDir);
3101
- if (rules) {
3102
- const ruleHeaders = rules.match(/^##?\s+.+$/gm)?.slice(0, 5) || [];
3103
- if (ruleHeaders.length > 0) {
3104
- summary.push("## Coding Rules (see trie://rules for full)", "");
3105
- ruleHeaders.forEach((h) => summary.push(`- ${h.replace(/^#+\s*/, "")}`));
3106
- summary.push("");
3107
- }
3108
- }
3109
- summary.push("## Available Tools", "");
3110
- summary.push("| Tool | Purpose |");
3111
- summary.push("|------|---------|");
3112
- summary.push("| `trie_fix` | Apply high-confidence fixes |");
3113
- summary.push("| `trie_fix` | Generate fix recommendations |");
3114
- summary.push("| `trie_memory` | Search issue history |");
3115
- summary.push("| `trie_init` | Initialize bootstrap files |");
3116
- summary.push("");
3117
- summary.push("---", "", "# Detailed Context", "");
3118
- const agentsMdPath = join3(getTrieDirectory(workDir), "AGENTS.md");
3119
- try {
3120
- if (existsSync3(agentsMdPath)) {
3121
- const agentsContent = await readFile3(agentsMdPath, "utf-8");
3122
- summary.push(agentsContent);
3123
- } else {
3124
- const contextSummary = await getContextForAI();
3125
- summary.push(contextSummary);
3126
- }
3127
- } catch {
3128
- const contextSummary = await getContextForAI();
3129
- summary.push(contextSummary);
3130
- }
3131
- return {
3132
- contents: [{
3133
- uri,
3134
- mimeType: "text/markdown",
3135
- text: summary.join("\n")
3136
- }]
3137
- };
3138
- }
3139
- async getContextStateResource(uri) {
3140
- const state = await loadContextState();
3141
- return {
3142
- contents: [{
3143
- uri,
3144
- mimeType: "application/json",
3145
- text: JSON.stringify(state, null, 2)
3146
- }]
3147
- };
3148
- }
3149
- async getProjectResource(uri) {
3150
- const workDir = getWorkingDirectory(void 0, true);
3151
- if (!projectInfoExists(workDir)) {
3152
- return {
3153
- contents: [{
3154
- uri,
3155
- mimeType: "text/markdown",
3156
- text: `# Project Information Not Found
3157
-
3158
- No \`.trie/PROJECT.md\` file exists in this project.
3159
-
3160
- ## Create One
3161
-
3162
- Use the \`trie_project\` tool with action="init" to create a PROJECT.md template:
3163
-
3164
- \`\`\`
3165
- trie_project action="init"
3166
- \`\`\`
3167
-
3168
- Or run from CLI:
3169
- \`\`\`
3170
- trie project init
3171
- \`\`\`
3172
-
3173
- ## What is PROJECT.md?
3174
-
3175
- PROJECT.md is a user-defined file that stores important project context:
3176
- - Project description and purpose
3177
- - Technology stack
3178
- - Architecture decisions
3179
- - Coding conventions
3180
- - Environment info
3181
- - Team ownership
3182
- - Compliance requirements
3183
- - Special instructions for AI assistants
3184
-
3185
- This information is automatically available to Claude Code, Cursor, and other AI tools.
3186
- `
3187
- }]
3188
- };
3189
- }
3190
- const content = await loadProjectInfo(workDir);
3191
- return {
3192
- contents: [{
3193
- uri,
3194
- mimeType: "text/markdown",
3195
- text: content || ""
3196
- }]
3197
- };
3198
- }
3199
- async getConfigResource(uri) {
3200
- const config = await loadConfig();
3201
- return {
3202
- contents: [{
3203
- uri,
3204
- mimeType: "application/json",
3205
- text: JSON.stringify(config, null, 2)
3206
- }]
3207
- };
3208
- }
3209
- async getCacheStatsResource(uri) {
3210
- try {
3211
- const cachePath = join3(getTrieDirectory(getWorkingDirectory(void 0, true)), ".trie-cache.json");
3212
- const cacheContent = await readFile3(cachePath, "utf-8");
3213
- const cache = JSON.parse(cacheContent);
3214
- const fileCount = Object.keys(cache.files || {}).length;
3215
- const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
3216
- return acc + (file.vulnerabilities?.length || 0);
3217
- }, 0);
3218
- return {
3219
- contents: [{
3220
- uri,
3221
- mimeType: "application/json",
3222
- text: JSON.stringify({
3223
- version: cache.version,
3224
- created: new Date(cache.created).toISOString(),
3225
- lastUpdated: new Date(cache.lastUpdated).toISOString(),
3226
- cachedFiles: fileCount,
3227
- totalVulnerabilitiesFound: totalVulns,
3228
- cacheAgeMinutes: Math.round((Date.now() - cache.lastUpdated) / 6e4)
3229
- }, null, 2)
3230
- }]
3231
- };
3232
- } catch {
3233
- return {
3234
- contents: [{
3235
- uri,
3236
- mimeType: "application/json",
3237
- text: JSON.stringify({
3238
- error: "No cache found. Run a scan first to build the cache.",
3239
- hint: "Run trie watch to analyze your codebase"
3240
- }, null, 2)
3241
- }]
3242
- };
3243
- }
3244
- }
3245
- async getSignaturesResource(uri) {
3246
- const { getVulnerabilityStats } = await import("./vulnerability-signatures-T7SKHORW.js");
3247
- const { getVibeCodeStats } = await import("./vibe-code-signatures-F6URTBW3.js");
3248
- const vulnStats = getVulnerabilityStats();
3249
- const vibeStats = getVibeCodeStats();
3250
- return {
3251
- contents: [{
3252
- uri,
3253
- mimeType: "application/json",
3254
- text: JSON.stringify({
3255
- vulnerabilitySignatures: vulnStats,
3256
- vibeCodeSignatures: vibeStats,
3257
- totalSignatures: vulnStats.total + vibeStats.total
3258
- }, null, 2)
3259
- }]
3260
- };
3261
- }
3262
- async getScanReportResource(uri, parsedUri) {
3263
- const fileName = parsedUri.replace("reports/", "");
3264
- const reportPath = join3(getWorkingDirectory(void 0, true), "trie-reports", fileName);
3265
- try {
3266
- const content = await readFile3(reportPath, "utf-8");
3267
- return {
3268
- contents: [{
3269
- uri,
3270
- mimeType: fileName.endsWith(".json") ? "application/json" : "text/plain",
3271
- text: content
3272
- }]
3273
- };
3274
- } catch {
3275
- throw new Error(`Report not found: ${fileName}`);
3276
- }
3277
- }
3278
- async getBootstrapResource(uri) {
3279
- const workDir = getWorkingDirectory(void 0, true);
3280
- const context = await loadBootstrapContext(workDir);
3281
- return {
3282
- contents: [{
3283
- uri,
3284
- mimeType: "application/json",
3285
- text: JSON.stringify({
3286
- needsBootstrap: context.needsBootstrap,
3287
- files: context.files.map((f) => ({
3288
- name: f.name,
3289
- type: f.type,
3290
- exists: f.exists
3291
- })),
3292
- hasInjectedContent: !!context.injectedContent
3293
- }, null, 2)
3294
- }]
3295
- };
3296
- }
3297
- async getRulesResource(uri) {
3298
- const rules = await loadRules();
3299
- if (!rules) {
3300
- return {
3301
- contents: [{
3302
- uri,
3303
- mimeType: "text/markdown",
3304
- text: `# No Rules Defined
3305
-
3306
- No \`.trie/RULES.md\` file exists in this project.
3307
-
3308
- Use \`trie_init\` to create one, or create it manually with your coding standards.
3309
- `
3310
- }]
3311
- };
3312
- }
3313
- return {
3314
- contents: [{
3315
- uri,
3316
- mimeType: "text/markdown",
3317
- text: rules
3318
- }]
3319
- };
3320
- }
3321
- async getTeamResource(uri) {
3322
- const team = await loadTeamInfo();
3323
- if (!team) {
3324
- return {
3325
- contents: [{
3326
- uri,
3327
- mimeType: "text/markdown",
3328
- text: `# No Team Info Defined
3329
-
3330
- No \`.trie/TEAM.md\` file exists in this project.
3331
-
3332
- Use \`trie_init\` to create one, or create it manually with team ownership info.
3333
- `
3334
- }]
3335
- };
3336
- }
3337
- return {
3338
- contents: [{
3339
- uri,
3340
- mimeType: "text/markdown",
3341
- text: team
3342
- }]
3343
- };
3344
- }
3345
- async getMemoryResource(uri) {
3346
- const workDir = getWorkingDirectory(void 0, true);
3347
- const stats = await getMemoryStats(workDir);
3348
- const recent = await getRecentIssues({ workDir, limit: 5 });
3349
- return {
3350
- contents: [{
3351
- uri,
3352
- mimeType: "application/json",
3353
- text: JSON.stringify({
3354
- stats,
3355
- recentIssues: recent.map((i) => ({
3356
- severity: i.severity,
3357
- issue: i.issue.slice(0, 100),
3358
- file: i.file,
3359
- agent: i.agent,
3360
- timestamp: i.timestamp,
3361
- resolved: i.resolved
3362
- }))
3363
- }, null, 2)
3364
- }]
3365
- };
3366
- }
3367
- async getGlobalMemoryResource(uri) {
3368
- const stats = await getGlobalMemoryStats();
3369
- const patterns = await findCrossProjectPatterns(2);
3370
- return {
3371
- contents: [{
3372
- uri,
3373
- mimeType: "application/json",
3374
- text: JSON.stringify({
3375
- stats,
3376
- topPatterns: patterns.slice(0, 10).map((p) => ({
3377
- pattern: p.pattern.slice(0, 80),
3378
- severity: p.severity,
3379
- agent: p.agent,
3380
- occurrences: p.occurrences,
3381
- projectCount: p.projects.length,
3382
- hasFixApplied: !!p.fixApplied
3383
- }))
3384
- }, null, 2)
3385
- }]
3386
- };
3387
- }
3388
- };
3389
-
3390
- // src/tools/visual-qa-browser.ts
3391
- import { chromium } from "playwright";
3392
- import { createServer } from "net";
3393
- import { request } from "http";
3394
- var DEFAULT_VIEWPORTS = [
3395
- { name: "mobile", width: 375, height: 812 },
3396
- // iPhone X
3397
- { name: "tablet", width: 768, height: 1024 },
3398
- // iPad
3399
- { name: "desktop", width: 1440, height: 900 }
3400
- // Standard desktop
3401
- ];
3402
- async function findOpenPort() {
3403
- const commonPorts = [3e3, 3001, 5173, 5174, 4200, 8080, 8e3, 8888, 5e3, 4e3];
3404
- const portChecks = await Promise.all(
3405
- commonPorts.map(async (port) => {
3406
- const isOpen = await checkPort(port);
3407
- return isOpen ? port : null;
3408
- })
3409
- );
3410
- return portChecks.find((port) => port !== null) || null;
3411
- }
3412
- async function checkPort(port) {
3413
- return new Promise((resolve3) => {
3414
- const testServer = createServer();
3415
- let portInUse = false;
3416
- testServer.once("error", (err) => {
3417
- if (err.code === "EADDRINUSE") {
3418
- portInUse = true;
3419
- testHttpPort(port).then(resolve3).catch(() => resolve3(false));
3420
- } else {
3421
- resolve3(false);
3422
- }
3423
- });
3424
- testServer.once("listening", () => {
3425
- testServer.close();
3426
- resolve3(false);
3427
- });
3428
- setTimeout(() => {
3429
- if (!portInUse) {
3430
- testServer.close();
3431
- resolve3(false);
3432
- }
3433
- }, 1e3);
3434
- testServer.listen(port, "127.0.0.1");
3435
- });
3436
- }
3437
- async function testHttpPort(port) {
3438
- return new Promise((resolve3) => {
3439
- const req = request({
3440
- hostname: "localhost",
3441
- port,
3442
- path: "/",
3443
- method: "GET",
3444
- timeout: 2e3
3445
- }, (res) => {
3446
- resolve3(res.statusCode !== void 0);
3447
- res.on("data", () => {
3448
- });
3449
- res.on("end", () => {
3450
- });
3451
- });
3452
- req.on("error", () => {
3453
- resolve3(false);
3454
- });
3455
- req.on("timeout", () => {
3456
- req.destroy();
3457
- resolve3(false);
3458
- });
3459
- req.end();
3460
- });
3461
- }
3462
- async function captureScreenshots(url, viewports, options) {
3463
- let browser = null;
3464
- const screenshots = [];
3465
- try {
3466
- browser = await chromium.launch({
3467
- headless: true
3468
- });
3469
- for (const viewport of viewports) {
3470
- const page = await browser.newPage({
3471
- viewport: { width: viewport.width, height: viewport.height }
3472
- });
3473
- try {
3474
- await page.goto(url, {
3475
- waitUntil: "networkidle",
3476
- timeout: 3e4
3477
- });
3478
- if (options.waitForSelector) {
3479
- await page.waitForSelector(options.waitForSelector, { timeout: 1e4 });
3480
- }
3481
- if (options.waitMs) {
3482
- await page.waitForTimeout(options.waitMs);
3483
- }
3484
- const screenshotBuffer = await page.screenshot({
3485
- fullPage: true,
3486
- type: "png"
3487
- });
3488
- screenshots.push({
3489
- viewport: viewport.name,
3490
- width: viewport.width,
3491
- height: viewport.height,
3492
- base64: screenshotBuffer.toString("base64"),
3493
- mimeType: "image/png"
3494
- });
3495
- } finally {
3496
- await page.close();
3497
- }
3498
- }
3499
- } finally {
3500
- if (browser) {
3501
- await browser.close();
3502
- }
3503
- }
3504
- return screenshots;
3505
- }
3506
- async function runVisualQA(options = {}) {
3507
- let url = options.url;
3508
- if (!url) {
3509
- if (options.port) {
3510
- const isServing = await testHttpPort(options.port);
3511
- if (!isServing) {
3512
- return {
3513
- success: false,
3514
- url: `http://localhost:${options.port}`,
3515
- screenshots: [],
3516
- error: `Port ${options.port} is not serving HTTP. Is your dev server running on this port?
3517
-
3518
- Try:
3519
- 1. Verify your dev server is running: curl http://localhost:${options.port}
3520
- 2. Check if the port is correct
3521
- 3. Provide full URL: trie_visual_qa_browser url:"http://localhost:${options.port}"`,
3522
- analysisPrompt: ""
3523
- };
3524
- }
3525
- url = `http://localhost:${options.port}`;
3526
- } else {
3527
- if (!isInteractiveMode()) {
3528
- console.error("Visual QA: Auto-detecting running dev server...");
3529
- }
3530
- const port = await findOpenPort();
3531
- if (!port) {
3532
- return {
3533
- success: false,
3534
- url: "",
3535
- screenshots: [],
3536
- error: 'No running dev server found on common ports (3000, 3001, 5173, 5174, 4200, 8080, 8000, 8888, 5000, 4000).\n\nPlease:\n1. Start your dev server, OR\n2. Provide a URL: trie_visual_qa_browser url:"http://localhost:3000", OR\n3. Specify a port: trie_visual_qa_browser port:3000',
3537
- analysisPrompt: ""
3538
- };
3539
- }
3540
- url = `http://localhost:${port}`;
3541
- if (!isInteractiveMode()) {
3542
- console.error(` \u2713 Found server on port ${port}`);
3543
- }
3544
- }
3545
- }
3546
- const viewports = options.viewports || DEFAULT_VIEWPORTS;
3547
- try {
3548
- if (!isInteractiveMode()) {
3549
- console.error(`\u{1F4F8} Visual QA: Capturing screenshots from ${url}`);
3550
- console.error(` Viewports: ${viewports.map((v) => `${v.name} (${v.width}x${v.height})`).join(", ")}`);
3551
- }
3552
- const screenshots = await captureScreenshots(url, viewports, {
3553
- waitForSelector: options.waitForSelector,
3554
- waitMs: options.waitMs
3555
- });
3556
- if (!isInteractiveMode()) {
3557
- console.error(` \u2713 Captured ${screenshots.length} screenshots`);
3558
- }
3559
- const analysisPrompt = buildAnalysisPrompt(url, screenshots);
3560
- return {
3561
- success: true,
3562
- url,
3563
- screenshots,
3564
- analysisPrompt
3565
- };
3566
- } catch (error) {
3567
- const errorMessage = error instanceof Error ? error.message : String(error);
3568
- if (errorMessage.includes("net::ERR_CONNECTION_REFUSED") || errorMessage.includes("ECONNREFUSED")) {
3569
- return {
3570
- success: false,
3571
- url,
3572
- screenshots: [],
3573
- error: `Cannot connect to ${url}. Is your dev server running?
3574
-
3575
- Try:
3576
- 1. Start your dev server (e.g., npm start, npm run dev)
3577
- 2. Specify the URL explicitly: trie_visual_qa_browser url:"http://localhost:3000"
3578
- 3. Or specify the port: trie_visual_qa_browser port:3000`,
3579
- analysisPrompt: ""
3580
- };
3581
- }
3582
- if (errorMessage.includes("Executable doesn't exist") || errorMessage.includes("BrowserType")) {
3583
- return {
3584
- success: false,
3585
- url,
3586
- screenshots: [],
3587
- error: "Playwright browsers not installed. Run: npx playwright install chromium",
3588
- analysisPrompt: ""
3589
- };
3590
- }
3591
- if (errorMessage.includes("timeout") || errorMessage.includes("Navigation timeout")) {
3592
- return {
3593
- success: false,
3594
- url,
3595
- screenshots: [],
3596
- error: `Page load timeout for ${url}. The page may be taking too long to load or may have JavaScript errors.
3597
-
3598
- Try:
3599
- 1. Check if the page loads in your browser
3600
- 2. Increase wait time: trie_visual_qa_browser url:"${url}" waitMs:5000
3601
- 3. Wait for specific element: trie_visual_qa_browser url:"${url}" waitForSelector:".main-content"`,
3602
- analysisPrompt: ""
3603
- };
3604
- }
3605
- return {
3606
- success: false,
3607
- url,
3608
- screenshots: [],
3609
- error: `Screenshot capture failed: ${errorMessage}
3610
-
3611
- Troubleshooting:
3612
- 1. Verify ${url} is accessible in your browser
3613
- 2. Check that your dev server is running
3614
- 3. Try specifying the URL explicitly: trie_visual_qa_browser url:"${url}"`,
3615
- analysisPrompt: ""
3616
- };
3617
- }
3618
- }
3619
- function buildAnalysisPrompt(url, screenshots) {
3620
- const viewportList = screenshots.map((s) => `- ${s.viewport}: ${s.width}x${s.height}`).join("\n");
3621
- return `## Visual QA Analysis
3622
-
3623
- I've captured screenshots of **${url}** at ${screenshots.length} viewports:
3624
-
3625
- ${viewportList}
3626
-
3627
- Please analyze these screenshots for:
3628
-
3629
- ### Layout Issues
3630
- - Overlapping elements or text
3631
- - Content overflow or clipping
3632
- - Broken layouts at specific breakpoints
3633
- - Misaligned elements
3634
- - Unexpected gaps or spacing
3635
-
3636
- ### Responsive Design
3637
- - Mobile navigation issues
3638
- - Text too small to read on mobile
3639
- - Touch targets too small (<44px)
3640
- - Horizontal scrolling on mobile
3641
- - Content not adapting to viewport
3642
-
3643
- ### Visual Polish
3644
- - Inconsistent spacing or alignment
3645
- - Poor color contrast (text hard to read)
3646
- - Broken images or missing assets
3647
- - Loading states stuck/visible
3648
- - Z-index issues (elements overlapping incorrectly)
3649
-
3650
- ### Accessibility
3651
- - Missing focus indicators
3652
- - Color-only information
3653
- - Text over images without sufficient contrast
3654
- - Very small text (<12px)
3655
-
3656
- ### General Quality
3657
- - Does it look professional?
3658
- - Is the hierarchy clear?
3659
- - Are interactive elements obvious?
3660
- - Any obvious bugs or glitches?
3661
-
3662
- For each issue found:
3663
- 1. **Viewport**: Which viewport(s) affected
3664
- 2. **Location**: Where on the page
3665
- 3. **Issue**: What's wrong
3666
- 4. **Severity**: Critical/Serious/Moderate/Low
3667
- 5. **Fix**: How to resolve it
3668
-
3669
- If the UI looks good, say so! Note what's working well.`;
3670
- }
3671
- function formatMCPResponse(result) {
3672
- if (!result.success) {
3673
- return {
3674
- content: [{
3675
- type: "text",
3676
- text: `# Visual QA Failed
3677
-
3678
- ${result.error}
3679
-
3680
- ## Troubleshooting
3681
-
3682
- 1. Make sure your dev server is running
3683
- 2. Try: \`trie_visual_qa url:"http://localhost:3000"\`
3684
- 3. If Playwright isn't installed: \`npx playwright install chromium\``
3685
- }]
3686
- };
3687
- }
3688
- const content = [];
3689
- content.push({
3690
- type: "text",
3691
- text: result.analysisPrompt
3692
- });
3693
- for (const screenshot of result.screenshots) {
3694
- content.push({
3695
- type: "text",
3696
- text: `
3697
- ### ${screenshot.viewport} (${screenshot.width}x${screenshot.height})
3698
- `
3699
- });
3700
- content.push({
3701
- type: "image",
3702
- data: screenshot.base64,
3703
- mimeType: screenshot.mimeType
3704
- });
3705
- }
3706
- return { content };
3707
- }
3708
-
3709
- // src/server/request-handlers.ts
3710
- var RequestHandlers = class {
3711
- constructor(toolRegistry, resourceManager) {
3712
- this.toolRegistry = toolRegistry;
3713
- this.resourceManager = resourceManager;
3714
- }
3715
- /**
3716
- * Handle tool execution requests
3717
- */
3718
- async handleToolCall(name, args) {
3719
- const normalizedName = this.normalizeName(name);
3720
- if (args && !args.directory && !args.files) {
3721
- const workingDir = getWorkingDirectory(void 0, true);
3722
- if (workingDir !== process.cwd()) {
3723
- args.directory = workingDir;
3724
- }
3725
- }
3726
- try {
3727
- switch (normalizedName) {
3728
- case "trie":
3729
- return await this.handleTrieMenu(args);
3730
- case "scan":
3731
- return await this.toolRegistry.getTool("scan").execute(args);
3732
- case "fix":
3733
- return await this.toolRegistry.getTool("fix").execute(args);
3734
- case "explain":
3735
- return await this.toolRegistry.getTool("explain").execute(args);
3736
- case "test":
3737
- return await this.toolRegistry.getTool("test").execute(args);
3738
- case "register_agent":
3739
- return await this.toolRegistry.getTool("register_agent").execute(args);
3740
- case "watch":
3741
- return await this.toolRegistry.getTool("watch").execute(args);
3742
- case "visual_qa_browser": {
3743
- try {
3744
- const result = await runVisualQA(args);
3745
- return formatMCPResponse(result);
3746
- } catch (error) {
3747
- const errorMessage = error instanceof Error ? error.message : String(error);
3748
- return {
3749
- content: [{
3750
- type: "text",
3751
- text: `# Visual QA Error
3752
-
3753
- Failed to capture screenshots: ${errorMessage}
3754
-
3755
- ## Troubleshooting
3756
-
3757
- 1. **Check if your dev server is running:**
3758
- - Try accessing the URL in your browser
3759
- - Common ports: 3000, 5173, 8080
3760
-
3761
- 2. **Specify the URL explicitly:**
3762
- \`\`\`
3763
- trie_visual_qa_browser url:"http://localhost:3000"
3764
- \`\`\`
3765
-
3766
- 3. **Specify a port:**
3767
- \`\`\`
3768
- trie_visual_qa_browser port:3000
3769
- \`\`\`
3770
-
3771
- 4. **Install Playwright browsers (if needed):**
3772
- \`\`\`
3773
- npx playwright install chromium
3774
- \`\`\`
3775
-
3776
- 5. **Check for JavaScript errors:**
3777
- - Open browser dev tools
3778
- - Look for console errors
3779
- - The page may be failing to load`
3780
- }]
3781
- };
3782
- }
3783
- }
3784
- case "pr_review":
3785
- return await this.toolRegistry.getTool("pr_review").execute(args);
3786
- case "project":
3787
- case "project_info":
3788
- return await this.toolRegistry.getTool("project").execute(args);
3789
- case "init":
3790
- return await this.toolRegistry.getTool("init").execute(args);
3791
- case "memory":
3792
- return await this.toolRegistry.getTool("memory").execute(args);
3793
- case "check":
3794
- return await this.toolRegistry.getTool("check").execute(args);
3795
- case "tell":
3796
- return await this.toolRegistry.getTool("tell").execute(args);
3797
- case "feedback":
3798
- case "ok":
3799
- case "bad":
3800
- return await this.toolRegistry.getTool("feedback").execute(args);
3801
- case "reconcile":
3802
- return await this.toolRegistry.getTool("reconcile").execute(args);
3803
- case "context":
3804
- return await this.toolRegistry.getTool("context").execute(args);
3805
- case "checkpoint":
3806
- case "cp":
3807
- case "save":
3808
- return await this.toolRegistry.getTool("checkpoint").execute(args);
3809
- case "cloud_fix":
3810
- return await this.toolRegistry.getTool("cloud_fix").execute(args);
3811
- case "linear_sync":
3812
- return await this.toolRegistry.getTool("linear_sync").execute(args);
3813
- case "github_sync":
3814
- return await this.toolRegistry.getTool("github_sync").execute(args);
3815
- case "github_branches":
3816
- return await this.toolRegistry.getTool("github_branches").execute(args);
3817
- case "pipeline":
3818
- return await this.toolRegistry.getTool("pipeline").execute(args);
3819
- case "index":
3820
- return await this.toolRegistry.getTool("index").execute(args);
3821
- default: {
3822
- const tool = this.toolRegistry.getTool(normalizedName);
3823
- if (tool) {
3824
- return await tool.execute(args);
3825
- }
3826
- throw new Error(`Unknown tool: ${name}`);
3827
- }
3828
- }
3829
- } catch (error) {
3830
- return {
3831
- content: [{
3832
- type: "text",
3833
- text: `Error: ${error instanceof Error ? error.message : String(error)}`
3834
- }]
3835
- };
3836
- }
3837
- }
3838
- /**
3839
- * Handle resource listing requests
3840
- */
3841
- async handleListResources() {
3842
- const resources = await this.resourceManager.getAvailableResources();
3843
- return { resources };
3844
- }
3845
- /**
3846
- * Handle resource reading requests
3847
- */
3848
- async handleReadResource(uri) {
3849
- return await this.resourceManager.readResourceContent(uri);
3850
- }
3851
- normalizeName(name) {
3852
- const stripNamespace = (n) => {
3853
- const trimmed = n.trim().replace(/\s*\([^)]*\)\s*$/, "").trim();
3854
- const withoutSlash = trimmed.startsWith("/") ? trimmed.slice(1) : trimmed;
3855
- const parts = withoutSlash.split(":");
3856
- const base = (parts[0] || withoutSlash).trim();
3857
- const slashParts = base.split("/");
3858
- return (slashParts[slashParts.length - 1] || base).trim();
3859
- };
3860
- const rawName = stripNamespace(name);
3861
- return rawName.startsWith("trie_") ? rawName.slice("trie_".length) : rawName;
3862
- }
3863
- async handleTrieMenu(args) {
3864
- const actionInput = typeof args?.action === "string" ? args.action : null;
3865
- if (actionInput) {
3866
- const normalizedAction = this.normalizeName(actionInput);
3867
- if (normalizedAction === "trie") {
3868
- return {
3869
- content: [{
3870
- type: "text",
3871
- text: 'Use `trie` without action for the menu, or provide a concrete action like "scan" or "security".'
3872
- }]
3873
- };
3874
- }
3875
- return await this.handleToolCall(normalizedAction, { ...args });
3876
- }
3877
- return {
3878
- content: [{
3879
- type: "text",
3880
- text: [
3881
- "## Trie Quick Actions",
3882
- "",
3883
- "**Most common:**",
3884
- "- `trie` (no args) \u2014 show this menu",
3885
- '- `action: "fix"` \u2014 high-confidence fixes',
3886
- '- `action: "watch"` \u2014 start/stop/status autonomous watch',
3887
- '- `action: "pr_review"` \u2014 interactive PR review',
3888
- "",
3889
- "**Other actions:**",
3890
- '- `action: "test"` \u2014 generate/coverage/suggest/run tests (requires `files` + `action`)',
3891
- '- `action: "explain"` \u2014 explain code/issues/risks',
3892
- '- `action: "visual_qa_browser"` \u2014 screenshots for visual QA',
3893
- "",
3894
- "All commands accept `trie_` prefix (e.g., `trie_fix`, `trie_watch`)."
3895
- ].join("\n")
3896
- }]
3897
- };
3898
- }
3899
- };
3900
-
3901
- // src/server/mcp-server.ts
3902
- var MCPServer = class {
3903
- server;
3904
- toolRegistry;
3905
- resourceManager;
3906
- requestHandlers;
3907
- constructor() {
3908
- this.server = new Server2(
3909
- {
3910
- name: "trie",
3911
- version: "1.0.0",
3912
- description: "Intelligent Agent Orchestration for AI Coding Tools. IMPORTANT: Read trie://context first to understand project state, priorities, and recent scan history before making changes."
3913
- },
3914
- {
3915
- capabilities: {
3916
- tools: {},
3917
- resources: {},
3918
- // Enable MCP Apps - interactive UI components in AI clients
3919
- // See: https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/
3920
- experimental: {
3921
- ui: {}
3922
- }
3923
- }
3924
- }
3925
- );
3926
- this.toolRegistry = new ToolRegistry();
3927
- this.resourceManager = new ResourceManager();
3928
- this.requestHandlers = new RequestHandlers(this.toolRegistry, this.resourceManager);
3929
- this.setupRequestHandlers();
3930
- }
3931
- setupRequestHandlers() {
3932
- this.server.setRequestHandler(ListToolsRequestSchema, async () => {
3933
- return {
3934
- tools: this.toolRegistry.getAllTools()
3935
- };
3936
- });
3937
- this.server.setRequestHandler(CallToolRequestSchema, async (request2) => {
3938
- const { name, arguments: args } = request2.params;
3939
- return await this.requestHandlers.handleToolCall(name, args);
3940
- });
3941
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
3942
- return await this.requestHandlers.handleListResources();
3943
- });
3944
- this.server.setRequestHandler(ReadResourceRequestSchema, async (request2) => {
3945
- const { uri } = request2.params;
3946
- return await this.requestHandlers.handleReadResource(uri);
3947
- });
3948
- }
3949
- /**
3950
- * Show startup banner
3951
- */
3952
- showStartupBanner(toolCount, aiTool) {
3953
- console.error(`
3954
- \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
3955
- \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
3956
- \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557
3957
- \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D
3958
- \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
3959
- \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D
3960
- Your central registry for Trie tools
3961
-
3962
- by Louis Kishfy
3963
-
3964
- Download the Trie workspace: https://www.trie.dev
3965
- Follow me on X: https://x.com/louiskishfy
3966
-
3967
- ${toolCount} tools ready | ${aiTool}
3968
-
3969
- Quick Start:
3970
- \u2022 "Scan this code" - Run Trie analysis
3971
- \u2022 "Use trie" - Open the Trie menu
3972
- \u2022 "Use trie_fix" - Apply high-confidence fixes
3973
-
3974
- Ready.
3975
- `);
3976
- }
3977
- /**
3978
- * Initialize and start the server
3979
- */
3980
- async start() {
3981
- try {
3982
- const aiTool = detectAITool();
3983
- await loadConfig();
3984
- const toolCount = this.toolRegistry.getAllTools().length;
3985
- this.showStartupBanner(toolCount, aiTool.name);
3986
- const transport = new StdioServerTransport();
3987
- await this.server.connect(transport);
3988
- } catch (error) {
3989
- console.error("Fatal error starting MCP server:", error);
3990
- process.exit(1);
3991
- }
3992
- }
3993
- };
3994
- async function startServer() {
3995
- const server = new MCPServer();
3996
- await server.start();
3997
- }
3998
-
3999
38
  // src/index.ts
4000
39
  startServer().catch((error) => {
4001
40
  console.error("Fatal error:", error);