@grec0/memory-bank-mcp 0.1.30 → 0.1.32

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.
@@ -115,6 +115,8 @@ export class AgentBoardSqlite {
115
115
  SET status = ?, focus = ?, last_heartbeat = ?
116
116
  WHERE id = ? AND project_id = ?
117
117
  `).run(status, focus, now, resolvedId, this.projectId);
118
+ // Flush WAL so external readers can see changes
119
+ databaseManager.flushForExternalReaders();
118
120
  }
119
121
  /**
120
122
  * Update heartbeat for an agent
@@ -127,6 +129,8 @@ export class AgentBoardSqlite {
127
129
  SET last_heartbeat = ?
128
130
  WHERE id = ? AND project_id = ?
129
131
  `).run(now, agentId, this.projectId);
132
+ // Flush WAL so external readers can see changes
133
+ databaseManager.flushForExternalReaders();
130
134
  }
131
135
  /**
132
136
  * Get all agents for this project (for session history view)
@@ -283,6 +287,7 @@ export class AgentBoardSqlite {
283
287
  WHERE resource = ? AND project_id = ? AND agent_id = ?
284
288
  `).run(resource, this.projectId, agentId);
285
289
  this.logMessage(agentId, `Released lock on ${resource}`);
290
+ // Note: logMessage already flushes
286
291
  }
287
292
  /**
288
293
  * Get all locks for this project
@@ -310,6 +315,8 @@ export class AgentBoardSqlite {
310
315
  DELETE FROM locks
311
316
  WHERE agent_id = ? AND project_id = ?
312
317
  `).run(agentId, this.projectId);
318
+ // Flush WAL so external readers can see changes
319
+ databaseManager.flushForExternalReaders();
313
320
  return result.changes;
314
321
  }
315
322
  /**
@@ -323,6 +330,8 @@ export class AgentBoardSqlite {
323
330
  SELECT id FROM agents WHERE project_id = ? AND status = 'ACTIVE'
324
331
  )
325
332
  `).run(this.projectId, this.projectId);
333
+ // Flush WAL so external readers can see changes
334
+ databaseManager.flushForExternalReaders();
326
335
  return result.changes;
327
336
  }
328
337
  // ========================================================================
@@ -330,6 +339,7 @@ export class AgentBoardSqlite {
330
339
  // ========================================================================
331
340
  /**
332
341
  * Log a session event
342
+ * Auto-flushes WAL for external readers since this tracks all agent actions
333
343
  */
334
344
  logSessionEvent(sessionId, eventType, eventData, agentId) {
335
345
  const db = databaseManager.getConnection();
@@ -338,6 +348,8 @@ export class AgentBoardSqlite {
338
348
  INSERT INTO session_events (project_id, session_id, agent_id, event_type, event_data, timestamp)
339
349
  VALUES (?, ?, ?, ?, ?, ?)
340
350
  `).run(this.projectId, sessionId, agentId || null, eventType, JSON.stringify(eventData), now);
351
+ // Flush WAL so external readers (sql.js) can see changes
352
+ databaseManager.flushForExternalReaders();
341
353
  }
342
354
  /**
343
355
  * Get session history for a specific session
@@ -390,6 +402,7 @@ export class AgentBoardSqlite {
390
402
  // ========================================================================
391
403
  /**
392
404
  * Log a message
405
+ * Auto-flushes WAL for external readers since this is called at end of most operations
393
406
  */
394
407
  logMessage(agentId, message) {
395
408
  const db = databaseManager.getConnection();
@@ -398,6 +411,8 @@ export class AgentBoardSqlite {
398
411
  INSERT INTO messages (project_id, agent_id, message, timestamp)
399
412
  VALUES (?, ?, ?, ?)
400
413
  `).run(this.projectId, agentId, message, now);
414
+ // Flush WAL so external readers (sql.js) can see changes
415
+ databaseManager.flushForExternalReaders();
401
416
  }
