@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.
Files changed (49) hide show
  1. package/dist/api-client.d.ts +4 -0
  2. package/dist/api-client.js +19 -0
  3. package/dist/context-enrichment.d.ts +44 -0
  4. package/dist/context-enrichment.js +190 -0
  5. package/dist/formatters.d.ts +33 -0
  6. package/dist/formatters.js +70 -0
  7. package/dist/index.d.ts +13 -0
  8. package/dist/index.js +109 -0
  9. package/dist/tool-registry.d.ts +20 -0
  10. package/dist/tool-registry.js +123 -0
  11. package/dist/tools/advanced.d.ts +9 -0
  12. package/dist/tools/advanced.js +315 -0
  13. package/dist/tools/agents.d.ts +8 -0
  14. package/dist/tools/agents.js +97 -0
  15. package/dist/tools/analytics.d.ts +9 -0
  16. package/dist/tools/analytics.js +261 -0
  17. package/dist/tools/architecture.d.ts +5 -0
  18. package/dist/tools/architecture.js +720 -0
  19. package/dist/tools/ask.d.ts +9 -0
  20. package/dist/tools/ask.js +256 -0
  21. package/dist/tools/cache.d.ts +5 -0
  22. package/dist/tools/cache.js +98 -0
  23. package/dist/tools/clustering.d.ts +9 -0
  24. package/dist/tools/clustering.js +251 -0
  25. package/dist/tools/confluence.d.ts +9 -0
  26. package/dist/tools/confluence.js +147 -0
  27. package/dist/tools/database.d.ts +5 -0
  28. package/dist/tools/database.js +429 -0
  29. package/dist/tools/feedback.d.ts +9 -0
  30. package/dist/tools/feedback.js +220 -0
  31. package/dist/tools/guidelines.d.ts +5 -0
  32. package/dist/tools/guidelines.js +146 -0
  33. package/dist/tools/indexing.d.ts +9 -0
  34. package/dist/tools/indexing.js +129 -0
  35. package/dist/tools/memory.d.ts +9 -0
  36. package/dist/tools/memory.js +565 -0
  37. package/dist/tools/pm.d.ts +9 -0
  38. package/dist/tools/pm.js +680 -0
  39. package/dist/tools/review.d.ts +8 -0
  40. package/dist/tools/review.js +213 -0
  41. package/dist/tools/search.d.ts +9 -0
  42. package/dist/tools/search.js +377 -0
  43. package/dist/tools/session.d.ts +10 -0
  44. package/dist/tools/session.js +386 -0
  45. package/dist/tools/suggestions.d.ts +9 -0
  46. package/dist/tools/suggestions.js +301 -0
  47. package/dist/types.d.ts +32 -0
  48. package/dist/types.js +4 -0
  49. package/package.json +40 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Ask tools module - question answering, code explanation, feature finding,
