UNPKG

squabble-mcp

Version:

Engineer-driven development with critical-thinking PM collaboration - MCP server for Claude

171 lines 6.62 kB
export class TaskManager { workspaceManager; constructor(workspaceManager) { this.workspaceManager = workspaceManager; } async getTasks() { return await this.workspaceManager.getTasks(); } async applyModifications(modifications) { let tasks = await this.getTasks(); for (const mod of modifications) { switch (mod.type) { case 'ADD': tasks = await this.addTask(tasks, mod); break; case 'DELETE': tasks = await this.deleteTask(tasks, mod); break; case 'MODIFY': tasks = await this.modifyTask(tasks, mod); break; case 'BLOCK': tasks = await this.blockTask(tasks, mod); break; case 'SPLIT': tasks = await this.splitTask(tasks, mod); break; case 'MERGE': tasks = await this.mergeTasks(tasks, mod); break; } } await this.workspaceManager.saveTasks(tasks); } async addTask(tasks, mod) { // Get the next sequential ID const nextId = await this.getNextTaskId(); const newTask = { id: nextId, title: mod.details.title, description: mod.details.description, status: 'pending', priority: mod.details.priority || 'medium', dependencies: mod.details.dependencies || [], requiresPlan: mod.details.requiresPlan !== undefined ? mod.details.requiresPlan : this.shouldRequirePlan(mod.details.priority), // Default based on priority branch: mod.details.branch, // PM-assigned git branch name modificationHistory: [{ ...mod, timestamp: new Date() }] }; return [...tasks, newTask]; } shouldRequirePlan(priority) { // Simple heuristic for MVP: high and critical priority tasks require plans by default return priority === 'high' || priority === 'critical'; } async getNextTaskId() { // Get current counter from workspace context let counter = await this.workspaceManager.getContext('task-counter'); if (!counter || typeof counter !== 'number') { counter = 0; } // Increment counter counter++; // Save updated counter await this.workspaceManager.saveContext('task-counter', counter); // Return formatted ID (e.g., SQBL-1, SQBL-2, etc.) return `SQBL-${counter}`; } async deleteTask(tasks, mod) { if (!mod.taskId) return tasks; return tasks.filter(task => { if (task.id === mod.taskId) { // Update tasks that depended on this one tasks.forEach(t => { if (t.dependencies.includes(mod.taskId)) { t.modificationHistory.push({ ...mod, reason: `Dependency ${mod.taskId} was deleted` }); t.dependencies = t.dependencies.filter(d => d !== mod.taskId); } }); return false; } return true; }); } async modifyTask(tasks, mod) { if (!mod.taskId) return tasks; return tasks.map(task => { if (task.id === mod.taskId) { return { ...task, description: mod.details.newDescription || task.description, title: mod.details.newTitle || task.title, priority: mod.details.newPriority || task.priority, status: mod.details.status || task.status, requiresPlan: mod.details.requiresPlan !== undefined ? mod.details.requiresPlan : task.requiresPlan, branch: mod.details.branch || task.branch, // Allow updating branch modificationHistory: [...task.modificationHistory, mod] }; } return task; }); } async blockTask(tasks, mod) { if (!mod.taskId) return tasks; return tasks.map(task => { if (task.id === mod.taskId) { return { ...task, blockedBy: mod.details.blockedBy, modificationHistory: [...task.modificationHistory, { ...mod, timestamp: new Date() }] }; } return task; }); } async splitTask(tasks, mod) { if (!mod.taskId) return tasks; const originalTask = tasks.find(t => t.id === mod.taskId); if (!originalTask) return tasks; // Remove original task const filteredTasks = tasks.filter(t => t.id !== mod.taskId); // Add subtasks const subtasks = mod.details.subtasks.map((title, index) => ({ id: `${mod.taskId}.${index + 1}`, // Use dot notation for subtasks (e.g., SQBL-1.1, SQBL-1.2) title, status: 'pending', priority: originalTask.priority, dependencies: index === 0 ? originalTask.dependencies : [`${mod.taskId}-${index}`], modificationHistory: [{ ...mod, reason: `Split from ${originalTask.title}` }] })); return [...filteredTasks, ...subtasks]; } async mergeTasks(tasks, mod) { // Implementation for merging multiple tasks // For now, return tasks unchanged return tasks; } async getNextTasks(count = 3) { const tasks = await this.getTasks(); // Get unblocked, pending tasks sorted by priority const availableTasks = tasks .filter(t => t.status === 'pending' && !t.blockedBy) .filter(t => t.dependencies.every(dep => tasks.find(dt => dt.id === dep)?.status === 'done')) .sort((a, b) => { const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 }; return priorityOrder[a.priority] - priorityOrder[b.priority]; }); return availableTasks.slice(0, count); } } //# sourceMappingURL=task-manager.js.map