@triedotdev/mcp 1.0.168 → 1.0.170

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