@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
package/dist/tools/database.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
}
|