Skip to main content
Glama

taskGenerateReport

Generate task progress Markdown reports with optional date range filtering to track and document project completion status.

Instructions

生成任務進度Markdown報告,可選擇日期篩選範圍

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
outputPathNo
startDateNo起始日期範圍 (YYYY-MM-DD 格式)
endDateNo結束日期範圍 (YYYY-MM-DD 格式)

Implementation Reference

  • Core handler logic for generating task progress report with optional date range filtering. Fetches tasks, filters, analyzes, builds Markdown content, and saves to file.
    public static async generateReportWithDateRange(outputPath: string, startDate?: string, endDate?: string): Promise<string> { try { // 獲取所有任務數據 let tasks = await TaskManagerTool.getAllTasks(); // 根據日期範圍篩選任務 if (startDate || endDate) { tasks = this.filterTasksByDateRange(tasks, startDate, endDate); } // 基於篩選後的任務獲取分析數據 const analysis = this.analyzeFilteredTasks(tasks); // 生成報告內容 const report = this.buildReportContent(tasks, analysis, startDate, endDate); // 確保輸出目錄存在 const outputDir = path.dirname(outputPath); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // 保存到文件 fs.writeFileSync(outputPath, report, 'utf-8'); return report; } catch (error) { console.error('生成任務報告失敗:', error); throw new Error(`生成任務報告失敗: ${error instanceof Error ? error.message : '未知錯誤'}`); } }
  • Zod input schema for the taskGenerateReport tool, defining optional parameters for output file path and date range.
    outputPath: z.string().optional(), startDate: z.string().optional().describe("起始日期範圍 (YYYY-MM-DD 格式)"), endDate: z.string().optional().describe("結束日期範圍 (YYYY-MM-DD 格式)") },
  • main.ts:886-920 (registration)
    Registration of the 'taskGenerateReport' MCP tool, including description, schema, and thin handler that delegates to TaskReportGenerator and returns a preview.
    server.tool("taskGenerateReport", "生成任務進度Markdown報告,可選擇日期篩選範圍", { outputPath: z.string().optional(), startDate: z.string().optional().describe("起始日期範圍 (YYYY-MM-DD 格式)"), endDate: z.string().optional().describe("結束日期範圍 (YYYY-MM-DD 格式)") }, async ({ outputPath = "./task/TaskProgressReport.md", startDate, endDate }) => { try { // 根據是否有日期參數,使用對應的生成方法 const report = await TaskReportGenerator.generateReportWithDateRange(outputPath, startDate, endDate); // 構建日期範圍的描述 let dateRangeMsg = ""; if (startDate && endDate) { dateRangeMsg = `從 ${startDate} 到 ${endDate} 期間的`; } else if (startDate) { dateRangeMsg = `從 ${startDate} 開始的`; } else if (endDate) { dateRangeMsg = `直到 ${endDate} 的`; } return { content: [{ type: "text", text: `${dateRangeMsg}任務報告已生成並保存至:${outputPath}\n\n報告預覽:\n${report.substring(0, 500)}...\n\n(完整報告請查看生成的文件)` }] }; } catch (error) { return { content: [{ type: "text", text: `生成任務報告失敗: ${error instanceof Error ? error.message : "未知錯誤"}` }] }; } } );
  • Helper method to analyze filtered tasks, computing statistics like task counts by status, overdue count, average completion time, time differences.
    private static analyzeFilteredTasks(tasks: Task[]): TaskAnalysis { // 初始化分析結果 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 => { // 只處理有實際完成時間的任務 if (!task.actualCompletionDate) return; // 使用工具類計算完成時間 const completionTimeHours = TaskTimeUtils.calculateTaskWorkTimeHours(task); if (!isNaN(completionTimeHours) && completionTimeHours > 0) { 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 => { // 只處理有步驟、有預計完成時間且有實際完成時間的任務 if (!task.actualCompletionDate) return; const stepsWithEstimatedTime = task.steps.filter(step => typeof step.estimatedTime === 'number' && step.estimatedTime > 0 ); if (stepsWithEstimatedTime.length > 0) { // 使用工具類計算時間 const timeInfo = TaskTimeUtils.getTaskTimeInfo(task); const totalEstimatedTime = timeInfo.estimatedTotalTime; const actualCompletionMinutes = timeInfo.actualWorkTime; const timeDifference = timeInfo.timeDifference || 0; // 如果返回null,使用0 // 只有在有有效的時間差異時才添加到分析中 if (timeDifference !== null && actualCompletionMinutes > 0) { // 記錄任務時間差異詳情 taskTimeDifferences.push({ taskId: task.id, title: task.title, completionDate: task.actualCompletionDate, estimatedTotalTime: totalEstimatedTime, actualWorkTime: actualCompletionMinutes, timeDifference }); totalTimeDifference += timeDifference; tasksWithTimeDifference++; } } }); // 計算平均時間差異 if (tasksWithTimeDifference > 0) { analysis.averageTimeDifference = totalTimeDifference / tasksWithTimeDifference; analysis.taskTimeDifferences = taskTimeDifferences; } } return analysis; }
  • Helper method to construct the detailed Markdown report content including progress overview, tag distribution, task lists, suggestions, and time analysis.
    private static buildReportContent(tasks: Task[], analysis: TaskAnalysis, startDate?: string, endDate?: string): string { let report = `# 任務進度分析報告\n\n`; // 添加日期範圍信息 if (startDate || endDate) { report += `## 報告日期範圍\n\n`; if (startDate && endDate) { report += `- 報告涵蓋從 **${startDate}** 到 **${endDate}** 期間的任務\n\n`; } else if (startDate) { report += `- 報告涵蓋從 **${startDate}** 開始的任務\n\n`; } else if (endDate) { report += `- 報告涵蓋直到 **${endDate}** 的任務\n\n`; } } // 1. 總體進度概況 report += `## 總體進度概況\n\n`; report += `| 指標 | 數值 |\n`; report += `|------|------|\n`; report += `| 總任務數 | ${analysis.totalTasks} |\n`; report += `| 已完成任務 | ${analysis.completedTasks} |\n`; report += `| 待處理任務 | ${analysis.pendingTasks} |\n`; report += `| 進行中任務 | ${analysis.inProgressTasks} |\n`; report += `| 已取消任務 | ${analysis.cancelledTasks} |\n`; report += `| 逾期任務數 | ${analysis.overdueTasksCount} |\n`; if (analysis.averageCompletionTime) { const hours = analysis.averageCompletionTime.toFixed(2); const minutes = Math.round(analysis.averageCompletionTime * 60); report += `| 平均完成時間 | ${hours} 小時 (約${minutes}分鐘) |\n`; } if (analysis.averageTimeDifference !== undefined) { const timeDiffHours = (analysis.averageTimeDifference / 60).toFixed(2); const timeDiffMinutes = Math.round(analysis.averageTimeDifference); const timeDiffStatus = analysis.averageTimeDifference > 0 ? '超出預計' : '提前完成'; report += `| 平均時間差異 | ${timeDiffHours} 小時 (約${Math.abs(timeDiffMinutes)}分鐘${timeDiffStatus}) |\n`; } report += `\n`; // 2. 任務標籤分布 report += `## 任務標籤分布\n\n`; report += `| 標籤 | 任務數量 |\n`; report += `|------|----------|\n`; const tagDistribution = analysis.tagDistribution; const sortedTags = Object.keys(tagDistribution).sort((a, b) => tagDistribution[b] - tagDistribution[a] ); for (const tag of sortedTags) { report += `| ${tag} | ${tagDistribution[tag]} |\n`; } report += `\n`; // 3. 分類任務 const completedTasks = tasks.filter(t => t.status === TaskStatus.COMPLETED); const pendingTasks = tasks.filter(t => t.status === TaskStatus.PENDING); const inProgressTasks = tasks.filter(t => t.status === TaskStatus.IN_PROGRESS); const cancelledTasks = tasks.filter(t => t.status === TaskStatus.CANCELLED); // 已完成任務列表 report += `## 任務完成情況\n\n`; if (completedTasks.length > 0) { report += `### 已完成任務 (${completedTasks.length})\n\n`; completedTasks.forEach((task, index) => { report += this.formatTaskDetails(task, index + 1); }); } // 進行中任務列表 if (inProgressTasks.length > 0) { report += `### 進行中任務 (${inProgressTasks.length})\n\n`; inProgressTasks.forEach((task, index) => { report += this.formatTaskDetails(task, index + 1); }); } // 待處理任務列表 if (pendingTasks.length > 0) { report += `### 待處理任務 (${pendingTasks.length})\n\n`; pendingTasks.forEach((task, index) => { report += this.formatTaskDetails(task, index + 1); }); } // 已取消任務列表 if (cancelledTasks.length > 0) { report += `### 已取消任務 (${cancelledTasks.length})\n\n`; cancelledTasks.forEach((task, index) => { report += this.formatTaskDetails(task, index + 1); }); } // 4. 為項目提出建議 report += `## 關鍵任務優先事項\n\n`; const incompleteSteps = pendingTasks .concat(inProgressTasks) .flatMap(task => task.steps.filter(step => !step.completed)) .sort((a, b) => (a.estimatedTime || 0) - (b.estimatedTime || 0)); if (incompleteSteps.length > 0) { report += `根據當前項目進度,建議優先處理以下項目:\n\n`; // 獲取優先處理的前3個(或更少)步驟 const prioritySteps = incompleteSteps.slice(0, 3); prioritySteps.forEach((step, index) => { const task = tasks.find(t => t.steps.some(s => s.id === step.id)); if (task) { report += `${index + 1}. **${step.description}** - 來自任務「${task.title}」\n`; } }); report += `\n`; } // 5. 實際開始時間分析 const tasksWithActualStartDate = tasks.filter(t => t.actualStartDate); if (tasksWithActualStartDate.length > 0) { report += `\n## 任務實際開始時間分析\n\n`; report += `共有 ${tasksWithActualStartDate.length} 個任務記錄了實際開始時間,佔總任務數的 ${((tasksWithActualStartDate.length / tasks.length) * 100).toFixed(1)}%\n\n`; // 計算計劃與實際開始時間的差異 const tasksWithBothDates = tasksWithActualStartDate.filter(t => t.plannedStartDate); if (tasksWithBothDates.length > 0) { report += `| 任務名稱 | 計劃開始日期 | 實際開始日期 | 差異(天) |\n`; report += `|---------|--------------|--------------|---------|\n`; tasksWithBothDates.forEach(task => { const plannedDate = new Date(task.plannedStartDate!); const actualDate = new Date(task.actualStartDate!); const diffDays = TaskTimeUtils.calculateDaysDifference(plannedDate, actualDate); const diffSymbol = diffDays > 0 ? '+' : ''; report += `| ${task.title} | ${plannedDate.toISOString().split('T')[0]} | ${actualDate.toISOString().split('T')[0]} | ${diffSymbol}${diffDays} |\n`; }); // 計算平均延遲天數 const totalDiffDays = tasksWithBothDates.reduce((sum, task) => { const plannedDate = new Date(task.plannedStartDate!); const actualDate = new Date(task.actualStartDate!); return sum + TaskTimeUtils.calculateDaysDifference(plannedDate, actualDate); }, 0); const avgDiffDays = totalDiffDays / tasksWithBothDates.length; report += `\n平均實際開始時間與計劃的差異:${avgDiffDays.toFixed(1)} 天\n`; } } // 6. 結語和建議 report += `\n## 建議\n\n`; if (analysis.overdueTasksCount > 0) { report += `1. 有 ${analysis.overdueTasksCount} 個任務已逾期,建議優先處理這些任務。\n`; } if (pendingTasks.length > 0) { report += `${analysis.overdueTasksCount > 0 ? '2' : '1'}. 有 ${pendingTasks.length} 個待處理任務,可以根據優先級開始處理。\n`; } const stepsCount = tasks.reduce((sum, task) => sum + task.steps.length, 0); const completedStepsCount = tasks.reduce((sum, task) => sum + task.steps.filter(step => step.completed).length, 0); const completionPercentage = stepsCount > 0 ? Math.round((completedStepsCount / stepsCount) * 100) : 0; report += `${analysis.overdueTasksCount > 0 || pendingTasks.length > 0 ? '3' : '1'}. 項目整體完成度約為 ${completionPercentage}%。`; if (completionPercentage < 25) { report += ` 項目處於初期階段,建議制定詳細的開發計劃。\n`; } else if (completionPercentage < 50) { report += ` 項目進展良好,繼續按計劃推進。\n`; } else if (completionPercentage < 75) { report += ` 項目已過半,可以開始考慮測試和優化工作。\n`; } else { report += ` 項目即將完成,可以開始進行最終測試和準備發布。\n`; } // 添加任務完成時間分析章節 if (analysis.taskTimeDifferences && analysis.taskTimeDifferences.length > 0) { report += `\n## 任務完成時間分析\n\n`; report += `| 任務名稱 | 完成日期 | 預計時間(分鐘) | 實際時間(分鐘) | 時間差異(分鐘) | 差異比例 |\n`; report += `|---------|----------|----------------|----------------|----------------|-----------|\n`; // 按時間差異排序,先列出超時最嚴重的 const sortedTasks = [...analysis.taskTimeDifferences].sort((a, b) => b.timeDifference - a.timeDifference); for (const task of sortedTasks) { // 確保完成日期使用真實的完成日期而不是updatedAt const completionDate = task.completionDate ? TaskTimeUtils.formatDate(task.completionDate) || new Date(task.completionDate).toISOString().split('T')[0] : ''; const timeDiff = task.timeDifference.toFixed(2); const diffPercentage = ((task.timeDifference / task.estimatedTotalTime) * 100).toFixed(1); const symbol = task.timeDifference > 0 ? '+' : ''; report += `| ${task.title} | ${completionDate} | ${task.estimatedTotalTime.toFixed(2)} | ${task.actualWorkTime.toFixed(2)} | ${symbol}${timeDiff} | ${symbol}${diffPercentage}% |\n`; } // 添加時間預估分析 report += `\n### 時間預估準確性分析\n\n`; // 計算準確和不準確的任務數量 const accurateTasks = sortedTasks.filter(t => Math.abs(t.timeDifference / t.estimatedTotalTime) <= 0.2).length; const underestimatedTasks = sortedTasks.filter(t => t.timeDifference > 0 && t.timeDifference / t.estimatedTotalTime > 0.2).length; const overestimatedTasks = sortedTasks.filter(t => t.timeDifference < 0 && Math.abs(t.timeDifference) / t.estimatedTotalTime > 0.2).length; report += `- 時間預估準確的任務 (差異在±20%內):${accurateTasks} 個,佔比 ${((accurateTasks / sortedTasks.length) * 100).toFixed(1)}%\n`; report += `- 時間被低估的任務 (實際超出預計20%以上):${underestimatedTasks} 個,佔比 ${((underestimatedTasks / sortedTasks.length) * 100).toFixed(1)}%\n`; report += `- 時間被高估的任務 (實際少於預計20%以上):${overestimatedTasks} 個,佔比 ${((overestimatedTasks / sortedTasks.length) * 100).toFixed(1)}%\n`; // 添加最顯著的任務說明 if (sortedTasks.length > 0) { const mostUnderestimated = sortedTasks[0]; // 最被低估的任務 const mostOverestimated = sortedTasks[sortedTasks.length - 1]; // 最被高估的任務 if (mostUnderestimated.timeDifference > 0) { const diff = ((mostUnderestimated.timeDifference / mostUnderestimated.estimatedTotalTime) * 100).toFixed(1); report += `\n最顯著被低估的任務:「${mostUnderestimated.title}」,實際用時超出預計 ${diff}%\n`; } if (mostOverestimated.timeDifference < 0) { const diff = ((Math.abs(mostOverestimated.timeDifference) / mostOverestimated.estimatedTotalTime) * 100).toFixed(1); report += `最顯著被高估的任務:「${mostOverestimated.title}」,實際用時少於預計 ${diff}%\n`; } } // 添加完成趨勢分析 if (sortedTasks.length >= 3) { report += `\n### 完成效率趨勢分析\n\n`; // 任務按創建日期排序 const chronologicalTasks = [...sortedTasks].sort((a, b) => { return new Date(a.completionDate).getTime() - new Date(b.completionDate).getTime(); }); // 計算前半部分和後半部分的平均時間差異 const halfIndex = Math.floor(chronologicalTasks.length / 2); const firstHalf = chronologicalTasks.slice(0, halfIndex); const secondHalf = chronologicalTasks.slice(halfIndex); // 計算平均差異比例(實際與預計的比值) const firstHalfRatio = firstHalf.reduce((sum, task) => sum + (task.actualWorkTime / task.estimatedTotalTime), 0) / firstHalf.length; const secondHalfRatio = secondHalf.reduce((sum, task) => sum + (task.actualWorkTime / task.estimatedTotalTime), 0) / secondHalf.length; report += `- 項目前期完成效率:實際時間為預計時間的 ${firstHalfRatio.toFixed(2)} 倍\n`; report += `- 項目後期完成效率:實際時間為預計時間的 ${secondHalfRatio.toFixed(2)} 倍\n`; if (secondHalfRatio < firstHalfRatio) { const improvement = ((1 - secondHalfRatio / firstHalfRatio) * 100).toFixed(1); report += `- 效率提升:項目後期完成效率提高了 ${improvement}%\n`; } else if (secondHalfRatio > firstHalfRatio) { const decrease = ((secondHalfRatio / firstHalfRatio - 1) * 100).toFixed(1); report += `- 效率下降:項目後期完成效率下降了 ${decrease}%\n`; } else { report += `- 效率穩定:項目前後期完成效率基本一致\n`; } } } return report; }

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

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