@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,720 @@
1
+ /**
2
+ * Architecture tools module - ADRs, patterns, tech debt, and structure analysis.
3
+ */
4
+ import { truncate } from "../formatters.js";
5
+ export function createArchitectureTools(projectName) {
6
+ const tools = [
7
+ {
8
+ name: "record_adr",
9
+ description: `Record an Architecture Decision Record (ADR). Use this to document important architectural decisions, technology choices, and design patterns for ${projectName}.`,
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ title: {
14
+ type: "string",
15
+ description: "Short title for the decision (e.g., 'Use WebSocket for real-time updates')",
16
+ },
17
+ context: {
18
+ type: "string",
19
+ description: "Why this decision was needed - the problem or requirement",
20
+ },
21
+ decision: {
22
+ type: "string",
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}
260
+
261
+ ## Status
262
+ ${status.toUpperCase()}
263
+
264
+ ## Context
265
+ ${context}
266
+
267
+ ## Decision
268
+ ${decision}
269
+
270
+ ${consequences ? `## Consequences\n${consequences}\n` : ""}
271
+ ${alternatives ? `## Alternatives Considered\n${alternatives}` : ""}`;
272
+ const response = await ctx.api.post("/api/memory", {
273
+ projectName: ctx.projectName,
274
+ content: adrContent,
275
+ type: "decision",
276
+ tags: ["adr", ...tags],
277
+ relatedTo: title,
278
+ metadata: { adrTitle: title, adrStatus: status },
279
+ });
280
+ return (`# ADR Recorded\n\n` +
281
+ `- **ID:** ${response.data.memory.id}\n` +
282
+ `- **Title:** ${title}\n` +
283
+ `- **Status:** ${status}\n` +
284
+ `- **Tags:** ${["adr", ...tags].join(", ")}\n\n` +
285
+ `Use \`get_adrs\` to retrieve this decision later.`);
286
+ }
287
+ async function handleGetAdrs(args, ctx) {
288
+ const { query, status = "all", limit = 10, } = args;
289
+ const response = await ctx.api.post("/api/memory/recall", {
290
+ projectName: ctx.projectName,
291
+ query: query || "architecture decision ADR",
292
+ type: "decision",
293
+ limit,
294
+ });
295
+ const results = response.data.results || [];
296
+ const adrs = results.filter((r) => r.memory.tags?.includes("adr") &&
297
+ (status === "all" || r.memory.metadata?.adrStatus === status));
298
+ if (adrs.length === 0) {
299
+ return `No ADRs found${query ? ` for "${query}"` : ""}`;
300
+ }
301
+ const statusIcons = {
302
+ proposed: "\uD83D\uDFE1",
303
+ accepted: "\uD83D\uDFE2",
304
+ deprecated: "\uD83D\uDD34",
305
+ superseded: "\u26AB",
306
+ };
307
+ let result = `# Architecture Decision Records (${adrs.length})\n\n`;
308
+ adrs.forEach((r, i) => {
309
+ const m = r.memory;
310
+ const adrStatus = (m.metadata?.adrStatus || "accepted");
311
+ const icon = statusIcons[adrStatus] || "\u26AA";
312
+ result += `### ${i + 1}. ${icon} ${m.metadata?.adrTitle || m.relatedTo || "ADR"}\n`;
313
+ result += `**Status:** ${adrStatus} | **ID:** \`${m.id}\`\n\n`;
314
+ result +=
315
+ truncate(m.content, 500) + "\n\n";
316
+ });
317
+ return result;
318
+ }
319
+ async function handleRecordPattern(args, ctx) {
320
+ const { name, description, structure, example, appliesTo, tags = [], } = args;
321
+ const patternContent = `# Pattern: ${name}
322
+
323
+ ## Description
324
+ ${description}
325
+
326
+ ## Structure
327
+ ${structure}
328
+
329
+ ${example ? `## Example\n\`\`\`\n${example}\n\`\`\`\n` : ""}
330
+ ${appliesTo ? `## Applies To\n${appliesTo}` : ""}`;
331
+ const response = await ctx.api.post("/api/memory", {
332
+ projectName: ctx.projectName,
333
+ content: patternContent,
334
+ type: "context",
335
+ tags: ["pattern", ...tags],
336
+ relatedTo: name,
337
+ metadata: { patternName: name, appliesTo },
338
+ });
339
+ return (`# Pattern Recorded\n\n` +
340
+ `- **Name:** ${name}\n` +
341
+ `- **ID:** ${response.data.memory.id}\n` +
342
+ (appliesTo ? `- **Applies To:** ${appliesTo}\n` : "") +
343
+ `- **Tags:** ${["pattern", ...tags].join(", ")}`);
344
+ }
345
+ async function handleGetPatterns(args, ctx) {
346
+ const { query, appliesTo, limit = 10, } = args;
347
+ const response = await ctx.api.post("/api/memory/recall", {
348
+ projectName: ctx.projectName,
349
+ query: query || "architectural pattern structure",
350
+ type: "context",
351
+ limit,
352
+ });
353
+ const results = response.data.results || [];
354
+ const patterns = results.filter((r) => {
355
+ const isPattern = r.memory.tags?.includes("pattern");
356
+ const matchesAppliesTo = !appliesTo ||
357
+ r.memory.metadata?.appliesTo
358
+ ?.toLowerCase()
359
+ .includes(appliesTo.toLowerCase());
360
+ return isPattern && matchesAppliesTo;
361
+ });
362
+ if (patterns.length === 0) {
363
+ return `No patterns found${query ? ` for "${query}"` : ""}`;
364
+ }
365
+ let result = `# Architectural Patterns (${patterns.length})\n\n`;
366
+ patterns.forEach((r, i) => {
367
+ const m = r.memory;
368
+ result += `### ${i + 1}. ${m.metadata?.patternName || m.relatedTo || "Pattern"}\n`;
369
+ if (m.metadata?.appliesTo) {
370
+ result += `**Applies to:** ${m.metadata.appliesTo}\n`;
371
+ }
372
+ result += `**ID:** \`${m.id}\`\n\n`;
373
+ result += truncate(m.content, 600) + "\n\n";
374
+ });
375
+ return result;
376
+ }
377
+ async function handleCheckArchitecture(args, ctx) {
378
+ const { code, filePath, featureDescription } = args;
379
+ const patternQuery = filePath || featureDescription || "architectural patterns";
380
+ // Get relevant patterns
381
+ const patternsResponse = await ctx.api.post("/api/memory/recall", {
382
+ projectName: ctx.projectName,
383
+ query: patternQuery,
384
+ type: "context",
385
+ limit: 5,
386
+ });
387
+ // Get relevant ADRs
388
+ const adrsResponse = await ctx.api.post("/api/memory/recall", {
389
+ projectName: ctx.projectName,
390
+ query: patternQuery,
391
+ type: "decision",
392
+ limit: 5,
393
+ });
394
+ // Search similar code in codebase
395
+ let similarCode = [];
396
+ if (code) {
397
+ const codeResponse = await ctx.api.post("/api/search", {
398
+ collection: `${ctx.collectionPrefix}codebase`,
399
+ query: code.slice(0, 500),
400
+ limit: 3,
401
+ });
402
+ similarCode = codeResponse.data.results || [];
403
+ }
404
+ const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
405
+ const adrs = (adrsResponse.data.results || []).filter((r) => r.memory.tags?.includes("adr"));
406
+ let result = `# Architecture Check\n\n`;
407
+ if (filePath) {
408
+ result += `**File:** ${filePath}\n\n`;
409
+ }
410
+ if (featureDescription) {
411
+ result += `**Feature:** ${featureDescription}\n\n`;
412
+ }
413
+ // If we have code and patterns/ADRs, perform LLM validation
414
+ if (code && (patterns.length > 0 || adrs.length > 0)) {
415
+ const patternRules = patterns
416
+ .map((p) => `Pattern: ${p.memory.metadata?.patternName || p.memory.relatedTo}\nDescription: ${truncate(p.memory.content, 300)}`)
417
+ .join("\n\n");
418
+ const adrRules = adrs
419
+ .map((a) => `ADR: ${a.memory.metadata?.adrTitle || a.memory.relatedTo}\nDecision: ${truncate(a.memory.content, 300)}`)
420
+ .join("\n\n");
421
+ const validationPrompt = `Analyze if this code follows the established architectural patterns and decisions.
422
+
423
+ Code to validate:
424
+ \`\`\`
425
+ ${code.slice(0, 2000)}
426
+ \`\`\`
427
+
428
+ Patterns to check against:
429
+ ${patternRules || "None recorded"}
430
+
431
+ Architectural Decisions (ADRs):
432
+ ${adrRules || "None recorded"}
433
+
434
+ Provide a structured analysis:
435
+ 1. List any violations of patterns or ADRs
436
+ 2. Rate compliance (1-10)
437
+ 3. Specific recommendations for improvements`;
438
+ try {
439
+ const validationResponse = await ctx.api.post("/api/ask", {
440
+ collection: `${ctx.collectionPrefix}codebase`,
441
+ question: validationPrompt,
442
+ });
443
+ result += `## Validation Results\n\n`;
444
+ result += validationResponse.data.answer;
445
+ result += "\n\n";
446
+ }
447
+ catch (_e) {
448
+ // Continue without LLM validation
449
+ }
450
+ }
451
+ result += `## Applicable Patterns (${patterns.length})\n`;
452
+ if (patterns.length === 0) {
453
+ result += `_No specific patterns recorded for this area._\n\n`;
454
+ }
455
+ else {
456
+ patterns.forEach((p) => {
457
+ result += `- **${p.memory.metadata?.patternName || p.memory.relatedTo}**: ${truncate(p.memory.content, 100)}\n`;
458
+ });
459
+ result += "\n";
460
+ }
461
+ result += `## Relevant ADRs (${adrs.length})\n`;
462
+ if (adrs.length === 0) {
463
+ result += `_No relevant architectural decisions found._\n\n`;
464
+ }
465
+ else {
466
+ adrs.forEach((a) => {
467
+ result += `- **${a.memory.metadata?.adrTitle || a.memory.relatedTo}** [${a.memory.metadata?.adrStatus || "accepted"}]: ${truncate(a.memory.content, 100)}\n`;
468
+ });
469
+ result += "\n";
470
+ }
471
+ if (similarCode.length > 0) {
472
+ result += `## Similar Existing Code\n`;
473
+ result += `_Review these for consistency:_\n`;
474
+ similarCode.forEach((c) => {
475
+ result += `- ${c.file}\n`;
476
+ });
477
+ result += "\n";
478
+ }
479
+ result += `## Recommendations\n`;
480
+ if (patterns.length > 0) {
481
+ result += `- Follow the patterns listed above for consistency\n`;
482
+ }
483
+ if (adrs.length > 0) {
484
+ result += `- Ensure compliance with recorded architectural decisions\n`;
485
+ }
486
+ if (similarCode.length > 0) {
487
+ result += `- Check similar code for established conventions\n`;
488
+ }
489
+ if (patterns.length === 0 && adrs.length === 0) {
490
+ result += `- Consider recording patterns/ADRs for this area with \`record_pattern\` and \`record_adr\`\n`;
491
+ }
492
+ return result;
493
+ }
494
+ async function handleSuggestArchitecture(args, ctx) {
495
+ const { feature, type = "other" } = args;
496
+ // Get patterns for this type
497
+ const patternsResponse = await ctx.api.post("/api/memory/recall", {
498
+ projectName: ctx.projectName,
499
+ query: `${type} ${feature} pattern structure`,
500
+ type: "context",
501
+ limit: 5,
502
+ });
503
+ // Get relevant ADRs
504
+ const adrsResponse = await ctx.api.post("/api/memory/recall", {
505
+ projectName: ctx.projectName,
506
+ query: `${type} ${feature}`,
507
+ type: "decision",
508
+ limit: 3,
509
+ });
510
+ // Get similar implementations
511
+ const codeResponse = await ctx.api.post("/api/search", {
512
+ collection: `${ctx.collectionPrefix}codebase`,
513
+ query: `${type} ${feature}`,
514
+ limit: 5,
515
+ });
516
+ const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
517
+ const adrs = (adrsResponse.data.results || []).filter((r) => r.memory.tags?.includes("adr"));
518
+ const existingCode = codeResponse.data.results || [];
519
+ let result = `# Architecture Suggestion: ${feature}\n\n`;
520
+ result += `**Type:** ${type}\n\n`;
521
+ result += `## Recommended Patterns\n`;
522
+ if (patterns.length === 0) {
523
+ result += `_No specific patterns recorded. Consider following existing code conventions._\n\n`;
524
+ }
525
+ else {
526
+ patterns.forEach((p) => {
527
+ result += `### ${p.memory.metadata?.patternName || p.memory.relatedTo}\n`;
528
+ result += truncate(p.memory.content, 400) + "\n\n";
529
+ });
530
+ }
531
+ result += `## Relevant Decisions (ADRs)\n`;
532
+ if (adrs.length === 0) {
533
+ result += `_No specific ADRs found for this area._\n\n`;
534
+ }
535
+ else {
536
+ adrs.forEach((a) => {
537
+ result += `- **${a.memory.metadata?.adrTitle || a.memory.relatedTo}**: `;
538
+ const decision = a.memory.content.match(/## Decision\n([\s\S]*?)(?=\n##|$)/);
539
+ result += decision
540
+ ? truncate(decision[1].trim(), 150)
541
+ : "See full ADR";
542
+ result += "\n";
543
+ });
544
+ result += "\n";
545
+ }
546
+ result += `## Reference Implementations\n`;
547
+ if (existingCode.length === 0) {
548
+ result += `_No similar implementations found._\n\n`;
549
+ }
550
+ else {
551
+ result += `_Study these for conventions:_\n`;
552
+ existingCode.forEach((c) => {
553
+ result += `- \`${c.file}\`\n`;
554
+ });
555
+ result += "\n";
556
+ }
557
+ result += `## Next Steps\n`;
558
+ result += `1. Review the patterns and ADRs above\n`;
559
+ result += `2. Study reference implementations for conventions\n`;
560
+ result += `3. Create your implementation following established structure\n`;
561
+ result += `4. Use \`check_architecture\` to validate before committing\n`;
562
+ return result;
563
+ }
564
+ async function handleRecordTechDebt(args, ctx) {
565
+ const { title, description, location, impact, suggestedFix, relatedAdr } = args;
566
+ const debtContent = `# Tech Debt: ${title}
567
+
568
+ ## Impact
569
+ ${impact.toUpperCase()}
570
+
571
+ ## Description
572
+ ${description}
573
+
574
+ ${location ? `## Location\n${location}\n` : ""}
575
+ ${suggestedFix ? `## Suggested Fix\n${suggestedFix}\n` : ""}
576
+ ${relatedAdr ? `## Related ADR\n${relatedAdr}` : ""}`;
577
+ const response = await ctx.api.post("/api/memory", {
578
+ projectName: ctx.projectName,
579
+ content: debtContent,
580
+ type: "insight",
581
+ tags: ["tech-debt", `impact-${impact}`],
582
+ relatedTo: title,
583
+ metadata: { debtTitle: title, impact, location },
584
+ });
585
+ const impactEmojis = {
586
+ low: "\uD83D\uDFE2",
587
+ medium: "\uD83D\uDFE1",
588
+ high: "\uD83D\uDFE0",
589
+ critical: "\uD83D\uDD34",
590
+ };
591
+ const emoji = impactEmojis[impact] || "\u26AA";
592
+ return (`${emoji} **Tech Debt Recorded**\n\n` +
593
+ `- **Title:** ${title}\n` +
594
+ `- **Impact:** ${impact}\n` +
595
+ `- **ID:** ${response.data.memory.id}\n` +
596
+ (location ? `- **Location:** ${location}\n` : ""));
597
+ }
598
+ async function handleGetTechDebt(args, ctx) {
599
+ const { impact = "all", limit = 10 } = args;
600
+ const response = await ctx.api.post("/api/memory/recall", {
601
+ projectName: ctx.projectName,
602
+ query: "technical debt violation issue",
603
+ type: "insight",
604
+ limit: limit * 2, // Fetch extra to account for filtering
605
+ });
606
+ const results = response.data.results || [];
607
+ const debts = results
608
+ .filter((r) => {
609
+ const isDebt = r.memory.tags?.includes("tech-debt");
610
+ const matchesImpact = impact === "all" ||
611
+ r.memory.metadata?.impact === impact ||
612
+ r.memory.tags?.includes(`impact-${impact}`);
613
+ return isDebt && matchesImpact;
614
+ })
615
+ .slice(0, limit);
616
+ if (debts.length === 0) {
617
+ return `No tech debt found${impact !== "all" ? ` with ${impact} impact` : ""}`;
618
+ }
619
+ const impactEmojis = {
620
+ low: "\uD83D\uDFE2",
621
+ medium: "\uD83D\uDFE1",
622
+ high: "\uD83D\uDFE0",
623
+ critical: "\uD83D\uDD34",
624
+ };
625
+ let result = `# Technical Debt (${debts.length})\n\n`;
626
+ debts.forEach((r, i) => {
627
+ const m = r.memory;
628
+ const debtImpact = m.metadata?.impact || "medium";
629
+ const emoji = impactEmojis[debtImpact] || "\u26AA";
630
+ result += `### ${i + 1}. ${emoji} ${m.metadata?.debtTitle || m.relatedTo || "Tech Debt"}\n`;
631
+ result += `**Impact:** ${debtImpact}`;
632
+ if (m.metadata?.location) {
633
+ result += ` | **Location:** ${m.metadata.location}`;
634
+ }
635
+ result += `\n**ID:** \`${m.id}\`\n\n`;
636
+ // Extract description section
637
+ const descMatch = m.content.match(/## Description\n([\s\S]*?)(?=\n##|$)/);
638
+ if (descMatch) {
639
+ result += truncate(descMatch[1].trim(), 200) + "\n\n";
640
+ }
641
+ });
642
+ return result;
643
+ }
644
+ async function handleAnalyzeProjectStructure(args, ctx) {
645
+ const { path, deep = false } = args;
646
+ // Get all recorded patterns
647
+ const patternsResponse = await ctx.api.post("/api/memory/recall", {
648
+ projectName: ctx.projectName,
649
+ query: "pattern structure organization",
650
+ type: "context",
651
+ limit: 10,
652
+ });
653
+ // Get codebase structure
654
+ const codeResponse = await ctx.api.post("/api/search", {
655
+ collection: `${ctx.collectionPrefix}codebase`,
656
+ query: path || "module service controller",
657
+ limit: deep ? 20 : 10,
658
+ });
659
+ const patterns = (patternsResponse.data.results || []).filter((r) => r.memory.tags?.includes("pattern"));
660
+ const codeFiles = codeResponse.data.results || [];
661
+ // Analyze file organization by directory
662
+ const filesByDir = {};
663
+ codeFiles.forEach((c) => {
664
+ const dir = c.file.split("/").slice(0, -1).join("/") || "/";
665
+ if (!filesByDir[dir])
666
+ filesByDir[dir] = [];
667
+ filesByDir[dir].push(c.file.split("/").pop());
668
+ });
669
+ let result = `# Project Structure Analysis\n\n`;
670
+ if (path) {
671
+ result += `**Scope:** ${path}\n\n`;
672
+ }
673
+ result += `## Directory Structure\n`;
674
+ Object.entries(filesByDir)
675
+ .slice(0, 10)
676
+ .forEach(([dir, files]) => {
677
+ result += `\n**${dir || "/"}/**\n`;
678
+ files.slice(0, 5).forEach((f) => {
679
+ result += ` - ${f}\n`;
680
+ });
681
+ if (files.length > 5) {
682
+ result += ` - ... and ${files.length - 5} more\n`;
683
+ }
684
+ });
685
+ result += `\n## Recorded Patterns (${patterns.length})\n`;
686
+ if (patterns.length === 0) {
687
+ result += `_No patterns recorded yet. Consider documenting your architectural patterns._\n\n`;
688
+ }
689
+ else {
690
+ patterns.forEach((p) => {
691
+ result += `- ${p.memory.metadata?.patternName || p.memory.relatedTo}`;
692
+ if (p.memory.metadata?.appliesTo) {
693
+ result += ` -> ${p.memory.metadata.appliesTo}`;
694
+ }
695
+ result += "\n";
696
+ });
697
+ }
698
+ result += `\n## Recommendations\n`;
699
+ if (patterns.length === 0) {
700
+ result += `1. **Record patterns** - Use \`record_pattern\` to document how code should be structured\n`;
701
+ }
702
+ result += `2. **Document decisions** - Use \`record_adr\` for important architectural choices\n`;
703
+ result += `3. **Track tech debt** - Use \`record_tech_debt\` for violations and issues\n`;
704
+ result += `4. **Validate changes** - Use \`check_architecture\` before committing new code\n`;
705
+ return result;
706
+ }
707
+ // ── Build handlers map ────────────────────────────────────────────────
708
+ const handlers = {
709
+ record_adr: handleRecordAdr,
710
+ get_adrs: handleGetAdrs,
711
+ record_pattern: handleRecordPattern,
712
+ get_patterns: handleGetPatterns,
713
+ check_architecture: handleCheckArchitecture,
714
+ suggest_architecture: handleSuggestArchitecture,
715
+ record_tech_debt: handleRecordTechDebt,
716
+ get_tech_debt: handleGetTechDebt,
717
+ analyze_project_structure: handleAnalyzeProjectStructure,
718
+ };
719
+ return { tools, handlers };
720
+ }