claudemaster
Version:
Task management MCP server optimized for Claude Code - no API keys required
398 lines (359 loc) • 11.8 kB
JavaScript
/**
* basic-task-operations.js
* Basic task operations for Claudemaster - no AI dependencies
*/
import { z } from 'zod';
import TaskEngine from '../core/task-engine.js';
export function registerBasicTaskOperationsTool(server) {
// List tasks
server.addTool({
name: 'list_tasks',
description: 'List all tasks with optional filtering by status or priority.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
status: z.enum(['pending', 'in-progress', 'done', 'cancelled', 'deferred']).optional().describe('Filter by task status'),
priority: z.enum(['high', 'medium', 'low']).optional().describe('Filter by priority'),
includeSubtasks: z.boolean().default(false).describe('Include subtasks in the listing')
}),
execute: async (args) => {
try {
const { projectRoot, status, priority, includeSubtasks } = args;
const taskEngine = new TaskEngine(projectRoot);
const filters = {};
if (status) filters.status = status;
if (priority) filters.priority = priority;
const tasks = taskEngine.listTasks(filters);
const stats = taskEngine.getProjectStats();
let processedTasks = tasks;
if (!includeSubtasks) {
processedTasks = tasks.map(task => {
const { subtasks, ...taskWithoutSubtasks } = task;
return {
...taskWithoutSubtasks,
subtaskCount: subtasks ? subtasks.length : 0
};
});
}
return {
success: true,
tasks: processedTasks,
totalCount: tasks.length,
projectStats: stats,
filters: filters
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Get specific task
server.addTool({
name: 'get_task',
description: 'Get detailed information about a specific task including subtasks.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
taskId: z.number().describe('ID of the task to retrieve')
}),
execute: async (args) => {
try {
const { projectRoot, taskId } = args;
const taskEngine = new TaskEngine(projectRoot);
const task = taskEngine.getTask(taskId);
// Add dependency information
const allTasks = taskEngine.listTasks();
const dependencyInfo = task.dependencies ? task.dependencies.map(depId => {
const depTask = allTasks.find(t => t.id === depId);
return depTask ? {
id: depId,
title: depTask.title,
status: depTask.status
} : {
id: depId,
title: 'Unknown Task',
status: 'missing'
};
}) : [];
return {
success: true,
task: {
...task,
dependencyInfo
}
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Set task status
server.addTool({
name: 'set_task_status',
description: 'Update the status of a task or subtask.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
taskId: z.number().describe('ID of the task to update'),
status: z.enum(['pending', 'in-progress', 'done', 'cancelled', 'deferred']).describe('New status for the task'),
subtaskId: z.string().optional().describe('ID of subtask to update (if updating a subtask)')
}),
execute: async (args) => {
try {
const { projectRoot, taskId, status, subtaskId } = args;
const taskEngine = new TaskEngine(projectRoot);
if (subtaskId) {
// Update subtask status
const task = taskEngine.getTask(taskId);
if (!task.subtasks) {
throw new Error(`Task ${taskId} has no subtasks`);
}
const subtaskIndex = task.subtasks.findIndex(st => st.id === subtaskId);
if (subtaskIndex === -1) {
throw new Error(`Subtask ${subtaskId} not found in task ${taskId}`);
}
task.subtasks[subtaskIndex].status = status;
task.subtasks[subtaskIndex].updatedAt = new Date().toISOString();
const updatedTask = taskEngine.updateTask(taskId, { subtasks: task.subtasks });
return {
success: true,
message: `Subtask ${subtaskId} status updated to ${status}`,
updatedTask
};
} else {
// Update main task status
const updatedTask = taskEngine.setTaskStatus(taskId, status);
// If marking as done, also mark all subtasks as done
if (status === 'done' && updatedTask.subtasks && updatedTask.subtasks.length > 0) {
updatedTask.subtasks = updatedTask.subtasks.map(st => ({
...st,
status: 'done',
updatedAt: new Date().toISOString()
}));
taskEngine.updateTask(taskId, { subtasks: updatedTask.subtasks });
}
return {
success: true,
message: `Task ${taskId} status updated to ${status}`,
updatedTask
};
}
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Add new task
server.addTool({
name: 'add_task',
description: 'Add a new task to the project.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
title: z.string().describe('Title of the new task'),
description: z.string().describe('Description of the task'),
priority: z.enum(['high', 'medium', 'low']).default('medium').describe('Priority level'),
dependencies: z.array(z.number()).default([]).describe('Array of task IDs this task depends on'),
details: z.string().default('').describe('Detailed implementation notes'),
testStrategy: z.string().default('').describe('Testing strategy for the task')
}),
execute: async (args) => {
try {
const { projectRoot, ...taskData } = args;
const taskEngine = new TaskEngine(projectRoot);
const newTask = taskEngine.addTask(taskData);
const stats = taskEngine.getProjectStats();
return {
success: true,
message: `Task "${newTask.title}" added successfully`,
task: newTask,
projectStats: stats
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Update task
server.addTool({
name: 'update_task',
description: 'Update an existing task with new information.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
taskId: z.number().describe('ID of the task to update'),
title: z.string().optional().describe('New title'),
description: z.string().optional().describe('New description'),
priority: z.enum(['high', 'medium', 'low']).optional().describe('New priority'),
dependencies: z.array(z.number()).optional().describe('New dependencies array'),
details: z.string().optional().describe('New implementation details'),
testStrategy: z.string().optional().describe('New testing strategy')
}),
execute: async (args) => {
try {
const { projectRoot, taskId, ...updates } = args;
const taskEngine = new TaskEngine(projectRoot);
// Remove undefined values
const cleanUpdates = Object.fromEntries(
Object.entries(updates).filter(([_, value]) => value !== undefined)
);
const updatedTask = taskEngine.updateTask(taskId, cleanUpdates);
return {
success: true,
message: `Task ${taskId} updated successfully`,
updatedTask
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Remove task
server.addTool({
name: 'remove_task',
description: 'Remove a task from the project.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
taskId: z.number().describe('ID of the task to remove')
}),
execute: async (args) => {
try {
const { projectRoot, taskId } = args;
const taskEngine = new TaskEngine(projectRoot);
// Check if task exists before removal
const task = taskEngine.getTask(taskId);
taskEngine.removeTask(taskId);
const stats = taskEngine.getProjectStats();
return {
success: true,
message: `Task "${task.title}" (ID: ${taskId}) removed successfully`,
removedTask: {
id: task.id,
title: task.title
},
projectStats: stats
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Validate dependencies
server.addTool({
name: 'validate_dependencies',
description: 'Validate task dependencies and identify issues.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory')
}),
execute: async (args) => {
try {
const { projectRoot } = args;
const taskEngine = new TaskEngine(projectRoot);
const issues = taskEngine.validateDependencies();
const stats = taskEngine.getProjectStats();
return {
success: true,
isValid: issues.length === 0,
issues,
issueCount: issues.length,
projectStats: stats,
recommendations: issues.length > 0 ? [
'Fix missing dependencies before proceeding',
'Use update_task to correct dependency references',
'Consider removing invalid dependencies'
] : [
'All dependencies are valid',
'Project structure is consistent'
]
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Add subtask
server.addTool({
name: 'add_subtask',
description: 'Add a subtask to an existing task.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
taskId: z.number().describe('ID of the parent task'),
title: z.string().describe('Title of the subtask'),
description: z.string().describe('Description of the subtask'),
priority: z.enum(['high', 'medium', 'low']).default('medium').describe('Priority level'),
estimatedHours: z.number().optional().describe('Estimated hours to complete')
}),
execute: async (args) => {
try {
const { projectRoot, taskId, ...subtaskData } = args;
const taskEngine = new TaskEngine(projectRoot);
const updatedTask = taskEngine.addSubtask(taskId, subtaskData);
return {
success: true,
message: `Subtask "${subtaskData.title}" added to task ${taskId}`,
parentTask: {
id: updatedTask.id,
title: updatedTask.title,
subtaskCount: updatedTask.subtasks.length
},
addedSubtask: updatedTask.subtasks[updatedTask.subtasks.length - 1]
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
// Remove subtask
server.addTool({
name: 'remove_subtask',
description: 'Remove a subtask from a task.',
parameters: z.object({
projectRoot: z.string().describe('Absolute path to project root directory'),
taskId: z.number().describe('ID of the parent task'),
subtaskId: z.string().describe('ID of the subtask to remove')
}),
execute: async (args) => {
try {
const { projectRoot, taskId, subtaskId } = args;
const taskEngine = new TaskEngine(projectRoot);
// Get subtask info before removal
const task = taskEngine.getTask(taskId);
const subtask = task.subtasks ? task.subtasks.find(st => st.id === subtaskId) : null;
const updatedTask = taskEngine.removeSubtask(taskId, subtaskId);
return {
success: true,
message: `Subtask "${subtask ? subtask.title : subtaskId}" removed from task ${taskId}`,
parentTask: {
id: updatedTask.id,
title: updatedTask.title,
subtaskCount: updatedTask.subtasks ? updatedTask.subtasks.length : 0
}
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
});
}