@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,249 +3,188 @@
3
3
  * similarity recommendations, and learning extraction.
4
4
  */
5
5
  import { truncate, pct, PREVIEW } from "../formatters.js";
6
+ import { z } from "zod";
7
+ import { TOOL_ANNOTATIONS } from "../annotations.js";
6
8
  /**
7
9
  * Create the clustering tools module with project-specific descriptions.
8
10
  */
9
11
  export function createClusteringTools(projectName) {
10
- const tools = [
12
+ return [
11
13
  {
12
14
  name: "cluster_code",
13
15
  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"],
16
+ schema: z.object({
17
+ seedIds: z.array(z.string()).describe("Seed point IDs to cluster around"),
18
+ limit: z.number().optional().describe("Max results per cluster (default: 5)"),
19
+ threshold: z.number().optional().describe("Minimum similarity threshold (0-1, default: 0.7)"),
20
+ }),
21
+ annotations: TOOL_ANNOTATIONS["cluster_code"],
22
+ handler: async (args, ctx) => {
23
+ const { seedIds, limit = 5, threshold = 0.7 } = args;
24
+ const response = await ctx.api.post("/api/clusters", {
25
+ collection: `${ctx.collectionPrefix}codebase`,
26
+ seedIds,
27
+ limit,
28
+ threshold,
29
+ });
30
+ const data = response.data;
31
+ const clusters = data.clusters || data;
32
+ if (!clusters || (Array.isArray(clusters) && clusters.length === 0)) {
33
+ return "No clusters found.";
34
+ }
35
+ let result = `## Code Clusters\n\n`;
36
+ for (const cluster of Array.isArray(clusters) ? clusters : [clusters]) {
37
+ result += `### Seed: ${cluster.seedId || cluster.seed || "unknown"}\n`;
38
+ const files = cluster.similar || cluster.files || cluster.results || [];
39
+ for (const f of files) {
40
+ result += `- **${f.file || f.name}** (${pct(f.score || f.similarity)})`;
41
+ if (f.content)
42
+ result += `\n ${truncate(f.content, PREVIEW.SHORT)}`;
43
+ result += "\n";
44
+ }
45
+ result += "\n";
46
+ }
47
+ return result;
34
48
  },
35
49
  },
36
50
  {
37
51
  name: "find_duplicates",
38
52
  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
- },
53
+ schema: z.object({
54
+ collection: z.string().optional().describe("Collection to search (default: codebase)"),
55
+ limit: z.number().optional().describe("Max duplicate groups to return (default: 10)"),
56
+ threshold: z.number().optional().describe("Minimum similarity threshold (0-1, default: 0.9)"),
57
+ }),
58
+ annotations: TOOL_ANNOTATIONS["find_duplicates"],
59
+ handler: async (args, ctx) => {
60
+ const { collection, limit = 10, threshold = 0.9 } = args;
61
+ const fullCollection = collection
62
+ ? (collection.startsWith(ctx.collectionPrefix) ? collection : `${ctx.collectionPrefix}${collection}`)
63
+ : `${ctx.collectionPrefix}codebase`;
64
+ const response = await ctx.api.post("/api/duplicates", {
65
+ collection: fullCollection,
66
+ limit,
67
+ threshold,
68
+ });
69
+ const data = response.data;
70
+ const groups = data.groups || data.duplicates || data;
71
+ if (!groups || (Array.isArray(groups) && groups.length === 0)) {
72
+ return "No duplicates found.";
73
+ }
74
+ let result = `## Duplicate Code Groups\n\n`;
75
+ let groupNum = 1;
76
+ for (const group of Array.isArray(groups) ? groups : [groups]) {
77
+ result += `### Group ${groupNum++}\n`;
78
+ const files = group.files || group.items || group.results || [];
79
+ const similarity = group.similarity || group.score;
80
+ if (similarity) {
81
+ result += `**Similarity:** ${pct(similarity)}\n`;
82
+ }
83
+ for (const f of files) {
84
+ result += `- **${f.file || f.name}**`;
85
+ if (f.content)
86
+ result += `\n ${truncate(f.content, 80)}`;
87
+ result += "\n";
88
+ }
89
+ result += "\n";
90
+ }
91
+ return result;
57
92
  },
58
93
  },
59
94
  {
60
95
  name: "recommend_similar",
61
96
  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"],
97
+ schema: z.object({
98
+ positiveIds: z.array(z.string()).describe("IDs of vectors to find similar code to"),
99
+ negativeIds: z.array(z.string()).optional().describe("IDs of vectors to avoid (dissimilar)"),
100
+ limit: z.number().optional().describe("Max results (default: 5)"),
101
+ }),
102
+ annotations: TOOL_ANNOTATIONS["recommend_similar"],
103
+ handler: async (args, ctx) => {
104
+ const { positiveIds, negativeIds, limit = 5 } = args;
105
+ const response = await ctx.api.post("/api/recommend", {
106
+ collection: `${ctx.collectionPrefix}codebase`,
107
+ positiveIds,
108
+ negativeIds,
109
+ limit,
110
+ });
111
+ const results = response.data.results || response.data;
112
+ if (!results || results.length === 0) {
113
+ return "No recommendations found.";
114
+ }
115
+ let result = `## Recommendations\n\n`;
116
+ for (const r of results) {
117
+ result += `- **${r.file || r.name}** (${pct(r.score || r.similarity)})`;
118
+ if (r.content)
119
+ result += `\n ${truncate(r.content, PREVIEW.SHORT)}`;
120
+ result += "\n";
121
+ }
122
+ return result;
82
123
  },
83
124
  },
84
125
  {
85
126
  name: "extract_learnings",
86
127
  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)}`;
128
+ schema: z.object({
129
+ text: z.string().describe("Text to extract learnings from"),
130
+ context: z.string().optional().describe("Additional context about the text"),
131
+ autoSave: z.boolean().optional().describe("Automatically save extracted learnings (default: false)"),
132
+ minConfidence: z.number().optional().describe("Minimum confidence threshold (0-1, default: 0.7)"),
133
+ }),
134
+ annotations: TOOL_ANNOTATIONS["extract_learnings"],
135
+ handler: async (args, ctx) => {
136
+ const { text, context, autoSave = false, minConfidence = 0.7 } = args;
137
+ const response = await ctx.api.post("/api/memory/extract", {
138
+ projectName: ctx.projectName,
139
+ text,
140
+ context,
141
+ autoSave,
142
+ minConfidence,
143
+ });
144
+ const data = response.data;
145
+ let result = `## Extracted Learnings\n\n`;
146
+ result += `**Summary:** ${data.summary || "N/A"}\n\n`;
147
+ if (data.learnings && data.learnings.length > 0) {
148
+ result += `### Learnings\n`;
149
+ for (const l of data.learnings) {
150
+ result += `- **[${l.type}]** (confidence: ${pct(l.confidence)}) ${l.content}\n`;
151
+ if (l.tags && l.tags.length > 0) {
152
+ result += ` Tags: ${l.tags.join(", ")}\n`;
153
+ }
154
+ if (l.reasoning) {
155
+ result += ` *Reasoning: ${l.reasoning}*\n`;
156
+ }
157
+ }
169
158
  result += "\n";
170
159
  }
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`;
160
+ if (data.entities) {
161
+ if (data.entities.files && data.entities.files.length > 0) {
162
+ result += `### Referenced Files\n`;
163
+ for (const f of data.entities.files) {
164
+ result += `- ${f}\n`;
165
+ }
166
+ result += "\n";
214
167
  }
