@crowley/rag-mcp 1.0.5 → 1.1.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/annotations.d.ts +16 -0
- package/dist/annotations.js +158 -0
- package/dist/context-enrichment.js +7 -0
- package/dist/formatters.d.ts +2 -0
- package/dist/formatters.js +12 -0
- package/dist/index.js +99 -47
- package/dist/schemas.d.ts +97 -0
- package/dist/schemas.js +128 -0
- package/dist/tool-middleware.d.ts +40 -0
- package/dist/tool-middleware.js +216 -0
- package/dist/tool-registry.js +2 -1
- package/dist/tools/advanced.d.ts +2 -2
- package/dist/tools/advanced.js +200 -275
- package/dist/tools/agents.d.ts +2 -2
- package/dist/tools/agents.js +59 -78
- package/dist/tools/analytics.d.ts +2 -2
- package/dist/tools/analytics.js +170 -210
- package/dist/tools/architecture.d.ts +2 -2
- package/dist/tools/architecture.js +506 -669
- package/dist/tools/ask.d.ts +2 -2
- package/dist/tools/ask.js +164 -219
- package/dist/tools/cache.d.ts +2 -2
- package/dist/tools/cache.js +63 -82
- package/dist/tools/clustering.d.ts +2 -2
- package/dist/tools/clustering.js +154 -215
- package/dist/tools/confluence.d.ts +2 -2
- package/dist/tools/confluence.js +80 -116
- package/dist/tools/database.d.ts +2 -2
- package/dist/tools/database.js +303 -380
- package/dist/tools/feedback.d.ts +2 -2
- package/dist/tools/feedback.js +143 -184
- package/dist/tools/guidelines.d.ts +2 -2
- package/dist/tools/guidelines.js +123 -135
- package/dist/tools/indexing.d.ts +2 -2
- package/dist/tools/indexing.js +100 -108
- package/dist/tools/memory.d.ts +2 -2
- package/dist/tools/memory.js +299 -485
- package/dist/tools/pm.d.ts +2 -2
- package/dist/tools/pm.js +367 -615
- package/dist/tools/quality.d.ts +8 -0
- package/dist/tools/quality.js +60 -0
- package/dist/tools/review.d.ts +2 -2
- package/dist/tools/review.js +142 -189
- package/dist/tools/search.d.ts +2 -2
- package/dist/tools/search.js +230 -305
- package/dist/tools/session.d.ts +2 -2
- package/dist/tools/session.js +288 -345
- package/dist/tools/suggestions.d.ts +2 -2
- package/dist/tools/suggestions.js +517 -512
- package/dist/types.d.ts +19 -2
- package/package.json +4 -2
|
@@ -2,261 +2,26 @@
|
|
|
2
2
|
* Architecture tools module - ADRs, patterns, tech debt, and structure analysis.
|
|
3
3
|
*/
|
|
4
4
|
import { truncate } from "../formatters.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { TOOL_ANNOTATIONS } from "../annotations.js";
|
|
5
7
|
export function createArchitectureTools(projectName) {
|
|
6
|
-
|
|
8
|
+
return [
|
|
7
9
|
{
|
|
8
10
|
name: "record_adr",
|
|
9
11
|
description: `Record an Architecture Decision Record (ADR). Use this to document important architectural decisions, technology choices, and design patterns for ${projectName}.`,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
description: "What was decided",
|
|
24
|
-
},
|
|
25
|
-
consequences: {
|
|
26
|
-
type: "string",
|
|
27
|
-
description: "Positive and negative consequences of this decision",
|
|
28
|
-
},
|
|
29
|
-
alternatives: {
|
|
30
|
-
type: "string",
|
|
31
|
-
description: "What alternatives were considered",
|
|
32
|
-
},
|
|
33
|
-
status: {
|
|
34
|
-
type: "string",
|
|
35
|
-
enum: ["proposed", "accepted", "deprecated", "superseded"],
|
|
36
|
-
description: "Status of the decision (default: accepted)",
|
|
37
|
-
default: "accepted",
|
|
38
|
-
},
|
|
39
|
-
tags: {
|
|
40
|
-
type: "array",
|
|
41
|
-
items: { type: "string" },
|
|
42
|
-
description: "Tags for categorization (e.g., ['api', 'security', 'database'])",
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
required: ["title", "context", "decision"],
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: "get_adrs",
|
|
50
|
-
description: `Get Architecture Decision Records for ${projectName}. Search by topic or list all ADRs.`,
|
|
51
|
-
inputSchema: {
|
|
52
|
-
type: "object",
|
|
53
|
-
properties: {
|
|
54
|
-
query: {
|
|
55
|
-
type: "string",
|
|
56
|
-
description: "Search query (optional - returns all if empty)",
|
|
57
|
-
},
|
|
58
|
-
status: {
|
|
59
|
-
type: "string",
|
|
60
|
-
enum: [
|
|
61
|
-
"proposed",
|
|
62
|
-
"accepted",
|
|
63
|
-
"deprecated",
|
|
64
|
-
"superseded",
|
|
65
|
-
"all",
|
|
66
|
-
],
|
|
67
|
-
description: "Filter by status",
|
|
68
|
-
default: "all",
|
|
69
|
-
},
|
|
70
|
-
limit: {
|
|
71
|
-
type: "number",
|
|
72
|
-
description: "Max results (default: 10)",
|
|
73
|
-
default: 10,
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
name: "record_pattern",
|
|
80
|
-
description: `Record an architectural pattern used in ${projectName}. Patterns define how specific types of code should be structured.`,
|
|
81
|
-
inputSchema: {
|
|
82
|
-
type: "object",
|
|
83
|
-
properties: {
|
|
84
|
-
name: {
|
|
85
|
-
type: "string",
|
|
86
|
-
description: "Pattern name (e.g., 'Service Layer', 'Repository Pattern', 'API Endpoint')",
|
|
87
|
-
},
|
|
88
|
-
description: {
|
|
89
|
-
type: "string",
|
|
90
|
-
description: "What this pattern is for and when to use it",
|
|
91
|
-
},
|
|
92
|
-
structure: {
|
|
93
|
-
type: "string",
|
|
94
|
-
description: "How code following this pattern should be structured (file organization, naming, etc.)",
|
|
95
|
-
},
|
|
96
|
-
example: {
|
|
97
|
-
type: "string",
|
|
98
|
-
description: "Example code or file reference demonstrating the pattern",
|
|
99
|
-
},
|
|
100
|
-
appliesTo: {
|
|
101
|
-
type: "string",
|
|
102
|
-
description: "Where this pattern applies (e.g., 'backend/src/modules/*', 'all API endpoints')",
|
|
103
|
-
},
|
|
104
|
-
tags: {
|
|
105
|
-
type: "array",
|
|
106
|
-
items: { type: "string" },
|
|
107
|
-
description: "Tags (e.g., ['backend', 'api', 'module'])",
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
required: ["name", "description", "structure"],
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
name: "get_patterns",
|
|
115
|
-
description: `Get architectural patterns for ${projectName}. Use to understand how to structure new code.`,
|
|
116
|
-
inputSchema: {
|
|
117
|
-
type: "object",
|
|
118
|
-
properties: {
|
|
119
|
-
query: {
|
|
120
|
-
type: "string",
|
|
121
|
-
description: "Search for patterns by name or description",
|
|
122
|
-
},
|
|
123
|
-
appliesTo: {
|
|
124
|
-
type: "string",
|
|
125
|
-
description: "Filter by what patterns apply to (e.g., 'api', 'module')",
|
|
126
|
-
},
|
|
127
|
-
limit: {
|
|
128
|
-
type: "number",
|
|
129
|
-
description: "Max results (default: 10)",
|
|
130
|
-
default: 10,
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: "check_architecture",
|
|
137
|
-
description: `Check if code or a feature follows established architectural patterns. Analyzes code against recorded patterns and ADRs.`,
|
|
138
|
-
inputSchema: {
|
|
139
|
-
type: "object",
|
|
140
|
-
properties: {
|
|
141
|
-
code: {
|
|
142
|
-
type: "string",
|
|
143
|
-
description: "Code snippet to check",
|
|
144
|
-
},
|
|
145
|
-
filePath: {
|
|
146
|
-
type: "string",
|
|
147
|
-
description: "File path for context (helps determine which patterns apply)",
|
|
148
|
-
},
|
|
149
|
-
featureDescription: {
|
|
150
|
-
type: "string",
|
|
151
|
-
description: "Description of what the code does (alternative to providing code)",
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
name: "suggest_architecture",
|
|
158
|
-
description: `Get architectural guidance for implementing a new feature. Suggests structure, patterns to follow, and relevant ADRs.`,
|
|
159
|
-
inputSchema: {
|
|
160
|
-
type: "object",
|
|
161
|
-
properties: {
|
|
162
|
-
feature: {
|
|
163
|
-
type: "string",
|
|
164
|
-
description: "Feature to implement",
|
|
165
|
-
},
|
|
166
|
-
type: {
|
|
167
|
-
type: "string",
|
|
168
|
-
enum: [
|
|
169
|
-
"api",
|
|
170
|
-
"module",
|
|
171
|
-
"service",
|
|
172
|
-
"component",
|
|
173
|
-
"integration",
|
|
174
|
-
"other",
|
|
175
|
-
],
|
|
176
|
-
description: "Type of feature",
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
required: ["feature"],
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
name: "record_tech_debt",
|
|
184
|
-
description: `Record technical debt or architectural violation that needs to be addressed later.`,
|
|
185
|
-
inputSchema: {
|
|
186
|
-
type: "object",
|
|
187
|
-
properties: {
|
|
188
|
-
title: {
|
|
189
|
-
type: "string",
|
|
190
|
-
description: "Short description of the tech debt",
|
|
191
|
-
},
|
|
192
|
-
description: {
|
|
193
|
-
type: "string",
|
|
194
|
-
description: "Detailed description of the issue",
|
|
195
|
-
},
|
|
196
|
-
location: {
|
|
197
|
-
type: "string",
|
|
198
|
-
description: "Where in the codebase (file paths, modules)",
|
|
199
|
-
},
|
|
200
|
-
impact: {
|
|
201
|
-
type: "string",
|
|
202
|
-
enum: ["low", "medium", "high", "critical"],
|
|
203
|
-
description: "Impact level",
|
|
204
|
-
},
|
|
205
|
-
suggestedFix: {
|
|
206
|
-
type: "string",
|
|
207
|
-
description: "How to fix this debt",
|
|
208
|
-
},
|
|
209
|
-
relatedAdr: {
|
|
210
|
-
type: "string",
|
|
211
|
-
description: "Related ADR ID if this violates a decision",
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
required: ["title", "description", "impact"],
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
name: "get_tech_debt",
|
|
219
|
-
description: `List technical debt items for ${projectName}.`,
|
|
220
|
-
inputSchema: {
|
|
221
|
-
type: "object",
|
|
222
|
-
properties: {
|
|
223
|
-
impact: {
|
|
224
|
-
type: "string",
|
|
225
|
-
enum: ["low", "medium", "high", "critical", "all"],
|
|
226
|
-
description: "Filter by impact",
|
|
227
|
-
default: "all",
|
|
228
|
-
},
|
|
229
|
-
limit: {
|
|
230
|
-
type: "number",
|
|
231
|
-
description: "Max results (default: 10)",
|
|
232
|
-
default: 10,
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
name: "analyze_project_structure",
|
|
239
|
-
description: `Analyze the current project structure and compare with established patterns. Identifies inconsistencies and suggests improvements.`,
|
|
240
|
-
inputSchema: {
|
|
241
|
-
type: "object",
|
|
242
|
-
properties: {
|
|
243
|
-
path: {
|
|
244
|
-
type: "string",
|
|
245
|
-
description: "Specific path to analyze (default: entire project)",
|
|
246
|
-
},
|
|
247
|
-
deep: {
|
|
248
|
-
type: "boolean",
|
|
249
|
-
description: "Perform deep analysis including code patterns (default: false)",
|
|
250
|
-
default: false,
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
];
|
|
256
|
-
// ── Handlers ──────────────────────────────────────────────────────────
|
|
257
|
-
async function handleRecordAdr(args, ctx) {
|
|
258
|
-
const { title, context, decision, consequences, alternatives, status = "accepted", tags = [], } = args;
|
|
259
|
-
const adrContent = `# ADR: ${title}
|
|
12
|
+
schema: z.object({
|
|
13
|
+
title: z.string().describe("Short title for the decision (e.g., 'Use WebSocket for real-time updates')"),
|
|
14
|
+
context: z.string().describe("Why this decision was needed - the problem or requirement"),
|
|
15
|
+
decision: z.string().describe("What was decided"),
|
|
16
|
+
consequences: z.string().optional().describe("Positive and negative consequences of this decision"),
|
|
17
|
+
alternatives: z.string().optional().describe("What alternatives were considered"),
|
|
18
|
+
status: z.enum(["proposed", "accepted", "deprecated", "superseded"]).optional().describe("Status of the decision (default: accepted)"),
|
|
19
|
+
tags: z.array(z.string()).optional().describe("Tags for categorization (e.g., ['api', 'security', 'database'])"),
|
|
20
|
+
}),
|
|
21
|
+
annotations: TOOL_ANNOTATIONS["record_adr"],
|
|
22
|
+
handler: async (args, ctx) => {
|
|
23
|
+
const { title, context, decision, consequences, alternatives, status = "accepted", tags = [], } = args;
|
|
24
|
+
const adrContent = `# ADR: ${title}
|
|
260
25
|
|
|
261
26
|
## Status
|
|
262
27
|
${status.toUpperCase()}
|
|
@@ -269,57 +34,80 @@ ${decision}
|
|
|
269
34
|
|
|
270
35
|
${consequences ? `## Consequences\n${consequences}\n` : ""}
|
|
271
36
|
${alternatives ? `## Alternatives Considered\n${alternatives}` : ""}`;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
37
|
+
const response = await ctx.api.post("/api/memory", {
|
|
38
|
+
projectName: ctx.projectName,
|
|
39
|
+
content: adrContent,
|
|
40
|
+
type: "decision",
|
|
41
|
+
tags: ["adr", ...tags],
|
|
42
|
+
relatedTo: title,
|
|
43
|
+
metadata: { adrTitle: title, adrStatus: status },
|
|
44
|
+
});
|
|
45
|
+
return (`# ADR Recorded\n\n` +
|
|
46
|
+
`- **ID:** ${response.data.memory.id}\n` +
|
|
47
|
+
`- **Title:** ${title}\n` +
|
|
48
|
+
`- **Status:** ${status}\n` +
|
|
49
|
+
`- **Tags:** ${["adr", ...tags].join(", ")}\n\n` +
|
|
50
|
+
`Use \`get_adrs\` to retrieve this decision later.`);
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "get_adrs",
|
|
55
|
+
description: `Get Architecture Decision Records for ${projectName}. Search by topic or list all ADRs.`,
|
|
56
|
+
schema: z.object({
|
|
57
|
+
query: z.string().optional().describe("Search query (optional - returns all if empty)"),
|
|
58
|
+
status: z.enum(["proposed", "accepted", "deprecated", "superseded", "all"]).optional().describe("Filter by status"),
|
|
59
|
+
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
60
|
+
}),
|
|
61
|
+
annotations: TOOL_ANNOTATIONS["get_adrs"],
|
|
62
|
+
handler: async (args, ctx) => {
|
|
63
|
+
const { query, status = "all", limit = 10, } = args;
|
|
64
|
+
const response = await ctx.api.post("/api/memory/recall", {
|
|
65
|
+
projectName: ctx.projectName,
|
|
66
|
+
query: query || "architecture decision ADR",
|
|
67
|
+
type: "decision",
|
|
68
|
+
limit,
|
|
69
|
+
tag: "adr",
|
|
70
|
+
});
|
|
71
|
+
const results = response.data.results || [];
|
|
72
|
+
const adrs = results.filter((r) => r.memory.tags?.includes("adr") &&
|
|
73
|
+
(status === "all" || r.memory.metadata?.adrStatus === status));
|
|
74
|
+
if (adrs.length === 0) {
|
|
75
|
+
return `No ADRs found${query ? ` for "${query}"` : ""}`;
|
|
76
|
+
}
|
|
77
|
+
const statusIcons = {
|
|
78
|
+
proposed: "\uD83D\uDFE1",
|
|
79
|
+
accepted: "\uD83D\uDFE2",
|
|
80
|
+
deprecated: "\uD83D\uDD34",
|
|
81
|
+
superseded: "\u26AB",
|
|
82
|
+
};
|
|
83
|
+
let result = `# Architecture Decision Records (${adrs.length})\n\n`;
|
|
84
|
+
adrs.forEach((r, i) => {
|
|
85
|
+
const m = r.memory;
|
|
86
|
+
const adrStatus = (m.metadata?.adrStatus || "accepted");
|
|
87
|
+
const icon = statusIcons[adrStatus] || "\u26AA";
|
|
88
|
+
result += `### ${i + 1}. ${icon} ${m.metadata?.adrTitle || m.relatedTo || "ADR"}\n`;
|
|
89
|
+
result += `**Status:** ${adrStatus} | **ID:** \`${m.id}\`\n\n`;
|
|
90
|
+
result +=
|
|
91
|
+
truncate(m.content, 500) + "\n\n";
|
|
92
|
+
});
|
|
93
|
+
return result;
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "record_pattern",
|
|
98
|
+
description: `Record an architectural pattern used in ${projectName}. Patterns define how specific types of code should be structured.`,
|
|
99
|
+
schema: z.object({
|
|
100
|
+
name: z.string().describe("Pattern name (e.g., 'Service Layer', 'Repository Pattern', 'API Endpoint')"),
|
|
101
|
+
description: z.string().describe("What this pattern is for and when to use it"),
|
|
102
|
+
structure: z.string().describe("How code following this pattern should be structured (file organization, naming, etc.)"),
|
|
103
|
+
example: z.string().optional().describe("Example code or file reference demonstrating the pattern"),
|
|
104
|
+
appliesTo: z.string().optional().describe("Where this pattern applies (e.g., 'backend/src/modules/*', 'all API endpoints')"),
|
|
105
|
+
tags: z.array(z.string()).optional().describe("Tags (e.g., ['backend', 'api', 'module'])"),
|
|
106
|
+
}),
|
|
107
|
+
annotations: TOOL_ANNOTATIONS["record_pattern"],
|
|
108
|
+
handler: async (args, ctx) => {
|
|
109
|
+
const { name, description, structure, example, appliesTo, tags = [], } = args;
|
|
110
|
+
const patternContent = `# Pattern: ${name}
|
|
323
111
|
|
|
324
112
|
## Description
|
|
325
113
|
${description}
|
|
@@ -329,100 +117,120 @@ ${structure}
|
|
|
329
117
|
|
|
330
118
|
${example ? `## Example\n\`\`\`\n${example}\n\`\`\`\n` : ""}
|
|
331
119
|
${appliesTo ? `## Applies To\n${appliesTo}` : ""}`;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
120
|
+
const response = await ctx.api.post("/api/memory", {
|
|
121
|
+
projectName: ctx.projectName,
|
|
122
|
+
content: patternContent,
|
|
123
|
+
type: "context",
|
|
124
|
+
tags: ["pattern", ...tags],
|
|
125
|
+
relatedTo: name,
|
|
126
|
+
metadata: { patternName: name, appliesTo },
|
|
127
|
+
});
|
|
128
|
+
return (`# Pattern Recorded\n\n` +
|
|
129
|
+
`- **Name:** ${name}\n` +
|
|
130
|
+
`- **ID:** ${response.data.memory.id}\n` +
|
|
131
|
+
(appliesTo ? `- **Applies To:** ${appliesTo}\n` : "") +
|
|
132
|
+
`- **Tags:** ${["pattern", ...tags].join(", ")}`);
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "get_patterns",
|
|
137
|
+
description: `Get architectural patterns for ${projectName}. Use to understand how to structure new code.`,
|
|
138
|
+
schema: z.object({
|
|
139
|
+
query: z.string().optional().describe("Search for patterns by name or description"),
|
|
140
|
+
appliesTo: z.string().optional().describe("Filter by what patterns apply to (e.g., 'api', 'module')"),
|
|
141
|
+
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
142
|
+
}),
|
|
143
|
+
annotations: TOOL_ANNOTATIONS["get_patterns"],
|
|
144
|
+
handler: async (args, ctx) => {
|
|
145
|
+
const { query, appliesTo, limit = 10, } = args;
|
|
146
|
+
const response = await ctx.api.post("/api/memory/recall", {
|
|
147
|
+
projectName: ctx.projectName,
|
|
148
|
+
query: query || "architectural pattern structure",
|
|
149
|
+
type: "context",
|
|
150
|
+
limit,
|
|
151
|
+
tag: "pattern",
|
|
152
|
+
});
|
|
153
|
+
const results = response.data.results || [];
|
|
154
|
+
const patterns = results.filter((r) => {
|
|
155
|
+
const isPattern = r.memory.tags?.includes("pattern");
|
|
156
|
+
const matchesAppliesTo = !appliesTo ||
|
|
157
|
+
r.memory.metadata?.appliesTo
|
|
158
|
+
?.toLowerCase()
|
|
159
|
+
.includes(appliesTo.toLowerCase());
|
|
160
|
+
return isPattern && matchesAppliesTo;
|
|
161
|
+
});
|
|
162
|
+
if (patterns.length === 0) {
|
|
163
|
+
return `No patterns found${query ? ` for "${query}"` : ""}`;
|
|
164
|
+
}
|
|
165
|
+
let result = `# Architectural Patterns (${patterns.length})\n\n`;
|
|
166
|
+
patterns.forEach((r, i) => {
|
|
167
|
+
const m = r.memory;
|
|
168
|
+
result += `### ${i + 1}. ${m.metadata?.patternName || m.relatedTo || "Pattern"}\n`;
|
|
169
|
+
if (m.metadata?.appliesTo) {
|
|
170
|
+
result += `**Applies to:** ${m.metadata.appliesTo}\n`;
|
|
171
|
+
}
|
|
172
|
+
result += `**ID:** \`${m.id}\`\n\n`;
|
|
173
|
+
result += truncate(m.content, 600) + "\n\n";
|
|
174
|
+
});
|
|
175
|
+
return result;
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "check_architecture",
|
|
180
|
+
description: `Check if code or a feature follows established architectural patterns. Analyzes code against recorded patterns and ADRs.`,
|
|
181
|
+
schema: z.object({
|
|
182
|
+
code: z.string().optional().describe("Code snippet to check"),
|
|
183
|
+
filePath: z.string().optional().describe("File path for context (helps determine which patterns apply)"),
|
|
184
|
+
featureDescription: z.string().optional().describe("Description of what the code does (alternative to providing code)"),
|
|
185
|
+
}),
|
|
186
|
+
annotations: TOOL_ANNOTATIONS["check_architecture"],
|
|
187
|
+
handler: async (args, ctx) => {
|
|
188
|
+
const { code, filePath, featureDescription } = args;
|
|
189
|
+
const patternQuery = filePath || featureDescription || "architectural patterns";
|
|
190
|
+
// Get relevant patterns
|
|
191
|
+
const patternsResponse = await ctx.api.post("/api/memory/recall", {
|
|
192
|
+
projectName: ctx.projectName,
|
|
193
|
+
query: patternQuery,
|
|
194
|
+
type: "context",
|
|
195
|
+
limit: 5,
|
|
196
|
+
tag: "pattern",
|
|
197
|
+
});
|
|
198
|
+
// Get relevant ADRs
|
|
199
|
+
const adrsResponse = await ctx.api.post("/api/memory/recall", {
|
|
200
|
+
projectName: ctx.projectName,
|
|
201
|
+
query: patternQuery,
|
|
202
|
+
type: "decision",
|
|
203
|
+
limit: 5,
|
|
204
|
+
tag: "adr",
|
|
205
|
+
});
|
|
206
|
+
// Search similar code in codebase
|
|
207
|
+
let similarCode = [];
|
|
208
|
+
if (code) {
|
|
209
|
+
const codeResponse = await ctx.api.post("/api/search", {
|
|
210
|
+
collection: `${ctx.collectionPrefix}codebase`,
|
|
211
|
+
query: code.slice(0, 500),
|
|
212
|
+
limit: 3,
|
|
213
|
+
});
|
|
214
|
+
similarCode = codeResponse.data.results || [];
|
|
215
|
+
}
|
|
216
|
+
const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
|
|
217
|
+
const adrs = (adrsResponse.data.results || []).filter((r) => r.memory.tags?.includes("adr"));
|
|
218
|
+
let result = `# Architecture Check\n\n`;
|
|
219
|
+
if (filePath) {
|
|
220
|
+
result += `**File:** ${filePath}\n\n`;
|
|
221
|
+
}
|
|
222
|
+
if (featureDescription) {
|
|
223
|
+
result += `**Feature:** ${featureDescription}\n\n`;
|
|
224
|
+
}
|
|
225
|
+
// If we have code and patterns/ADRs, perform LLM validation
|
|
226
|
+
if (code && (patterns.length > 0 || adrs.length > 0)) {
|
|
227
|
+
const patternRules = patterns
|
|
228
|
+
.map((p) => `Pattern: ${p.memory.metadata?.patternName || p.memory.relatedTo}\nDescription: ${truncate(p.memory.content, 300)}`)
|
|
229
|
+
.join("\n\n");
|
|
230
|
+
const adrRules = adrs
|
|
231
|
+
.map((a) => `ADR: ${a.memory.metadata?.adrTitle || a.memory.relatedTo}\nDecision: ${truncate(a.memory.content, 300)}`)
|
|
232
|
+
.join("\n\n");
|
|
233
|
+
const validationPrompt = `Analyze if this code follows the established architectural patterns and decisions.
|
|
426
234
|
|
|
427
235
|
Code to validate:
|
|
428
236
|
\`\`\`
|
|
@@ -439,137 +247,159 @@ Provide a structured analysis:
|
|
|
439
247
|
1. List any violations of patterns or ADRs
|
|
440
248
|
2. Rate compliance (1-10)
|
|
441
249
|
3. Specific recommendations for improvements`;
|
|
442
|
-
|
|
443
|
-
|
|
250
|
+
try {
|
|
251
|
+
const validationResponse = await ctx.api.post("/api/ask", {
|
|
252
|
+
collection: `${ctx.collectionPrefix}codebase`,
|
|
253
|
+
question: validationPrompt,
|
|
254
|
+
});
|
|
255
|
+
result += `## Validation Results\n\n`;
|
|
256
|
+
result += validationResponse.data.answer;
|
|
257
|
+
result += "\n\n";
|
|
258
|
+
}
|
|
259
|
+
catch (_e) {
|
|
260
|
+
// Continue without LLM validation
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
result += `## Applicable Patterns (${patterns.length})\n`;
|
|
264
|
+
if (patterns.length === 0) {
|
|
265
|
+
result += `_No specific patterns recorded for this area._\n\n`;
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
patterns.forEach((p) => {
|
|
269
|
+
result += `- **${p.memory.metadata?.patternName || p.memory.relatedTo}**: ${truncate(p.memory.content, 100)}\n`;
|
|
270
|
+
});
|
|
271
|
+
result += "\n";
|
|
272
|
+
}
|
|
273
|
+
result += `## Relevant ADRs (${adrs.length})\n`;
|
|
274
|
+
if (adrs.length === 0) {
|
|
275
|
+
result += `_No relevant architectural decisions found._\n\n`;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
adrs.forEach((a) => {
|
|
279
|
+
result += `- **${a.memory.metadata?.adrTitle || a.memory.relatedTo}** [${a.memory.metadata?.adrStatus || "accepted"}]: ${truncate(a.memory.content, 100)}\n`;
|
|
280
|
+
});
|
|
281
|
+
result += "\n";
|
|
282
|
+
}
|
|
283
|
+
if (similarCode.length > 0) {
|
|
284
|
+
result += `## Similar Existing Code\n`;
|
|
285
|
+
result += `_Review these for consistency:_\n`;
|
|
286
|
+
similarCode.forEach((c) => {
|
|
287
|
+
result += `- ${c.file}\n`;
|
|
288
|
+
});
|
|
289
|
+
result += "\n";
|
|
290
|
+
}
|
|
291
|
+
result += `## Recommendations\n`;
|
|
292
|
+
if (patterns.length > 0) {
|
|
293
|
+
result += `- Follow the patterns listed above for consistency\n`;
|
|
294
|
+
}
|
|
295
|
+
if (adrs.length > 0) {
|
|
296
|
+
result += `- Ensure compliance with recorded architectural decisions\n`;
|
|
297
|
+
}
|
|
298
|
+
if (similarCode.length > 0) {
|
|
299
|
+
result += `- Check similar code for established conventions\n`;
|
|
300
|
+
}
|
|
301
|
+
if (patterns.length === 0 && adrs.length === 0) {
|
|
302
|
+
result += `- Consider recording patterns/ADRs for this area with \`record_pattern\` and \`record_adr\`\n`;
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: "suggest_architecture",
|
|
309
|
+
description: `Get architectural guidance for implementing a new feature. Suggests structure, patterns to follow, and relevant ADRs.`,
|
|
310
|
+
schema: z.object({
|
|
311
|
+
feature: z.string().describe("Feature to implement"),
|
|
312
|
+
type: z.enum(["api", "module", "service", "component", "integration", "other"]).optional().describe("Type of feature"),
|
|
313
|
+
}),
|
|
314
|
+
annotations: TOOL_ANNOTATIONS["suggest_architecture"],
|
|
315
|
+
handler: async (args, ctx) => {
|
|
316
|
+
const { feature, type = "other" } = args;
|
|
317
|
+
// Get patterns for this type
|
|
318
|
+
const patternsResponse = await ctx.api.post("/api/memory/recall", {
|
|
319
|
+
projectName: ctx.projectName,
|
|
320
|
+
query: `${type} ${feature} pattern structure`,
|
|
321
|
+
type: "context",
|
|
322
|
+
limit: 5,
|
|
323
|
+
tag: "pattern",
|
|
324
|
+
});
|
|
325
|
+
// Get relevant ADRs
|
|
326
|
+
const adrsResponse = await ctx.api.post("/api/memory/recall", {
|
|
327
|
+
projectName: ctx.projectName,
|
|
328
|
+
query: `${type} ${feature}`,
|
|
329
|
+
type: "decision",
|
|
330
|
+
limit: 3,
|
|
331
|
+
tag: "adr",
|
|
332
|
+
});
|
|
333
|
+
// Get similar implementations
|
|
334
|
+
const codeResponse = await ctx.api.post("/api/search", {
|
|
444
335
|
collection: `${ctx.collectionPrefix}codebase`,
|
|
445
|
-
|
|
336
|
+
query: `${type} ${feature}`,
|
|
337
|
+
limit: 5,
|
|
446
338
|
});
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
query: `${type} ${feature}`,
|
|
512
|
-
type: "decision",
|
|
513
|
-
limit: 3,
|
|
514
|
-
tag: "adr",
|
|
515
|
-
});
|
|
516
|
-
// Get similar implementations
|
|
517
|
-
const codeResponse = await ctx.api.post("/api/search", {
|
|
518
|
-
collection: `${ctx.collectionPrefix}codebase`,
|
|
519
|
-
query: `${type} ${feature}`,
|
|
520
|
-
limit: 5,
|
|
521
|
-
});
|
|
522
|
-
const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
|
|
523
|
-
const adrs = (adrsResponse.data.results || []).filter((r) => r.memory.tags?.includes("adr"));
|
|
524
|
-
const existingCode = codeResponse.data.results || [];
|
|
525
|
-
let result = `# Architecture Suggestion: ${feature}\n\n`;
|
|
526
|
-
result += `**Type:** ${type}\n\n`;
|
|
527
|
-
result += `## Recommended Patterns\n`;
|
|
528
|
-
if (patterns.length === 0) {
|
|
529
|
-
result += `_No specific patterns recorded. Consider following existing code conventions._\n\n`;
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
patterns.forEach((p) => {
|
|
533
|
-
result += `### ${p.memory.metadata?.patternName || p.memory.relatedTo}\n`;
|
|
534
|
-
result += truncate(p.memory.content, 400) + "\n\n";
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
result += `## Relevant Decisions (ADRs)\n`;
|
|
538
|
-
if (adrs.length === 0) {
|
|
539
|
-
result += `_No specific ADRs found for this area._\n\n`;
|
|
540
|
-
}
|
|
541
|
-
else {
|
|
542
|
-
adrs.forEach((a) => {
|
|
543
|
-
result += `- **${a.memory.metadata?.adrTitle || a.memory.relatedTo}**: `;
|
|
544
|
-
const decision = a.memory.content.match(/## Decision\n([\s\S]*?)(?=\n##|$)/);
|
|
545
|
-
result += decision
|
|
546
|
-
? truncate(decision[1].trim(), 150)
|
|
547
|
-
: "See full ADR";
|
|
548
|
-
result += "\n";
|
|
549
|
-
});
|
|
550
|
-
result += "\n";
|
|
551
|
-
}
|
|
552
|
-
result += `## Reference Implementations\n`;
|
|
553
|
-
if (existingCode.length === 0) {
|
|
554
|
-
result += `_No similar implementations found._\n\n`;
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
result += `_Study these for conventions:_\n`;
|
|
558
|
-
existingCode.forEach((c) => {
|
|
559
|
-
result += `- \`${c.file}\`\n`;
|
|
560
|
-
});
|
|
561
|
-
result += "\n";
|
|
562
|
-
}
|
|
563
|
-
result += `## Next Steps\n`;
|
|
564
|
-
result += `1. Review the patterns and ADRs above\n`;
|
|
565
|
-
result += `2. Study reference implementations for conventions\n`;
|
|
566
|
-
result += `3. Create your implementation following established structure\n`;
|
|
567
|
-
result += `4. Use \`check_architecture\` to validate before committing\n`;
|
|
568
|
-
return result;
|
|
569
|
-
}
|
|
570
|
-
async function handleRecordTechDebt(args, ctx) {
|
|
571
|
-
const { title, description, location, impact, suggestedFix, relatedAdr } = args;
|
|
572
|
-
const debtContent = `# Tech Debt: ${title}
|
|
339
|
+
const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
|
|
340
|
+
const adrs = (adrsResponse.data.results || []).filter((r) => r.memory.tags?.includes("adr"));
|
|
341
|
+
const existingCode = codeResponse.data.results || [];
|
|
342
|
+
let result = `# Architecture Suggestion: ${feature}\n\n`;
|
|
343
|
+
result += `**Type:** ${type}\n\n`;
|
|
344
|
+
result += `## Recommended Patterns\n`;
|
|
345
|
+
if (patterns.length === 0) {
|
|
346
|
+
result += `_No specific patterns recorded. Consider following existing code conventions._\n\n`;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
patterns.forEach((p) => {
|
|
350
|
+
result += `### ${p.memory.metadata?.patternName || p.memory.relatedTo}\n`;
|
|
351
|
+
result += truncate(p.memory.content, 400) + "\n\n";
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
result += `## Relevant Decisions (ADRs)\n`;
|
|
355
|
+
if (adrs.length === 0) {
|
|
356
|
+
result += `_No specific ADRs found for this area._\n\n`;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
adrs.forEach((a) => {
|
|
360
|
+
result += `- **${a.memory.metadata?.adrTitle || a.memory.relatedTo}**: `;
|
|
361
|
+
const decision = a.memory.content.match(/## Decision\n([\s\S]*?)(?=\n##|$)/);
|
|
362
|
+
result += decision
|
|
363
|
+
? truncate(decision[1].trim(), 150)
|
|
364
|
+
: "See full ADR";
|
|
365
|
+
result += "\n";
|
|
366
|
+
});
|
|
367
|
+
result += "\n";
|
|
368
|
+
}
|
|
369
|
+
result += `## Reference Implementations\n`;
|
|
370
|
+
if (existingCode.length === 0) {
|
|
371
|
+
result += `_No similar implementations found._\n\n`;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
result += `_Study these for conventions:_\n`;
|
|
375
|
+
existingCode.forEach((c) => {
|
|
376
|
+
result += `- \`${c.file}\`\n`;
|
|
377
|
+
});
|
|
378
|
+
result += "\n";
|
|
379
|
+
}
|
|
380
|
+
result += `## Next Steps\n`;
|
|
381
|
+
result += `1. Review the patterns and ADRs above\n`;
|
|
382
|
+
result += `2. Study reference implementations for conventions\n`;
|
|
383
|
+
result += `3. Create your implementation following established structure\n`;
|
|
384
|
+
result += `4. Use \`check_architecture\` to validate before committing\n`;
|
|
385
|
+
return result;
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: "record_tech_debt",
|
|
390
|
+
description: `Record technical debt or architectural violation that needs to be addressed later.`,
|
|
391
|
+
schema: z.object({
|
|
392
|
+
title: z.string().describe("Short description of the tech debt"),
|
|
393
|
+
description: z.string().describe("Detailed description of the issue"),
|
|
394
|
+
location: z.string().optional().describe("Where in the codebase (file paths, modules)"),
|
|
395
|
+
impact: z.enum(["low", "medium", "high", "critical"]).describe("Impact level"),
|
|
396
|
+
suggestedFix: z.string().optional().describe("How to fix this debt"),
|
|
397
|
+
relatedAdr: z.string().optional().describe("Related ADR ID if this violates a decision"),
|
|
398
|
+
}),
|
|
399
|
+
annotations: TOOL_ANNOTATIONS["record_tech_debt"],
|
|
400
|
+
handler: async (args, ctx) => {
|
|
401
|
+
const { title, description, location, impact, suggestedFix, relatedAdr } = args;
|
|
402
|
+
const debtContent = `# Tech Debt: ${title}
|
|
573
403
|
|
|
574
404
|
## Impact
|
|
575
405
|
${impact.toUpperCase()}
|
|
@@ -580,149 +410,156 @@ ${description}
|
|
|
580
410
|
${location ? `## Location\n${location}\n` : ""}
|
|
581
411
|
${suggestedFix ? `## Suggested Fix\n${suggestedFix}\n` : ""}
|
|
582
412
|
${relatedAdr ? `## Related ADR\n${relatedAdr}` : ""}`;
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
result +=
|
|
700
|
-
|
|
701
|
-
|
|
413
|
+
const response = await ctx.api.post("/api/memory", {
|
|
414
|
+
projectName: ctx.projectName,
|
|
415
|
+
content: debtContent,
|
|
416
|
+
type: "insight",
|
|
417
|
+
tags: ["tech-debt", `impact-${impact}`],
|
|
418
|
+
relatedTo: title,
|
|
419
|
+
metadata: { debtTitle: title, impact, location },
|
|
420
|
+
});
|
|
421
|
+
const impactEmojis = {
|
|
422
|
+
low: "\uD83D\uDFE2",
|
|
423
|
+
medium: "\uD83D\uDFE1",
|
|
424
|
+
high: "\uD83D\uDFE0",
|
|
425
|
+
critical: "\uD83D\uDD34",
|
|
426
|
+
};
|
|
427
|
+
const emoji = impactEmojis[impact] || "\u26AA";
|
|
428
|
+
return (`${emoji} **Tech Debt Recorded**\n\n` +
|
|
429
|
+
`- **Title:** ${title}\n` +
|
|
430
|
+
`- **Impact:** ${impact}\n` +
|
|
431
|
+
`- **ID:** ${response.data.memory.id}\n` +
|
|
432
|
+
(location ? `- **Location:** ${location}\n` : ""));
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "get_tech_debt",
|
|
437
|
+
description: `List technical debt items for ${projectName}.`,
|
|
438
|
+
schema: z.object({
|
|
439
|
+
impact: z.enum(["low", "medium", "high", "critical", "all"]).optional().describe("Filter by impact"),
|
|
440
|
+
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
441
|
+
}),
|
|
442
|
+
annotations: TOOL_ANNOTATIONS["get_tech_debt"],
|
|
443
|
+
handler: async (args, ctx) => {
|
|
444
|
+
const { impact = "all", limit = 10 } = args;
|
|
445
|
+
const response = await ctx.api.post("/api/memory/recall", {
|
|
446
|
+
projectName: ctx.projectName,
|
|
447
|
+
query: "technical debt violation issue",
|
|
448
|
+
type: "insight",
|
|
449
|
+
limit: limit * 2, // Fetch extra to account for filtering
|
|
450
|
+
tag: "tech-debt",
|
|
451
|
+
});
|
|
452
|
+
const results = response.data.results || [];
|
|
453
|
+
const debts = results
|
|
454
|
+
.filter((r) => {
|
|
455
|
+
const isDebt = r.memory.tags?.includes("tech-debt");
|
|
456
|
+
const matchesImpact = impact === "all" ||
|
|
457
|
+
r.memory.metadata?.impact === impact ||
|
|
458
|
+
r.memory.tags?.includes(`impact-${impact}`);
|
|
459
|
+
return isDebt && matchesImpact;
|
|
460
|
+
})
|
|
461
|
+
.slice(0, limit);
|
|
462
|
+
if (debts.length === 0) {
|
|
463
|
+
return `No tech debt found${impact !== "all" ? ` with ${impact} impact` : ""}`;
|
|
464
|
+
}
|
|
465
|
+
const impactEmojis = {
|
|
466
|
+
low: "\uD83D\uDFE2",
|
|
467
|
+
medium: "\uD83D\uDFE1",
|
|
468
|
+
high: "\uD83D\uDFE0",
|
|
469
|
+
critical: "\uD83D\uDD34",
|
|
470
|
+
};
|
|
471
|
+
let result = `# Technical Debt (${debts.length})\n\n`;
|
|
472
|
+
debts.forEach((r, i) => {
|
|
473
|
+
const m = r.memory;
|
|
474
|
+
const debtImpact = m.metadata?.impact || "medium";
|
|
475
|
+
const emoji = impactEmojis[debtImpact] || "\u26AA";
|
|
476
|
+
result += `### ${i + 1}. ${emoji} ${m.metadata?.debtTitle || m.relatedTo || "Tech Debt"}\n`;
|
|
477
|
+
result += `**Impact:** ${debtImpact}`;
|
|
478
|
+
if (m.metadata?.location) {
|
|
479
|
+
result += ` | **Location:** ${m.metadata.location}`;
|
|
480
|
+
}
|
|
481
|
+
result += `\n**ID:** \`${m.id}\`\n\n`;
|
|
482
|
+
// Extract description section
|
|
483
|
+
const descMatch = m.content.match(/## Description\n([\s\S]*?)(?=\n##|$)/);
|
|
484
|
+
if (descMatch) {
|
|
485
|
+
result += truncate(descMatch[1].trim(), 200) + "\n\n";
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
return result;
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "analyze_project_structure",
|
|
493
|
+
description: `Analyze the current project structure and compare with established patterns. Identifies inconsistencies and suggests improvements.`,
|
|
494
|
+
schema: z.object({
|
|
495
|
+
path: z.string().optional().describe("Specific path to analyze (default: entire project)"),
|
|
496
|
+
deep: z.boolean().optional().describe("Perform deep analysis including code patterns (default: false)"),
|
|
497
|
+
}),
|
|
498
|
+
annotations: TOOL_ANNOTATIONS["analyze_project_structure"],
|
|
499
|
+
handler: async (args, ctx) => {
|
|
500
|
+
const { path, deep = false } = args;
|
|
501
|
+
// Get all recorded patterns
|
|
502
|
+
const patternsResponse = await ctx.api.post("/api/memory/recall", {
|
|
503
|
+
projectName: ctx.projectName,
|
|
504
|
+
query: "pattern structure organization",
|
|
505
|
+
type: "context",
|
|
506
|
+
limit: 10,
|
|
507
|
+
tag: "pattern",
|
|
508
|
+
});
|
|
509
|
+
// Get codebase structure
|
|
510
|
+
const codeResponse = await ctx.api.post("/api/search", {
|
|
511
|
+
collection: `${ctx.collectionPrefix}codebase`,
|
|
512
|
+
query: path || "module service controller",
|
|
513
|
+
limit: deep ? 20 : 10,
|
|
514
|
+
});
|
|
515
|
+
const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
|
|
516
|
+
const codeFiles = codeResponse.data.results || [];
|
|
517
|
+
// Analyze file organization by directory
|
|
518
|
+
const filesByDir = {};
|
|
519
|
+
codeFiles.forEach((c) => {
|
|
520
|
+
const dir = c.file.split("/").slice(0, -1).join("/") || "/";
|
|
521
|
+
if (!filesByDir[dir])
|
|
522
|
+
filesByDir[dir] = [];
|
|
523
|
+
filesByDir[dir].push(c.file.split("/").pop());
|
|
524
|
+
});
|
|
525
|
+
let result = `# Project Structure Analysis\n\n`;
|
|
526
|
+
if (path) {
|
|
527
|
+
result += `**Scope:** ${path}\n\n`;
|
|
528
|
+
}
|
|
529
|
+
result += `## Directory Structure\n`;
|
|
530
|
+
Object.entries(filesByDir)
|
|
531
|
+
.slice(0, 10)
|
|
532
|
+
.forEach(([dir, files]) => {
|
|
533
|
+
result += `\n**${dir || "/"}/**\n`;
|
|
534
|
+
files.slice(0, 5).forEach((f) => {
|
|
535
|
+
result += ` - ${f}\n`;
|
|
536
|
+
});
|
|
537
|
+
if (files.length > 5) {
|
|
538
|
+
result += ` - ... and ${files.length - 5} more\n`;
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
result += `\n## Recorded Patterns (${patterns.length})\n`;
|
|
542
|
+
if (patterns.length === 0) {
|
|
543
|
+
result += `_No patterns recorded yet. Consider documenting your architectural patterns._\n\n`;
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
patterns.forEach((p) => {
|
|
547
|
+
result += `- ${p.memory.metadata?.patternName || p.memory.relatedTo}`;
|
|
548
|
+
if (p.memory.metadata?.appliesTo) {
|
|
549
|
+
result += ` -> ${p.memory.metadata.appliesTo}`;
|
|
550
|
+
}
|
|
551
|
+
result += "\n";
|
|
552
|
+
});
|
|
702
553
|
}
|
|
703
|
-
result +=
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
// ── Build handlers map ────────────────────────────────────────────────
|
|
716
|
-
const handlers = {
|
|
717
|
-
record_adr: handleRecordAdr,
|
|
718
|
-
get_adrs: handleGetAdrs,
|
|
719
|
-
record_pattern: handleRecordPattern,
|
|
720
|
-
get_patterns: handleGetPatterns,
|
|
721
|
-
check_architecture: handleCheckArchitecture,
|
|
722
|
-
suggest_architecture: handleSuggestArchitecture,
|
|
723
|
-
record_tech_debt: handleRecordTechDebt,
|
|
724
|
-
get_tech_debt: handleGetTechDebt,
|
|
725
|
-
analyze_project_structure: handleAnalyzeProjectStructure,
|
|
726
|
-
};
|
|
727
|
-
return { tools, handlers };
|
|
554
|
+
result += `\n## Recommendations\n`;
|
|
555
|
+
if (patterns.length === 0) {
|
|
556
|
+
result += `1. **Record patterns** - Use \`record_pattern\` to document how code should be structured\n`;
|
|
557
|
+
}
|
|
558
|
+
result += `2. **Document decisions** - Use \`record_adr\` for important architectural choices\n`;
|
|
559
|
+
result += `3. **Track tech debt** - Use \`record_tech_debt\` for violations and issues\n`;
|
|
560
|
+
result += `4. **Validate changes** - Use \`check_architecture\` before committing new code\n`;
|
|
561
|
+
return result;
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
];
|
|
728
565
|
}
|