@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.
Files changed (51) 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 +99 -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/quality.d.ts +8 -0
  41. package/dist/tools/quality.js +60 -0
  42. package/dist/tools/review.d.ts +2 -2
  43. package/dist/tools/review.js +142 -189
  44. package/dist/tools/search.d.ts +2 -2
  45. package/dist/tools/search.js +230 -305
  46. package/dist/tools/session.d.ts +2 -2
  47. package/dist/tools/session.js +288 -345
  48. package/dist/tools/suggestions.d.ts +2 -2
  49. package/dist/tools/suggestions.js +517 -512
  50. package/dist/types.d.ts +19 -2
  51. package/package.json +4 -2
@@ -1,429 +1,352 @@
1
1
  /**
2
2
  * Database Tools - Schema documentation, rules, enums, and validation.
3
3
  */
4
+ import { z } from "zod";
5
+ import { TOOL_ANNOTATIONS } from "../annotations.js";
4
6
  export function createDatabaseTools(projectName) {
5
- const tools = [
7
+ return [
6
8
  {
7
9
  name: "record_table",
8
10
  description: `Record a database table definition with its purpose, columns, and relationships. Use this to document the database schema for ${projectName}.`,
9
- inputSchema: {
10
- type: "object",
11
- properties: {
12
- tableName: {
13
- type: "string",
14
- description: "Name of the table (e.g., 'claims', 'documents')",
15
- },
16
- purpose: {
17
- type: "string",
18
- description: "What this table is for and when it's used",
19
- },
20
- columns: {
21
- type: "string",
22
- description: "Key columns and their purposes (format: 'column_name: description')",
23
- },
24
- relationships: {
25
- type: "string",
26
- description: "Relationships to other tables (FK references)",
27
- },
28
- indexes: {
29
- type: "string",
30
- description: "Important indexes and their purpose",
31
- },
32
- rules: {
33
- type: "string",
34
- description: "Business rules and constraints for this table",
11
+ schema: z.object({
12
+ tableName: z.string().describe("Name of the table (e.g., 'claims', 'documents')"),
13
+ purpose: z.string().describe("What this table is for and when it's used"),
14
+ columns: z.string().describe("Key columns and their purposes (format: 'column_name: description')"),
15
+ relationships: z.string().optional().describe("Relationships to other tables (FK references)"),
16
+ indexes: z.string().optional().describe("Important indexes and their purpose"),
17
+ rules: z.string().optional().describe("Business rules and constraints for this table"),
18
+ }),
19
+ annotations: TOOL_ANNOTATIONS["record_table"],
20
+ handler: async (args, ctx) => {
21
+ const { tableName, purpose, columns, relationships, indexes, rules } = args;
22
+ const content = `## Table: ${tableName}
23
+
24
+ **Purpose:** ${purpose}
25
+
26
+ ### Columns
27
+ ${columns}
28
+
29
+ ${relationships ? `### Relationships\n${relationships}\n` : ""}
30
+ ${indexes ? `### Indexes\n${indexes}\n` : ""}
31
+ ${rules ? `### Business Rules\n${rules}` : ""}`;
32
+ await ctx.api.post("/api/memory", {
33
+ projectName: ctx.projectName,
34
+ content,
35
+ type: "context",
36
+ tags: ["database", "schema", "table", tableName.toLowerCase()],
37
+ relatedTo: `table:${tableName}`,
38
+ metadata: {
39
+ tableType: "table",
40
+ tableName,
35
41
  },
36
- },
37
- required: ["tableName", "purpose", "columns"],
42
+ });
43
+ return `Recorded table **${tableName}** documentation.\n\nUse \`get_table_info "${tableName}"\` to retrieve it later.`;
38
44
  },
39
45
  },
40
46
  {
41
47
  name: "get_table_info",
42
48
  description: "Get documented information about a database table including its purpose, columns, relationships, and rules.",
43
- inputSchema: {
44
- type: "object",
45
- properties: {
46
- tableName: {
47
- type: "string",
48
- description: "Table name to look up (or 'all' to list all tables)",
49
- },
50
- },
51
- required: ["tableName"],
49
+ schema: z.object({
50
+ tableName: z.string().describe("Table name to look up (or 'all' to list all tables)"),
51
+ }),
52
+ annotations: TOOL_ANNOTATIONS["get_table_info"],
53
+ handler: async (args, ctx) => {
54
+ const { tableName } = args;
55
+ const response = await ctx.api.post("/api/memory/recall", {
56
+ projectName: ctx.projectName,
57
+ query: tableName === "all" ? "database table schema" : `table ${tableName}`,
58
+ tag: "table",
59
+ limit: tableName === "all" ? 20 : 5,
60
+ });
61
+ const tables = response.data.results || [];
62
+ if (tables.length === 0) {
63
+ return `No documentation found for ${tableName === "all" ? "any tables" : `table "${tableName}"`}.\n\nUse \`record_table\` to document tables.`;
64
+ }
65
+ let result = tableName === "all"
66
+ ? `# Database Tables (${tables.length})\n\n`
67
+ : `# Table: ${tableName}\n\n`;
68
+ tables.forEach((t) => {
69
+ result += t.memory.content + "\n\n---\n\n";
70
+ });
71
+ return result;
52
72
  },
53
73
  },
54
74
  {
55
75
  name: "record_db_rule",
56
76
  description: "Record a database rule or constraint that should be followed. Use this for data integrity rules, naming conventions, or query patterns.",
57
- inputSchema: {
58
- type: "object",
59
- properties: {
60
- ruleName: {
61
- type: "string",
62
- description: "Short name for the rule",
63
- },
64
- description: {
65
- type: "string",
66
- description: "Detailed description of the rule",
67
- },
68
- scope: {
69
- type: "string",
70
- enum: ["global", "table", "column", "query", "migration"],
71
- description: "Where this rule applies",
72
- },
73
- examples: {
74
- type: "string",
75
- description: "Good and bad examples of applying this rule",
77
+ schema: z.object({
78
+ ruleName: z.string().describe("Short name for the rule"),
79
+ description: z.string().describe("Detailed description of the rule"),
80
+ scope: z.enum(["global", "table", "column", "query", "migration", "naming", "primary-key", "timestamps", "multi-tenancy", "soft-delete", "status"]).describe("Where this rule applies"),
81
+ examples: z.string().optional().describe("Good and bad examples of applying this rule"),
82
+ }),
83
+ annotations: TOOL_ANNOTATIONS["record_db_rule"],
84
+ handler: async (args, ctx) => {
85
+ const { ruleName, description, scope, examples } = args;
86
+ const content = `## DB Rule: ${ruleName}
87
+
88
+ **Scope:** ${scope}
89
+
90
+ ${description}
91
+
92
+ ${examples ? `### Examples\n${examples}` : ""}`;
93
+ await ctx.api.post("/api/memory", {
94
+ projectName: ctx.projectName,
95
+ content,
96
+ type: "decision",
97
+ tags: ["database", "rule", scope],
98
+ relatedTo: `db-rule:${ruleName}`,
99
+ metadata: {
100
+ ruleType: "db-rule",
101
+ ruleName,
102
+ scope,
76
103
  },
77
- },
78
- required: ["ruleName", "description", "scope"],
104
+ });
105
+ return `Recorded database rule: **${ruleName}** (scope: ${scope})`;
79
106
  },
80
107
  },
81
108
  {
82
109
  name: "get_db_rules",
83
110
  description: `Get database rules and constraints for ${projectName}. Filter by scope or get all rules.`,
84
- inputSchema: {
85
- type: "object",
86
- properties: {
87
- scope: {
88
- type: "string",
89
- enum: ["global", "table", "column", "query", "migration", "all"],
90
- description: "Filter by scope (default: all)",
91
- default: "all",
92
- },
93
- },
111
+ schema: z.object({
112
+ scope: z.enum(["global", "table", "column", "query", "migration", "all"]).optional().describe("Filter by scope (default: all)"),
113
+ }),
114
+ annotations: TOOL_ANNOTATIONS["get_db_rules"],
115
+ handler: async (args, ctx) => {
116
+ const { scope = "all" } = args;
117
+ const response = await ctx.api.post("/api/memory/recall", {
118
+ projectName: ctx.projectName,
119
+ query: scope === "all"
120
+ ? "database rule constraint"
121
+ : `database rule ${scope}`,
122
+ tag: "rule",
123
+ limit: 15,
124
+ });
125
+ const rules = response.data.results || [];
126
+ if (rules.length === 0) {
127
+ return `No database rules found${scope !== "all" ? ` for scope "${scope}"` : ""}.\n\nUse \`record_db_rule\` to document rules.`;
128
+ }
129
+ let result = `# Database Rules (${rules.length})\n\n`;
130
+ rules.forEach((r) => {
131
+ const m = r.memory;
132
+ result += m.content + "\n\n---\n\n";
133
+ });
134
+ return result;
94
135
  },
95
136
  },
96
137
  {
97
138
  name: "record_enum",
98
139
  description: "Record a database enum type with its values and usage. Use this to document allowed values for status fields, types, etc.",
99
- inputSchema: {
100
- type: "object",
101
- properties: {
102
- enumName: {
103
- type: "string",
104
- description: "Name of the enum (e.g., 'ClaimStatus', 'DocumentType')",
105
- },
106
- values: {
107
- type: "string",
108
- description: "List of enum values with descriptions (format: 'value: description')",
109
- },
110
- usedIn: {
111
- type: "string",
112
- description: "Tables and columns where this enum is used",
113
- },
114
- transitions: {
115
- type: "string",
116
- description: "Allowed state transitions (for status enums)",
140
+ schema: z.object({
141
+ enumName: z.string().describe("Name of the enum (e.g., 'ClaimStatus', 'DocumentType')"),
142
+ values: z.string().describe("List of enum values with descriptions (format: 'value: description')"),
143
+ usedIn: z.string().optional().describe("Tables and columns where this enum is used"),
144
+ transitions: z.string().optional().describe("Allowed state transitions (for status enums)"),
145
+ }),
146
+ annotations: TOOL_ANNOTATIONS["record_enum"],
147
+ handler: async (args, ctx) => {
148
+ const { enumName, values, usedIn, transitions } = args;
149
+ const content = `## Enum: ${enumName}
150
+
151
+ ### Values
152
+ ${values}
153
+
154
+ ${usedIn ? `### Used In\n${usedIn}\n` : ""}
155
+ ${transitions ? `### State Transitions\n${transitions}` : ""}`;
156
+ await ctx.api.post("/api/memory", {
157
+ projectName: ctx.projectName,
158
+ content,
159
+ type: "context",
160
+ tags: ["database", "schema", "enum", enumName.toLowerCase()],
161
+ relatedTo: `enum:${enumName}`,
162
+ metadata: {
163
+ tableType: "enum",
164
+ enumName,
117
165
  },
118
- },
119
- required: ["enumName", "values"],
166
+ });
167
+ return `Recorded enum **${enumName}** documentation.`;
120
168
  },
121
169
  },
122
170
  {
123
171
  name: "get_enums",
124
172
  description: `Get documented enum types for ${projectName} database.`,
125
- inputSchema: {
126
- type: "object",
127
- properties: {
128
- enumName: {
129
- type: "string",
130
- description: "Specific enum to look up (or empty for all)",
131
- },
132
- },
173
+ schema: z.object({
174
+ enumName: z.string().optional().describe("Specific enum to look up (or empty for all)"),
175
+ }),
176
+ annotations: TOOL_ANNOTATIONS["get_enums"],
177
+ handler: async (args, ctx) => {
178
+ const { enumName } = args;
179
+ const response = await ctx.api.post("/api/memory/recall", {
180
+ projectName: ctx.projectName,
181
+ query: enumName
182
+ ? `enum ${enumName}`
183
+ : "database enum type values",
184
+ tag: "enum",
185
+ limit: 15,
186
+ });
187
+ const enums = response.data.results || [];
188
+ if (enums.length === 0) {
189
+ return `No enum documentation found${enumName ? ` for "${enumName}"` : ""}.\n\nUse \`record_enum\` to document enums.`;
190
+ }
191
+ let result = `# Database Enums (${enums.length})\n\n`;
192
+ enums.forEach((e) => {
193
+ result += e.memory.content + "\n\n---\n\n";
194
+ });
195
+ return result;
133
196
  },
134
197
  },
135
198
  {
136
199
  name: "check_db_schema",
137
200
  description: "Check if a proposed database change follows the documented rules and patterns. Use before creating migrations.",
138
- inputSchema: {
139
- type: "object",
140
- properties: {
141
- change: {
142
- type: "string",
143
- description: "Description of the proposed change (new table, column, index, etc.)",
144
- },
145
- sql: {
146
- type: "string",
147
- description: "Optional SQL or Prisma schema for the change",
148
- },
149
- },
150
- required: ["change"],
201
+ schema: z.object({
202
+ change: z.string().describe("Description of the proposed change (new table, column, index, etc.)"),
203
+ sql: z.string().optional().describe("Optional SQL or Prisma schema for the change"),
204
+ }),
205
+ annotations: TOOL_ANNOTATIONS["check_db_schema"],
206
+ handler: async (args, ctx) => {
207
+ const { change, sql } = args;
208
+ // Get relevant rules and existing schema
209
+ const [rulesRes, tablesRes] = await Promise.all([
210
+ ctx.api.post("/api/memory/recall", {
211
+ projectName: ctx.projectName,
212
+ query: "database rule constraint naming convention",
213
+ tag: "rule",
214
+ limit: 10,
215
+ }),
216
+ ctx.api.post("/api/memory/recall", {
217
+ projectName: ctx.projectName,
218
+ query: change,
219
+ tag: "table",
220
+ limit: 5,
221
+ }),
222
+ ]);
223
+ const rules = rulesRes.data.results || [];
224
+ const relatedTables = tablesRes.data.results || [];
225
+ let result = `# Schema Change Review\n\n`;
226
+ result += `**Proposed Change:** ${change}\n\n`;
227
+ if (sql) {
228
+ result += `**SQL/Schema:**\n\`\`\`sql\n${sql}\n\`\`\`\n\n`;
229
+ }
230
+ result += `## Applicable Rules (${rules.length})\n`;
231
+ if (rules.length === 0) {
232
+ result += `_No database rules documented. Consider adding rules with \`record_db_rule\`._\n\n`;
233
+ }
234
+ else {
235
+ rules.forEach((r) => {
236
+ const m = r.memory;
237
+ result += `- **${m.metadata?.ruleName || m.relatedTo}** (${m.metadata?.scope || "general"})\n`;
238
+ });
239
+ result += "\n";
240
+ }
241
+ result += `## Related Tables (${relatedTables.length})\n`;
242
+ if (relatedTables.length === 0) {
243
+ result += `_No documented tables found related to this change._\n\n`;
244
+ }
245
+ else {
246
+ relatedTables.forEach((t) => {
247
+ result += `- ${t.memory.metadata?.tableName || t.memory.relatedTo}\n`;
248
+ });
249
+ result += "\n";
250
+ }
251
+ result += `## Checklist\n`;
252
+ result += `- [ ] Follows naming conventions\n`;
253
+ result += `- [ ] Has appropriate indexes\n`;
254
+ result += `- [ ] Foreign keys properly defined\n`;
255
+ result += `- [ ] NOT NULL constraints where needed\n`;
256
+ result += `- [ ] Default values appropriate\n`;
257
+ result += `- [ ] Multi-tenant (partnerId) considered\n`;
258
+ result += `- [ ] Migration is reversible\n`;
259
+ return result;
151
260
  },
152
261
  },
153
262
  {
154
263
  name: "suggest_db_schema",
155
264
  description: "Get suggestions for database schema design for a new feature or data requirement.",
156
- inputSchema: {
157
- type: "object",
158
- properties: {
159
- requirement: {
160
- type: "string",
161
- description: "What data needs to be stored or what feature needs support",
162
- },
163
- relatedTables: {
164
- type: "string",
165
- description: "Existing tables that might be related",
166
- },
167
- },
168
- required: ["requirement"],
265
+ schema: z.object({
266
+ requirement: z.string().describe("What data needs to be stored or what feature needs support"),
267
+ relatedTables: z.string().optional().describe("Existing tables that might be related"),
268
+ }),
269
+ annotations: TOOL_ANNOTATIONS["suggest_db_schema"],
270
+ handler: async (args, ctx) => {
271
+ const { requirement, relatedTables } = args;
272
+ // Get existing schema patterns and rules
273
+ const [rulesRes, tablesRes, enumsRes] = await Promise.all([
274
+ ctx.api.post("/api/memory/recall", {
275
+ projectName: ctx.projectName,
276
+ query: "database rule naming convention pattern",
277
+ tag: "rule",
278
+ limit: 5,
279
+ }),
280
+ ctx.api.post("/api/memory/recall", {
281
+ projectName: ctx.projectName,
282
+ query: relatedTables || requirement,
283
+ tag: "table",
284
+ limit: 5,
285
+ }),
286
+ ctx.api.post("/api/memory/recall", {
287
+ projectName: ctx.projectName,
288
+ query: requirement,
289
+ tag: "enum",
290
+ limit: 3,
291
+ }),
292
+ ]);
293
+ const rules = rulesRes.data.results || [];
294
+ const tables = tablesRes.data.results || [];
295
+ const enums = enumsRes.data.results || [];
296
+ let result = `# Schema Suggestion\n\n`;
297
+ result += `**Requirement:** ${requirement}\n\n`;
298
+ if (relatedTables) {
299
+ result += `**Related Tables:** ${relatedTables}\n\n`;
300
+ }
301
+ result += `## Existing Context\n\n`;
302
+ if (tables.length > 0) {
303
+ result += `### Related Tables\n`;
304
+ tables.forEach((t) => {
305
+ result += `- **${t.memory.metadata?.tableName || t.memory.relatedTo}**\n`;
306
+ });
307
+ result += "\n";
308
+ }
309
+ if (enums.length > 0) {
310
+ result += `### Available Enums\n`;
311
+ enums.forEach((e) => {
312
+ result += `- ${e.memory.metadata?.enumName || e.memory.relatedTo}\n`;
313
+ });
314
+ result += "\n";
315
+ }
316
+ if (rules.length > 0) {
317
+ result += `### Rules to Follow\n`;
318
+ rules.forEach((r) => {
319
+ result += `- ${r.memory.metadata?.ruleName || r.memory.relatedTo}: ${r.memory.content.slice(0, 100)}...\n`;
320
+ });
321
+ result += "\n";
322
+ }
323
+ result += `## Suggestions\n\n`;
324
+ if (rules.length > 0) {
325
+ result += `Based on your project's documented DB rules:\n\n`;
326
+ rules.forEach((r, i) => {
327
+ const name = r.memory.metadata?.ruleName || r.memory.relatedTo || "Rule";
328
+ const scope = r.memory.metadata?.scope || "";
329
+ const desc = r.memory.content.replace(/^## DB Rule:.*\n*/m, "").replace(/\*\*Scope:\*\*.*\n*/m, "").trim();
330
+ result += `${i + 1}. **${name}**${scope ? ` (${scope})` : ""}: ${desc.split("\n")[0]}\n`;
331
+ });
332
+ result += "\n";
333
+ }
334
+ else {
335
+ result += `No project-specific DB rules found. Using defaults:\n\n`;
336
+ result += `1. **Table naming**: Use snake_case, plural (e.g., \`notifications\`)\n`;
337
+ result += `2. **Primary key**: UUID with \`gen_random_uuid()\`\n`;
338
+ result += `3. **Timestamps**: Include \`created_at\`, \`updated_at\`\n`;
339
+ result += `4. **Soft delete**: Consider \`deleted_at\` timestamp\n`;
340
+ result += `5. **Status fields**: Use PostgreSQL ENUMs\n`;
341
+ result += `\n_Tip: Use \`record_db_rule\` to document project-specific conventions._\n`;
342
+ }
343
+ result += `\n## Next Steps\n`;
344
+ result += `1. Design the schema using suggestions above\n`;
345
+ result += `2. Run \`check_db_schema\` to validate\n`;
346
+ result += `3. Create Prisma migration\n`;
347
+ result += `4. Document with \`record_table\` after creation\n`;
348
+ return result;
169
349
  },
170
350
  },
171
351
  ];
172
- const handlers = {
173
- async record_table(args, ctx) {
174
- const { tableName, purpose, columns, relationships, indexes, rules } = args;
175
- const content = `## Table: ${tableName}
176
-
177
- **Purpose:** ${purpose}
178
-
179
- ### Columns
180
- ${columns}
181
-
182
- ${relationships ? `### Relationships\n${relationships}\n` : ""}
183
- ${indexes ? `### Indexes\n${indexes}\n` : ""}
184
- ${rules ? `### Business Rules\n${rules}` : ""}`;
185
- await ctx.api.post("/api/memory", {
186
- projectName: ctx.projectName,
187
- content,
188
- type: "context",
189
- tags: ["database", "schema", "table", tableName.toLowerCase()],
190
- relatedTo: `table:${tableName}`,
191
- metadata: {
192
- tableType: "table",
193
- tableName,
194
- },
195
- });
196
- return `Recorded table **${tableName}** documentation.\n\nUse \`get_table_info "${tableName}"\` to retrieve it later.`;
197
- },
198
- async get_table_info(args, ctx) {
199
- const { tableName } = args;
200
- const response = await ctx.api.post("/api/memory/recall", {
201
- projectName: ctx.projectName,
202
- query: tableName === "all" ? "database table schema" : `table ${tableName}`,
203
- tag: "table",
204
- limit: tableName === "all" ? 20 : 5,
205
- });
206
- const tables = response.data.results || [];
207
- if (tables.length === 0) {
208
- return `No documentation found for ${tableName === "all" ? "any tables" : `table "${tableName}"`}.\n\nUse \`record_table\` to document tables.`;
209
- }
210
- let result = tableName === "all"
211
- ? `# Database Tables (${tables.length})\n\n`
212
- : `# Table: ${tableName}\n\n`;
213
- tables.forEach((t) => {
214
- result += t.memory.content + "\n\n---\n\n";
215
- });
216
- return result;
217
- },
218
- async record_db_rule(args, ctx) {
219
- const { ruleName, description, scope, examples } = args;
220
- const content = `## DB Rule: ${ruleName}
221
-
222
- **Scope:** ${scope}
223
-
224
- ${description}
225
-
226
- ${examples ? `### Examples\n${examples}` : ""}`;
227
- await ctx.api.post("/api/memory", {
228
- projectName: ctx.projectName,
229
- content,
230
- type: "decision",
231
- tags: ["database", "rule", scope],
232
- relatedTo: `db-rule:${ruleName}`,
233
- metadata: {
234
- ruleType: "db-rule",
235
- ruleName,
236
- scope,
237
- },
238
- });
239
- return `Recorded database rule: **${ruleName}** (scope: ${scope})`;
240
- },
241
- async get_db_rules(args, ctx) {
242
- const { scope = "all" } = args;
243
- const response = await ctx.api.post("/api/memory/recall", {
244
- projectName: ctx.projectName,
245
- query: scope === "all"
246
- ? "database rule constraint"
247
- : `database rule ${scope}`,
248
- tag: "rule",
249
- limit: 15,
250
- });
251
- const rules = response.data.results || [];
252
- if (rules.length === 0) {
253
- return `No database rules found${scope !== "all" ? ` for scope "${scope}"` : ""}.\n\nUse \`record_db_rule\` to document rules.`;
254
- }
255
- let result = `# Database Rules (${rules.length})\n\n`;
256
- rules.forEach((r) => {
257
- const m = r.memory;
258
- result += m.content + "\n\n---\n\n";
259
- });
260
- return result;
261
- },
262
- async record_enum(args, ctx) {
263
- const { enumName, values, usedIn, transitions } = args;
264
- const content = `## Enum: ${enumName}
265
-
266
- ### Values
267
- ${values}
268
-
269
- ${usedIn ? `### Used In\n${usedIn}\n` : ""}
270
- ${transitions ? `### State Transitions\n${transitions}` : ""}`;
271
- await ctx.api.post("/api/memory", {
272
- projectName: ctx.projectName,
273
- content,
274
- type: "context",
275
- tags: ["database", "schema", "enum", enumName.toLowerCase()],
276
- relatedTo: `enum:${enumName}`,
277
- metadata: {
278
- tableType: "enum",
279
- enumName,
280
- },
281
- });
282
- return `Recorded enum **${enumName}** documentation.`;
283
- },
284
- async get_enums(args, ctx) {
285
- const { enumName } = args;
286
- const response = await ctx.api.post("/api/memory/recall", {
287
- projectName: ctx.projectName,
288
- query: enumName
289
- ? `enum ${enumName}`
290
- : "database enum type values",
291
- tag: "enum",
292
- limit: 15,
293
- });
294
- const enums = response.data.results || [];
295
- if (enums.length === 0) {
296
- return `No enum documentation found${enumName ? ` for "${enumName}"` : ""}.\n\nUse \`record_enum\` to document enums.`;
297
- }
298
- let result = `# Database Enums (${enums.length})\n\n`;
299
- enums.forEach((e) => {
300
- result += e.memory.content + "\n\n---\n\n";
301
- });
302
- return result;
303
- },
304
- async check_db_schema(args, ctx) {
305
- const { change, sql } = args;
306
- // Get relevant rules and existing schema
307
- const [rulesRes, tablesRes] = await Promise.all([
308
- ctx.api.post("/api/memory/recall", {
309
- projectName: ctx.projectName,
310
- query: "database rule constraint naming convention",
311
- tag: "rule",
312
- limit: 10,
313
- }),
314
- ctx.api.post("/api/memory/recall", {
315
- projectName: ctx.projectName,
316
- query: change,
317
- tag: "table",
318
- limit: 5,
319
- }),
320
- ]);
321
- const rules = rulesRes.data.results || [];
322
- const relatedTables = tablesRes.data.results || [];
323
- let result = `# Schema Change Review\n\n`;
324
- result += `**Proposed Change:** ${change}\n\n`;
325
- if (sql) {
326
- result += `**SQL/Schema:**\n\`\`\`sql\n${sql}\n\`\`\`\n\n`;
327
- }
328
- result += `## Applicable Rules (${rules.length})\n`;
329
- if (rules.length === 0) {
330
- result += `_No database rules documented. Consider adding rules with \`record_db_rule\`._\n\n`;
331
- }
332
- else {
333
- rules.forEach((r) => {
334
- const m = r.memory;
335
- result += `- **${m.metadata?.ruleName || m.relatedTo}** (${m.metadata?.scope || "general"})\n`;
336
- });
337
- result += "\n";
338
- }
339
- result += `## Related Tables (${relatedTables.length})\n`;
340
- if (relatedTables.length === 0) {
341
- result += `_No documented tables found related to this change._\n\n`;
342
- }
343
- else {
344
- relatedTables.forEach((t) => {
345
- result += `- ${t.memory.metadata?.tableName || t.memory.relatedTo}\n`;
346
- });
347
- result += "\n";
348
- }
349
- result += `## Checklist\n`;
350
- result += `- [ ] Follows naming conventions\n`;
351
- result += `- [ ] Has appropriate indexes\n`;
352
- result += `- [ ] Foreign keys properly defined\n`;
353
- result += `- [ ] NOT NULL constraints where needed\n`;
354
- result += `- [ ] Default values appropriate\n`;
355
- result += `- [ ] Multi-tenant (partnerId) considered\n`;
356
- result += `- [ ] Migration is reversible\n`;
357
- return result;
358
- },
359
- async suggest_db_schema(args, ctx) {
360
- const { requirement, relatedTables } = args;
361
- // Get existing schema patterns and rules
362
- const [rulesRes, tablesRes, enumsRes] = await Promise.all([
363
- ctx.api.post("/api/memory/recall", {
364
- projectName: ctx.projectName,
365
- query: "database rule naming convention pattern",
366
- tag: "rule",
367
- limit: 5,
368
- }),
369
- ctx.api.post("/api/memory/recall", {
370
- projectName: ctx.projectName,
371
- query: relatedTables || requirement,
372
- tag: "table",
373
- limit: 5,
374
- }),
375
- ctx.api.post("/api/memory/recall", {
376
- projectName: ctx.projectName,
377
- query: requirement,
378
- tag: "enum",
379
- limit: 3,
380
- }),
381
- ]);
382
- const rules = rulesRes.data.results || [];
383
- const tables = tablesRes.data.results || [];
384
- const enums = enumsRes.data.results || [];
385
- let result = `# Schema Suggestion\n\n`;
386
- result += `**Requirement:** ${requirement}\n\n`;
387
- if (relatedTables) {
388
- result += `**Related Tables:** ${relatedTables}\n\n`;
389
- }
390
- result += `## Existing Context\n\n`;
391
- if (tables.length > 0) {
392
- result += `### Related Tables\n`;
393
- tables.forEach((t) => {
394
- result += `- **${t.memory.metadata?.tableName || t.memory.relatedTo}**\n`;
395
- });
396
- result += "\n";
397
- }
398
- if (enums.length > 0) {
399
- result += `### Available Enums\n`;
400
- enums.forEach((e) => {
401
- result += `- ${e.memory.metadata?.enumName || e.memory.relatedTo}\n`;
402
- });
403
- result += "\n";
404
- }
405
- if (rules.length > 0) {
406
- result += `### Rules to Follow\n`;
407
- rules.forEach((r) => {
408
- result += `- ${r.memory.metadata?.ruleName || r.memory.relatedTo}: ${r.memory.content.slice(0, 100)}...\n`;
409
- });
410
- result += "\n";
411
- }
412
- result += `## Suggestions\n\n`;
413
- result += `Based on the existing schema patterns:\n\n`;
414
- result += `1. **Table naming**: Use snake_case, plural (e.g., \`notifications\`)\n`;
415
- result += `2. **Primary key**: UUID with \`gen_random_uuid()\`\n`;
416
- result += `3. **Timestamps**: Include \`created_at\`, \`updated_at\`\n`;
417
- result += `4. **Multi-tenant**: Add \`partner_id UUID NOT NULL\` with FK\n`;
418
- result += `5. **Soft delete**: Consider \`deleted_at\` timestamp\n`;
419
- result += `6. **Status fields**: Use PostgreSQL ENUMs\n`;
420
- result += `\n## Next Steps\n`;
421
- result += `1. Design the schema using suggestions above\n`;
422
- result += `2. Run \`check_db_schema\` to validate\n`;
423
- result += `3. Create Prisma migration\n`;
424
- result += `4. Document with \`record_table\` after creation\n`;
425
- return result;
426
- },
427
- };
428
- return { tools, handlers };
429
352
  }