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;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the tool generates a Markdown report but doesn't describe where the report is saved (e.g., to a file or returned as output), what data sources it uses, whether it requires specific permissions, or any side effects. This leaves significant gaps for a tool that likely accesses and processes task data.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the core purpose in a single sentence. It efficiently communicates the main function and optional feature without unnecessary details, though it could be slightly more structured by explicitly separating the primary action from the optional parameters.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (generating reports with date filtering), no annotations, no output schema, and incomplete parameter documentation (67% coverage), the description is insufficient. It doesn't explain the output format, how the report is delivered, or behavioral aspects like error handling, making it inadequate for safe and effective use by an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 67% (2 out of 3 parameters have descriptions). The description adds minimal value beyond the schema by mentioning date filtering, which aligns with the schema's startDate and endDate parameters. However, it doesn't explain the outputPath parameter or provide additional context like format constraints or default behaviors, resulting in an adequate but not enhanced baseline score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: '生成任務進度Markdown報告' (generate task progress Markdown report) with optional date filtering. It specifies both the verb (generate) and resource (task progress report), though it doesn't explicitly distinguish it from sibling tools like taskAnalyze or taskGetAll, which prevents a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It mentions optional date filtering but doesn't specify scenarios where this is preferable over other task-related tools like taskSearch or taskGetAll, nor does it mention any prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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