402
417
  /**
403
418
  * Get recent messages
@@ -469,6 +484,8 @@ export function cleanupStaleAgents(staleMinutes = 30) {
469
484
  WHERE status = 'ACTIVE'
470
485
  AND datetime(last_heartbeat) < datetime('now', '-' || ? || ' minutes')
471
486
  `).run(staleMinutes);
487
+ // Flush WAL so external readers can see changes
488
+ databaseManager.flushForExternalReaders();
472
489
  return result.changes;
473
490
  }
474
491
  /**
@@ -482,5 +499,7 @@ export function cleanupAllOrphanedLocks() {
482
499
  SELECT id FROM agents WHERE status = 'ACTIVE'
483
500
  )
484
501
  `).run();
502
+ // Flush WAL so external readers can see changes
503
+ databaseManager.flushForExternalReaders();
485
504
  return result.changes;
486
505
  }
@@ -188,11 +188,69 @@ class DatabaseManager {
188
188
  return fs.existsSync(this.dbPath);
189
189
  }
190
190
  /**
191
- * Run a transaction with automatic rollback on error
191
+ * Run a transaction with automatic rollback on error.
192
+ * Automatically flushes WAL after successful transaction for external readers.
192
193
  */
193
194
  transaction(fn) {
194
195
  const db = this.getConnection();
195
- return db.transaction(fn)();
196
+ const result = db.transaction(fn)();
197
+ // Auto-flush after any transaction so sql.js can read the changes
198
+ this.flushForExternalReaders();
199
+ return result;
200
+ }
201
+ /**
202
+ * Execute a write operation (INSERT, UPDATE, DELETE) and auto-flush WAL.
203
+ * Use this for single statements that modify data.
204
+ * For multiple related writes, use transaction() instead.
205
+ *
206
+ * @param sql - SQL statement to execute
207
+ * @param params - Parameters to bind to the statement
208
+ * @returns RunResult with changes count and lastInsertRowid
209
+ */
210
+ runWrite(sql, ...params) {
211
+ const db = this.getConnection();
212
+ const result = db.prepare(sql).run(...params);
213
+ // Auto-flush so sql.js can read the changes immediately
214
+ this.flushForExternalReaders();
215
+ return result;
216
+ }
217
+ /**
218
+ * Force WAL checkpoint to flush pending writes to the main database file.
219
+ * This is necessary for external readers (like sql.js in VS Code extension)
220
+ * that cannot read WAL files directly.
221
+ *
222
+ * @param mode - Checkpoint mode:
223
+ * - 'PASSIVE': Checkpoint as much as possible without blocking (default)
224
+ * - 'FULL': Wait for all readers, then checkpoint everything
225
+ * - 'RESTART': Like FULL, but also restart the WAL file
226
+ * - 'TRUNCATE': Like RESTART, but also truncate WAL file to zero bytes
227
+ * @returns Object with busy (pages that couldn't be checkpointed) and log (total WAL pages)
228
+ */
229
+ checkpoint(mode = 'PASSIVE') {
230
+ if (!this.db) {
231
+ return { busy: 0, log: 0 };
232
+ }
233
+ try {
234
+ const result = this.db.pragma(`wal_checkpoint(${mode})`);
235
+ const checkpointResult = result[0] || { busy: 0, log: 0 };
236
+ // Only log if there were actual pages to flush (reduce noise)
237
+ if (checkpointResult.log > 0) {
238
+ console.error(`[Database] WAL checkpoint: ${checkpointResult.log - checkpointResult.busy}/${checkpointResult.log} pages flushed`);
239
+ }
240
+ return checkpointResult;
241
+ }
242
+ catch (error) {
243
+ console.error(`[Database] Checkpoint error: ${error}`);
244
+ return { busy: 0, log: 0 };
245
+ }
246
+ }
247
+ /**
248
+ * Perform a full checkpoint and ensure all data is written to main DB file.
249
+ * Called automatically after write operations.
250
+ * Can also be called manually if needed.
251
+ */
252
+ flushForExternalReaders() {
253
+ this.checkpoint('TRUNCATE');
196
254
  }
197
255
  }
198
256
  // Export singleton instance
@@ -1,2 +1,2 @@
1
1
  // Version of the MCP Kanban server
