diff --git a/src/index.ts b/src/index.ts index aed5016..1c7c08e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,7 @@ import { toolDefinitions } from './tools/index.js'; import { taskAdd, taskList, taskShow, taskClose, taskUpdate } from './tools/crud.js'; import { taskSimilar, taskContext } from './tools/search.js'; import { taskLink, checklistAdd, checklistToggle, taskResolveDuplicate } from './tools/relations.js'; -import { epicAdd, epicList, epicShow, epicAssign } from './tools/epics.js'; +import { epicAdd, epicList, epicShow, epicAssign, epicClose } from './tools/epics.js'; import { taskDelegations, taskDelegationQuery } from './tools/delegations.js'; // Create MCP server @@ -147,6 +147,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { epic_id: a.epic_id, }); break; + case 'epic_close': + result = await epicClose(a.id); + break; // Delegations case 'task_delegations': diff --git a/src/tools/epics.ts b/src/tools/epics.ts index 32a4440..71f8ecc 100644 --- a/src/tools/epics.ts +++ b/src/tools/epics.ts @@ -209,3 +209,30 @@ export async function epicUnassign(task_id: string): Promise { return `Unassigned ${task_id} from its epic`; } + +/** + * Close an epic (mark as completed) + */ +export async function epicClose(id: string): Promise { + // Verify epic exists + const epic = await queryOne<{ id: string; title: string; status: string }>( + `SELECT id, title, status FROM epics WHERE id = $1`, + [id] + ); + + if (!epic) { + return `Epic not found: ${id}`; + } + + if (epic.status === 'completed') { + return `Epic already completed: ${id}`; + } + + // Update epic status + await execute( + `UPDATE epics SET status = 'completed', updated_at = NOW() WHERE id = $1`, + [id] + ); + + return `Closed: ${id} (${epic.title})`; +} diff --git a/src/tools/index.ts b/src/tools/index.ts index 6f13828..9019200 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -197,6 +197,17 @@ export const toolDefinitions = [ required: ['task_id', 'epic_id'], }, }, + { + name: 'epic_close', + description: 'Close an epic (mark as completed)', + inputSchema: { + type: 'object', + properties: { + id: { type: 'string', description: 'Epic ID to close (e.g., VPN-E1, ST-E3)' }, + }, + required: ['id'], + }, + }, // Delegation Tools {