@crowley/rag-mcp 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotations.d.ts +16 -0
- package/dist/annotations.js +158 -0
- package/dist/context-enrichment.d.ts +2 -2
- package/dist/context-enrichment.js +37 -14
- package/dist/formatters.d.ts +2 -0
- package/dist/formatters.js +12 -0
- package/dist/index.js +64 -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 -661
- 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 +104 -100
- 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/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 +444 -255
- package/dist/types.d.ts +19 -2
- package/package.json +4 -2
package/dist/tools/memory.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* batch_remember, validate_memory, review_memories,
|
|
6
6
|
* promote_memory, run_quality_gates, memory_maintenance
|
|
7
7
|
*/
|
|
8
|
-
import { formatMemoryResults, truncate, PREVIEW } from "../formatters.js";
|
|
8
|
+
import { formatMemoryResults, truncate, paginationFooter, PREVIEW } from "../formatters.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { TOOL_ANNOTATIONS } from "../annotations.js";
|
|
9
11
|
const typeEmojis = {
|
|
10
12
|
decision: "\u{1F3AF}",
|
|
11
13
|
insight: "\u{1F4A1}",
|
|
@@ -21,545 +23,357 @@ const statusEmojis = {
|
|
|
21
23
|
cancelled: "\u274C",
|
|
22
24
|
};
|
|
23
25
|
export function createMemoryTools(projectName) {
|
|
24
|
-
|
|
26
|
+
return [
|
|
25
27
|
{
|
|
26
28
|
name: "remember",
|
|
27
29
|
description: "Store important information in agent memory. Use this to save decisions, insights, context, todos, or important conversations for future reference.",
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
relatedTo:
|
|
54
|
-
|
|
55
|
-
description: "Related feature or topic",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
required: ["content"],
|
|
30
|
+
schema: z.object({
|
|
31
|
+
content: z.string().describe("Information to remember"),
|
|
32
|
+
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Type of memory (default: note)"),
|
|
33
|
+
tags: z.array(z.string()).optional().describe("Tags for categorization (e.g., ['feature-x', 'important'])"),
|
|
34
|
+
relatedTo: z.string().optional().describe("Related feature or topic"),
|
|
35
|
+
}),
|
|
36
|
+
annotations: TOOL_ANNOTATIONS["remember"],
|
|
37
|
+
handler: async (args, ctx) => {
|
|
38
|
+
const content = args.content;
|
|
39
|
+
const type = args.type || "note";
|
|
40
|
+
const tags = args.tags || [];
|
|
41
|
+
const relatedTo = args.relatedTo;
|
|
42
|
+
const response = await ctx.api.post("/api/memory", {
|
|
43
|
+
projectName: ctx.projectName,
|
|
44
|
+
content,
|
|
45
|
+
type,
|
|
46
|
+
tags,
|
|
47
|
+
relatedTo,
|
|
48
|
+
});
|
|
49
|
+
const memory = response.data.memory;
|
|
50
|
+
return (`\u2705 **Memory stored**\n\n` +
|
|
51
|
+
`- **ID:** ${memory.id}\n` +
|
|
52
|
+
`- **Type:** ${memory.type}\n` +
|
|
53
|
+
`- **Content:** ${truncate(content, 200)}\n` +
|
|
54
|
+
(tags.length > 0 ? `- **Tags:** ${tags.join(", ")}\n` : "") +
|
|
55
|
+
(relatedTo ? `- **Related to:** ${relatedTo}\n` : "") +
|
|
56
|
+
`- **Created:** ${new Date(memory.createdAt).toLocaleString()}`);
|
|
59
57
|
},
|
|
60
58
|
},
|
|
61
59
|
{
|
|
62
60
|
name: "recall",
|
|
63
61
|
description: "Retrieve relevant memories based on context. Searches agent memory for past decisions, insights, and notes related to the query.",
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
type: "number",
|
|
87
|
-
description: "Max memories to retrieve (default: 5)",
|
|
88
|
-
default: 5,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
required: ["query"],
|
|
62
|
+
schema: z.object({
|
|
63
|
+
query: z.string().describe("What to recall (semantic search)"),
|
|
64
|
+
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note", "all"]).optional().describe("Filter by memory type (default: all)"),
|
|
65
|
+
limit: z.number().optional().describe("Max memories to retrieve (default: 5)"),
|
|
66
|
+
}),
|
|
67
|
+
annotations: TOOL_ANNOTATIONS["recall"],
|
|
68
|
+
handler: async (args, ctx) => {
|
|
69
|
+
const query = args.query;
|
|
70
|
+
const type = args.type || "all";
|
|
71
|
+
const limit = args.limit || 5;
|
|
72
|
+
const response = await ctx.api.post("/api/memory/recall", {
|
|
73
|
+
projectName: ctx.projectName,
|
|
74
|
+
query,
|
|
75
|
+
type,
|
|
76
|
+
limit,
|
|
77
|
+
});
|
|
78
|
+
const results = response.data.results || [];
|
|
79
|
+
if (results.length === 0) {
|
|
80
|
+
return `\u{1F50D} No memories found for: "${query}"`;
|
|
81
|
+
}
|
|
82
|
+
const header = `\u{1F9E0} **Recalled Memories** (${results.length} found)\n\n`;
|
|
83
|
+
return header + formatMemoryResults(results);
|
|
92
84
|
},
|
|
93
85
|
},
|
|
94
86
|
{
|
|
95
87
|
name: "list_memories",
|
|
96
88
|
description: "List recent memories or filter by type/tags. Shows what the agent has remembered.",
|
|
97
|
-
|
|
98
|
-
type: "
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
89
|
+
schema: z.object({
|
|
90
|
+
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note", "all"]).optional().describe("Filter by type"),
|
|
91
|
+
tag: z.string().optional().describe("Filter by tag"),
|
|
92
|
+
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
93
|
+
offset: z.number().optional().describe("Pagination offset (default: 0)"),
|
|
94
|
+
}),
|
|
95
|
+
annotations: TOOL_ANNOTATIONS["list_memories"],
|
|
96
|
+
handler: async (args, ctx) => {
|
|
97
|
+
const type = args.type || "all";
|
|
98
|
+
const tag = args.tag;
|
|
99
|
+
const limit = args.limit || 10;
|
|
100
|
+
const offset = args.offset || 0;
|
|
101
|
+
const params = new URLSearchParams({
|
|
102
|
+
projectName: ctx.projectName,
|
|
103
|
+
limit: limit.toString(),
|
|
104
|
+
offset: offset.toString(),
|
|
105
|
+
});
|
|
106
|
+
if (type && type !== "all")
|
|
107
|
+
params.append("type", type);
|
|
108
|
+
if (tag)
|
|
109
|
+
params.append("tag", tag);
|
|
110
|
+
const response = await ctx.api.get(`/api/memory/list?${params}`);
|
|
111
|
+
const memories = response.data.memories || [];
|
|
112
|
+
if (memories.length === 0) {
|
|
113
|
+
return `\u{1F4ED} No memories found${type !== "all" ? ` of type "${type}"` : ""}`;
|
|
114
|
+
}
|
|
115
|
+
let result = `\u{1F4DA} **Agent Memories** (${memories.length})\n\n`;
|
|
116
|
+
memories.forEach((m, i) => {
|
|
117
|
+
const emoji = typeEmojis[m.type] || "\u{1F4DD}";
|
|
118
|
+
const statusStr = m.status ? ` [${m.status}]` : "";
|
|
119
|
+
result += `${offset + i + 1}. ${emoji} **${m.type}**${statusStr}: ${truncate(m.content, PREVIEW.SHORT)}\n`;
|
|
120
|
+
result += ` ID: \`${m.id}\` | ${new Date(m.createdAt).toLocaleDateString()}\n\n`;
|
|
121
|
+
});
|
|
122
|
+
result += paginationFooter(memories.length, limit, offset);
|
|
123
|
+
return result;
|
|
124
124
|
},
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
name: "forget",
|
|
128
128
|
description: "Delete a specific memory by ID or clear memories by type.",
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
type: "number",
|
|
150
|
-
description: "Delete memories older than N days",
|
|
151
|
-
},
|
|
152
|
-
},
|
|
129
|
+
schema: z.object({
|
|
130
|
+
memoryId: z.string().optional().describe("Specific memory ID to delete"),
|
|
131
|
+
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Delete all memories of this type"),
|
|
132
|
+
olderThanDays: z.number().optional().describe("Delete memories older than N days"),
|
|
133
|
+
}),
|
|
134
|
+
annotations: TOOL_ANNOTATIONS["forget"],
|
|
135
|
+
handler: async (args, ctx) => {
|
|
136
|
+
const memoryId = args.memoryId;
|
|
137
|
+
const type = args.type;
|
|
138
|
+
if (memoryId) {
|
|
139
|
+
const response = await ctx.api.delete(`/api/memory/${memoryId}?projectName=${ctx.projectName}`);
|
|
140
|
+
return response.data.success
|
|
141
|
+
? `\u{1F5D1}\uFE0F Memory deleted: ${memoryId}`
|
|
142
|
+
: `\u274C Failed to delete memory: ${memoryId}`;
|
|
143
|
+
}
|
|
144
|
+
if (type) {
|
|
145
|
+
await ctx.api.delete(`/api/memory/type/${type}?projectName=${ctx.projectName}`);
|
|
146
|
+
return `\u{1F5D1}\uFE0F Deleted all memories of type: ${type}`;
|
|
147
|
+
}
|
|
148
|
+
return "Please specify memoryId or type to delete.";
|
|
153
149
|
},
|
|
154
150
|
},
|
|
155
151
|
{
|
|
156
152
|
name: "update_todo",
|
|
157
153
|
description: "Update status of a todo/task in memory.",
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
154
|
+
schema: z.object({
|
|
155
|
+
todoId: z.string().describe("Todo memory ID"),
|
|
156
|
+
status: z.enum(["pending", "in_progress", "done", "cancelled"]).describe("New status"),
|
|
157
|
+
note: z.string().optional().describe("Optional note about the update"),
|
|
158
|
+
}),
|
|
159
|
+
annotations: TOOL_ANNOTATIONS["update_todo"],
|
|
160
|
+
handler: async (args, ctx) => {
|
|
161
|
+
const todoId = args.todoId;
|
|
162
|
+
const status = args.status;
|
|
163
|
+
const note = args.note;
|
|
164
|
+
const response = await ctx.api.patch(`/api/memory/todo/${todoId}`, {
|
|
165
|
+
projectName: ctx.projectName,
|
|
166
|
+
status,
|
|
167
|
+
note,
|
|
168
|
+
});
|
|
169
|
+
if (!response.data.memory) {
|
|
170
|
+
return `\u274C Todo not found: ${todoId}`;
|
|
171
|
+
}
|
|
172
|
+
return (`${statusEmojis[status] || "\u{1F4CB}"} **Todo updated**\n\n` +
|
|
173
|
+
`- **ID:** ${todoId}\n` +
|
|
174
|
+
`- **Status:** ${status}\n` +
|
|
175
|
+
(note ? `- **Note:** ${note}\n` : "") +
|
|
176
|
+
`- **Content:** ${response.data.memory.content}`);
|
|
176
177
|
},
|
|
177
178
|
},
|
|
178
179
|
{
|
|
179
180
|
name: "batch_remember",
|
|
180
181
|
description: `Efficiently store multiple memories at once in ${projectName}. Faster than individual remember calls.`,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
description: "Related feature or topic",
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
required: ["content"],
|
|
216
|
-
},
|
|
217
|
-
description: "Array of memories to store",
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
required: ["items"],
|
|
182
|
+
schema: z.object({
|
|
183
|
+
items: z.array(z.object({
|
|
184
|
+
content: z.string().describe("Content to remember"),
|
|
185
|
+
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Memory type (default: note)"),
|
|
186
|
+
tags: z.array(z.string()).optional().describe("Tags for categorization"),
|
|
187
|
+
relatedTo: z.string().optional().describe("Related feature or topic"),
|
|
188
|
+
})).describe("Array of memories to store"),
|
|
189
|
+
}),
|
|
190
|
+
annotations: TOOL_ANNOTATIONS["batch_remember"],
|
|
191
|
+
handler: async (args, ctx) => {
|
|
192
|
+
const items = args.items;
|
|
193
|
+
const response = await ctx.api.post("/api/memory/batch", {
|
|
194
|
+
items,
|
|
195
|
+
});
|
|
196
|
+
const { savedCount, errors, memories } = response.data;
|
|
197
|
+
let result = `# \u{1F4E6} Batch Memory Result\n\n`;
|
|
198
|
+
result += `**Saved**: ${savedCount} memories\n\n`;
|
|
199
|
+
if (memories && memories.length > 0) {
|
|
200
|
+
result += `## Stored Memories\n`;
|
|
201
|
+
memories.forEach((m) => {
|
|
202
|
+
result += `- [${m.type}] ${truncate(m.content, 80)}\n`;
|
|
203
|
+
result += ` ID: \`${m.id}\`\n`;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (errors && errors.length > 0) {
|
|
207
|
+
result += `\n## \u26A0\uFE0F Errors\n`;
|
|
208
|
+
errors.forEach((e) => {
|
|
209
|
+
result += `- ${e}\n`;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
221
213
|
},
|
|
222
214
|
},
|
|
223
215
|
{
|
|
224
216
|
name: "validate_memory",
|
|
225
217
|
description: `Validate or reject an auto-extracted memory in ${projectName}. Helps improve future extraction accuracy.`,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
218
|
+
schema: z.object({
|
|
219
|
+
memoryId: z.string().describe("ID of the memory to validate"),
|
|
220
|
+
validated: z.boolean().describe("true to confirm the memory is valuable, false to reject it"),
|
|
221
|
+
}),
|
|
222
|
+
annotations: TOOL_ANNOTATIONS["validate_memory"],
|
|
223
|
+
handler: async (args, ctx) => {
|
|
224
|
+
const memoryId = args.memoryId;
|
|
225
|
+
const validated = args.validated;
|
|
226
|
+
const response = await ctx.api.patch(`/api/memory/${memoryId}/validate`, {
|
|
227
|
+
validated,
|
|
228
|
+
});
|
|
229
|
+
const { memory } = response.data;
|
|
230
|
+
return (`\u2705 Memory ${validated ? "validated" : "rejected"}\n\n` +
|
|
231
|
+
`- **ID**: ${memory.id}\n` +
|
|
232
|
+
`- **Type**: ${memory.type}\n` +
|
|
233
|
+
`- **Content**: ${truncate(memory.content, PREVIEW.SHORT)}\n` +
|
|
234
|
+
`- **Validated**: ${memory.validated}`);
|
|
239
235
|
},
|
|
240
236
|
},
|
|
241
237
|
{
|
|
242
238
|
name: "review_memories",
|
|
243
239
|
description: `Get auto-extracted memories pending review in ${projectName}. Shows unvalidated learnings that need human confirmation.`,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
240
|
+
schema: z.object({
|
|
241
|
+
limit: z.number().optional().describe("Max memories to return (default: 20)"),
|
|
242
|
+
offset: z.number().optional().describe("Pagination offset (default: 0)"),
|
|
243
|
+
}),
|
|
244
|
+
annotations: TOOL_ANNOTATIONS["review_memories"],
|
|
245
|
+
handler: async (args, ctx) => {
|
|
246
|
+
const limit = args.limit || 20;
|
|
247
|
+
const offset = args.offset || 0;
|
|
248
|
+
const response = await ctx.api.get(`/api/memory/quarantine?limit=${limit}&offset=${offset}`);
|
|
249
|
+
const { memories, count } = response.data;
|
|
250
|
+
if (count === 0) {
|
|
251
|
+
return "No unvalidated memories to review. All auto-extracted learnings have been reviewed.";
|
|
252
|
+
}
|
|
253
|
+
let result = `# \u{1F4CB} Memories Pending Review (${count} total)\n\n`;
|
|
254
|
+
result += `These are auto-extracted learnings that need validation.\n\n`;
|
|
255
|
+
memories.forEach((m, i) => {
|
|
256
|
+
result += `## ${offset + i + 1}. ${m.type.toUpperCase()}\n`;
|
|
257
|
+
result += `**ID**: \`${m.id}\`\n`;
|
|
258
|
+
result += `**Confidence**: ${((m.confidence || 0) * 100).toFixed(0)}%\n`;
|
|
259
|
+
result += `**Source**: ${m.source || "unknown"}\n`;
|
|
260
|
+
result += `**Content**: ${m.content}\n`;
|
|
261
|
+
if (m.tags && m.tags.length > 0) {
|
|
262
|
+
result += `**Tags**: ${m.tags.join(", ")}\n`;
|
|
263
|
+
}
|
|
264
|
+
result += `\nTo validate: \`validate_memory(memoryId="${m.id}", validated=true)\`\n`;
|
|
265
|
+
result += `To reject: \`validate_memory(memoryId="${m.id}", validated=false)\`\n\n`;
|
|
266
|
+
});
|
|
267
|
+
result += paginationFooter(memories.length, limit, offset);
|
|
268
|
+
return result;
|
|
253
269
|
},
|
|
254
270
|
},
|
|
255
271
|
{
|
|
256
272
|
name: "promote_memory",
|
|
257
273
|
description: `Promote a quarantine memory to durable storage in ${projectName}. Requires a reason for promotion. Optionally runs quality gates before promotion.`,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
274
|
+
schema: z.object({
|
|
275
|
+
memoryId: z.string().describe("ID of the memory to promote"),
|
|
276
|
+
reason: z.enum(["human_validated", "pr_merged", "tests_passed"]).describe("Reason for promotion"),
|
|
277
|
+
evidence: z.string().optional().describe("Optional evidence supporting the promotion"),
|
|
278
|
+
runGates: z.boolean().optional().describe("Run quality gates before promotion (default: false)"),
|
|
279
|
+
affectedFiles: z.array(z.string()).optional().describe("Files affected by this memory (for quality gate checking)"),
|
|
280
|
+
}),
|
|
281
|
+
annotations: TOOL_ANNOTATIONS["promote_memory"],
|
|
282
|
+
handler: async (args, ctx) => {
|
|
283
|
+
const memoryId = args.memoryId;
|
|
284
|
+
const reason = args.reason;
|
|
285
|
+
const evidence = args.evidence;
|
|
286
|
+
const runGates = args.runGates;
|
|
287
|
+
const affectedFiles = args.affectedFiles;
|
|
288
|
+
const response = await ctx.api.post("/api/memory/promote", {
|
|
289
|
+
projectName: ctx.projectName,
|
|
290
|
+
memoryId,
|
|
291
|
+
reason,
|
|
292
|
+
evidence,
|
|
293
|
+
runGates: runGates || false,
|
|
294
|
+
projectPath: runGates ? ctx.projectPath : undefined,
|
|
295
|
+
affectedFiles: runGates ? affectedFiles : undefined,
|
|
296
|
+
});
|
|
297
|
+
const { memory } = response.data;
|
|
298
|
+
return (`\u2705 **Memory promoted to durable storage**\n\n` +
|
|
299
|
+
`- **ID:** ${memory.id}\n` +
|
|
300
|
+
`- **Type:** ${memory.type}\n` +
|
|
301
|
+
`- **Reason:** ${reason}\n` +
|
|
302
|
+
(evidence ? `- **Evidence:** ${evidence}\n` : "") +
|
|
303
|
+
(runGates ? `- **Quality Gates:** passed\n` : "") +
|
|
304
|
+
`- **Content:** ${truncate(memory.content, 200)}`);
|
|
285
305
|
},
|
|
286
306
|
},
|
|
287
307
|
{
|
|
288
308
|
name: "run_quality_gates",
|
|
289
309
|
description: `Run quality gates (typecheck, tests, blast radius) for ${projectName}.`,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
310
|
+
schema: z.object({
|
|
311
|
+
affectedFiles: z.array(z.string()).optional().describe("Files to check (for related tests and blast radius)"),
|
|
312
|
+
skipGates: z.array(z.string()).optional().describe("Gates to skip (typecheck, test, blast_radius)"),
|
|
313
|
+
}),
|
|
314
|
+
annotations: TOOL_ANNOTATIONS["run_quality_gates"],
|
|
315
|
+
handler: async (args, ctx) => {
|
|
316
|
+
const affectedFiles = args.affectedFiles;
|
|
317
|
+
const skipGates = args.skipGates;
|
|
318
|
+
const response = await ctx.api.post("/api/quality/run", {
|
|
319
|
+
projectName: ctx.projectName,
|
|
320
|
+
projectPath: ctx.projectPath,
|
|
321
|
+
affectedFiles,
|
|
322
|
+
skipGates,
|
|
323
|
+
});
|
|
324
|
+
const report = response.data;
|
|
325
|
+
let result = `**Quality Report**: ${report.passed ? "\u2705 All gates passed" : "\u274C Some gates failed"}\n\n`;
|
|
326
|
+
for (const gate of report.gates) {
|
|
327
|
+
const icon = gate.passed ? "\u2705" : "\u274C";
|
|
328
|
+
result += `${icon} **${gate.gate}** (${(gate.duration / 1000).toFixed(1)}s)\n`;
|
|
329
|
+
result += ` ${gate.details.slice(0, 500)}\n\n`;
|
|
330
|
+
}
|
|
331
|
+
if (report.blastRadius) {
|
|
332
|
+
result += `\n**Blast Radius**: ${report.blastRadius.affectedFiles.length} files, depth ${report.blastRadius.depth}\n`;
|
|
333
|
+
if (report.blastRadius.affectedFiles.length > 0) {
|
|
334
|
+
result += report.blastRadius.affectedFiles
|
|
335
|
+
.slice(0, 10)
|
|
336
|
+
.map((f) => ` - ${f}`)
|
|
337
|
+
.join("\n");
|
|
338
|
+
if (report.blastRadius.affectedFiles.length > 10) {
|
|
339
|
+
result += `\n ... and ${report.blastRadius.affectedFiles.length - 10} more`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return result;
|
|
304
344
|
},
|
|
305
345
|
},
|
|
306
346
|
{
|
|
307
347
|
name: "memory_maintenance",
|
|
308
348
|
description: `Run feedback-driven memory maintenance for ${projectName}: auto-promote memories with 3+ positive feedback, auto-prune memories with 2+ incorrect feedback.`,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
];
|
|
315
|
-
const handlers = {
|
|
316
|
-
// ----- remember -----
|
|
317
|
-
async remember(args, ctx) {
|
|
318
|
-
const content = args.content;
|
|
319
|
-
const type = args.type || "note";
|
|
320
|
-
const tags = args.tags || [];
|
|
321
|
-
const relatedTo = args.relatedTo;
|
|
322
|
-
const response = await ctx.api.post("/api/memory", {
|
|
323
|
-
projectName: ctx.projectName,
|
|
324
|
-
content,
|
|
325
|
-
type,
|
|
326
|
-
tags,
|
|
327
|
-
relatedTo,
|
|
328
|
-
});
|
|
329
|
-
const memory = response.data.memory;
|
|
330
|
-
return (`\u2705 **Memory stored**\n\n` +
|
|
331
|
-
`- **ID:** ${memory.id}\n` +
|
|
332
|
-
`- **Type:** ${memory.type}\n` +
|
|
333
|
-
`- **Content:** ${truncate(content, 200)}\n` +
|
|
334
|
-
(tags.length > 0 ? `- **Tags:** ${tags.join(", ")}\n` : "") +
|
|
335
|
-
(relatedTo ? `- **Related to:** ${relatedTo}\n` : "") +
|
|
336
|
-
`- **Created:** ${new Date(memory.createdAt).toLocaleString()}`);
|
|
337
|
-
},
|
|
338
|
-
// ----- recall -----
|
|
339
|
-
async recall(args, ctx) {
|
|
340
|
-
const query = args.query;
|
|
341
|
-
const type = args.type || "all";
|
|
342
|
-
const limit = args.limit || 5;
|
|
343
|
-
const response = await ctx.api.post("/api/memory/recall", {
|
|
344
|
-
projectName: ctx.projectName,
|
|
345
|
-
query,
|
|
346
|
-
type,
|
|
347
|
-
limit,
|
|
348
|
-
});
|
|
349
|
-
const results = response.data.results || [];
|
|
350
|
-
if (results.length === 0) {
|
|
351
|
-
return `\u{1F50D} No memories found for: "${query}"`;
|
|
352
|
-
}
|
|
353
|
-
const header = `\u{1F9E0} **Recalled Memories** (${results.length} found)\n\n`;
|
|
354
|
-
return header + formatMemoryResults(results);
|
|
355
|
-
},
|
|
356
|
-
// ----- list_memories -----
|
|
357
|
-
async list_memories(args, ctx) {
|
|
358
|
-
const type = args.type || "all";
|
|
359
|
-
const tag = args.tag;
|
|
360
|
-
const limit = args.limit || 10;
|
|
361
|
-
const params = new URLSearchParams({
|
|
362
|
-
projectName: ctx.projectName,
|
|
363
|
-
limit: limit.toString(),
|
|
364
|
-
});
|
|
365
|
-
if (type && type !== "all")
|
|
366
|
-
params.append("type", type);
|
|
367
|
-
if (tag)
|
|
368
|
-
params.append("tag", tag);
|
|
369
|
-
const response = await ctx.api.get(`/api/memory/list?${params}`);
|
|
370
|
-
const memories = response.data.memories || [];
|
|
371
|
-
if (memories.length === 0) {
|
|
372
|
-
return `\u{1F4ED} No memories found${type !== "all" ? ` of type "${type}"` : ""}`;
|
|
373
|
-
}
|
|
374
|
-
let result = `\u{1F4DA} **Agent Memories** (${memories.length})\n\n`;
|
|
375
|
-
memories.forEach((m, i) => {
|
|
376
|
-
const emoji = typeEmojis[m.type] || "\u{1F4DD}";
|
|
377
|
-
const statusStr = m.status ? ` [${m.status}]` : "";
|
|
378
|
-
result += `${i + 1}. ${emoji} **${m.type}**${statusStr}: ${truncate(m.content, PREVIEW.SHORT)}\n`;
|
|
379
|
-
result += ` ID: \`${m.id}\` | ${new Date(m.createdAt).toLocaleDateString()}\n\n`;
|
|
380
|
-
});
|
|
381
|
-
return result;
|
|
382
|
-
},
|
|
383
|
-
// ----- forget -----
|
|
384
|
-
async forget(args, ctx) {
|
|
385
|
-
const memoryId = args.memoryId;
|
|
386
|
-
const type = args.type;
|
|
387
|
-
if (memoryId) {
|
|
388
|
-
const response = await ctx.api.delete(`/api/memory/${memoryId}?projectName=${ctx.projectName}`);
|
|
389
|
-
return response.data.success
|
|
390
|
-
? `\u{1F5D1}\uFE0F Memory deleted: ${memoryId}`
|
|
391
|
-
: `\u274C Failed to delete memory: ${memoryId}`;
|
|
392
|
-
}
|
|
393
|
-
if (type) {
|
|
394
|
-
await ctx.api.delete(`/api/memory/type/${type}?projectName=${ctx.projectName}`);
|
|
395
|
-
return `\u{1F5D1}\uFE0F Deleted all memories of type: ${type}`;
|
|
396
|
-
}
|
|
397
|
-
return "Please specify memoryId or type to delete.";
|
|
398
|
-
},
|
|
399
|
-
// ----- update_todo -----
|
|
400
|
-
async update_todo(args, ctx) {
|
|
401
|
-
const todoId = args.todoId;
|
|
402
|
-
const status = args.status;
|
|
403
|
-
const note = args.note;
|
|
404
|
-
const response = await ctx.api.patch(`/api/memory/todo/${todoId}`, {
|
|
405
|
-
projectName: ctx.projectName,
|
|
406
|
-
status,
|
|
407
|
-
note,
|
|
408
|
-
});
|
|
409
|
-
if (!response.data.memory) {
|
|
410
|
-
return `\u274C Todo not found: ${todoId}`;
|
|
411
|
-
}
|
|
412
|
-
return (`${statusEmojis[status] || "\u{1F4CB}"} **Todo updated**\n\n` +
|
|
413
|
-
`- **ID:** ${todoId}\n` +
|
|
414
|
-
`- **Status:** ${status}\n` +
|
|
415
|
-
(note ? `- **Note:** ${note}\n` : "") +
|
|
416
|
-
`- **Content:** ${response.data.memory.content}`);
|
|
417
|
-
},
|
|
418
|
-
// ----- batch_remember -----
|
|
419
|
-
async batch_remember(args, ctx) {
|
|
420
|
-
const items = args.items;
|
|
421
|
-
const response = await ctx.api.post("/api/memory/batch", {
|
|
422
|
-
items,
|
|
423
|
-
});
|
|
424
|
-
const { savedCount, errors, memories } = response.data;
|
|
425
|
-
let result = `# \u{1F4E6} Batch Memory Result\n\n`;
|
|
426
|
-
result += `**Saved**: ${savedCount} memories\n\n`;
|
|
427
|
-
if (memories && memories.length > 0) {
|
|
428
|
-
result += `## Stored Memories\n`;
|
|
429
|
-
memories.forEach((m) => {
|
|
430
|
-
result += `- [${m.type}] ${truncate(m.content, 80)}\n`;
|
|
431
|
-
result += ` ID: \`${m.id}\`\n`;
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
if (errors && errors.length > 0) {
|
|
435
|
-
result += `\n## \u26A0\uFE0F Errors\n`;
|
|
436
|
-
errors.forEach((e) => {
|
|
437
|
-
result += `- ${e}\n`;
|
|
349
|
+
schema: z.object({}),
|
|
350
|
+
annotations: TOOL_ANNOTATIONS["memory_maintenance"],
|
|
351
|
+
handler: async (_args, ctx) => {
|
|
352
|
+
const response = await ctx.api.post("/api/memory/maintenance", {
|
|
353
|
+
projectName: ctx.projectName,
|
|
438
354
|
});
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const validated = args.validated;
|
|
446
|
-
const response = await ctx.api.patch(`/api/memory/${memoryId}/validate`, {
|
|
447
|
-
validated,
|
|
448
|
-
});
|
|
449
|
-
const { memory } = response.data;
|
|
450
|
-
return (`\u2705 Memory ${validated ? "validated" : "rejected"}\n\n` +
|
|
451
|
-
`- **ID**: ${memory.id}\n` +
|
|
452
|
-
`- **Type**: ${memory.type}\n` +
|
|
453
|
-
`- **Content**: ${truncate(memory.content, PREVIEW.SHORT)}\n` +
|
|
454
|
-
`- **Validated**: ${memory.validated}`);
|
|
455
|
-
},
|
|
456
|
-
// ----- promote_memory -----
|
|
457
|
-
async promote_memory(args, ctx) {
|
|
458
|
-
const memoryId = args.memoryId;
|
|
459
|
-
const reason = args.reason;
|
|
460
|
-
const evidence = args.evidence;
|
|
461
|
-
const runGates = args.runGates;
|
|
462
|
-
const affectedFiles = args.affectedFiles;
|
|
463
|
-
const response = await ctx.api.post("/api/memory/promote", {
|
|
464
|
-
projectName: ctx.projectName,
|
|
465
|
-
memoryId,
|
|
466
|
-
reason,
|
|
467
|
-
evidence,
|
|
468
|
-
runGates: runGates || false,
|
|
469
|
-
projectPath: runGates ? ctx.projectPath : undefined,
|
|
470
|
-
affectedFiles: runGates ? affectedFiles : undefined,
|
|
471
|
-
});
|
|
472
|
-
const { memory } = response.data;
|
|
473
|
-
return (`\u2705 **Memory promoted to durable storage**\n\n` +
|
|
474
|
-
`- **ID:** ${memory.id}\n` +
|
|
475
|
-
`- **Type:** ${memory.type}\n` +
|
|
476
|
-
`- **Reason:** ${reason}\n` +
|
|
477
|
-
(evidence ? `- **Evidence:** ${evidence}\n` : "") +
|
|
478
|
-
(runGates ? `- **Quality Gates:** passed\n` : "") +
|
|
479
|
-
`- **Content:** ${truncate(memory.content, 200)}`);
|
|
480
|
-
},
|
|
481
|
-
// ----- run_quality_gates -----
|
|
482
|
-
async run_quality_gates(args, ctx) {
|
|
483
|
-
const affectedFiles = args.affectedFiles;
|
|
484
|
-
const skipGates = args.skipGates;
|
|
485
|
-
const response = await ctx.api.post("/api/quality/run", {
|
|
486
|
-
projectName: ctx.projectName,
|
|
487
|
-
projectPath: ctx.projectPath,
|
|
488
|
-
affectedFiles,
|
|
489
|
-
skipGates,
|
|
490
|
-
});
|
|
491
|
-
const report = response.data;
|
|
492
|
-
let result = `**Quality Report**: ${report.passed ? "\u2705 All gates passed" : "\u274C Some gates failed"}\n\n`;
|
|
493
|
-
for (const gate of report.gates) {
|
|
494
|
-
const icon = gate.passed ? "\u2705" : "\u274C";
|
|
495
|
-
result += `${icon} **${gate.gate}** (${(gate.duration / 1000).toFixed(1)}s)\n`;
|
|
496
|
-
result += ` ${gate.details.slice(0, 500)}\n\n`;
|
|
497
|
-
}
|
|
498
|
-
if (report.blastRadius) {
|
|
499
|
-
result += `\n**Blast Radius**: ${report.blastRadius.affectedFiles.length} files, depth ${report.blastRadius.depth}\n`;
|
|
500
|
-
if (report.blastRadius.affectedFiles.length > 0) {
|
|
501
|
-
result += report.blastRadius.affectedFiles
|
|
502
|
-
.slice(0, 10)
|
|
503
|
-
.map((f) => ` - ${f}`)
|
|
504
|
-
.join("\n");
|
|
505
|
-
if (report.blastRadius.affectedFiles.length > 10) {
|
|
506
|
-
result += `\n ... and ${report.blastRadius.affectedFiles.length - 10} more`;
|
|
507
|
-
}
|
|
355
|
+
const { promoted, pruned, errors } = response.data;
|
|
356
|
+
let result = `# \u{1F9F9} Memory Maintenance Results\n\n`;
|
|
357
|
+
if (promoted.length > 0) {
|
|
358
|
+
result += `**Promoted** (${promoted.length}): memories with 3+ positive feedback moved to durable\n`;
|
|
359
|
+
promoted.forEach((id) => { result += ` \u2705 ${id}\n`; });
|
|
360
|
+
result += `\n`;
|
|
508
361
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
promoted.forEach((id) => { result += ` \u2705 ${id}\n`; });
|
|
522
|
-
result += `\n`;
|
|
523
|
-
}
|
|
524
|
-
if (pruned.length > 0) {
|
|
525
|
-
result += `**Pruned** (${pruned.length}): memories with 2+ incorrect feedback removed\n`;
|
|
526
|
-
pruned.forEach((id) => { result += ` \u{1F5D1}\u{FE0F} ${id}\n`; });
|
|
527
|
-
result += `\n`;
|
|
528
|
-
}
|
|
529
|
-
if (errors.length > 0) {
|
|
530
|
-
result += `**Errors** (${errors.length}):\n`;
|
|
531
|
-
errors.forEach((e) => { result += ` \u26A0\u{FE0F} ${e}\n`; });
|
|
532
|
-
result += `\n`;
|
|
533
|
-
}
|
|
534
|
-
if (promoted.length === 0 && pruned.length === 0) {
|
|
535
|
-
result += `No memories needed maintenance. All feedback thresholds are below auto-action levels.\n`;
|
|
536
|
-
}
|
|
537
|
-
return result;
|
|
538
|
-
},
|
|
539
|
-
// ----- review_memories -----
|
|
540
|
-
async review_memories(args, ctx) {
|
|
541
|
-
const limit = args.limit || 20;
|
|
542
|
-
const response = await ctx.api.get(`/api/memory/quarantine?limit=${limit}`);
|
|
543
|
-
const { memories, count } = response.data;
|
|
544
|
-
if (count === 0) {
|
|
545
|
-
return "No unvalidated memories to review. All auto-extracted learnings have been reviewed.";
|
|
546
|
-
}
|
|
547
|
-
let result = `# \u{1F4CB} Memories Pending Review (${count})\n\n`;
|
|
548
|
-
result += `These are auto-extracted learnings that need validation.\n\n`;
|
|
549
|
-
memories.forEach((m, i) => {
|
|
550
|
-
result += `## ${i + 1}. ${m.type.toUpperCase()}\n`;
|
|
551
|
-
result += `**ID**: \`${m.id}\`\n`;
|
|
552
|
-
result += `**Confidence**: ${((m.confidence || 0) * 100).toFixed(0)}%\n`;
|
|
553
|
-
result += `**Source**: ${m.source || "unknown"}\n`;
|
|
554
|
-
result += `**Content**: ${m.content}\n`;
|
|
555
|
-
if (m.tags && m.tags.length > 0) {
|
|
556
|
-
result += `**Tags**: ${m.tags.join(", ")}\n`;
|
|
362
|
+
if (pruned.length > 0) {
|
|
363
|
+
result += `**Pruned** (${pruned.length}): memories with 2+ incorrect feedback removed\n`;
|
|
364
|
+
pruned.forEach((id) => { result += ` \u{1F5D1}\u{FE0F} ${id}\n`; });
|
|
365
|
+
result += `\n`;
|
|
366
|
+
}
|
|
367
|
+
if (errors.length > 0) {
|
|
368
|
+
result += `**Errors** (${errors.length}):\n`;
|
|
369
|
+
errors.forEach((e) => { result += ` \u26A0\u{FE0F} ${e}\n`; });
|
|
370
|
+
result += `\n`;
|
|
371
|
+
}
|
|
372
|
+
if (promoted.length === 0 && pruned.length === 0) {
|
|
373
|
+
result += `No memories needed maintenance. All feedback thresholds are below auto-action levels.\n`;
|
|
557
374
|
}
|
|
558
|
-
result
|
|
559
|
-
|
|
560
|
-
});
|
|
561
|
-
return result;
|
|
375
|
+
return result;
|
|
376
|
+
},
|
|
562
377
|
},
|
|
563
|
-
|
|
564
|
-
return { tools, handlers };
|
|
378
|
+
];
|
|
565
379
|
}
|