Skip to main content
Glama

GonMCPtool

taskManagerTool.ts18.5 kB
/** * 任務管理器工具 * 提供創建、讀取、更新和刪除任務的功能 */ import fs from 'fs'; import path from 'path'; import { v4 as uuidv4 } from 'uuid'; import { Task, TaskStep, TaskStatus, TaskFilter, TaskAnalysis } from './interfaces/TaskManager.js'; import { TaskTimeUtils } from './utils/taskTimeUtils.js'; export class TaskManagerTool { private static TASKS_DIR = './task'; private static TASKS_FILE = 'tasks.json'; /** * 確保任務目錄存在 */ private static async ensureTasksDirectory(): Promise<void> { const tasksPath = path.resolve(this.TASKS_DIR); try { if (!fs.existsSync(tasksPath)) { fs.mkdirSync(tasksPath, { recursive: true }); } const tasksFilePath = path.join(tasksPath, this.TASKS_FILE); if (!fs.existsSync(tasksFilePath)) { fs.writeFileSync(tasksFilePath, JSON.stringify({ tasks: [] }, null, 2)); } } catch (error) { console.error('Error ensuring tasks directory:', error); throw new Error(`無法創建任務目錄: ${error instanceof Error ? error.message : '未知錯誤'}`); } } /** * 獲取任務文件路徑 */ private static getTasksFilePath(): string { return path.join(path.resolve(this.TASKS_DIR), this.TASKS_FILE); } /** * 讀取所有任務 */ private static async readTasks(): Promise<Task[]> { await this.ensureTasksDirectory(); try { const tasksFile = this.getTasksFilePath(); const fileContent = fs.readFileSync(tasksFile, 'utf-8'); const data = JSON.parse(fileContent); return data.tasks || []; } catch (error) { console.error('Error reading tasks:', error); throw new Error(`讀取任務失敗: ${error instanceof Error ? error.message : '未知錯誤'}`); } } /** * 寫入所有任務 */ private static async writeTasks(tasks: Task[]): Promise<void> { await this.ensureTasksDirectory(); try { const tasksFile = this.getTasksFilePath(); fs.writeFileSync(tasksFile, JSON.stringify({ tasks }, null, 2)); } catch (error) { console.error('Error writing tasks:', error); throw new Error(`寫入任務失敗: ${error instanceof Error ? error.message : '未知錯誤'}`); } } /** * 創建新任務 */ public static async createTask( title: string, description: string, steps: Omit<TaskStep, 'id'>[], tags: string[] = [], dueDate?: string, plannedStartDate?: string, priority: number = 3 ): Promise<Task> { if (!title || !description) { throw new Error('任務標題和描述不能為空'); } if (priority < 1 || priority > 5) { throw new Error('任務優先級必須在1到5之間'); } // 獲取現有任務 const tasks = await this.readTasks(); // 創建新任務 const now = new Date().toISOString(); const taskSteps = steps.map((step, index) => ({ ...step, id: uuidv4(), order: step.order !== undefined ? step.order : index + 1, completed: step.completed !== undefined ? step.completed : false })); const newTask: Task = { id: uuidv4(), title, description, steps: taskSteps, tags, createdAt: now, plannedStartDate, updatedAt: now, status: TaskStatus.PENDING, priority }; // 添加新任務 tasks.push(newTask); // 保存所有任務 await this.writeTasks(tasks); return newTask; } /** * 獲取所有任務 */ public static async getAllTasks(): Promise<Task[]> { return await this.readTasks(); } /** * 根據ID獲取任務 */ public static async getTaskById(id: string): Promise<Task | null> { const tasks = await this.readTasks(); const task = tasks.find(t => t.id === id); return task || null; } /** * 更新任務 * 注意:此方法不能用於將任務標記為已完成,請使用 completeTask 方法 */ public static async updateTask(id: string, updates: Partial<Omit<Task, 'id' | 'createdAt'>>): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === id); if (taskIndex === -1) { return null; } // 檢查是否嘗試將任務設置為已完成狀態 if (updates.status === TaskStatus.COMPLETED) { throw new Error('不能使用 updateTask 方法將任務標記為已完成,請使用 completeTask 方法'); } // 更新任務 tasks[taskIndex] = { ...tasks[taskIndex], ...updates, updatedAt: new Date().toISOString() }; // 保存所有任務 await this.writeTasks(tasks); return tasks[taskIndex]; } /** * 完成任務 * 專門用於將任務標記為已完成並設置實際完成時間 * @param taskId 任務ID * @param completionDate 可選的完成時間,默認為當前時間 * @returns 更新後的任務,如果任務不存在則返回null */ public static async completeTask(taskId: string, completionDate?: string): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return null; } const task = tasks[taskIndex]; // 設置完成時間 const now = completionDate || new Date().toISOString(); // 更新任務狀態 task.status = TaskStatus.COMPLETED; task.actualCompletionDate = now; task.updatedAt = new Date().toISOString(); // 保存所有任務 await this.writeTasks(tasks); return task; } /** * 刪除任務 */ public static async deleteTask(id: string): Promise<boolean> { const tasks = await this.readTasks(); const initialLength = tasks.length; const filteredTasks = tasks.filter(t => t.id !== id); if (filteredTasks.length === initialLength) { return false; } // 保存所有任務 await this.writeTasks(filteredTasks); return true; } /** * 更新任務步驟 */ public static async updateTaskStep(taskId: string, stepId: string, updates: Partial<Omit<TaskStep, 'id'>>): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return null; } const task = tasks[taskIndex]; const stepIndex = task.steps.findIndex(s => s.id === stepId); if (stepIndex === -1) { return null; } // 更新步驟 task.steps[stepIndex] = { ...task.steps[stepIndex], ...updates }; // 更新任務 task.updatedAt = new Date().toISOString(); // 檢查所有步驟是否完成,如果所有步驟完成則提示用戶使用 completeTask const allStepsCompleted = task.steps.every(s => s.completed); if (allStepsCompleted && task.status !== TaskStatus.COMPLETED) { // 不自動設置任務為完成狀態,顯示提示信息 console.log(`任務 "${task.title}" 的所有步驟已完成,請使用 completeTask 方法將任務標記為已完成`); } // 保存所有任務 await this.writeTasks(tasks); return task; } /** * 添加任務步驟 */ public static async addTaskStep( taskId: string, description: string, order?: number, estimatedTime?: number ): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return null; } const task = tasks[taskIndex]; // 確定步驟順序 const newOrder = order || task.steps.length + 1; // 創建新步驟 const newStep: TaskStep = { id: uuidv4(), description, completed: false, order: newOrder, estimatedTime }; // 添加新步驟 task.steps.push(newStep); // 重新排序步驟 task.steps.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); // 更新任務 task.updatedAt = new Date().toISOString(); // 保存所有任務 await this.writeTasks(tasks); return task; } /** * 刪除任務步驟 */ public static async deleteTaskStep(taskId: string, stepId: string): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return null; } const task = tasks[taskIndex]; const initialStepsLength = task.steps.length; // 刪除步驟 task.steps = task.steps.filter(s => s.id !== stepId); if (task.steps.length === initialStepsLength) { return null; } // 重新排序步驟 task.steps = task.steps.map((step, index) => ({ ...step, order: index + 1 })); // 更新任務 task.updatedAt = new Date().toISOString(); // 保存所有任務 await this.writeTasks(tasks); return task; } /** * 搜索任務 */ public static async searchTasks(filter: TaskFilter): Promise<Task[]> { const tasks = await this.readTasks(); return tasks.filter(task => { // 根據任務狀態過濾 if (filter.status && task.status !== filter.status) { return false; } // 根據標籤過濾 if (filter.tags && filter.tags.length > 0) { const hasAllTags = filter.tags.every(tag => task.tags.includes(tag)); if (!hasAllTags) { return false; } } // 根據優先級過濾 if (filter.priority && task.priority !== filter.priority) { return false; } // 根據期限過濾 if (filter.dueDateFrom && task.dueDate) { const dueDateFrom = new Date(filter.dueDateFrom); const taskDueDate = new Date(task.dueDate); if (taskDueDate < dueDateFrom) { return false; } } if (filter.dueDateTo && task.dueDate) { const dueDateTo = new Date(filter.dueDateTo); const taskDueDate = new Date(task.dueDate); // 根據預計開始時間過濾 if (filter.plannedStartDateFrom && task.plannedStartDate) { const plannedStartDateFrom = new Date(filter.plannedStartDateFrom); const taskPlannedStartDate = new Date(task.plannedStartDate); if (taskPlannedStartDate < plannedStartDateFrom) { return false; } } if (filter.plannedStartDateTo && task.plannedStartDate) { const plannedStartDateTo = new Date(filter.plannedStartDateTo); const taskPlannedStartDate = new Date(task.plannedStartDate); if (taskPlannedStartDate > plannedStartDateTo) { return false; } } if (taskDueDate > dueDateTo) { return false; } } // 根據建立時間過濾 if (filter.createdFrom) { const createdFrom = new Date(filter.createdFrom); const taskCreatedDate = new Date(task.createdAt); if (taskCreatedDate < createdFrom) { return false; } } if (filter.createdTo) { const createdTo = new Date(filter.createdTo); const taskCreatedDate = new Date(task.createdAt); if (taskCreatedDate > createdTo) { return false; } } // 根據文本搜索 if (filter.searchText) { const searchText = filter.searchText.toLowerCase(); const matchesTitle = task.title.toLowerCase().includes(searchText); const matchesDescription = task.description.toLowerCase().includes(searchText); const matchesSteps = task.steps.some(step => step.description.toLowerCase().includes(searchText) ); if (!matchesTitle && !matchesDescription && !matchesSteps) { return false; } } return true; }); } /** * 獲取任務分析 * @param filter 可選的任務篩選條件,無篩選條件則分析所有任務 * @returns 分析結果 */ public static async analyzeTaskStatus(filter?: TaskFilter): Promise<TaskAnalysis> { // 如果提供了篩選條件,先進行篩選;否則使用所有任務 const tasks = filter ? await this.searchTasks(filter) : await this.readTasks(); // 初始化分析結果 const analysis: TaskAnalysis = { totalTasks: tasks.length, completedTasks: 0, pendingTasks: 0, inProgressTasks: 0, cancelledTasks: 0, overdueTasksCount: 0, tagDistribution: {} }; // 計算任務分佈 tasks.forEach(task => { // 根據狀態分類 switch (task.status) { case TaskStatus.COMPLETED: analysis.completedTasks++; break; case TaskStatus.PENDING: analysis.pendingTasks++; break; case TaskStatus.IN_PROGRESS: analysis.inProgressTasks++; break; case TaskStatus.CANCELLED: analysis.cancelledTasks++; break; } // 檢查過期任務 if (task.dueDate && task.status !== TaskStatus.COMPLETED && task.status !== TaskStatus.CANCELLED) { const dueDate = new Date(task.dueDate); const now = new Date(); if (dueDate < now) { analysis.overdueTasksCount++; } } // 計算標籤分佈 task.tags.forEach(tag => { if (!analysis.tagDistribution[tag]) { analysis.tagDistribution[tag] = 0; } analysis.tagDistribution[tag]++; }); }); // 計算完成任務的平均完成時間 const completedTasks = tasks.filter(task => task.status === TaskStatus.COMPLETED); if (completedTasks.length > 0) { let totalCompletionTime = 0; let completedTasksWithTimes = 0; completedTasks.forEach(task => { // 使用工具類計算完成時間 const completionTimeHours = TaskTimeUtils.calculateTaskWorkTimeHours(task); if (!isNaN(completionTimeHours)) { totalCompletionTime += completionTimeHours; completedTasksWithTimes++; } }); if (completedTasksWithTimes > 0) { analysis.averageCompletionTime = totalCompletionTime / completedTasksWithTimes; } } // 計算完成任務的實際時間與預計時間的差異 if (completedTasks.length > 0) { let totalTimeDifference = 0; let tasksWithTimeDifference = 0; const taskTimeDifferences: Array<{ taskId: string; title: string; completionDate: string; estimatedTotalTime: number; actualWorkTime: number; timeDifference: number; }> = []; completedTasks.forEach(task => { // 只處理有步驟且有預計完成時間的任務 const stepsWithEstimatedTime = task.steps.filter(step => typeof step.estimatedTime === 'number' && step.estimatedTime > 0 ); if (stepsWithEstimatedTime.length > 0) { // 使用工具類計算任務時間 const totalEstimatedTime = TaskTimeUtils.calculateTotalEstimatedTime(task); const actualCompletionMinutes = TaskTimeUtils.calculateTaskWorkTimeMinutes(task); // 計算時間差異(正值表示超時,負值表示提前完成) const timeDifference = TaskTimeUtils.calculateTimeDifference(task) || 0; // 如果返回null,使用0 // 記錄任務時間差異詳情 const timeInfo = TaskTimeUtils.getTaskTimeInfo(task); taskTimeDifferences.push({ taskId: task.id, title: task.title, completionDate: task.actualCompletionDate || task.updatedAt, estimatedTotalTime: timeInfo.estimatedTotalTime, actualWorkTime: timeInfo.actualWorkTime, // 實際工作時間 timeDifference: timeInfo.timeDifference || 0 // 如果返回null,使用0 }); totalTimeDifference += timeDifference; tasksWithTimeDifference++; } }); // 計算平均時間差異 if (tasksWithTimeDifference > 0) { analysis.averageTimeDifference = totalTimeDifference / tasksWithTimeDifference; analysis.taskTimeDifferences = taskTimeDifferences; } } return analysis; } /** * 設置任務所有步驟的完成狀態 * @param taskId 任務ID * @param completed 是否完成 * @returns 更新後的任務,如果任務不存在則返回null */ public static async setAllStepsStatus(taskId: string, completed: boolean): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return null; } const task = tasks[taskIndex]; // 更新所有步驟狀態 task.steps = task.steps.map(step => ({ ...step, completed })); // 更新任務 task.updatedAt = new Date().toISOString(); // 當所有步驟完成時,提示使用completeTask來標記任務為已完成 if (completed && task.status !== TaskStatus.COMPLETED) { // 不自動設置任務為完成狀態,僅顯示提示信息 console.log(`任務 "${task.title}" 的所有步驟已完成,請使用 completeTask 方法將任務標記為已完成`); } else if (!completed && task.status === TaskStatus.COMPLETED) { // 如果將步驟設為未完成,且當前任務狀態為已完成,則將狀態改為進行中 task.status = TaskStatus.IN_PROGRESS; // 移除實際完成時間 delete task.actualCompletionDate; } // 保存所有任務 await this.writeTasks(tasks); return task; } /** * 開始一個任務 * @param taskId 任務ID * @returns 更新後的任務,如果任務不存在則返回null */ public static async startTask(taskId: string): Promise<Task | null> { const tasks = await this.readTasks(); const taskIndex = tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return null; } const task = tasks[taskIndex]; // 只有待處理的任務才能被啟動 if (task.status !== TaskStatus.PENDING) { throw new Error(`無法啟動任務,當前狀態為: ${task.status}`); } // 設置當前時間為實際開始時間 const now = new Date().toISOString(); // 更新任務 task.status = TaskStatus.IN_PROGRESS; task.actualStartDate = now; task.updatedAt = now; // 保存所有任務 await this.writeTasks(tasks); return task; } }

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/GonTwVn/GonMCPtool'

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