@grec0/memory-bank-mcp 0.1.0 → 0.1.2

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.
@@ -0,0 +1,355 @@
1
+ /**
2
+ * @fileoverview Track Progress tool for Memory Bank
3
+ * Updates the progress tracking document with tasks, milestones, and blockers
4
+ */
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ /**
8
+ * Parses existing progress data from the markdown content
9
+ */
10
+ function parseExistingProgress(content) {
11
+ const result = {
12
+ completed: [],
13
+ inProgress: [],
14
+ blocked: [],
15
+ upcoming: [],
16
+ milestones: [],
17
+ blockers: [],
18
+ phase: "Development",
19
+ phaseStatus: "In Progress",
20
+ };
21
+ // Parse completed tasks
22
+ const completedMatch = content.match(/## Completed\n([\s\S]*?)(?=\n##|$)/);
23
+ if (completedMatch) {
24
+ const items = completedMatch[1].match(/- \[x\] .+/g) || [];
25
+ result.completed = items.map(i => i.replace(/- \[x\] /, ""));
26
+ }
27
+ // Parse in progress tasks
28
+ const inProgressMatch = content.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
29
+ if (inProgressMatch) {
30
+ const items = inProgressMatch[1].match(/- \[ \] .+/g) || [];
31
+ result.inProgress = items.map(i => i.replace(/- \[ \] /, ""));
32
+ }
33
+ // Parse upcoming tasks
34
+ const upcomingMatch = content.match(/## Upcoming\n([\s\S]*?)(?=\n##|$)/);
35
+ if (upcomingMatch) {
36
+ const items = upcomingMatch[1].match(/- \[ \] .+/g) || [];
37
+ result.upcoming = items.map(i => i.replace(/- \[ \] /, ""));
38
+ }
39
+ // Parse phase
40
+ const phaseMatch = content.match(/\*\*Phase\*\*: (.+)/);
41
+ if (phaseMatch)
42
+ result.phase = phaseMatch[1];
43
+ const statusMatch = content.match(/\*\*Status\*\*: (.+)/);
44
+ if (statusMatch)
45
+ result.phaseStatus = statusMatch[1];
46
+ // Parse milestones from table
47
+ const tableMatch = content.match(/\| Milestone \| Status \| Target Date \| Notes \|[\s\S]*?(?=\n\n|\n##|$)/);
48
+ if (tableMatch) {
49
+ const lines = tableMatch[0].split("\n").slice(2);
50
+ for (const line of lines) {
51
+ const cells = line.split("|").map(c => c.trim()).filter(c => c);
52
+ if (cells.length >= 4) {
53
+ result.milestones.push({
54
+ name: cells[0],
55
+ status: cells[1],
56
+ targetDate: cells[2],
57
+ notes: cells[3],
58
+ });
59
+ }
60
+ }
61
+ }
62
+ // Parse blockers
63
+ const blockersMatch = content.match(/## Blockers\n([\s\S]*?)(?=\n##|$)/);
64
+ if (blockersMatch && !blockersMatch[1].includes("No blockers")) {
65
+ const items = blockersMatch[1].match(/- .+/g) || [];
66
+ result.blockers = items.map(i => i.replace(/- /, ""));
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Generates the updated progress.md content
72
+ */
73
+ function generateProgressContent(projectId, completed, inProgress, blocked, upcoming, milestones, blockers, phase, phaseStatus) {
74
+ const date = new Date().toISOString().split("T")[0];
75
+ // Format task lists
76
+ const completedList = completed.length > 0
77
+ ? completed.map(t => `- [x] ${t}`).join("\n")
78
+ : "- [x] Project initialization";
79
+ const inProgressList = inProgress.length > 0
80
+ ? inProgress.map(t => `- [ ] ${t}`).join("\n")
81
+ : "_No tasks in progress_";
82
+ const blockedList = blocked.length > 0
83
+ ? blocked.map(t => `- [ ] ⚠️ ${t}`).join("\n")
84
+ : "";
85
+ const upcomingList = upcoming.length > 0
86
+ ? upcoming.map(t => `- [ ] ${t}`).join("\n")
87
+ : "_No upcoming tasks defined_";
88
+ // Format milestones table
89
+ const milestonesRows = milestones.length > 0
90
+ ? milestones.map(m => `| ${m.name} | ${m.status} | ${m.targetDate || "-"} | ${m.notes || "-"} |`).join("\n")
91
+ : "| Initial Setup | In Progress | - | Getting started |";
92
+ // Format blockers
93
+ const severityEmoji = {
94
+ low: "🟡",
95
+ medium: "🟠",
96
+ high: "🔴",
97
+ };
98
+ const blockersContent = blockers.length > 0
99
+ ? blockers.map(b => `- ${severityEmoji[b.severity] || "🟡"} **${b.severity.toUpperCase()}**: ${b.description}`).join("\n")
100
+ : "_No blockers currently_";
101
+ return `# Progress Tracking
102
+
103
+ ## Current Phase
104
+ **Phase**: ${phase}
105
+ **Status**: ${phaseStatus}
106
+ **Last Updated**: ${date}
107
+
108
+ ## Completed
109
+ ${completedList}
110
+
111
+ ## In Progress
112
+ ${inProgressList}
113
+ ${blockedList ? `\n### Blocked\n${blockedList}` : ""}
114
+
115
+ ## Upcoming
116
+ ${upcomingList}
117
+
118
+ ## Blockers
119
+ ${blockersContent}
120
+
121
+ ## Milestones
122
+ | Milestone | Status | Target Date | Notes |
123
+ |-----------|--------|-------------|-------|
124
+ ${milestonesRows}
125
+
126
+ ## Statistics
127
+ - **Completed**: ${completed.length} tasks
128
+ - **In Progress**: ${inProgress.length} tasks
129
+ - **Blocked**: ${blocked.length} tasks
130
+ - **Upcoming**: ${upcoming.length} tasks
131
+
132
+ ---
133
+ *Update with \`memorybank_track_progress\` to track tasks and milestones.*
134
+ *Last updated: ${new Date().toISOString()}*
135
+ `;
136
+ }
137
+ /**
138
+ * Merges task arrays, avoiding duplicates
139
+ */
140
+ function mergeTasks(existing, incoming) {
141
+ if (!incoming || incoming.length === 0)
142
+ return existing;
143
+ const merged = [...existing];
144
+ for (const task of incoming) {
145
+ if (!merged.some(t => t.toLowerCase() === task.toLowerCase())) {
146
+ merged.push(task);
147
+ }
148
+ }
149
+ return merged;
150
+ }
151
+ /**
152
+ * Updates the progress tracking document
153
+ */
154
+ export async function trackProgress(params, storagePath = ".memorybank") {
155
+ const { projectId, progress = {}, milestone, blockers = [], phase, phaseStatus, } = params;
156
+ console.error(`\n=== Tracking Progress ===`);
157
+ console.error(`Project ID: ${projectId}`);
158
+ const docsPath = path.join(storagePath, "projects", projectId, "docs");
159
+ const progressPath = path.join(docsPath, "progress.md");
160
+ // Check if Memory Bank exists
161
+ if (!fs.existsSync(docsPath)) {
162
+ return {
163
+ success: false,
164
+ message: `Memory Bank not initialized for project "${projectId}". Run \`memorybank_initialize\` first.`,
165
+ projectId,
166
+ updatedSections: [],
167
+ stats: { completed: 0, inProgress: 0, blocked: 0, upcoming: 0, milestones: 0, blockers: 0 },
168
+ };
169
+ }
170
+ // Parse existing progress if file exists
171
+ let existing = {
172
+ completed: [],
173
+ inProgress: [],
174
+ blocked: [],
175
+ upcoming: [],
176
+ milestones: [],
177
+ blockers: [],
178
+ phase: "Development",
179
+ phaseStatus: "In Progress",
180
+ };
181
+ if (fs.existsSync(progressPath)) {
182
+ const content = fs.readFileSync(progressPath, "utf-8");
183
+ existing = parseExistingProgress(content);
184
+ console.error(` Found existing progress data`);
185
+ }
186
+ // Track updated sections
187
+ const updatedSections = [];
188
+ // Merge tasks
189
+ const completed = mergeTasks(existing.completed, progress.completed);
190
+ if (progress.completed?.length)
191
+ updatedSections.push("Completed");
192
+ // Move completed tasks from inProgress to completed
193
+ let inProgress = existing.inProgress.filter(t => !progress.completed?.some(c => c.toLowerCase() === t.toLowerCase()));
194
+ inProgress = mergeTasks(inProgress, progress.inProgress);
195
+ if (progress.inProgress?.length)
196
+ updatedSections.push("In Progress");
197
+ const blocked = mergeTasks(existing.blocked, progress.blocked);
198
+ if (progress.blocked?.length)
199
+ updatedSections.push("Blocked");
200
+ const upcoming = mergeTasks(existing.upcoming, progress.upcoming);
201
+ if (progress.upcoming?.length)
202
+ updatedSections.push("Upcoming");
203
+ // Update or add milestone
204
+ let milestones = [...existing.milestones];
205
+ if (milestone) {
206
+ const existingIndex = milestones.findIndex(m => m.name.toLowerCase() === milestone.name.toLowerCase());
207
+ const newMilestone = {
208
+ name: milestone.name,
209
+ status: milestone.status,
210
+ targetDate: milestone.targetDate || "-",
211
+ notes: milestone.notes || "-",
212
+ };
213
+ if (existingIndex >= 0) {
214
+ milestones[existingIndex] = newMilestone;
215
+ }
216
+ else {
217
+ milestones.push(newMilestone);
218
+ }
219
+ updatedSections.push("Milestones");
220
+ }
221
+ // Update phase if provided
222
+ const finalPhase = phase || existing.phase;
223
+ const finalPhaseStatus = phaseStatus || existing.phaseStatus;
224
+ if (phase || phaseStatus)
225
+ updatedSections.push("Phase");
226
+ // Convert blockers
227
+ const finalBlockers = blockers.length > 0 ? blockers : [];
228
+ if (blockers.length > 0)
229
+ updatedSections.push("Blockers");
230
+ // Generate new content
231
+ const newContent = generateProgressContent(projectId, completed, inProgress, blocked, upcoming, milestones, finalBlockers, finalPhase, finalPhaseStatus);
232
+ // Write to file
233
+ fs.writeFileSync(progressPath, newContent, "utf-8");
234
+ const stats = {
235
+ completed: completed.length,
236
+ inProgress: inProgress.length,
237
+ blocked: blocked.length,
238
+ upcoming: upcoming.length,
239
+ milestones: milestones.length,
240
+ blockers: finalBlockers.length,
241
+ };
242
+ console.error(` Updated sections: ${updatedSections.join(", ") || "None"}`);
243
+ console.error(` Stats: ${JSON.stringify(stats)}`);
244
+ console.error(`\n=== Progress Updated ===`);
245
+ return {
246
+ success: true,
247
+ message: `Progress updated for project "${projectId}". ${updatedSections.length > 0 ? `Updated: ${updatedSections.join(", ")}` : "No changes"}. Stats: ${stats.completed} completed, ${stats.inProgress} in progress, ${stats.upcoming} upcoming.`,
248
+ projectId,
249
+ updatedSections,
250
+ stats,
251
+ };
252
+ }
253
+ /**
254
+ * Tool definition for MCP
255
+ */
256
+ export const trackProgressToolDefinition = {
257
+ name: "memorybank_track_progress",
258
+ description: `Actualiza el seguimiento de progreso del proyecto con tareas, milestones y blockers.
259
+
260
+ Permite:
261
+ - Marcar tareas como completadas, en progreso, bloqueadas o próximas
262
+ - Añadir/actualizar milestones con estado y fecha objetivo
263
+ - Registrar blockers con severidad (low/medium/high)
264
+ - Actualizar fase y estado del proyecto
265
+
266
+ Las tareas se fusionan inteligentemente evitando duplicados.
267
+ No usa IA - actualización directa del documento.`,
268
+ inputSchema: {
269
+ type: "object",
270
+ properties: {
271
+ projectId: {
272
+ type: "string",
273
+ description: "Identificador único del proyecto (OBLIGATORIO)",
274
+ },
275
+ progress: {
276
+ type: "object",
277
+ description: "Tareas a actualizar",
278
+ properties: {
279
+ completed: {
280
+ type: "array",
281
+ items: { type: "string" },
282
+ description: "Tareas completadas",
283
+ },
284
+ inProgress: {
285
+ type: "array",
286
+ items: { type: "string" },
287
+ description: "Tareas en progreso",
288
+ },
289
+ blocked: {
290
+ type: "array",
291
+ items: { type: "string" },
292
+ description: "Tareas bloqueadas",
293
+ },
294
+ upcoming: {
295
+ type: "array",
296
+ items: { type: "string" },
297
+ description: "Próximas tareas",
298
+ },
299
+ },
300
+ },
301
+ milestone: {
302
+ type: "object",
303
+ description: "Milestone a añadir o actualizar",
304
+ properties: {
305
+ name: {
306
+ type: "string",
307
+ description: "Nombre del milestone",
308
+ },
309
+ status: {
310
+ type: "string",
311
+ enum: ["pending", "in_progress", "completed"],
312
+ description: "Estado del milestone",
313
+ },
314
+ targetDate: {
315
+ type: "string",
316
+ description: "Fecha objetivo (opcional)",
317
+ },
318
+ notes: {
319
+ type: "string",
320
+ description: "Notas adicionales (opcional)",
321
+ },
322
+ },
323
+ required: ["name", "status"],
324
+ },
325
+ blockers: {
326
+ type: "array",
327
+ description: "Blockers a registrar",
328
+ items: {
329
+ type: "object",
330
+ properties: {
331
+ description: {
332
+ type: "string",
333
+ description: "Descripción del blocker",
334
+ },
335
+ severity: {
336
+ type: "string",
337
+ enum: ["low", "medium", "high"],
338
+ description: "Severidad del blocker",
339
+ },
340
+ },
341
+ required: ["description", "severity"],
342
+ },
343
+ },
344
+ phase: {
345
+ type: "string",
346
+ description: "Fase actual del proyecto (ej: Planning, Development, Testing, Deployment)",
347
+ },
348
+ phaseStatus: {
349
+ type: "string",
350
+ description: "Estado de la fase (ej: Not Started, In Progress, Completed)",
351
+ },
352
+ },
353
+ required: ["projectId"],
354
+ },
355
+ };
@@ -0,0 +1,201 @@
1
+ /**
2
+ * @fileoverview Update Context tool for Memory Bank
3
+ * Updates the active context document with current session information
4
+ */
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ /**
8
+ * Parses the existing activeContext.md to extract session history
9
+ */
10
+ function parseSessionHistory(content) {
11
+ const history = [];
12
+ // Find the Session History table
13
+ const tableMatch = content.match(/\| Date \| Mode \| Task \| Notes \|[\s\S]*?(?=\n\n|\n##|$)/);
14
+ if (!tableMatch)
15
+ return history;
16
+ const lines = tableMatch[0].split("\n").slice(2); // Skip header and separator
17
+ for (const line of lines) {
18
+ const cells = line.split("|").map(c => c.trim()).filter(c => c);
19
+ if (cells.length >= 4) {
20
+ history.push({
21
+ date: cells[0],
22
+ mode: cells[1],
23
+ task: cells[2],
24
+ notes: cells[3],
25
+ });
26
+ }
27
+ }
28
+ return history;
29
+ }
30
+ /**
31
+ * Generates the updated activeContext.md content
32
+ */
33
+ function generateActiveContextContent(projectId, currentSession, recentChanges, openQuestions, nextSteps, notes, sessionHistory) {
34
+ const date = currentSession.date || new Date().toISOString().split("T")[0];
35
+ const mode = currentSession.mode || "development";
36
+ const task = currentSession.task || "Working on project";
37
+ // Add current session to history (limit to last 10)
38
+ const newHistory = [
39
+ { date, mode, task, notes: notes || "Session updated" },
40
+ ...sessionHistory,
41
+ ].slice(0, 10);
42
+ // Build session history table
43
+ const historyRows = newHistory
44
+ .map(h => `| ${h.date} | ${h.mode} | ${h.task} | ${h.notes} |`)
45
+ .join("\n");
46
+ // Build recent changes list
47
+ const changesContent = recentChanges.length > 0
48
+ ? recentChanges.map(c => `- ${c}`).join("\n")
49
+ : "- No recent changes recorded";
50
+ // Build open questions list
51
+ const questionsContent = openQuestions.length > 0
52
+ ? openQuestions.map(q => `- ${q}`).join("\n")
53
+ : "- No open questions";
54
+ // Build next steps list
55
+ const stepsContent = nextSteps.length > 0
56
+ ? nextSteps.map(s => `- [ ] ${s}`).join("\n")
57
+ : "- [ ] Continue development";
58
+ return `# Active Context
59
+
60
+ ## Current Session
61
+ - **Date**: ${date}
62
+ - **Mode**: ${mode}
63
+ - **Current Task**: ${task}
64
+
65
+ ## Session History
66
+ | Date | Mode | Task | Notes |
67
+ |------|------|------|-------|
68
+ ${historyRows}
69
+
70
+ ## Recent Changes
71
+ ${changesContent}
72
+
73
+ ## Open Questions
74
+ ${questionsContent}
75
+
76
+ ## Next Steps
77
+ ${stepsContent}
78
+
79
+ ## Active Considerations
80
+ ${notes || "_No additional considerations_"}
81
+
82
+ ---
83
+ *Last updated: ${new Date().toISOString()}*
84
+ *Update with \`memorybank_update_context\` to track session progress.*
85
+ `;
86
+ }
87
+ /**
88
+ * Updates the active context document
89
+ */
90
+ export async function updateContext(params, storagePath = ".memorybank") {
91
+ const { projectId, currentSession = {}, recentChanges = [], openQuestions = [], nextSteps = [], notes = "", } = params;
92
+ console.error(`\n=== Updating Active Context ===`);
93
+ console.error(`Project ID: ${projectId}`);
94
+ const docsPath = path.join(storagePath, "projects", projectId, "docs");
95
+ const activeContextPath = path.join(docsPath, "activeContext.md");
96
+ // Check if Memory Bank exists
97
+ if (!fs.existsSync(docsPath)) {
98
+ return {
99
+ success: false,
100
+ message: `Memory Bank not initialized for project "${projectId}". Run \`memorybank_initialize\` first.`,
101
+ projectId,
102
+ updatedSections: [],
103
+ sessionHistory: 0,
104
+ };
105
+ }
106
+ // Parse existing session history if file exists
107
+ let sessionHistory = [];
108
+ if (fs.existsSync(activeContextPath)) {
109
+ const existingContent = fs.readFileSync(activeContextPath, "utf-8");
110
+ sessionHistory = parseSessionHistory(existingContent);
111
+ console.error(` Found ${sessionHistory.length} previous sessions`);
112
+ }
113
+ // Track which sections were updated
114
+ const updatedSections = ["Current Session"];
115
+ if (recentChanges.length > 0)
116
+ updatedSections.push("Recent Changes");
117
+ if (openQuestions.length > 0)
118
+ updatedSections.push("Open Questions");
119
+ if (nextSteps.length > 0)
120
+ updatedSections.push("Next Steps");
121
+ if (notes)
122
+ updatedSections.push("Active Considerations");
123
+ // Generate new content
124
+ const newContent = generateActiveContextContent(projectId, currentSession, recentChanges, openQuestions, nextSteps, notes, sessionHistory);
125
+ // Write to file
126
+ fs.writeFileSync(activeContextPath, newContent, "utf-8");
127
+ console.error(` Updated sections: ${updatedSections.join(", ")}`);
128
+ console.error(` Session history: ${sessionHistory.length + 1} entries`);
129
+ console.error(`\n=== Context Updated ===`);
130
+ return {
131
+ success: true,
132
+ message: `Active context updated for project "${projectId}". Updated: ${updatedSections.join(", ")}. Session history: ${sessionHistory.length + 1} entries.`,
133
+ projectId,
134
+ updatedSections,
135
+ sessionHistory: sessionHistory.length + 1,
136
+ };
137
+ }
138
+ /**
139
+ * Tool definition for MCP
140
+ */
141
+ export const updateContextToolDefinition = {
142
+ name: "memorybank_update_context",
143
+ description: `Actualiza el contexto activo del proyecto con información de la sesión actual.
144
+
145
+ Permite registrar:
146
+ - Sesión actual (fecha, modo de trabajo, tarea)
147
+ - Cambios recientes realizados
148
+ - Preguntas abiertas pendientes
149
+ - Próximos pasos planificados
150
+ - Notas y consideraciones
151
+
152
+ Mantiene un historial de las últimas 10 sesiones para tracking de progreso.
153
+ No usa IA - actualización directa del documento.`,
154
+ inputSchema: {
155
+ type: "object",
156
+ properties: {
157
+ projectId: {
158
+ type: "string",
159
+ description: "Identificador único del proyecto (OBLIGATORIO)",
160
+ },
161
+ currentSession: {
162
+ type: "object",
163
+ description: "Información de la sesión actual",
164
+ properties: {
165
+ date: {
166
+ type: "string",
167
+ description: "Fecha de la sesión (YYYY-MM-DD). Auto-genera si no se especifica",
168
+ },
169
+ mode: {
170
+ type: "string",
171
+ description: "Modo de trabajo: development, debugging, refactoring, review, planning, etc.",
172
+ },
173
+ task: {
174
+ type: "string",
175
+ description: "Descripción de la tarea actual",
176
+ },
177
+ },
178
+ },
179
+ recentChanges: {
180
+ type: "array",
181
+ items: { type: "string" },
182
+ description: "Lista de cambios recientes realizados",
183
+ },
184
+ openQuestions: {
185
+ type: "array",
186
+ items: { type: "string" },
187
+ description: "Preguntas o dudas pendientes de resolver",
188
+ },
189
+ nextSteps: {
190
+ type: "array",
191
+ items: { type: "string" },
192
+ description: "Próximos pasos planificados",
193
+ },
194
+ notes: {
195
+ type: "string",
196
+ description: "Notas adicionales o consideraciones activas",
197
+ },
198
+ },
199
+ required: ["projectId"],
200
+ },
201
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grec0/memory-bank-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server for semantic code indexing with Memory Bank - AI-powered codebase understanding",
5
5
  "license": "MIT",
6
6
  "author": "@grec0",