215
- if (l.reasoning) {
216
- result += ` *Reasoning: ${l.reasoning}*\n`;
168
+ if (data.entities.functions && data.entities.functions.length > 0) {
169
+ result += `### Referenced Functions\n`;
170
+ for (const f of data.entities.functions) {
171
+ result += `- ${f}\n`;
172
+ }
173
+ result += "\n";
217
174
  }
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`;
175
+ if (data.entities.concepts && data.entities.concepts.length > 0) {
176
+ result += `### Concepts\n`;
177
+ for (const c of data.entities.concepts) {
178
+ result += `- ${c}\n`;
179
+ }
180
+ result += "\n";
226
181
  }
227
- result += "\n";
228
182
  }
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";
183
+ if (data.savedCount !== undefined) {
184
+ result += `**Saved:** ${data.savedCount} learnings\n`;
235
185
  }
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;
186
+ return result;
187
+ },
248
188
  },
249
- };
250
- return { tools, handlers };
189
+ ];
251
190
  }
@@ -2,8 +2,8 @@
2
2
  * Confluence tools module - search, index, status, and space listing
3
3
  * for Confluence integration.
4
4
  */
5
- import type { ToolModule } from "../types.js";
5
+ import type { ToolSpec } from "../types.js";
6
6
  /**
7
7
  * Create the Confluence tools module with project-specific descriptions.
8
8
  */
9
- export declare function createConfluenceTools(projectName: string): ToolModule;
9
+ export declare function createConfluenceTools(projectName: string): ToolSpec[];
@@ -3,145 +3,109 @@
3
3
  * for Confluence integration.
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 Confluence tools module with project-specific descriptions.
8
10
  */
