Skip to main content
Glama
tasks.ts4.62 kB
/** * Tasks 类工具 * 任务追踪和进度管理 */ import { z } from 'zod'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { OpenSpecCli } from '../../core/openspec-cli.js'; import type { ProgressSummary } from '../../types/openspec.js'; export function registerTasksTools(server: McpServer, cli: OpenSpecCli): void { /** * 获取变更的任务列表和进度 */ server.tool( 'openspec_get_tasks', 'Get tasks and progress for a change', { changeId: z.string().describe('Change ID'), }, async ({ changeId }) => { const { tasks, progress } = await cli.getTasks(changeId); // 格式化输出 let text = `Tasks for: ${changeId}\n`; text += `========================\n\n`; text += `Progress: ${progress.percentage}% (${progress.completed}/${progress.total})\n`; text += ` ✅ Completed: ${progress.completed}\n`; text += ` 🔄 In Progress: ${progress.inProgress}\n`; text += ` ⏳ Pending: ${progress.pending}\n\n`; if (tasks.length > 0) { text += `Tasks:\n`; let currentSection = ''; for (const task of tasks) { if (task.section !== currentSection) { currentSection = task.section; text += `\n### ${currentSection}\n`; } const statusIcon = task.status === 'done' ? '✅' : task.status === 'in_progress' ? '🔄' : '⏳'; text += `${statusIcon} [${task.id}] ${task.title}\n`; } } else { text += `No tasks found.\n`; } return { content: [{ type: 'text', text }], }; } ); /** * 更新任务状态 */ server.tool( 'openspec_update_task', 'Update task status', { changeId: z.string().describe('Change ID'), taskId: z.string().describe('Task ID (e.g., 1.1, 2.3)'), status: z .enum(['pending', 'in_progress', 'done']) .describe('New status'), }, async ({ changeId, taskId, status }) => { const result = await cli.updateTaskStatus(changeId, taskId, status); if (result.success) { const statusIcon = status === 'done' ? '✅' : status === 'in_progress' ? '🔄' : '⏳'; return { content: [ { type: 'text', text: `${statusIcon} Task ${taskId} updated to: ${status}`, }, ], }; } else { return { content: [ { type: 'text', text: `❌ Failed to update task: ${result.error}`, }, ], isError: true, }; } } ); /** * 获取所有变更的进度汇总 */ server.tool( 'openspec_get_progress_summary', 'Get progress summary for all changes', {}, async () => { const changes = await cli.listChanges(); const summaries: ProgressSummary[] = []; for (const change of changes) { const { progress } = await cli.getTasks(change.id); summaries.push({ changeId: change.id, title: change.title, progress, }); } // 格式化输出 let text = `Progress Summary\n`; text += `================\n\n`; if (summaries.length === 0) { text += `No active changes found.\n`; } else { // 按进度排序 summaries.sort((a, b) => b.progress.percentage - a.progress.percentage); for (const summary of summaries) { const bar = renderProgressBar(summary.progress.percentage); text += `${summary.title}\n`; text += ` ${bar} ${summary.progress.percentage}%\n`; text += ` (${summary.progress.completed}/${summary.progress.total} tasks)\n\n`; } // 总体统计 const totalTasks = summaries.reduce((sum, s) => sum + s.progress.total, 0); const completedTasks = summaries.reduce( (sum, s) => sum + s.progress.completed, 0 ); const overallPercentage = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0; text += `---\n`; text += `Overall: ${completedTasks}/${totalTasks} tasks (${overallPercentage}%)\n`; } return { content: [{ type: 'text', text }], }; } ); } /** * 渲染进度条 */ function renderProgressBar(percentage: number): string { const total = 20; const filled = Math.round((percentage / 100) * total); const empty = total - filled; return '[' + '█'.repeat(filled) + '░'.repeat(empty) + ']'; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Lumiaqian/openspec-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server