@crowley/rag-mcp 1.0.5 → 1.0.6

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 (49) hide show
  1. package/dist/annotations.d.ts +16 -0
  2. package/dist/annotations.js +158 -0
  3. package/dist/context-enrichment.js +7 -0
  4. package/dist/formatters.d.ts +2 -0
  5. package/dist/formatters.js +12 -0
  6. package/dist/index.js +46 -47
  7. package/dist/schemas.d.ts +97 -0
  8. package/dist/schemas.js +128 -0
  9. package/dist/tool-middleware.d.ts +40 -0
  10. package/dist/tool-middleware.js +216 -0
  11. package/dist/tool-registry.js +2 -1
  12. package/dist/tools/advanced.d.ts +2 -2
  13. package/dist/tools/advanced.js +200 -275
  14. package/dist/tools/agents.d.ts +2 -2
  15. package/dist/tools/agents.js +59 -78
  16. package/dist/tools/analytics.d.ts +2 -2
  17. package/dist/tools/analytics.js +170 -210
  18. package/dist/tools/architecture.d.ts +2 -2
  19. package/dist/tools/architecture.js +506 -669
  20. package/dist/tools/ask.d.ts +2 -2
  21. package/dist/tools/ask.js +164 -219
  22. package/dist/tools/cache.d.ts +2 -2
  23. package/dist/tools/cache.js +63 -82
  24. package/dist/tools/clustering.d.ts +2 -2
  25. package/dist/tools/clustering.js +154 -215
  26. package/dist/tools/confluence.d.ts +2 -2
  27. package/dist/tools/confluence.js +80 -116
  28. package/dist/tools/database.d.ts +2 -2
  29. package/dist/tools/database.js +303 -380
  30. package/dist/tools/feedback.d.ts +2 -2
  31. package/dist/tools/feedback.js +143 -184
  32. package/dist/tools/guidelines.d.ts +2 -2
  33. package/dist/tools/guidelines.js +123 -135
  34. package/dist/tools/indexing.d.ts +2 -2
  35. package/dist/tools/indexing.js +100 -108
  36. package/dist/tools/memory.d.ts +2 -2
  37. package/dist/tools/memory.js +299 -485
  38. package/dist/tools/pm.d.ts +2 -2
  39. package/dist/tools/pm.js +367 -615
  40. package/dist/tools/review.d.ts +2 -2
  41. package/dist/tools/review.js +142 -189
  42. package/dist/tools/search.d.ts +2 -2
  43. package/dist/tools/search.js +230 -305
  44. package/dist/tools/session.d.ts +2 -2
  45. package/dist/tools/session.js +288 -345
  46. package/dist/tools/suggestions.d.ts +2 -2
  47. package/dist/tools/suggestions.js +425 -512
  48. package/dist/types.d.ts +19 -2
  49. package/package.json +4 -2
@@ -3,313 +3,238 @@
3
3
  * import suggestions, type context, and behavior patterns.
4
4
  */
5
5
  import { truncate, pct } from "../formatters.js";
6
+ import { z } from "zod";
7
+ import { TOOL_ANNOTATIONS } from "../annotations.js";
6
8
  /**
7
9
  * Create the advanced tools module with project-specific descriptions.
8
10
  */