9
11
  export function createConfluenceTools(projectName) {
10
- const tools = [
12
+ return [
11
13
  {
12
14
  name: "search_confluence",
13
15
  description: `Search indexed Confluence documentation for ${projectName}. Returns relevant pages with content snippets.`,
14
- inputSchema: {
15
- type: "object",
16
- properties: {
17
- query: {
18
- type: "string",
19
- description: "Search query for Confluence content",
20
- },
21
- limit: {
22
- type: "number",
23
- description: "Max results (default: 5)",
24
- default: 5,
25
- },
26
- spaceKey: {
27
- type: "string",
28
- description: "Filter by Confluence space key",
29
- },
30
- },
31
- required: ["query"],
16
+ schema: z.object({
17
+ query: z.string().describe("Search query for Confluence content"),
18
+ limit: z.number().optional().describe("Max results (default: 5)"),
19
+ spaceKey: z.string().optional().describe("Filter by Confluence space key"),
20
+ }),
21
+ annotations: TOOL_ANNOTATIONS["search_confluence"],
22
+ handler: async (args, ctx) => {
23
+ const { query, limit = 5, spaceKey } = args;
24
+ const response = await ctx.api.post("/api/search", {
25
+ collection: `${ctx.collectionPrefix}confluence`,
26
+ query,
27
+ limit,
28
+ filters: spaceKey ? { spaceKey } : undefined,
29
+ });
30
+ const results = response.data.results;
31
+ if (!results || results.length === 0) {
32
+ return "No Confluence results found.";
33
+ }
34
+ return results
35
+ .map((r) => `### ${r.title || r.file || "Untitled"}\n` +
36
+ `**Score:** ${pct(r.score)}` +
37
+ (r.spaceKey ? ` | **Space:** ${r.spaceKey}` : "") +
38
+ (r.url ? ` | [View](${r.url})` : "") +
39
+ `\n\n${truncate(r.content || "", 600)}`)
40
+ .join("\n\n---\n\n");
32
41
  },
33
42
  },
34
43
  {
35
44
  name: "index_confluence",
36
45
  description: `Index Confluence spaces/pages for ${projectName}. Requires Confluence credentials in RAG API.`,
37
- inputSchema: {
38
- type: "object",
39
- properties: {
40
- spaceKeys: {
41
- type: "array",
42
- items: { type: "string" },
43
- description: "Specific space keys to index (indexes all accessible if empty)",
44
- },
45
- labels: {
46
- type: "array",
47
- items: { type: "string" },
48
- description: "Filter pages by labels",
49
- },
50
- maxPages: {
51
- type: "number",
52
- description: "Maximum pages to index (default: 500)",
53
- default: 500,
54
- },
55
- force: {
56
- type: "boolean",
57
- description: "Force re-index even if already indexed",
58
- default: false,
59
- },
60
- },
46
+ schema: z.object({
47
+ spaceKeys: z.array(z.string()).optional().describe("Specific space keys to index (indexes all accessible if empty)"),
48
+ labels: z.array(z.string()).optional().describe("Filter pages by labels"),
49
+ maxPages: z.number().optional().describe("Maximum pages to index (default: 500)"),
50
+ force: z.boolean().optional().describe("Force re-index even if already indexed"),
51
+ }),
52
+ annotations: TOOL_ANNOTATIONS["index_confluence"],
53
+ handler: async (args, ctx) => {
54
+ const { spaceKeys, labels, maxPages = 500, force = false } = args;
55
+ const response = await ctx.api.post("/api/index/confluence", {
56
+ projectName: ctx.projectName,
57
+ spaceKeys,
58
+ labels,
59
+ maxPages,
60
+ force,
61
+ });
62
+ const data = response.data;
63
+ let result = `## Confluence Indexing\n\n`;
64
+ result += `- **Status:** ${data.status || "started"}\n`;
65
+ result += `- **Collection:** ${data.collection || `${ctx.collectionPrefix}confluence`}\n`;
66
+ result += `- **Options:**\n`;
67
+ if (spaceKeys && spaceKeys.length > 0) {
68
+ result += ` - Spaces: ${spaceKeys.join(", ")}\n`;
69
+ }
70
+ if (labels && labels.length > 0) {
71
+ result += ` - Labels: ${labels.join(", ")}\n`;
72
+ }
73
+ result += ` - Max Pages: ${maxPages}\n`;
74
+ result += ` - Force: ${force}\n`;
75
+ return result;
61
76
  },
62
77
  },
63
78
  {
64
79
  name: "get_confluence_status",
65
80
  description: "Check if Confluence integration is configured and available.",
66
- inputSchema: {
67
- type: "object",
68
- properties: {},
81
+ schema: z.object({}),
82
+ annotations: TOOL_ANNOTATIONS["get_confluence_status"],
83
+ handler: async (_args, ctx) => {
84
+ const response = await ctx.api.get("/api/confluence/status");
85
+ const data = response.data;
86
+ let result = `## Confluence Status\n\n`;
87
+ result += `- **Configured:** ${data.configured ? "Yes" : "No"}\n`;
88
+ result += `- **Message:** ${data.message || "N/A"}\n`;
89
+ return result;
69
90
  },
70
91
  },
71
92
  {
72
93
  name: "list_confluence_spaces",
73
94
  description: "List available Confluence spaces that can be indexed.",
74
- inputSchema: {
75
- type: "object",
76
- properties: {},
95
+ schema: z.object({}),
96
+ annotations: TOOL_ANNOTATIONS["list_confluence_spaces"],
97
+ handler: async (_args, ctx) => {
98
+ const response = await ctx.api.get("/api/confluence/spaces");
99
+ const spaces = response.data.spaces || response.data;
100
+ if (!spaces || spaces.length === 0) {
101
+ return "No Confluence spaces available.";
102
+ }
103
+ let result = `## Confluence Spaces\n\n`;
104
+ for (const s of spaces) {
105
+ result += `- **${s.key}** - ${s.name} (${s.type || "global"})\n`;
106
+ }
107
+ return result;
77
108
  },
78
109
  },
79
110
  ];
80
- const handlers = {
81
- search_confluence: async (args, ctx) => {
82
- const { query, limit = 5, spaceKey } = args;
83
- const response = await ctx.api.post("/api/search", {
84
- collection: `${ctx.collectionPrefix}confluence`,
85
- query,
86
- limit,
87
- filters: spaceKey ? { spaceKey } : undefined,
88
- });
89
- const results = response.data.results;
90
- if (!results || results.length === 0) {
91
- return "No Confluence results found.";
92
- }
93
- return results
94
- .map((r) => `### ${r.title || r.file || "Untitled"}\n` +
95
- `**Score:** ${pct(r.score)}` +
96
- (r.spaceKey ? ` | **Space:** ${r.spaceKey}` : "") +
97
- (r.url ? ` | [View](${r.url})` : "") +
98
- `\n\n${truncate(r.content || "", 600)}`)
99
- .join("\n\n---\n\n");
100
- },
101
- index_confluence: async (args, ctx) => {
102
- const { spaceKeys, labels, maxPages = 500, force = false } = args;
103
- const response = await ctx.api.post("/api/index/confluence", {
104
- projectName: ctx.projectName,
105
- spaceKeys,
106
- labels,
107
- maxPages,
108
- force,
109
- });
110
- const data = response.data;
111
- let result = `## Confluence Indexing\n\n`;
112
- result += `- **Status:** ${data.status || "started"}\n`;
113
- result += `- **Collection:** ${data.collection || `${ctx.collectionPrefix}confluence`}\n`;
114
- result += `- **Options:**\n`;
115
- if (spaceKeys && spaceKeys.length > 0) {
116
- result += ` - Spaces: ${spaceKeys.join(", ")}\n`;
117
- }
118
- if (labels && labels.length > 0) {
119
- result += ` - Labels: ${labels.join(", ")}\n`;
120
- }
121
- result += ` - Max Pages: ${maxPages}\n`;
122
- result += ` - Force: ${force}\n`;
123
- return result;
124
- },
125
- get_confluence_status: async (_args, ctx) => {
126
- const response = await ctx.api.get("/api/confluence/status");
127
- const data = response.data;
128
- let result = `## Confluence Status\n\n`;
129
- result += `- **Configured:** ${data.configured ? "Yes" : "No"}\n`;
130
- result += `- **Message:** ${data.message || "N/A"}\n`;
131
- return result;
132
- },
133
- list_confluence_spaces: async (_args, ctx) => {
134
- const response = await ctx.api.get("/api/confluence/spaces");
135
- const spaces = response.data.spaces || response.data;
136
- if (!spaces || spaces.length === 0) {
137
- return "No Confluence spaces available.";
138
- }
139
- let result = `## Confluence Spaces\n\n`;
140
- for (const s of spaces) {
141
- result += `- **${s.key}** - ${s.name} (${s.type || "global"})\n`;
142
- }
143
- return result;
144
- },
145
- };
146
- return { tools, handlers };
147
111
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Database Tools - Schema documentation, rules, enums, and validation.
3
3
  */
4
- import type { ToolModule } from "../types.js";
5
- export declare function createDatabaseTools(projectName: string): ToolModule;
4
+ import type { ToolSpec } from "../types.js";
5
+ export declare function createDatabaseTools(projectName: string): ToolSpec[];