@crowley/rag-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-client.d.ts +4 -0
- package/dist/api-client.js +19 -0
- package/dist/context-enrichment.d.ts +44 -0
- package/dist/context-enrichment.js +190 -0
- package/dist/formatters.d.ts +33 -0
- package/dist/formatters.js +70 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +109 -0
- package/dist/tool-registry.d.ts +20 -0
- package/dist/tool-registry.js +123 -0
- package/dist/tools/advanced.d.ts +9 -0
- package/dist/tools/advanced.js +315 -0
- package/dist/tools/agents.d.ts +8 -0
- package/dist/tools/agents.js +97 -0
- package/dist/tools/analytics.d.ts +9 -0
- package/dist/tools/analytics.js +261 -0
- package/dist/tools/architecture.d.ts +5 -0
- package/dist/tools/architecture.js +720 -0
- package/dist/tools/ask.d.ts +9 -0
- package/dist/tools/ask.js +256 -0
- package/dist/tools/cache.d.ts +5 -0
- package/dist/tools/cache.js +98 -0
- package/dist/tools/clustering.d.ts +9 -0
- package/dist/tools/clustering.js +251 -0
- package/dist/tools/confluence.d.ts +9 -0
- package/dist/tools/confluence.js +147 -0
- package/dist/tools/database.d.ts +5 -0
- package/dist/tools/database.js +429 -0
- package/dist/tools/feedback.d.ts +9 -0
- package/dist/tools/feedback.js +220 -0
- package/dist/tools/guidelines.d.ts +5 -0
- package/dist/tools/guidelines.js +146 -0
- package/dist/tools/indexing.d.ts +9 -0
- package/dist/tools/indexing.js +129 -0
- package/dist/tools/memory.d.ts +9 -0
- package/dist/tools/memory.js +565 -0
- package/dist/tools/pm.d.ts +9 -0
- package/dist/tools/pm.js +680 -0
- package/dist/tools/review.d.ts +8 -0
- package/dist/tools/review.js +213 -0
- package/dist/tools/search.d.ts +9 -0
- package/dist/tools/search.js +377 -0
- package/dist/tools/session.d.ts +10 -0
- package/dist/tools/session.js +386 -0
- package/dist/tools/suggestions.d.ts +9 -0
- package/dist/tools/suggestions.js +301 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +4 -0
- package/package.json +40 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced tools module - memory merging, code completion context,
|
|
3
|
+
* import suggestions, type context, and behavior patterns.
|
|
4
|
+
*/
|
|
5
|
+
import { truncate, pct } from "../formatters.js";
|
|
6
|
+
/**
|
|
7
|
+
* Create the advanced tools module with project-specific descriptions.
|
|
8
|
+
*/
|
|
9
|
+
export function createAdvancedTools(projectName) {
|
|
10
|
+
const tools = [
|
|
11
|
+
{
|
|
12
|
+
name: "merge_memories",
|
|
13
|
+
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
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "get_completion_context",
|
|
42
|
+
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"],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "get_import_suggestions",
|
|
69
|
+
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"],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "get_type_context",
|
|
96
|
+
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
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "get_behavior_patterns",
|
|
122
|
+
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`;
|
|
170
|
+
}
|
|
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(", ")})`;
|
|
228
|
+
}
|
|
229
|
+
result += "\n";
|
|
230
|
+
}
|
|
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`;
|
|
300
|
+
}
|
|
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`;
|
|
308
|
+
}
|
|
309
|
+
result += "\n";
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
return { tools, handlers };
|
|
315
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools module - run specialized agents and list agent types.
|
|
3
|
+
*/
|
|
4
|
+
import type { ToolModule } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Create the agent tools module with project-specific descriptions.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createAgentTools(projectName: string): ToolModule;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools module - run specialized agents and list agent types.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Create the agent tools module with project-specific descriptions.
|
|
6
|
+
*/
|
|
7
|
+
export function createAgentTools(projectName) {
|
|
8
|
+
const tools = [
|
|
9
|
+
{
|
|
10
|
+
name: "run_agent",
|
|
11
|
+
description: `Run a specialized agent for ${projectName}. Agents autonomously research, review, or analyze using multiple tool calls. Returns result + reasoning trace.`,
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
type: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Agent type: research, review, documentation, refactor, or test",
|
|
18
|
+
enum: ["research", "review", "documentation", "refactor", "test"],
|
|
19
|
+
},
|
|
20
|
+
task: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "The task for the agent to perform",
|
|
23
|
+
},
|
|
24
|
+
context: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Optional additional context (code, requirements, etc.)",
|
|
27
|
+
},
|
|
28
|
+
maxIterations: {
|
|
29
|
+
type: "number",
|
|
30
|
+
description: "Maximum ReAct iterations (default: varies by agent type)",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: ["type", "task"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "get_agent_types",
|
|
38
|
+
description: `List available agent types for ${projectName} with descriptions.`,
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
const handlers = {
|
|
46
|
+
run_agent: async (args, ctx) => {
|
|
47
|
+
const { type, task, context, maxIterations } = args;
|
|
48
|
+
const response = await ctx.api.post("/api/agent/run", {
|
|
49
|
+
projectName: ctx.projectName,
|
|
50
|
+
agentType: type,
|
|
51
|
+
task,
|
|
52
|
+
context,
|
|
53
|
+
maxIterations,
|
|
54
|
+
});
|
|
55
|
+
const data = response.data;
|
|
56
|
+
// Format result with reasoning trace
|
|
57
|
+
let result = `## Agent Result (${data.type})\n`;
|
|
58
|
+
result += `**Task:** ${data.task}\n`;
|
|
59
|
+
result += `**Status:** ${data.status}`;
|
|
60
|
+
result += ` | **Iterations:** ${data.usage?.iterations || 0}`;
|
|
61
|
+
result += ` | **Tool Calls:** ${data.usage?.toolCalls || 0}`;
|
|
62
|
+
result += ` | **Duration:** ${data.usage?.durationMs ? Math.round(data.usage.durationMs / 1000) + "s" : "N/A"}`;
|
|
63
|
+
result += "\n\n";
|
|
64
|
+
if (data.error) {
|
|
65
|
+
result += `**Error:** ${data.error}\n\n`;
|
|
66
|
+
}
|
|
67
|
+
if (data.result) {
|
|
68
|
+
result += `### Result\n${data.result}\n\n`;
|
|
69
|
+
}
|
|
70
|
+
// Reasoning trace
|
|
71
|
+
if (data.steps && data.steps.length > 0) {
|
|
72
|
+
result += `### Reasoning Trace\n`;
|
|
73
|
+
for (const step of data.steps) {
|
|
74
|
+
result += `**Step ${step.iteration}:** ${step.thought?.slice(0, 200) || "..."}\n`;
|
|
75
|
+
if (step.action) {
|
|
76
|
+
result += ` Action: ${step.action.tool}(${JSON.stringify(step.action.input).slice(0, 100)})\n`;
|
|
77
|
+
}
|
|
78
|
+
if (step.observation) {
|
|
79
|
+
const obsPreview = step.observation.result?.slice(0, 150) || "...";
|
|
80
|
+
result += ` Result: ${obsPreview}${step.observation.truncated ? " [truncated]" : ""}\n`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
},
|
|
86
|
+
get_agent_types: async (_args, ctx) => {
|
|
87
|
+
const response = await ctx.api.get("/api/agent/types");
|
|
88
|
+
const data = response.data;
|
|
89
|
+
let result = `## Available Agent Types\n\n`;
|
|
90
|
+
for (const agent of data.agents || []) {
|
|
91
|
+
result += `- **${agent.name}**: ${agent.description}\n`;
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
return { tools, handlers };
|
|
97
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics tools module - tool analytics, knowledge gaps, collection analytics,
|
|
3
|
+
* backups, and quantization.
|
|
4
|
+
*/
|
|
5
|
+
import type { ToolModule } from "../types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Create the analytics tools module with project-specific descriptions.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createAnalyticsTools(projectName: string): ToolModule;
|