9
11
  export function createAdvancedTools(projectName) {
10
- const tools = [
12
+ return [
11
13
  {
12
14
  name: "merge_memories",
13
15
  description: `Consolidate duplicate memories for ${projectName}. Finds similar memories and merges them using LLM to reduce clutter.`,
14
- inputSchema: {
15
- type: "object",
16
- properties: {
17
- type: {
18
- type: "string",
19
- description: "Filter by memory type (decision, insight, context, todo, conversation, note, or all). Default: all",
20
- default: "all",
21
- },
22
- threshold: {
23
- type: "number",
24
- description: "Similarity threshold for merging (0.5-1.0, default: 0.9). Lower = more aggressive merging.",
25
- default: 0.9,
26
- },
27
- dryRun: {
28
- type: "boolean",
29
- description: "If true, preview merge candidates without making changes (default: true).",
30
- default: true,
31
- },
32
- limit: {
33
- type: "number",
34
- description: "Max clusters to process (default: 50).",
35
- default: 50,
36
- },
37
- },
16
+ schema: z.object({
17
+ type: z.string().optional().describe("Filter by memory type (decision, insight, context, todo, conversation, note, or all). Default: all"),
18
+ threshold: z.number().optional().describe("Similarity threshold for merging (0.5-1.0, default: 0.9). Lower = more aggressive merging."),
19
+ dryRun: z.boolean().optional().describe("If true, preview merge candidates without making changes (default: true)."),
20
+ limit: z.number().optional().describe("Max clusters to process (default: 50)."),
21
+ }),
22
+ annotations: TOOL_ANNOTATIONS["merge_memories"],
23
+ handler: async (args, ctx) => {
24
+ const { type = "all", threshold = 0.9, dryRun = true, limit = 50 } = args;
25
+ const response = await ctx.api.post("/api/memory/merge", {
26
+ type,
27
+ threshold,
28
+ dryRun,
29
+ limit,
30
+ });
31
+ const data = response.data;
32
+ let result = `## Memory Merge${dryRun ? " (Dry Run)" : ""}\n\n`;
33
+ result += `- **Memories Scanned:** ${data.totalFound ?? 0}\n`;
34
+ result += `- **Merge Clusters Found:** ${data.totalMerged ?? 0}\n`;
35
+ result += `- **Threshold:** ${threshold}\n`;
36
+ if (dryRun) {
37
+ result += `\n*This was a dry run. Set dryRun=false to apply merges.*\n`;
38
+ }
39
+ if (data.merged && data.merged.length > 0) {
40
+ result += `\n### Merge Candidates\n\n`;
41
+ for (const cluster of data.merged.slice(0, 10)) {
42
+ const origCount = cluster.original?.length ?? 0;
43
+ result += `**Cluster (${origCount} memories → 1):**\n`;
44
+ if (cluster.original) {
45
+ for (const orig of cluster.original.slice(0, 3)) {
46
+ result += ` - ${truncate(orig.content || "", 80)}\n`;
47
+ }
48
+ if (origCount > 3) {
49
+ result += ` - ... and ${origCount - 3} more\n`;
50
+ }
51
+ }
52
+ result += ` **→ Merged:** ${truncate(cluster.merged?.content || "", 120)}\n\n`;
53
+ }
54
+ if (data.merged.length > 10) {
55
+ result += `... and ${data.merged.length - 10} more clusters\n`;
56
+ }
57
+ }
58
+ return result;
38
59
  },
39
60
  },
40
61
  {
41
62
  name: "get_completion_context",
42
63
  description: `Get code completion context for ${projectName}. Finds similar patterns, imports, and symbols from the codebase to aid code completion.`,
43
- inputSchema: {
44
- type: "object",
45
- properties: {
46
- currentFile: {
47
- type: "string",
48
- description: "Path of the file being edited",
49
- },
50
- currentCode: {
51
- type: "string",
52
- description: "Current code snippet or file content",
53
- },
54
- language: {
55
- type: "string",
56
- description: "Programming language filter (optional)",
57
- },
58
- limit: {
59
- type: "number",
60
- description: "Max results (default: 5)",
61
- default: 5,
62
- },
63
- },
64
- required: ["currentFile", "currentCode"],
64
+ schema: z.object({
65
+ currentFile: z.string().describe("Path of the file being edited"),
66
+ currentCode: z.string().describe("Current code snippet or file content"),
67
+ language: z.string().optional().describe("Programming language filter (optional)"),
68
+ limit: z.number().optional().describe("Max results (default: 5)"),
69
+ }),
70
+ annotations: TOOL_ANNOTATIONS["get_completion_context"],
71
+ handler: async (args, ctx) => {
72
+ const { currentFile, currentCode, language, limit = 5 } = args;
73
+ const response = await ctx.api.post("/api/code/completion-context", {
74
+ currentFile,
75
+ currentCode,
76
+ language,
77
+ limit,
78
+ });
79
+ const data = response.data;
80
+ let result = `## Completion Context\n\n`;
81
+ if (data.patterns && data.patterns.length > 0) {
82
+ result += `### Similar Patterns (${data.patterns.length})\n\n`;
83
+ for (const p of data.patterns) {
84
+ result += `**${p.file}** (${pct(p.score)} match)\n`;
85
+ result += "```\n" + truncate(p.content, 300) + "\n```\n\n";
86
+ }
87
+ }
88
+ else {
89
+ result += "No similar patterns found.\n\n";
90
+ }
91
+ if (data.imports && data.imports.length > 0) {
92
+ result += `### Imports from Similar Files\n`;
93
+ result += data.imports.map((i) => `- \`${i}\``).join("\n");
94
+ result += "\n\n";
95
+ }
96
+ if (data.symbols && data.symbols.length > 0) {
97
+ result += `### Available Symbols\n`;
98
+ result += data.symbols.map((s) => `- \`${s}\``).join("\n");
99
+ result += "\n";
100
+ }
101
+ return result;
65
102
  },
66
103
  },
67
104
  {
68
105
  name: "get_import_suggestions",
69
106
  description: `Suggest missing imports for ${projectName}. Analyzes similar files to find commonly used imports not present in your current file.`,
70
- inputSchema: {
71
- type: "object",
72
- properties: {
73
- currentFile: {
74
- type: "string",
75
- description: "Path of the file being edited",
76
- },
77
- currentCode: {
78
- type: "string",
79
- description: "Current code content",
80
- },
81
- language: {
82
- type: "string",
83
- description: "Programming language filter (optional)",
84
- },
85
- limit: {
86
- type: "number",
87
- description: "Max suggestions (default: 10)",
88
- default: 10,
89
- },
90
- },
91
- required: ["currentFile", "currentCode"],
107
+ schema: z.object({
108
+ currentFile: z.string().describe("Path of the file being edited"),
109
+ currentCode: z.string().describe("Current code content"),
110
+ language: z.string().optional().describe("Programming language filter (optional)"),
111
+ limit: z.number().optional().describe("Max suggestions (default: 10)"),
112
+ }),
113
+ annotations: TOOL_ANNOTATIONS["get_import_suggestions"],
114
+ handler: async (args, ctx) => {
115
+ const { currentFile, currentCode, language, limit = 10 } = args;
116
+ const response = await ctx.api.post("/api/code/import-suggestions", {
117
+ currentFile,
118
+ currentCode,
119
+ language,
120
+ limit,
121
+ });
122
+ const data = response.data;
123
+ let result = `## Import Suggestions\n\n`;
124
+ if (data.currentImports && data.currentImports.length > 0) {
125
+ result += `**Current Imports (${data.currentImports.length}):** ${data.currentImports.map((i) => `\`${i}\``).join(", ")}\n\n`;
126
+ }
127
+ if (data.suggestions && data.suggestions.length > 0) {
128
+ result += `### Suggested Imports\n\n`;
129
+ for (const s of data.suggestions) {
130
+ result += `- **\`${s.importPath}\`** — used in ${s.frequency} similar files`;
131
+ if (s.usedBy && s.usedBy.length > 0) {
132
+ result += ` (${s.usedBy.map((f) => truncate(f, 40)).join(", ")})`;
133
+ }
134
+ result += "\n";
135
+ }
136
+ }
137
+ else {
138
+ result += "No additional imports suggested.\n";
139
+ }
140
+ return result;
92
141
  },
93
142
  },
94
143
  {
95
144
  name: "get_type_context",
96
145
  description: `Look up type/interface/class definitions and usage in ${projectName}. Finds where a type is defined and how it's used across the codebase.`,
97
- inputSchema: {
98
- type: "object",
99
- properties: {
100
- typeName: {
101
- type: "string",
102
- description: "Name of the type/interface/class to look up",
103
- },
104
- code: {
105
- type: "string",
106
- description: "Code containing types to look up (alternative to typeName)",
107
- },
108
- currentFile: {
109
- type: "string",
110
- description: "Current file to exclude from results",
111
- },
112
- limit: {
113
- type: "number",
114
- description: "Max results per category (default: 5)",
115
- default: 5,
116
- },
117
- },
146
+ schema: z.object({
147
+ typeName: z.string().optional().describe("Name of the type/interface/class to look up"),
148
+ code: z.string().optional().describe("Code containing types to look up (alternative to typeName)"),
149
+ currentFile: z.string().optional().describe("Current file to exclude from results"),
150
+ limit: z.number().optional().describe("Max results per category (default: 5)"),
151
+ }),
152
+ annotations: TOOL_ANNOTATIONS["get_type_context"],
153
+ handler: async (args, ctx) => {
154
+ const { typeName, code, currentFile, limit = 5 } = args;
155
+ if (!typeName && !code) {
156
+ return "Error: Either typeName or code is required.";
157
+ }
158
+ const response = await ctx.api.post("/api/code/type-context", {
159
+ typeName,
160
+ code,
161
+ currentFile,
162
+ limit,
163
+ });
164
+ const data = response.data;
165
+ let result = `## Type Context${typeName ? `: ${typeName}` : ""}\n\n`;
166
+ if (data.definitions && data.definitions.length > 0) {
167
+ result += `### Definitions (${data.definitions.length})\n\n`;
168
+ for (const d of data.definitions) {
169
+ result += `**${d.file}** (${pct(d.score)} match)\n`;
170
+ result += "```\n" + truncate(d.content, 400) + "\n```\n\n";
171
+ }
172
+ }
173
+ else {
174
+ result += "No type definitions found.\n\n";
175
+ }
176
+ if (data.usages && data.usages.length > 0) {
177
+ result += `### Usage Examples (${data.usages.length})\n\n`;
178
+ for (const u of data.usages) {
179
+ result += `**${u.file}** (${pct(u.score)} match)\n`;
180
+ result += "```\n" + truncate(u.content, 300) + "\n```\n\n";
181
+ }
182
+ }
183
+ return result;
118
184
  },
119
185
  },
120
186
  {
121
187
  name: "get_behavior_patterns",
122
188
  description: `Analyze user workflow patterns for ${projectName}. Shows peak hours, tool preferences, common sequences, and session statistics.`,
123
- inputSchema: {
124
- type: "object",
125
- properties: {
126
- days: {
127
- type: "number",
128
- description: "Number of days to analyze (default: 7)",
129
- default: 7,
130
- },
131
- sessionId: {
132
- type: "string",
133
- description: "Filter to a specific session (optional)",
134
- },
135
- },
136
- },
137
- },
138
- ];
139
- const handlers = {
140
- merge_memories: async (args, ctx) => {
141
- const { type = "all", threshold = 0.9, dryRun = true, limit = 50 } = args;
142
- const response = await ctx.api.post("/api/memory/merge", {
143
- type,
144
- threshold,
145
- dryRun,
146
- limit,
147
- });
148
- const data = response.data;
149
- let result = `## Memory Merge${dryRun ? " (Dry Run)" : ""}\n\n`;
150
- result += `- **Memories Scanned:** ${data.totalFound ?? 0}\n`;
151
- result += `- **Merge Clusters Found:** ${data.totalMerged ?? 0}\n`;
152
- result += `- **Threshold:** ${threshold}\n`;
153
- if (dryRun) {
154
- result += `\n*This was a dry run. Set dryRun=false to apply merges.*\n`;
155
- }
156
- if (data.merged && data.merged.length > 0) {
157
- result += `\n### Merge Candidates\n\n`;
158
- for (const cluster of data.merged.slice(0, 10)) {
159
- const origCount = cluster.original?.length ?? 0;
160
- result += `**Cluster (${origCount} memories → 1):**\n`;
161
- if (cluster.original) {
162
- for (const orig of cluster.original.slice(0, 3)) {
163
- result += ` - ${truncate(orig.content || "", 80)}\n`;
164
- }
165
- if (origCount > 3) {
166
- result += ` - ... and ${origCount - 3} more\n`;
167
- }
168
- }
169
- result += ` **→ Merged:** ${truncate(cluster.merged?.content || "", 120)}\n\n`;
189
+ schema: z.object({
190
+ days: z.number().optional().describe("Number of days to analyze (default: 7)"),
191
+ sessionId: z.string().optional().describe("Filter to a specific session (optional)"),
192
+ }),
193
+ annotations: TOOL_ANNOTATIONS["get_behavior_patterns"],
194
+ handler: async (args, ctx) => {
195
+ const { days = 7, sessionId } = args;
196
+ const params = new URLSearchParams();
197
+ params.set("days", String(days));
198
+ if (sessionId)
199
+ params.set("sessionId", sessionId);
200
+ const response = await ctx.api.get(`/api/behavior-patterns?${params.toString()}`);
201
+ const data = response.data;
202
+ let result = `## Behavior Patterns (last ${days} days)\n\n`;
203
+ // Session stats
204
+ const ss = data.sessionStats;
205
+ if (ss) {
206
+ result += `### Session Statistics\n`;
207
+ result += `- **Total Sessions:** ${ss.totalSessions}\n`;
208
+ result += `- **Avg Tools/Session:** ${ss.avgToolsPerSession}\n`;
209
+ result += `- **Avg Duration:** ${ss.avgDurationMinutes} min\n\n`;
170
210
  }
171
- if (data.merged.length > 10) {
172
- result += `... and ${data.merged.length - 10} more clusters\n`;
173
- }
174
- }
175
- return result;
176
- },
177
- get_completion_context: async (args, ctx) => {
178
- const { currentFile, currentCode, language, limit = 5 } = args;
179
- const response = await ctx.api.post("/api/code/completion-context", {
180
- currentFile,
181
- currentCode,
182
- language,
183
- limit,
184
- });
185
- const data = response.data;
186
- let result = `## Completion Context\n\n`;
187
- if (data.patterns && data.patterns.length > 0) {
188
- result += `### Similar Patterns (${data.patterns.length})\n\n`;
189
- for (const p of data.patterns) {
190
- result += `**${p.file}** (${pct(p.score)} match)\n`;
191
- result += "```\n" + truncate(p.content, 300) + "\n```\n\n";
192
- }
193
- }
194
- else {
195
- result += "No similar patterns found.\n\n";
196
- }
197
- if (data.imports && data.imports.length > 0) {
198
- result += `### Imports from Similar Files\n`;
199
- result += data.imports.map((i) => `- \`${i}\``).join("\n");
200
- result += "\n\n";
201
- }
202
- if (data.symbols && data.symbols.length > 0) {
203
- result += `### Available Symbols\n`;
204
- result += data.symbols.map((s) => `- \`${s}\``).join("\n");
205
- result += "\n";
206
- }
207
- return result;
208
- },
209
- get_import_suggestions: async (args, ctx) => {
210
- const { currentFile, currentCode, language, limit = 10 } = args;
211
- const response = await ctx.api.post("/api/code/import-suggestions", {
212
- currentFile,
213
- currentCode,
214
- language,
215
- limit,
216
- });
217
- const data = response.data;
218
- let result = `## Import Suggestions\n\n`;
219
- if (data.currentImports && data.currentImports.length > 0) {
220
- result += `**Current Imports (${data.currentImports.length}):** ${data.currentImports.map((i) => `\`${i}\``).join(", ")}\n\n`;
221
- }
222
- if (data.suggestions && data.suggestions.length > 0) {
223
- result += `### Suggested Imports\n\n`;
224
- for (const s of data.suggestions) {
225
- result += `- **\`${s.importPath}\`** — used in ${s.frequency} similar files`;
226
- if (s.usedBy && s.usedBy.length > 0) {
227
- result += ` (${s.usedBy.map((f) => truncate(f, 40)).join(", ")})`;
211
+ // Peak hours
212
+ if (data.peakHours && data.peakHours.length > 0) {
213
+ result += `### Peak Hours\n`;
214
+ const top5 = data.peakHours.slice(0, 5);
215
+ for (const h of top5) {
216
+ result += `- **${String(h.hour).padStart(2, "0")}:00** — ${h.count} calls\n`;
228
217
  }
229
218
  result += "\n";
230
219
  }
231
- }
232
- else {
233
- result += "No additional imports suggested.\n";
234
- }
235
- return result;
236
- },
237
- get_type_context: async (args, ctx) => {
238
- const { typeName, code, currentFile, limit = 5 } = args;
239
- if (!typeName && !code) {
240
- return "Error: Either typeName or code is required.";
241
- }
242
- const response = await ctx.api.post("/api/code/type-context", {
243
- typeName,
244
- code,
245
- currentFile,
246
- limit,
247
- });
248
- const data = response.data;
249
- let result = `## Type Context${typeName ? `: ${typeName}` : ""}\n\n`;
250
- if (data.definitions && data.definitions.length > 0) {
251
- result += `### Definitions (${data.definitions.length})\n\n`;
252
- for (const d of data.definitions) {
253
- result += `**${d.file}** (${pct(d.score)} match)\n`;
254
- result += "```\n" + truncate(d.content, 400) + "\n```\n\n";
255
- }
256
- }
257
- else {
258
- result += "No type definitions found.\n\n";
259
- }
260
- if (data.usages && data.usages.length > 0) {
261
- result += `### Usage Examples (${data.usages.length})\n\n`;
262
- for (const u of data.usages) {
263
- result += `**${u.file}** (${pct(u.score)} match)\n`;
264
- result += "```\n" + truncate(u.content, 300) + "\n```\n\n";
265
- }
266
- }
267
- return result;
268
- },
269
- get_behavior_patterns: async (args, ctx) => {
270
- const { days = 7, sessionId } = args;
271
- const params = new URLSearchParams();
272
- params.set("days", String(days));
273
- if (sessionId)
274
- params.set("sessionId", sessionId);
275
- const response = await ctx.api.get(`/api/behavior-patterns?${params.toString()}`);
276
- const data = response.data;
277
- let result = `## Behavior Patterns (last ${days} days)\n\n`;
278
- // Session stats
279
- const ss = data.sessionStats;
280
- if (ss) {
281
- result += `### Session Statistics\n`;
282
- result += `- **Total Sessions:** ${ss.totalSessions}\n`;
283
- result += `- **Avg Tools/Session:** ${ss.avgToolsPerSession}\n`;
284
- result += `- **Avg Duration:** ${ss.avgDurationMinutes} min\n\n`;
285
- }
286
- // Peak hours
287
- if (data.peakHours && data.peakHours.length > 0) {
288
- result += `### Peak Hours\n`;
289
- const top5 = data.peakHours.slice(0, 5);
290
- for (const h of top5) {
291
- result += `- **${String(h.hour).padStart(2, "0")}:00** — ${h.count} calls\n`;
292
- }
293
- result += "\n";
294
- }
295
- // Tool preferences
296
- if (data.toolPreferences && data.toolPreferences.length > 0) {
297
- result += `### Tool Preferences\n`;
298
- for (const t of data.toolPreferences.slice(0, 10)) {
299
- result += `- **${t.tool}**: ${t.count} calls (avg ${t.avgDuration}ms)\n`;
220
+ // Tool preferences
221
+ if (data.toolPreferences && data.toolPreferences.length > 0) {
222
+ result += `### Tool Preferences\n`;
223
+ for (const t of data.toolPreferences.slice(0, 10)) {
224
+ result += `- **${t.tool}**: ${t.count} calls (avg ${t.avgDuration}ms)\n`;
225
+ }
226
+ result += "\n";
300
227
  }
301
- result += "\n";
302
- }
303
- // Workflows
304
- if (data.workflows && data.workflows.length > 0) {
305
- result += `### Common Workflows\n`;
306
- for (const w of data.workflows.slice(0, 10)) {
307
- result += `- ${w.sequence.join(" → ")} (${w.count}x)\n`;
228
+ // Workflows
229
+ if (data.workflows && data.workflows.length > 0) {
230
+ result += `### Common Workflows\n`;
231
+ for (const w of data.workflows.slice(0, 10)) {
232
+ result += `- ${w.sequence.join(" → ")} (${w.count}x)\n`;
233
+ }
234
+ result += "\n";
308
235
  }
309
- result += "\n";
310
- }
311
- return result;
236
+ return result;
237
+ },
312
238
  },
313
- };
314
- return { tools, handlers };
239
+ ];
315
240
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Agent tools module - run specialized agents and list agent types.
3
3
  */
4
- import type { ToolModule } from "../types.js";
4
+ import type { ToolSpec } from "../types.js";
5
5
  /**
6
6
  * Create the agent tools module with project-specific descriptions.
7
7
  */
8
- export declare function createAgentTools(projectName: string): ToolModule;
8
+ export declare function createAgentTools(projectName: string): ToolSpec[];