3
+ * conversation analysis, and auto-remember.
4
+ */
5
+ import type { ToolModule } from "../types.js";
6
+ /**
7
+ * Create the ask tools module with project-specific descriptions.
8
+ */
9
+ export declare function createAskTools(projectName: string): ToolModule;
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Ask tools module - question answering, code explanation, feature finding,
3
+ * conversation analysis, and auto-remember.
4
+ */
5
+ import { pct } from "../formatters.js";
6
+ /**
7
+ * Create the ask tools module with project-specific descriptions.
8
+ */
9
+ export function createAskTools(projectName) {
10
+ const tools = [
11
+ {
12
+ name: "ask_codebase",
13
+ description: `Ask a question about the ${projectName} codebase. Uses RAG + LLM to provide contextual answers.`,
14
+ inputSchema: {
15
+ type: "object",
16
+ properties: {
17
+ question: {
18
+ type: "string",
19
+ description: "Question about the codebase",
20
+ },
21
+ },
22
+ required: ["question"],
23
+ },
24
+ },
25
+ {
26
+ name: "explain_code",
27
+ description: "Get a detailed explanation of a code snippet.",
28
+ inputSchema: {
29
+ type: "object",
30
+ properties: {
31
+ code: {
32
+ type: "string",
33
+ description: "Code snippet to explain",
34
+ },
35
+ filePath: {
36
+ type: "string",
37
+ description: "Optional file path for context",
38
+ },
39
+ },
40
+ required: ["code"],
41
+ },
42
+ },
43
+ {
44
+ name: "find_feature",
45
+ description: `Find where a specific feature is implemented in the ${projectName} codebase.`,
46
+ inputSchema: {
47
+ type: "object",
48
+ properties: {
49
+ description: {
50
+ type: "string",
51
+ description: "Description of the feature to find",
52
+ },
53
+ },
54
+ required: ["description"],
55
+ },
56
+ },
57
+ {
58
+ name: "analyze_conversation",
59
+ description: `Analyze a conversation to extract learnings, decisions, and insights for ${projectName}.`,
60
+ inputSchema: {
61
+ type: "object",
62
+ properties: {
63
+ conversation: {
64
+ type: "string",
65
+ description: "The conversation text to analyze",
66
+ },
67
+ context: {
68
+ type: "string",
69
+ description: "Additional context about the conversation",
70
+ },
71
+ autoSave: {
72
+ type: "boolean",
73
+ description: "Automatically save extracted learnings (default: false)",
74
+ default: false,
75
+ },
76
+ minConfidence: {
77
+ type: "number",
78
+ description: "Minimum confidence threshold for learnings (0-1, default: 0.7)",
79
+ default: 0.7,
80
+ },
81
+ },
82
+ required: ["conversation"],
83
+ },
84
+ },
85
+ {
86
+ name: "auto_remember",
87
+ description: `Automatically classify and remember information for ${projectName}. Analyzes content to determine the best memory type.`,
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ content: {
92
+ type: "string",
93
+ description: "Content to analyze and remember",
94
+ },
95
+ context: {
96
+ type: "string",
97
+ description: "Additional context",
98
+ },
99
+ relatedTo: {
100
+ type: "string",
101
+ description: "Related feature or topic",
102
+ },
103
+ tags: {
104
+ type: "array",
105
+ items: { type: "string" },
106
+ description: "Tags for categorization",
107
+ },
108
+ },
109
+ required: ["content"],
110
+ },
111
+ },
112
+ ];
113
+ const handlers = {
114
+ ask_codebase: async (args, ctx) => {
115
+ const { question } = args;
116
+ const response = await ctx.api.post("/api/ask", {
117
+ collection: `${ctx.collectionPrefix}codebase`,
118
+ question,
119
+ });
120
+ return response.data.answer || "No answer could be generated.";
121
+ },
122
+ explain_code: async (args, ctx) => {
123
+ const { code, filePath } = args;
124
+ const response = await ctx.api.post("/api/explain", {
125
+ collection: `${ctx.collectionPrefix}codebase`,
126
+ code,
127
+ filePath,
128
+ });
129
+ const data = response.data;
130
+ let result = `## Summary\n${data.summary || "N/A"}\n\n`;
131
+ result += `## Purpose\n${data.purpose || "N/A"}\n\n`;
132
+ if (data.keyComponents && data.keyComponents.length > 0) {
133
+ result += `## Key Components\n`;
134
+ for (const comp of data.keyComponents) {
135
+ result += `- ${comp}\n`;
136
+ }
137
+ result += "\n";
138
+ }
139
+ if (data.dependencies && data.dependencies.length > 0) {
140
+ result += `## Dependencies\n`;
141
+ for (const dep of data.dependencies) {
142
+ result += `- ${dep}\n`;
143
+ }
144
+ }
145
+ return result;
146
+ },
147
+ find_feature: async (args, ctx) => {
148
+ const { description } = args;
149
+ const response = await ctx.api.post("/api/find-feature", {
150
+ collection: `${ctx.collectionPrefix}codebase`,
151
+ description,
152
+ });
153
+ const data = response.data;
154
+ let result = `## Feature: ${data.name || description}\n\n`;
155
+ result += `${data.explanation || "No explanation available."}\n\n`;
156
+ if (data.mainFiles && data.mainFiles.length > 0) {
157
+ result += `### Main Files\n`;
158
+ for (const f of data.mainFiles) {
159
+ result += `- **${f.file}** (${pct(f.score)} match)\n`;
160
+ }
161
+ result += "\n";
162
+ }
163
+ if (data.relatedFiles && data.relatedFiles.length > 0) {
164
+ result += `### Related Files\n`;
165
+ for (const f of data.relatedFiles) {
166
+ result += `- ${f.file}\n`;
167
+ }
168
+ }
169
+ return result;
170
+ },
171
+ analyze_conversation: async (args, ctx) => {
172
+ const { conversation, context, autoSave = false, minConfidence = 0.7 } = args;
173
+ const response = await ctx.api.post("/api/analyze-conversation", {
174
+ projectName: ctx.projectName,
175
+ conversation,
176
+ context,
177
+ autoSave,
178
+ minConfidence,
179
+ });
180
+ const data = response.data;
181
+ let result = `## Conversation Analysis\n\n`;
182
+ result += `**Summary:** ${data.summary || "N/A"}\n\n`;
183
+ if (data.learnings && data.learnings.length > 0) {
184
+ result += `### Extracted Learnings\n`;
185
+ for (const l of data.learnings) {
186
+ result += `- **[${l.type}]** (confidence: ${pct(l.confidence)}) ${l.content}\n`;
187
+ if (l.tags && l.tags.length > 0) {
188
+ result += ` Tags: ${l.tags.join(", ")}\n`;
189
+ }
190
+ }
191
+ result += "\n";
192
+ }
193
+ if (data.entities) {
194
+ if (data.entities.files && data.entities.files.length > 0) {
195
+ result += `### Referenced Files\n`;
196
+ for (const f of data.entities.files) {
197
+ result += `- ${f}\n`;
198
+ }
199
+ result += "\n";
200
+ }
201
+ if (data.entities.functions && data.entities.functions.length > 0) {
202
+ result += `### Referenced Functions\n`;
203
+ for (const f of data.entities.functions) {
204
+ result += `- ${f}\n`;
205
+ }
206
+ result += "\n";
207
+ }
208
+ }
209
+ if (data.savedCount !== undefined) {
210
+ result += `**Saved:** ${data.savedCount} learnings\n`;
211
+ }
212
+ return result;
213
+ },
214
+ auto_remember: async (args, ctx) => {
215
+ const { content, context, relatedTo, tags } = args;
216
+ // First, analyze the content to classify it
217
+ const analyzeResponse = await ctx.api.post("/api/analyze-conversation", {
218
+ projectName: ctx.projectName,
219
+ conversation: content,
220
+ context,
221
+ autoSave: false,
222
+ minConfidence: 0.5,
223
+ });
224
+ const analysis = analyzeResponse.data;
225
+ let memoryType = "note";
226
+ let confidence = 0;
227
+ if (analysis.learnings && analysis.learnings.length > 0) {
228
+ const best = analysis.learnings[0];
229
+ memoryType = best.type || "note";
230
+ confidence = best.confidence || 0;
231
+ }
232
+ // Save with the classified type
233
+ const rememberResponse = await ctx.api.post("/api/memory", {
234
+ projectName: ctx.projectName,
235
+ type: memoryType,
236
+ content,
237
+ relatedTo,
238
+ tags: tags || (analysis.learnings?.[0]?.tags),
239
+ metadata: {
240
+ source: 'auto_pattern',
241
+ confidence,
242
+ },
243
+ });
244
+ const saved = rememberResponse.data;
245
+ let result = `## Auto-Remembered\n\n`;
246
+ result += `- **Type:** ${memoryType}\n`;
247
+ result += `- **Confidence:** ${confidence > 0 ? pct(confidence) : "N/A (fallback to note)"}\n`;
248
+ result += `- **ID:** ${saved.id || "N/A"}\n`;
249
+ if (saved.tags && saved.tags.length > 0) {
250
+ result += `- **Tags:** ${saved.tags.join(", ")}\n`;
251
+ }
252
+ return result;
253
+ },
254
+ };
255
+ return { tools, handlers };
256
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Cache Management Tools
3
+ */
4
+ import type { ToolModule } from "../types.js";
5
+ export declare function createCacheTools(projectName: string): ToolModule;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Cache Management Tools
3
+ */
4
+ export function createCacheTools(projectName) {
5
+ const tools = [
6
+ {
7
+ name: "get_cache_stats",
8
+ description: `Get cache statistics for ${projectName}. Shows hit rates, cache levels, and memory usage.`,
9
+ inputSchema: {
10
+ type: "object",
11
+ properties: {
12
+ sessionId: {
13
+ type: "string",
14
+ description: "Optional session ID for session-specific stats",
15
+ },
16
+ },
17
+ },
18
+ },
19
+ {
20
+ name: "warm_cache",
21
+ description: `Warm the embedding cache for ${projectName}. Pre-loads frequently used embeddings for faster responses.`,
22
+ inputSchema: {
23
+ type: "object",
24
+ properties: {
25
+ sessionId: {
26
+ type: "string",
27
+ description: "Session ID to warm cache for",
28
+ },
29
+ previousSessionId: {
30
+ type: "string",
31
+ description: "Previous session to copy cache from (for session resumption)",
32
+ },
33
+ recentQueries: {
34
+ type: "array",
35
+ items: { type: "string" },
36
+ description: "Recent queries to pre-warm in cache",
37
+ },
38
+ },
39
+ required: ["sessionId"],
40
+ },
41
+ },
42
+ ];
43
+ const handlers = {
44
+ get_cache_stats: async (args, ctx) => {
45
+ const { sessionId } = args;
46
+ if (sessionId) {
47
+ const response = await ctx.api.get(`/api/cache/session/${sessionId}`);
48
+ const stats = response.data;
49
+ let result = `# 📊 Cache Stats for Session\n\n`;
50
+ result += `**Session ID**: ${sessionId}\n\n`;
51
+ result += `## Hit Rates\n`;
52
+ result += `- **Total Hits**: ${stats.hits}\n`;
53
+ result += `- **Misses**: ${stats.misses}\n`;
54
+ result += `- **Hit Rate**: ${(stats.hitRate * 100).toFixed(1)}%\n\n`;
55
+ result += `## Cache Level Distribution\n`;
56
+ result += `- **L1 (Session)**: ${stats.l1Hits} hits\n`;
57
+ result += `- **L2 (Project)**: ${stats.l2Hits} hits\n`;
58
+ result += `- **L3 (Global)**: ${stats.l3Hits} hits\n`;
59
+ return result;
60
+ }
61
+ const response = await ctx.api.get("/api/cache/analytics");
62
+ const analytics = response.data;
63
+ let result = `# 📊 Global Cache Analytics\n\n`;
64
+ result += `**Status**: ${analytics.enabled ? (analytics.connected ? "🟢 Connected" : "🟡 Disconnected") : "🔴 Disabled"}\n\n`;
65
+ if (analytics.connected) {
66
+ result += `## Keys\n`;
67
+ result += `- **Total**: ${analytics.totalKeys?.toLocaleString() || "N/A"}\n`;
68
+ result += `- **Embeddings**: ${analytics.embeddingKeys?.toLocaleString() || "N/A"}\n`;
69
+ result += `- **Search**: ${analytics.searchKeys?.toLocaleString() || "N/A"}\n`;
70
+ result += `- **Sessions**: ${analytics.sessionKeys?.toLocaleString() || "N/A"}\n\n`;
71
+ result += `## Memory\n`;
72
+ result += `- **Used**: ${analytics.memoryUsage || "N/A"}\n`;
73
+ }
74
+ return result;
75
+ },
76
+ warm_cache: async (args, ctx) => {
77
+ const { sessionId, previousSessionId, recentQueries } = args;
78
+ const response = await ctx.api.post("/api/cache/warm", {
79
+ sessionId,
80
+ previousSessionId,
81
+ recentQueries,
82
+ });
83
+ const { warmedCount } = response.data;
84
+ let result = `🔥 **Cache Warmed**\n\n`;
85
+ result += `- **Session ID**: ${sessionId}\n`;
86
+ result += `- **Pre-loaded**: ${warmedCount} embeddings\n`;
87
+ if (previousSessionId) {
88
+ result += `- **Resumed from**: ${previousSessionId}\n`;
89
+ }
90
+ if (recentQueries && recentQueries.length > 0) {
91
+ result += `- **Queries warmed**: ${recentQueries.length}\n`;
92
+ }
93
+ result += `\nThe session cache is now primed for faster responses!`;
94
+ return result;
95
+ },
96
+ };
97
+ return { tools, handlers };
98
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Clustering tools module - code clustering, duplicate detection,
3
+ * similarity recommendations, and learning extraction.
4
+ */
5
+ import type { ToolModule } from "../types.js";
6
+ /**
7
+ * Create the clustering tools module with project-specific descriptions.
8
+ */
9
+ export declare function createClusteringTools(projectName: string): ToolModule;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Clustering tools module - code clustering, duplicate detection,
3
+ * similarity recommendations, and learning extraction.
4
+ */
5
+ import { truncate, pct, PREVIEW } from "../formatters.js";
6
+ /**
7
+ * Create the clustering tools module with project-specific descriptions.
8
+ */
9
+ export function createClusteringTools(projectName) {
10
+ const tools = [
11
+ {
12
+ name: "cluster_code",
13
+ description: `Cluster code in the ${projectName} codebase by similarity. Groups related files around seed points.`,
14
+ inputSchema: {
15
+ type: "object",
16
+ properties: {
17
+ seedIds: {
18
+ type: "array",
19
+ items: { type: "string" },
20
+ description: "Seed point IDs to cluster around",
21
+ },
22
+ limit: {
23
+ type: "number",
24
+ description: "Max results per cluster (default: 5)",
25
+ default: 5,
26
+ },
27
+ threshold: {
28
+ type: "number",
29
+ description: "Minimum similarity threshold (0-1, default: 0.7)",
30
+ default: 0.7,
31
+ },
32
+ },
33
+ required: ["seedIds"],
34
+ },
35
+ },
36
+ {
37
+ name: "find_duplicates",
38
+ description: `Find duplicate or near-duplicate code in ${projectName}. Groups similar files by content.`,
39
+ inputSchema: {
40
+ type: "object",
41
+ properties: {
42
+ collection: {
43
+ type: "string",
44
+ description: "Collection to search (default: codebase)",
45
+ },
46
+ limit: {
47
+ type: "number",
48
+ description: "Max duplicate groups to return (default: 10)",
49
+ default: 10,
50
+ },
51
+ threshold: {
52
+ type: "number",
53
+ description: "Minimum similarity threshold (0-1, default: 0.9)",
54
+ default: 0.9,
55
+ },
56
+ },
57
+ },
58
+ },
59
+ {
60
+ name: "recommend_similar",
61
+ description: `Recommend similar code based on positive and negative examples in ${projectName}.`,
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {
65
+ positiveIds: {
66
+ type: "array",
67
+ items: { type: "string" },
68
+ description: "IDs of vectors to find similar code to",
69
+ },
70
+ negativeIds: {
71
+ type: "array",
72
+ items: { type: "string" },
73
+ description: "IDs of vectors to avoid (dissimilar)",
74
+ },
75
+ limit: {
76
+ type: "number",
77
+ description: "Max results (default: 5)",
78
+ default: 5,
79
+ },
80
+ },
81
+ required: ["positiveIds"],
82
+ },
83
+ },
84
+ {
85
+ name: "extract_learnings",
86
+ description: `Extract learnings and insights from text for ${projectName}. Identifies decisions, patterns, and concepts.`,
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ text: {
91
+ type: "string",
92
+ description: "Text to extract learnings from",
93
+ },
94
+ context: {
95
+ type: "string",
96
+ description: "Additional context about the text",
97
+ },
98
+ autoSave: {
99
+ type: "boolean",
100
+ description: "Automatically save extracted learnings (default: false)",
101
+ default: false,
102
+ },
103
+ minConfidence: {
104
+ type: "number",
105
+ description: "Minimum confidence threshold (0-1, default: 0.7)",
106
+ default: 0.7,
107
+ },
108
+ },
109
+ required: ["text"],
110
+ },
111
+ },
112
+ ];
113
+ const handlers = {
114
+ cluster_code: async (args, ctx) => {
115
+ const { seedIds, limit = 5, threshold = 0.7 } = args;
116
+ const response = await ctx.api.post("/api/clusters", {
117
+ collection: `${ctx.projectName}_codebase`,
118
+ seedIds,
119
+ limit,
120
+ threshold,
121
+ });
122
+ const data = response.data;
123
+ const clusters = data.clusters || data;
124
+ if (!clusters || (Array.isArray(clusters) && clusters.length === 0)) {
125
+ return "No clusters found.";
126
+ }
127
+ let result = `## Code Clusters\n\n`;
128
+ for (const cluster of Array.isArray(clusters) ? clusters : [clusters]) {
129
+ result += `### Seed: ${cluster.seedId || cluster.seed || "unknown"}\n`;
130
+ const files = cluster.similar || cluster.files || cluster.results || [];
131
+ for (const f of files) {
132
+ result += `- **${f.file || f.name}** (${pct(f.score || f.similarity)})`;
133
+ if (f.content)
134
+ result += `\n ${truncate(f.content, PREVIEW.SHORT)}`;
135
+ result += "\n";
136
+ }
137
+ result += "\n";
138
+ }
139
+ return result;
140
+ },
141
+ find_duplicates: async (args, ctx) => {
142
+ const { collection, limit = 10, threshold = 0.9 } = args;
143
+ const fullCollection = collection
144
+ ? (collection.startsWith(ctx.collectionPrefix) ? collection : `${ctx.collectionPrefix}${collection}`)
145
+ : `${ctx.collectionPrefix}codebase`;
146
+ const response = await ctx.api.post("/api/duplicates", {
147
+ collection: fullCollection,
148
+ limit,
149
+ threshold,
150
+ });
151
+ const data = response.data;
152
+ const groups = data.groups || data.duplicates || data;
153
+ if (!groups || (Array.isArray(groups) && groups.length === 0)) {
154
+ return "No duplicates found.";
155
+ }
156
+ let result = `## Duplicate Code Groups\n\n`;
157
+ let groupNum = 1;
158
+ for (const group of Array.isArray(groups) ? groups : [groups]) {
159
+ result += `### Group ${groupNum++}\n`;
160
+ const files = group.files || group.items || group.results || [];
161
+ const similarity = group.similarity || group.score;
162
+ if (similarity) {
163
+ result += `**Similarity:** ${pct(similarity)}\n`;
164
+ }
165
+ for (const f of files) {
166
+ result += `- **${f.file || f.name}**`;
167
+ if (f.content)
168
+ result += `\n ${truncate(f.content, 80)}`;
169
+ result += "\n";
170
+ }
171
+ result += "\n";
172
+ }
173
+ return result;
174
+ },
175
+ recommend_similar: async (args, ctx) => {
176
+ const { positiveIds, negativeIds, limit = 5 } = args;
177
+ const response = await ctx.api.post("/api/recommend", {
178
+ collection: `${ctx.projectName}_codebase`,
179
+ positiveIds,
180
+ negativeIds,
181
+ limit,
182
+ });
183
+ const results = response.data.results || response.data;
184
+ if (!results || results.length === 0) {
185
+ return "No recommendations found.";
186
+ }
187
+ let result = `## Recommendations\n\n`;
188
+ for (const r of results) {
189
+ result += `- **${r.file || r.name}** (${pct(r.score || r.similarity)})`;
190
+ if (r.content)
191
+ result += `\n ${truncate(r.content, PREVIEW.SHORT)}`;
192
+ result += "\n";
193
+ }
194
+ return result;
195
+ },
196
+ extract_learnings: async (args, ctx) => {
197
+ const { text, context, autoSave = false, minConfidence = 0.7 } = args;
198
+ const response = await ctx.api.post("/api/memory/extract", {
199
+ projectName: ctx.projectName,
200
+ text,
201
+ context,
202
+ autoSave,
203
+ minConfidence,
204
+ });
205
+ const data = response.data;
206
+ let result = `## Extracted Learnings\n\n`;
207
+ result += `**Summary:** ${data.summary || "N/A"}\n\n`;
208
+ if (data.learnings && data.learnings.length > 0) {
209
+ result += `### Learnings\n`;
210
+ for (const l of data.learnings) {
211
+ result += `- **[${l.type}]** (confidence: ${pct(l.confidence)}) ${l.content}\n`;
212
+ if (l.tags && l.tags.length > 0) {
213
+ result += ` Tags: ${l.tags.join(", ")}\n`;
214
+ }
215
+ if (l.reasoning) {
216
+ result += ` *Reasoning: ${l.reasoning}*\n`;
217
+ }
218
+ }
219
+ result += "\n";
220
+ }
221
+ if (data.entities) {
222
+ if (data.entities.files && data.entities.files.length > 0) {
223
+ result += `### Referenced Files\n`;
224
+ for (const f of data.entities.files) {
225
+ result += `- ${f}\n`;
226
+ }
227
+ result += "\n";
228
+ }
229
+ if (data.entities.functions && data.entities.functions.length > 0) {
230
+ result += `### Referenced Functions\n`;
231
+ for (const f of data.entities.functions) {
232
+ result += `- ${f}\n`;
233
+ }
234
+ result += "\n";
235
+ }
236
+ if (data.entities.concepts && data.entities.concepts.length > 0) {
237
+ result += `### Concepts\n`;
238
+ for (const c of data.entities.concepts) {
239
+ result += `- ${c}\n`;
240
+ }
241
+ result += "\n";
242
+ }
243
+ }
244
+ if (data.savedCount !== undefined) {
245
+ result += `**Saved:** ${data.savedCount} learnings\n`;
246
+ }
247
+ return result;
248
+ },
249
+ };
250
+ return { tools, handlers };
251
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Confluence tools module - search, index, status, and space listing
3
+ * for Confluence integration.
4
+ */
5
+ import type { ToolModule } from "../types.js";
6
+ /**
7
+ * Create the Confluence tools module with project-specific descriptions.
8
+ */
9
+ export declare function createConfluenceTools(projectName: string): ToolModule;