diff --git a/src/index.ts b/src/index.ts index 23899c7..e2d7484 100644 --- a/src/index.ts +++ b/src/index.ts @@ -132,6 +132,12 @@ async function main() { .describe( "任務更新模式選擇:'append'(保留所有現有任務並添加新任務)、'overwrite'(清除所有未完成任務並完全替換,保留已完成任務)、'selective'(智能更新:根據任務名稱匹配更新現有任務,保留不在列表中的任務,推薦用於任務微調)、'clearAllTasks'(清除所有任務並創建備份)。\n預設為'clearAllTasks'模式,只有用戶要求變更或修改計畫內容才使用其他模式" ), + globalAnalysisResult: z + .string() + .optional() + .describe( + "全局分析結果:來自 reflect_task 的完整分析結果,適用於所有任務的通用部分" + ), tasks: z .array( z.object({ @@ -178,6 +184,14 @@ async function main() { ) .optional() .describe("與任務相關的檔案列表,包含檔案路徑、類型和描述"), + implementationGuide: z + .string() + .optional() + .describe("此特定任務的具體實現方法和步驟"), + verificationCriteria: z + .string() + .optional() + .describe("此特定任務的驗證標準和檢驗方法"), }) ) .describe("結構化的任務清單,每個任務應保持原子性且有明確的完成標準"), @@ -303,7 +317,7 @@ async function main() { .enum([ RelatedFileType.TO_MODIFY, RelatedFileType.REFERENCE, - RelatedFileType.OUTPUT, + RelatedFileType.CREATE, RelatedFileType.DEPENDENCY, RelatedFileType.OTHER, ]) diff --git a/src/models/taskModel.ts b/src/models/taskModel.ts index ff22c86..19cdedc 100644 --- a/src/models/taskModel.ts +++ b/src/models/taskModel.ts @@ -262,150 +262,202 @@ export async function batchCreateOrUpdateTasks( notes?: string; dependencies?: string[]; relatedFiles?: RelatedFile[]; + implementationGuide?: string; // 新增:實現指南 + verificationCriteria?: string; // 新增:驗證標準 }>, - updateMode: "append" | "overwrite" | "selective" | "clearAllTasks" // 必填參數,指定任務更新策略 + updateMode: "append" | "overwrite" | "selective" | "clearAllTasks", // 必填參數,指定任務更新策略 + globalAnalysisResult?: string // 新增:全局分析結果 ): Promise { - // 獲取現有任務 + // 讀取現有的所有任務 const existingTasks = await readTasks(); - const nameToIdMap = new Map(); - const idToTaskMap = new Map(); - // 添加現有任務到映射表 - existingTasks.forEach((task) => { - nameToIdMap.set(task.name, task.id); - idToTaskMap.set(task.id, task); - }); + // 根據更新模式處理現有任務 + let tasksToKeep: Task[] = []; - // 處理不同的更新模式 - if (updateMode === "overwrite") { - // 覆蓋模式:只刪除未完成的任務,保留已完成的任務 - const completedTasks = existingTasks.filter( + if (updateMode === "append") { + // 追加模式:保留所有現有任務 + tasksToKeep = [...existingTasks]; + } else if (updateMode === "overwrite") { + // 覆蓋模式:僅保留已完成的任務,清除所有未完成任務 + tasksToKeep = existingTasks.filter( (task) => task.status === TaskStatus.COMPLETED ); - await writeTasks(completedTasks); - - // 更新映射表,只保留已完成的任務 - nameToIdMap.clear(); - idToTaskMap.clear(); - completedTasks.forEach((task) => { - nameToIdMap.set(task.name, task.id); - idToTaskMap.set(task.id, task); - }); } else if (updateMode === "selective") { - // selective 模式:保留未在清單中的現有任務,更新名稱匹配的任務 - // 不做任何預處理,保留所有現有任務,在處理每個新任務時進行選擇性更新 - } else { - // append 模式:保留所有現有任務,不需要特殊處理 + // 選擇性更新模式:根據任務名稱選擇性更新,保留未在更新列表中的任務 + // 1. 提取待更新任務的名稱清單 + const updateTaskNames = new Set(taskDataList.map((task) => task.name)); + + // 2. 保留所有沒有出現在更新列表中的任務 + tasksToKeep = existingTasks.filter( + (task) => !updateTaskNames.has(task.name) + ); + } else if (updateMode === "clearAllTasks") { + // 清除所有任務模式:清空任務列表 + tasksToKeep = []; } - // 創建任務名稱集合,用於選擇性更新模式 - const taskNameSet = new Set(taskDataList.map((task) => task.name)); + // 這個映射將用於存儲名稱到任務ID的映射,用於支持通過名稱引用任務 + const taskNameToIdMap = new Map(); - // 準備最終的任務清單,如果是 selective 模式,先保留不在新清單中的任務 - let finalTaskList: Task[] = []; + // 對於選擇性更新模式,先將現有任務的名稱和ID記錄下來 if (updateMode === "selective") { - finalTaskList = existingTasks.filter((task) => !taskNameSet.has(task.name)); + existingTasks.forEach((task) => { + taskNameToIdMap.set(task.name, task.id); + }); } - // 第一階段:創建或更新任務,但不設置依賴 - const firstPassTasks: Array<{ task: Task; originalDeps: string[] }> = []; + // 記錄所有任務的名稱和ID,無論是要保留的任務還是新建的任務 + // 這將用於稍後解析依賴關係 + tasksToKeep.forEach((task) => { + taskNameToIdMap.set(task.name, task.id); + }); + + // 創建新任務的列表 + const newTasks: Task[] = []; for (const taskData of taskDataList) { - let newTask: Task; + // 檢查是否為選擇性更新模式且該任務名稱已存在 + if (updateMode === "selective" && taskNameToIdMap.has(taskData.name)) { + // 獲取現有任務的ID + const existingTaskId = taskNameToIdMap.get(taskData.name)!; - // 查找是否存在同名任務 - const existingTaskId = nameToIdMap.get(taskData.name); - const isExistingTask = existingTaskId !== undefined; + // 查找現有任務 + const existingTaskIndex = existingTasks.findIndex( + (task) => task.id === existingTaskId + ); - if (isExistingTask && updateMode === "selective") { - // 選擇性更新模式:更新現有任務 - const existingTask = idToTaskMap.get(existingTaskId)!; + // 如果找到現有任務並且該任務未完成,則更新它 + if ( + existingTaskIndex !== -1 && + existingTasks[existingTaskIndex].status !== TaskStatus.COMPLETED + ) { + const taskToUpdate = existingTasks[existingTaskIndex]; - // 更新任務內容但保留原始ID和創建時間 - newTask = (await updateTask(existingTaskId, { - name: taskData.name, - description: taskData.description, - notes: taskData.notes, - // 暫時不更新依賴,在第二階段處理 - relatedFiles: taskData.relatedFiles, - })) as Task; + // 更新任務的基本信息,但保留原始ID、創建時間等 + const updatedTask: Task = { + ...taskToUpdate, + name: taskData.name, + description: taskData.description, + notes: taskData.notes, + // 後面會處理 dependencies + updatedAt: new Date(), + // 新增:保存實現指南(如果有) + implementationGuide: taskData.implementationGuide, + // 新增:保存驗證標準(如果有) + verificationCriteria: taskData.verificationCriteria, + // 新增:保存全局分析結果(如果有) + analysisResult: globalAnalysisResult, + }; - // 如果更新失敗,使用現有任務作為後備 - if (!newTask) { - console.warn( - `警告:更新任務 "${taskData.name}" 失敗,使用現有任務作為後備` - ); - newTask = existingTask; + // 處理相關文件(如果有) + if (taskData.relatedFiles) { + updatedTask.relatedFiles = taskData.relatedFiles; + } + + // 將更新後的任務添加到新任務列表 + newTasks.push(updatedTask); + + // 從tasksToKeep中移除此任務,因為它已經被更新並添加到newTasks中了 + tasksToKeep = tasksToKeep.filter((task) => task.id !== existingTaskId); + } else { + // 如果任務已完成或找不到,則跳過更新,可能在控制台輸出警告 + if ( + existingTaskIndex !== -1 && + existingTasks[existingTaskIndex].status === TaskStatus.COMPLETED + ) { + console.warn( + `警告:嘗試更新已完成的任務 "${taskData.name}",操作被忽略` + ); + } else { + console.warn( + `警告:嘗試更新不存在的任務 "${taskData.name}",操作被忽略` + ); + } } } else { // 創建新任務 - newTask = await createTask( - taskData.name, - taskData.description, - taskData.notes, - [], // 空依賴列表,在第二階段設置 - taskData.relatedFiles - ); + const newTaskId = uuidv4(); + + // 將新任務的名稱和ID添加到映射中 + taskNameToIdMap.set(taskData.name, newTaskId); + + const newTask: Task = { + id: newTaskId, + name: taskData.name, + description: taskData.description, + notes: taskData.notes, + status: TaskStatus.PENDING, + dependencies: [], // 後面會填充 + createdAt: new Date(), + updatedAt: new Date(), + relatedFiles: taskData.relatedFiles, + // 新增:保存實現指南(如果有) + implementationGuide: taskData.implementationGuide, + // 新增:保存驗證標準(如果有) + verificationCriteria: taskData.verificationCriteria, + // 新增:保存全局分析結果(如果有) + analysisResult: globalAnalysisResult, + }; + + newTasks.push(newTask); } - - // 將新任務添加到映射表 - nameToIdMap.set(newTask.name, newTask.id); - idToTaskMap.set(newTask.id, newTask); - - // 保存原始依賴信息 - firstPassTasks.push({ - task: newTask, - originalDeps: taskData.dependencies || [], - }); } - // 第二階段:更新所有新增或修改任務的依賴關係 - const processedTasks: Task[] = []; + // 處理任務之間的依賴關係 + for (let i = 0; i < taskDataList.length; i++) { + const taskData = taskDataList[i]; + const newTask = newTasks[i]; - for (const { task, originalDeps } of firstPassTasks) { - // 解析依賴關係 - const resolvedDependencies: TaskDependency[] = []; + // 如果存在依賴關係,處理它們 + if (taskData.dependencies && taskData.dependencies.length > 0) { + const resolvedDependencies: TaskDependency[] = []; - for (const dep of originalDeps) { - // 嘗試將依賴名稱解析為ID - let depId = dep; + for (const dependencyName of taskData.dependencies) { + // 首先嘗試將依賴項解釋為任務ID + let dependencyTaskId = dependencyName; - // 如果不是UUID格式,嘗試將其作為任務名稱解析 - if ( - !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test( - dep - ) - ) { - const resolvedId = nameToIdMap.get(dep); - if (resolvedId) { - depId = resolvedId; + // 如果依賴項看起來不像UUID,則嘗試將其解釋為任務名稱 + if ( + !dependencyName.match( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i + ) + ) { + // 如果映射中存在此名稱,則使用對應的ID + if (taskNameToIdMap.has(dependencyName)) { + dependencyTaskId = taskNameToIdMap.get(dependencyName)!; + } else { + console.warn( + `警告:任務 "${taskData.name}" 引用了未知的依賴任務 "${dependencyName}",此依賴將被忽略` + ); + continue; // 跳過此依賴 + } } else { - console.warn(`警告:找不到名為 "${dep}" 的任務,已忽略此依賴`); - continue; // 跳過此依賴 + // 是UUID格式,但需要確認此ID是否對應實際存在的任務 + const idExists = [...tasksToKeep, ...newTasks].some( + (task) => task.id === dependencyTaskId + ); + if (!idExists) { + console.warn( + `警告:任務 "${taskData.name}" 引用了未知的依賴任務ID "${dependencyTaskId}",此依賴將被忽略` + ); + continue; // 跳過此依賴 + } } + + resolvedDependencies.push({ taskId: dependencyTaskId }); } - resolvedDependencies.push({ taskId: depId }); - } - - // 更新任務的依賴 - const updatedTask = await updateTask(task.id, { - dependencies: resolvedDependencies, - }); - - if (updatedTask) { - processedTasks.push(updatedTask); - } else { - processedTasks.push(task); // 回退到原始任務 + newTask.dependencies = resolvedDependencies; } } - // 如果是選擇性更新模式,合併保留的任務和新建/更新的任務 - if (updateMode === "selective") { - return [...finalTaskList, ...processedTasks]; - } + // 合併保留的任務和新任務 + const allTasks = [...tasksToKeep, ...newTasks]; - return processedTasks; + // 寫入更新後的任務列表 + await writeTasks(allTasks); + + return newTasks; } // 檢查任務是否可以執行(所有依賴都已完成) diff --git a/src/tools/taskTools.ts b/src/tools/taskTools.ts index f684aa2..7969f56 100644 --- a/src/tools/taskTools.ts +++ b/src/tools/taskTools.ts @@ -404,6 +404,16 @@ export async function reflectTask({ - 若希望**選擇性更新特定任務**,同時保留其他未完成任務,使用 updateMode="selective" - 若希望**清除所有任務**並自動創建備份,使用 updateMode="clearAllTasks" +## 知識傳遞機制說明 + +拆分任務時,已將規劃與分析階段產生的關鍵知識保留並融入到任務中: + +1. **全局分析結果** - 所有任務都已關聯完整的分析文檔,在執行時可查看 +2. **任務專屬實現指南** - 每個任務都已保存具體的實現方法和建議 +3. **任務專屬驗證標準** - 每個任務都設置了明確的驗證要求和檢查點 + +此機制確保了任務執行者可以獲得完整的背景知識和技術細節,無需重新思考或推導解決方案。 + 您的批判性評估將決定最終方案的質量,請務必嚴格審查,不放過任何潛在問題。`; return { @@ -424,6 +434,12 @@ export const splitTasksSchema = z .describe( "任務更新模式(必填):'append'(保留現有任務並新增)、'overwrite'(清除所有未完成任務並重建)、'selective'(根據名稱匹配更新現有任務,保留其餘任務)、'clearAllTasks'(清除所有任務並創建備份)" ), + globalAnalysisResult: z + .string() + .optional() + .describe( + "全局分析結果:來自 reflect_task 的完整分析結果,適用於所有任務的通用部分" + ), tasks: z .array( z.object({ @@ -468,6 +484,14 @@ export const splitTasksSchema = z ) .optional() .describe("與任務相關的檔案列表,包含檔案路徑、類型和描述"), + implementationGuide: z + .string() + .optional() + .describe("此特定任務的具體實現方法和步驟"), + verificationCriteria: z + .string() + .optional() + .describe("此特定任務的驗證標準和檢驗方法"), }) ) .min(1, { message: "至少需要提供一個任務,請確保任務列表不為空" }) @@ -494,6 +518,7 @@ export const splitTasksSchema = z export async function splitTasks({ updateMode, tasks, + globalAnalysisResult, }: z.infer) { // 處理 clearAllTasks 模式,直接調用 modelClearAllTasks 函數 if (updateMode === "clearAllTasks") { @@ -546,8 +571,12 @@ export async function splitTasks({ updateModeMessage = "清除模式:清除所有任務並創建備份"; } - // 批量創建任務 - 將 updateMode 傳遞給 batchCreateOrUpdateTasks - const createdTasks = await batchCreateOrUpdateTasks(tasks, updateMode); + // 批量創建任務 - 將 updateMode 和 globalAnalysisResult 傳遞給 batchCreateOrUpdateTasks + const createdTasks = await batchCreateOrUpdateTasks( + tasks, + updateMode, + globalAnalysisResult + ); // 獲取所有任務,用於顯示完整的依賴關係 const allTasks = await getAllTasks(); @@ -596,26 +625,52 @@ export async function splitTasks({ - 是否明確了任務的驗收標準? ## 詳細任務清單\n\n${createdTasks - .map( - (task, index) => - `### 任務 ${index + 1}:${task.name}\n**ID:** \`${ - task.id - }\`\n**描述:** ${task.description}\n${ - task.notes ? `**注意事項:** ${task.notes}\n` : "" - }${ - task.dependencies.length > 0 - ? `**依賴任務:** ${task.dependencies - .map((d) => { - // 查找依賴任務的名稱,提供更友好的顯示 - const depTask = allTasks.find((t) => t.id === d.taskId); - return depTask - ? `"${depTask.name}" (\`${d.taskId}\`)` - : `\`${d.taskId}\``; - }) - .join(", ")}\n` - : "**依賴任務:** 無\n" - }` - ) + .map((task, index) => { + let taskInfo = `### 任務 ${index + 1}:${task.name}\n**ID:** \`${ + task.id + }\`\n**描述:** ${task.description}\n`; + + if (task.notes) { + taskInfo += `**注意事項:** ${task.notes}\n`; + } + + // 添加實現指南的顯示(如果有) + if (task.implementationGuide) { + taskInfo += `**實現指南:** ${ + task.implementationGuide.length > 100 + ? task.implementationGuide.substring(0, 100) + + "... (執行時可查看完整內容)" + : task.implementationGuide + }\n`; + } + + // 添加驗證標準的顯示(如果有) + if (task.verificationCriteria) { + taskInfo += `**驗證標準:** ${ + task.verificationCriteria.length > 100 + ? task.verificationCriteria.substring(0, 100) + + "... (驗證時可查看完整內容)" + : task.verificationCriteria + }\n`; + } + + // 添加依賴任務 + taskInfo += `${ + task.dependencies.length > 0 + ? `**依賴任務:** ${task.dependencies + .map((d) => { + // 查找依賴任務的名稱,提供更友好的顯示 + const depTask = allTasks.find((t) => t.id === d.taskId); + return depTask + ? `"${depTask.name}" (\`${d.taskId}\`)` + : `\`${d.taskId}\``; + }) + .join(", ")}\n` + : "**依賴任務:** 無\n" + }`; + + return taskInfo; + }) .join( "\n" )}\n\n## 任務依賴管理技巧\n\n### 依賴關係設置\n在建立新任務時,可以通過以下方式指定依賴關係:\n\n1. **使用任務名稱**(推薦):直接使用其他任務的名稱,如 \`"建立用戶界面"\`\n2. **使用任務ID**:使用任務的唯一標識符,如 \`"${ @@ -784,6 +839,21 @@ export async function executeTask({ task.notes ? `- **注意事項:** ${task.notes}\n` : "" }\n`; + // ===== 增強:顯示實現指南(如果有) ===== + if (task.implementationGuide) { + prompt += `\n## 實現指南\n\n${task.implementationGuide}\n\n`; + } + + // ===== 增強:顯示驗證標準(如果有) ===== + if (task.verificationCriteria) { + prompt += `\n## 驗證標準\n\n${task.verificationCriteria}\n\n`; + } + + // ===== 增強:顯示分析結果(如果有) ===== + if (task.analysisResult) { + prompt += `\n## 分析背景\n\n${task.analysisResult}\n\n`; + } + // ===== 增強:處理相關文件內容 ===== let relatedFilesSummary = ""; let contextInfo = ""; @@ -1039,13 +1109,36 @@ export async function verifyTask({ taskId }: z.infer) { }; } - const prompt = `## 任務驗證評估\n\n### 任務資料\n\n- **名稱:** ${ + // 構建基本的任務詳情 + let prompt = `## 任務驗證評估\n\n### 任務資料\n\n- **名稱:** ${ task.name }\n- **ID:** \`${task.id}\`\n- **描述:** ${task.description}\n${ task.notes ? `- **注意事項:** ${task.notes}\n` : "" + }\n`; + + // 新增:顯示任務特定的驗證標準(如果有) + if (task.verificationCriteria) { + prompt += `\n## 任務特定驗證標準\n\n${task.verificationCriteria}\n\n`; } -## 完整性驗證標準\n\n請根據以下關鍵標準進行嚴格的質量檢查,為每個評估項目提供詳細的證據和具體範例: + // 新增:顯示實現指南的主要內容(如果有) + if (task.implementationGuide) { + prompt += `\n## 實現指南摘要\n\n${ + task.implementationGuide.length > 200 + ? task.implementationGuide.substring(0, 200) + "... (參見完整實現指南)" + : task.implementationGuide + }\n\n`; + } + + // 新增:顯示分析結果摘要(如果有) + if (task.analysisResult) { + prompt += `\n## 分析要點摘要\n\n在驗證時應考慮原始分析中強調的以下關鍵點:\n\n${extractSummary( + task.analysisResult, + 300 + )}\n\n`; + } + + prompt += `## 完整性驗證標準\n\n請根據以下關鍵標準進行嚴格的質量檢查,為每個評估項目提供詳細的證據和具體範例: ### 1. 需求符合性 (30%) #### 核心評估項目: @@ -1403,7 +1496,7 @@ export const updateTaskContentSchema = z .enum([ RelatedFileType.TO_MODIFY, RelatedFileType.REFERENCE, - RelatedFileType.OUTPUT, + RelatedFileType.CREATE, RelatedFileType.DEPENDENCY, RelatedFileType.OTHER, ]) @@ -1570,7 +1663,7 @@ export const updateTaskRelatedFilesSchema = z .enum([ RelatedFileType.TO_MODIFY, RelatedFileType.REFERENCE, - RelatedFileType.OUTPUT, + RelatedFileType.CREATE, RelatedFileType.DEPENDENCY, RelatedFileType.OTHER, ]) diff --git a/src/types/index.ts b/src/types/index.ts index 3806eba..cd90692 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -15,7 +15,7 @@ export interface TaskDependency { export enum RelatedFileType { TO_MODIFY = "待修改", // 需要在任務中修改的文件 REFERENCE = "參考資料", // 任務的參考資料或相關文檔 - OUTPUT = "輸出結果", // 任務產生的輸出文件 + CREATE = "待建立", // 需要在任務中建立的文件 DEPENDENCY = "依賴文件", // 任務依賴的組件或庫文件 OTHER = "其他", // 其他類型的相關文件 } @@ -42,6 +42,15 @@ export interface Task { completedAt?: Date; // 任務完成的時間戳(僅適用於已完成的任務) summary?: string; // 任務完成摘要,簡潔描述實施結果和重要決策(僅適用於已完成的任務) relatedFiles?: RelatedFile[]; // 與任務相關的文件列表(選填) + + // 新增欄位:保存完整的技術分析結果 + analysisResult?: string; // 來自 analyze_task 和 reflect_task 階段的完整分析結果 + + // 新增欄位:保存具體的實現指南 + implementationGuide?: string; // 具體的實現方法、步驟和建議 + + // 新增欄位:保存驗證標準和檢驗方法 + verificationCriteria?: string; // 明確的驗證標準、測試要點和驗收條件 } // 規劃任務的參數:用於初始化任務規劃階段 @@ -73,12 +82,22 @@ export interface SplitTasksArgs { * - "clearAllTasks":清除所有任務並創建備份 */ updateMode: "append" | "overwrite" | "selective" | "clearAllTasks"; + + // 全局分析結果:用於所有任務的共享分析數據 + globalAnalysisResult?: string; // 來自 reflect_task 的完整分析結果,適用於所有任務的通用部分 + tasks: Array<{ name: string; // 簡潔明確的任務名稱,應能清晰表達任務目的 description: string; // 詳細的任務描述,包含實施要點、技術細節和驗收標準 notes?: string; // 補充說明、特殊處理要求或實施建議(選填) dependencies?: string[]; // 此任務依賴的前置任務ID列表,形成任務的有向無環依賴圖 relatedFiles?: RelatedFile[]; // 與任務相關的文件列表(選填) + + // 新增欄位:任務專屬的實現指南 + implementationGuide?: string; // 此特定任務的具體實現方法和步驟 + + // 新增欄位:任務專屬的驗證標準 + verificationCriteria?: string; // 此特定任務的驗證標準和檢驗方法 }>; } diff --git a/src/utils/fileLoader.ts b/src/utils/fileLoader.ts index c69d937..c325091 100644 --- a/src/utils/fileLoader.ts +++ b/src/utils/fileLoader.ts @@ -33,7 +33,7 @@ export async function loadTaskRelatedFiles( [RelatedFileType.TO_MODIFY]: 1, [RelatedFileType.REFERENCE]: 2, [RelatedFileType.DEPENDENCY]: 3, - [RelatedFileType.OUTPUT]: 4, + [RelatedFileType.CREATE]: 4, [RelatedFileType.OTHER]: 5, };