2
- export const VERSION = "0.1.29";
2
+ export const VERSION = "0.1.31";
@@ -4,7 +4,7 @@ import { sessionState } from '../common/sessionState.js';
4
4
  import { sessionLogger } from '../common/sessionLogger.js';
5
5
  import os from 'os';
6
6
  export async function manageAgentsTool(params) {
7
- const { projectId, action, agentId, sessionId, status, focus, resource, workspacePath } = params;
7
+ const { projectId, action, agentId, sessionId, status, focus, resource, taskId, workspacePath } = params;
8
8
  // For register action, workspacePath is REQUIRED to correctly register the project
9
9
  if (action === 'register' && !workspacePath) {
10
10
  throw new Error('workspacePath is REQUIRED for register action. Please provide the absolute path to the project workspace.');
@@ -72,6 +72,42 @@ export async function manageAgentsTool(params) {
72
72
  case 'get_board':
73
73
  const content = await board.getBoardContent();
74
74
  return { success: true, content };
75
+ case 'complete_task':
76
+ if (!taskId)
77
+ throw new Error('taskId is required for complete_task');
78
+ const completeAgentId = agentId
79
+ ? await board.resolveActiveAgentId(agentId)
80
+ : sessionState.getCurrentAgentId() || 'SYSTEM';
81
+ await board.completeTask(taskId, completeAgentId);
82
+ return {
83
+ success: true,
84
+ message: `Task ${taskId} marked as COMPLETED by ${completeAgentId}`,
85
+ taskId,
86
+ completedBy: completeAgentId
87
+ };
88
+ case 'claim_task':
89
+ if (!taskId)
90
+ throw new Error('taskId is required for claim_task');
91
+ const claimAgentId = agentId
92
+ ? await board.resolveActiveAgentId(agentId)
93
+ : sessionState.getCurrentAgentId();
94
+ if (!claimAgentId)
95
+ throw new Error('agentId is required for claim_task (or register first)');
96
+ const taskClaimed = board.claimTask(taskId, claimAgentId);
97
+ if (taskClaimed) {
98
+ return {
99
+ success: true,
100
+ message: `Task ${taskId} claimed by ${claimAgentId}`,
101
+ taskId,
102
+ claimedBy: claimAgentId
103
+ };
104
+ }
105
+ else {
106
+ return {
107
+ success: false,
108
+ message: `Task ${taskId} could not be claimed (already claimed or doesn't exist)`
109
+ };
110
+ }
75
111
  default:
76
112
  throw new Error(`Unknown action: ${action}`);
77
113
  }
@@ -96,7 +132,7 @@ export const manageAgentsToolDefinition = {
96
132
  action: {
97
133
  type: "string",
98
134
  description: "Acción a realizar",
99
- enum: ["register", "update_status", "claim_resource", "release_resource", "get_board"],
135
+ enum: ["register", "update_status", "claim_resource", "release_resource", "get_board", "complete_task", "claim_task"],
100
136
  },
101
137
  agentId: {
102
138
  type: "string",
@@ -118,9 +154,13 @@ export const manageAgentsToolDefinition = {
118
154
  type: "string",
119
155
  description: "Identificador del recurso a bloquear (ej: 'src/auth/').",
120
156
  },
157
+ taskId: {
158
+ type: "string",
159
+ description: "ID de la tarea para complete_task o claim_task (ej: 'EXT-123456', 'TASK-789012').",
160
+ },
121
161
  workspacePath: {
122
162
  type: "string",
123
- description: "RUTA ABSOLUTA al directorio raíz del workspace. IMPORTANTE: Debe coincidir con la ruta real del proyecto, no usar rutas relativas.",
163
+ description: "RUTA ABSOLUTA al directorio raíz del workspace. IMPORTANTE para registro correcto del proyecto.",
124
164
  },
125
165
  },
126
166
  required: ["projectId", "action"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grec0/memory-bank-mcp",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "MCP server for semantic code indexing with Memory Bank - AI-powered codebase understanding",
5
5
  "license": "MIT",
6
6
  "author": "@grec0",