@snapspecter/mcp-meta-mind
Version:
Meta Mind MCP Server - Advanced Model Context Protocol server for intelligent task management, workflow orchestration, and automatic archiving with hierarchical structures and agent specialization
458 lines • 17.7 kB
JavaScript
import { getDb } from './database.js';
export class TaskRepository {
db;
constructor() {
this.db = getDb();
}
// ==================== UTILITY METHODS ====================
/**
* Converts a database row to a Task object
*/
rowToTask(row) {
return {
id: row.taskId,
title: row.title,
description: row.description || '',
status: row.status,
priority: row.priority,
type: row.type || undefined,
dependsOn: row.dependsOn ? JSON.parse(row.dependsOn) : [],
parentId: row.parentId || undefined,
subtaskIds: row.subtaskIds ? JSON.parse(row.subtaskIds) : [],
failureReason: row.failureReason || undefined,
suggestedRetryStrategy: row.suggestedRetryStrategy || undefined,
completedDetails: row.completedDetails || undefined,
artifactsGenerated: row.artifactsGenerated ? JSON.parse(row.artifactsGenerated) : [],
environmentContext: row.environmentContext || undefined,
summaryFilePath: row.summaryFilePath || undefined,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
};
}
/**
* Converts a Task object to database row format
*/
taskToRow(task, requestId) {
return {
taskId: task.id,
requestId,
parentId: task.parentId || null,
title: task.title,
description: task.description || null,
status: task.status,
priority: task.priority,
type: task.type || null,
dependsOn: task.dependsOn && task.dependsOn.length > 0 ? JSON.stringify(task.dependsOn) : null,
subtaskIds: task.subtaskIds && task.subtaskIds.length > 0 ? JSON.stringify(task.subtaskIds) : null,
failureReason: task.failureReason || null,
suggestedRetryStrategy: task.suggestedRetryStrategy || null,
completedDetails: task.completedDetails || null,
artifactsGenerated: task.artifactsGenerated && task.artifactsGenerated.length > 0 ? JSON.stringify(task.artifactsGenerated) : null,
environmentContext: task.environmentContext || null,
summaryFilePath: task.summaryFilePath || null,
createdAt: task.createdAt,
updatedAt: task.updatedAt,
};
}
/**
* Converts a database row to a RequestEntry object
*/
rowToRequest(row, tasks) {
return {
requestId: row.requestId,
originalRequest: row.originalRequest,
splitDetails: row.splitDetails || '',
tasks,
completed: Boolean(row.completed),
createdAt: row.createdAt,
updatedAt: row.updatedAt,
};
}
// ==================== METADATA METHODS ====================
/**
* Gets metadata value by key
*/
getMetadata(key) {
const stmt = this.db.prepare('SELECT value FROM metadata WHERE key = ?');
const row = stmt.get(key);
return row ? row.value : null;
}
/**
* Sets metadata value by key
*/
setMetadata(key, value) {
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO metadata (key, value, updatedAt)
VALUES (?, ?, datetime('now'))
`);
stmt.run(key, value);
}
/**
* Gets the next request ID and increments the counter
*/
getNextRequestId() {
const currentId = parseInt(this.getMetadata('lastRequestId') || '0', 10);
const nextId = currentId + 1;
this.setMetadata('lastRequestId', nextId.toString());
return `req-${nextId}`;
}
/**
* Gets the next task ID and increments the counter
*/
getNextTaskId() {
const currentId = parseInt(this.getMetadata('lastTaskId') || '0', 10);
const nextId = currentId + 1;
this.setMetadata('lastTaskId', nextId.toString());
return `task-${nextId}`;
}
// ==================== REQUEST METHODS ====================
/**
* Creates a new request
*/
createRequest(originalRequest, splitDetails = '') {
const requestId = this.getNextRequestId();
const now = new Date().toISOString();
const stmt = this.db.prepare(`
INSERT INTO requests (requestId, originalRequest, splitDetails, completed, createdAt, updatedAt)
VALUES (?, ?, ?, 0, ?, ?)
`);
stmt.run(requestId, originalRequest, splitDetails, now, now);
return requestId;
}
/**
* Finds request by ID
*/
findRequestById(requestId) {
const requestStmt = this.db.prepare('SELECT * FROM requests WHERE requestId = ?');
const requestRow = requestStmt.get(requestId);
if (!requestRow)
return null;
const tasks = this.findTasksByRequestId(requestId);
return this.rowToRequest(requestRow, tasks);
}
/**
* Gets all requests
*/
findAllRequests() {
const requestStmt = this.db.prepare('SELECT * FROM requests ORDER BY createdAt DESC');
const requestRows = requestStmt.all();
return requestRows.map(row => {
const tasks = this.findTasksByRequestId(row.requestId);
return this.rowToRequest(row, tasks);
});
}
/**
* Updates request completion status
*/
updateRequestCompletion(requestId, completed) {
const stmt = this.db.prepare(`
UPDATE requests
SET completed = ?, updatedAt = datetime('now')
WHERE requestId = ?
`);
const result = stmt.run(completed ? 1 : 0, requestId);
return result.changes;
}
/**
* Deletes a request and all its tasks
*/
deleteRequest(requestId) {
const stmt = this.db.prepare('DELETE FROM requests WHERE requestId = ?');
const result = stmt.run(requestId);
return result.changes;
}
// ==================== TASK METHODS ====================
/**
* Creates a new task
*/
createTask(task, requestId) {
const taskRow = this.taskToRow(task, requestId);
const stmt = this.db.prepare(`
INSERT INTO tasks (
taskId, requestId, parentId, title, description, status, priority, type,
dependsOn, subtaskIds, failureReason, suggestedRetryStrategy, completedDetails,
artifactsGenerated, environmentContext, summaryFilePath, costData,
feedbackHistory, retryCount, createdAt, updatedAt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '{}', '{}', 0, ?, ?)
`);
stmt.run(taskRow.taskId, taskRow.requestId, taskRow.parentId, taskRow.title, taskRow.description, taskRow.status, taskRow.priority, taskRow.type, taskRow.dependsOn, taskRow.subtaskIds, taskRow.failureReason, taskRow.suggestedRetryStrategy, taskRow.completedDetails, taskRow.artifactsGenerated, taskRow.environmentContext, taskRow.summaryFilePath, taskRow.createdAt, taskRow.updatedAt);
}
/**
* Finds task by ID
*/
findTaskById(taskId) {
const stmt = this.db.prepare('SELECT * FROM tasks WHERE taskId = ?');
const row = stmt.get(taskId);
return row ? this.rowToTask(row) : null;
}
/**
* Finds all tasks for a request
*/
findTasksByRequestId(requestId) {
const stmt = this.db.prepare('SELECT * FROM tasks WHERE requestId = ? ORDER BY createdAt ASC');
const rows = stmt.all(requestId);
return rows.map(row => this.rowToTask(row));
}
/**
* Finds tasks by parent ID
*/
findTasksByParentId(parentId) {
const stmt = this.db.prepare('SELECT * FROM tasks WHERE parentId = ? ORDER BY createdAt ASC');
const rows = stmt.all(parentId);
return rows.map(row => this.rowToTask(row));
}
/**
* Finds tasks by status
*/
findTasksByStatus(status) {
const stmt = this.db.prepare('SELECT * FROM tasks WHERE status = ? ORDER BY createdAt ASC');
const rows = stmt.all(status);
return rows.map(row => this.rowToTask(row));
}
/**
* Updates task status
*/
updateTaskStatus(taskId, status, completedDetails) {
const stmt = this.db.prepare(`
UPDATE tasks
SET status = ?, completedDetails = ?, updatedAt = datetime('now')
WHERE taskId = ?
`);
const result = stmt.run(status, completedDetails || null, taskId);
return result.changes;
}
/**
* Updates task failure information
*/
updateTaskFailure(taskId, failureReason, suggestedRetryStrategy) {
const stmt = this.db.prepare(`
UPDATE tasks
SET status = 'failed', failureReason = ?, suggestedRetryStrategy = ?,
retryCount = retryCount + 1, updatedAt = datetime('now')
WHERE taskId = ?
`);
const result = stmt.run(failureReason, suggestedRetryStrategy || null, taskId);
return result.changes;
}
/**
* Updates task details
*/
updateTask(taskId, updates) {
const fields = [];
const values = [];
if (updates.title !== undefined) {
fields.push('title = ?');
values.push(updates.title);
}
if (updates.description !== undefined) {
fields.push('description = ?');
values.push(updates.description);
}
if (updates.priority !== undefined) {
fields.push('priority = ?');
values.push(updates.priority);
}
if (updates.type !== undefined) {
fields.push('type = ?');
values.push(updates.type);
}
if (updates.dependsOn !== undefined) {
fields.push('dependsOn = ?');
values.push(updates.dependsOn.length > 0 ? JSON.stringify(updates.dependsOn) : null);
}
if (updates.subtaskIds !== undefined) {
fields.push('subtaskIds = ?');
values.push(updates.subtaskIds.length > 0 ? JSON.stringify(updates.subtaskIds) : null);
}
if (updates.environmentContext !== undefined) {
fields.push('environmentContext = ?');
values.push(updates.environmentContext);
}
if (fields.length === 0)
return 0;
fields.push('updatedAt = datetime(\'now\')');
values.push(taskId);
const stmt = this.db.prepare(`UPDATE tasks SET ${fields.join(', ')} WHERE taskId = ?`);
const result = stmt.run(...values);
return result.changes;
}
/**
* Deletes a task
*/
deleteTask(taskId) {
const stmt = this.db.prepare('DELETE FROM tasks WHERE taskId = ?');
const result = stmt.run(taskId);
return result.changes;
}
/**
* Deletes all tasks for a request
*/
deleteTasksByRequestId(requestId) {
const stmt = this.db.prepare('DELETE FROM tasks WHERE requestId = ?');
const result = stmt.run(requestId);
return result.changes;
}
// ==================== DEPENDENCY METHODS ====================
/**
* Adds a dependency to a task
*/
addDependency(taskId, dependencyId) {
const task = this.findTaskById(taskId);
if (!task)
return 0;
const dependsOn = task.dependsOn || [];
if (!dependsOn.includes(dependencyId)) {
dependsOn.push(dependencyId);
const stmt = this.db.prepare(`
UPDATE tasks
SET dependsOn = ?, updatedAt = datetime('now')
WHERE taskId = ?
`);
const result = stmt.run(JSON.stringify(dependsOn), taskId);
return result.changes;
}
return 0;
}
/**
* Removes a dependency from a task
*/
removeDependency(taskId, dependencyId) {
const task = this.findTaskById(taskId);
if (!task)
return 0;
const dependsOn = task.dependsOn || [];
const index = dependsOn.indexOf(dependencyId);
if (index > -1) {
dependsOn.splice(index, 1);
const stmt = this.db.prepare(`
UPDATE tasks
SET dependsOn = ?, updatedAt = datetime('now')
WHERE taskId = ?
`);
const result = stmt.run(dependsOn.length > 0 ? JSON.stringify(dependsOn) : null, taskId);
return result.changes;
}
return 0;
}
// ==================== SUBTASK METHODS ====================
/**
* Adds a subtask to a parent task
*/
addSubtask(parentId, subtaskId) {
const parent = this.findTaskById(parentId);
if (!parent)
return 0;
const subtaskIds = parent.subtaskIds || [];
if (!subtaskIds.includes(subtaskId)) {
subtaskIds.push(subtaskId);
const stmt = this.db.prepare(`
UPDATE tasks
SET subtaskIds = ?, updatedAt = datetime('now')
WHERE taskId = ?
`);
const result = stmt.run(JSON.stringify(subtaskIds), parentId);
return result.changes;
}
return 0;
}
/**
* Removes a subtask from a parent task
*/
removeSubtask(parentId, subtaskId) {
const parent = this.findTaskById(parentId);
if (!parent)
return 0;
const subtaskIds = parent.subtaskIds || [];
const index = subtaskIds.indexOf(subtaskId);
if (index > -1) {
subtaskIds.splice(index, 1);
const stmt = this.db.prepare(`
UPDATE tasks
SET subtaskIds = ?, updatedAt = datetime('now')
WHERE taskId = ?
`);
const result = stmt.run(subtaskIds.length > 0 ? JSON.stringify(subtaskIds) : null, parentId);
return result.changes;
}
return 0;
}
// ==================== ARCHIVAL METHODS ====================
/**
* Archives a task tree (moves tasks to archived_tasks table)
*/
archiveTaskTree(taskIds, originalRequestId, originalRequestText) {
const transaction = this.db.transaction(() => {
const now = new Date().toISOString();
let archivedCount = 0;
for (const taskId of taskIds) {
const task = this.findTaskById(taskId);
if (task) {
// Insert into archived_tasks
const archiveStmt = this.db.prepare(`
INSERT INTO archived_tasks (
taskId, originalRequestId, originalRequestText, parentId, title, description,
status, priority, type, dependsOn, subtaskIds, failureReason, suggestedRetryStrategy,
completedDetails, artifactsGenerated, environmentContext, summaryFilePath,
costData, feedbackHistory, retryCount, createdAt, updatedAt, archivedAt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '{}', '{}', 0, ?, ?, ?)
`);
archiveStmt.run(task.id, originalRequestId, originalRequestText, task.parentId, task.title, task.description, task.status, task.priority, task.type, task.dependsOn && task.dependsOn.length > 0 ? JSON.stringify(task.dependsOn) : null, task.subtaskIds && task.subtaskIds.length > 0 ? JSON.stringify(task.subtaskIds) : null, task.failureReason, task.suggestedRetryStrategy, task.completedDetails, task.artifactsGenerated && task.artifactsGenerated.length > 0 ? JSON.stringify(task.artifactsGenerated) : null, task.environmentContext, task.summaryFilePath, task.createdAt, task.updatedAt, now);
// Delete from active tasks
this.deleteTask(taskId);
archivedCount++;
}
}
return archivedCount;
});
return transaction();
}
/**
* Gets archived tasks by original request ID
*/
findArchivedTasksByRequestId(originalRequestId) {
const stmt = this.db.prepare('SELECT * FROM archived_tasks WHERE originalRequestId = ? ORDER BY createdAt ASC');
const rows = stmt.all(originalRequestId);
return rows.map(row => ({
id: row.taskId,
title: row.title,
description: row.description || '',
status: row.status,
priority: row.priority,
type: row.type || undefined,
dependsOn: row.dependsOn ? JSON.parse(row.dependsOn) : [],
parentId: row.parentId || undefined,
subtaskIds: row.subtaskIds ? JSON.parse(row.subtaskIds) : [],
failureReason: row.failureReason || undefined,
suggestedRetryStrategy: row.suggestedRetryStrategy || undefined,
completedDetails: row.completedDetails || undefined,
artifactsGenerated: row.artifactsGenerated ? JSON.parse(row.artifactsGenerated) : [],
environmentContext: row.environmentContext || undefined,
summaryFilePath: row.summaryFilePath || undefined,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
}));
}
// ==================== TRANSACTIONAL METHODS ====================
/**
* Merges multiple tasks into one (transactional)
*/
mergeTasks(taskIds, mergedTask, requestId) {
const transaction = this.db.transaction(() => {
// Delete old tasks
for (const taskId of taskIds) {
this.deleteTask(taskId);
}
// Create new merged task
this.createTask(mergedTask, requestId);
});
transaction();
}
/**
* Executes multiple operations in a transaction
*/
transaction(operations) {
const transaction = this.db.transaction(operations);
return transaction();
}
}
//# sourceMappingURL=taskRepository.js.map