diff --git a/src/index.ts b/src/index.ts index 8dfba98..ebf908c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,6 +25,8 @@ import { reflectTaskSchema, splitTasks, splitTasksSchema, + splitTasksRaw, + splitTasksRawSchema, listTasksSchema, listTasks, executeTask, @@ -207,7 +209,7 @@ async function main() { description: loadPromptFromTemplate( "toolsDescription/splitTasks.md" ), - inputSchema: zodToJsonSchema(splitTasksSchema), + inputSchema: zodToJsonSchema(splitTasksRawSchema), }, { name: "list_tasks", @@ -324,7 +326,7 @@ async function main() { } return await reflectTask(parsedArgs.data); case "split_tasks": - parsedArgs = await splitTasksSchema.safeParseAsync( + parsedArgs = await splitTasksRawSchema.safeParseAsync( request.params.arguments ); if (!parsedArgs.success) { @@ -332,7 +334,7 @@ async function main() { `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` ); } - return await splitTasks(parsedArgs.data); + return await splitTasksRaw(parsedArgs.data); case "list_tasks": parsedArgs = await listTasksSchema.safeParseAsync( request.params.arguments diff --git a/src/prompts/templates_en/reflectTask/index.md b/src/prompts/templates_en/reflectTask/index.md index f6ce0c9..249ff9f 100644 --- a/src/prompts/templates_en/reflectTask/index.md +++ b/src/prompts/templates_en/reflectTask/index.md @@ -39,6 +39,9 @@ After receiving the solution and suggestions, conduct self-reflection and final - Ensure that the task group after splitting still maintains overall architectural consistency 7. **Submit Final Results** + - **No Comments Allowed**: JSON does not support comments — Any use of `#` or `//` will cause parsing failures + - **Proper Escaping Required**: All special characters (e.g., double quotes `\"`, backslashes `\\`) must be properly escaped, or they will be considered invalid. + - **Line Breaks**: If you need line breaks, use escape sequences like \\n or \\r. Direct line breaks will cause parsing errors. - Adjusted final solution + reflection report - Call tool: ``` diff --git a/src/prompts/templates_en/toolsDescription/splitTasks.md b/src/prompts/templates_en/toolsDescription/splitTasks.md index 299b213..291b7b6 100644 --- a/src/prompts/templates_en/toolsDescription/splitTasks.md +++ b/src/prompts/templates_en/toolsDescription/splitTasks.md @@ -37,6 +37,15 @@ Break down complex tasks into independent subtasks, establishing dependencies an - Only outline high-level logic and key steps. - Avoid providing complete source code. - Check **dependencies** between subtasks and specify them in the `dependencies` field. +- If the task involves interface design, always provide a complete and consistent definition, including: + + - Function/class/schema definitions (including names, parameters, return values) + - Data types, usage descriptions, and optional/required status for each item + - Error handling methods and expected exception scenarios + - Dependency and naming conventions (if any) + - Sample data and usage examples + + This ensures consistency, readability, and development precision between tasks. ## 3. **Dependencies and Prioritization** @@ -64,3 +73,11 @@ When you need to create a new task that is not related to the current task list, - ### **Proper Escaping Required** All special characters (e.g., double quotes `\"`, backslashes `\\`) must be properly escaped, or they will be considered invalid. + +## 6. **Important Notes** + +These tasks will be executed by low-intelligence models, so please follow the guidelines below: + +- `Clear and Explicit Instructions`: This prevents the model from producing incorrect or inconsistent architecture/code styles. Provide clear commands or specifications. +- `Encapsulated Interfaces`: Each task runs independently. Define the interfaces clearly — such as function names, parameters, return values — so that other task-executing models can easily understand how to interact with or integrate these functions. +- `Dependencies`: If there are dependencies between tasks, define the interaction interfaces first. Tasks do not need to know each other's implementation, but must know how to interact with one another. diff --git a/src/prompts/templates_zh/reflectTask/index.md b/src/prompts/templates_zh/reflectTask/index.md index bf4ba7e..2005c75 100644 --- a/src/prompts/templates_zh/reflectTask/index.md +++ b/src/prompts/templates_zh/reflectTask/index.md @@ -39,8 +39,13 @@ - 確保拆分後的任務群組仍維持架構的整體一致性 7. **提交最終結果** + + - **禁止註解**:JSON 本身不支援註解,任何 `#` 或 `//` 都會導致解析失敗 + - **注意轉義**:所有特殊字元(如雙引號 `\"`、反斜線 `\\`)必須正確轉義,否則視為非法字元 + - **換行符號**:如果需要換行請使用跳脫符號`\\n` 或 `\\r`,直接換行會導致解析失敗 - 調整後的最終方案 + 反思報告 - 呼叫工具: + ``` split_tasks( ... ) ``` diff --git a/src/prompts/templates_zh/toolsDescription/splitTasks.md b/src/prompts/templates_zh/toolsDescription/splitTasks.md index 1be77be..a6b34ee 100644 --- a/src/prompts/templates_zh/toolsDescription/splitTasks.md +++ b/src/prompts/templates_zh/toolsDescription/splitTasks.md @@ -19,6 +19,15 @@ - 在每個子任務下標註「輸入/輸出」與「驗收標準」 - 如果需要請提供「pseudocode」,僅提供高級邏輯流程和關鍵步驟避免完整代碼 - 檢查子任務間「前後依賴」,並在 `dependencies` 欄位標明 + - 若任務涉及介面設計,請務必提供完整且一致的定義,包括: + + - function / class / schema 定義(含名稱、參數、回傳值) + - 各項目之資料型別、用途描述、是否為選填 + - 錯誤處理方式與預期異常情境 + - 依賴關係與命名規範(如有) + - 範例資料與使用方式 + + 這將有助於任務之間的連貫性、可讀性與開發精確性。 3. **依賴與優先順序** @@ -35,5 +44,14 @@ - `clearAllTasks`:清除所有任務並自動備份現有列表 5. **JSON 嚴謹規則** + - **禁止註解**:JSON 本身不支援註解,任何 `#` 或 `//` 都會導致解析失敗 - **注意轉義**:所有特殊字元(如雙引號 `\"`、反斜線 `\\`)必須正確轉義,否則視為非法字元 + +6. **重要訊息** + +這些任務將會分配給**低智能模型**執行,所以你必須參考以下幾點 + +- `明確且清楚的指導`:這將避免**低智能模型**設計出錯誤或架構風格不一致的程式碼,所以請給予明確的指令或規範 +- `封裝接口`:每個任務都會是獨立執行,所以需要定義好接口,例如你會暴露出什麼 function name 有什麼參數,會回傳什麼等等的資訊,方便其他任務執行模型可以快速的知道如何使用或穿接相關功能 +- `依賴性`:如果任務與任務之間有依賴性,那應該是先定義好交互的介面,任務之間不需要知道各自的實作,但需要知道如何與對方交互 diff --git a/src/tools/task/index.ts b/src/tools/task/index.ts index c6d79f9..a5c9136 100644 --- a/src/tools/task/index.ts +++ b/src/tools/task/index.ts @@ -35,3 +35,5 @@ export { queryTask, queryTaskSchema } from "./queryTask.js"; // getTaskDetail export { getTaskDetail, getTaskDetailSchema } from "./getTaskDetail.js"; + +export { splitTasksRaw, splitTasksRawSchema } from "./splitTasksRaw.js"; diff --git a/src/tools/task/splitTasksRaw.ts b/src/tools/task/splitTasksRaw.ts new file mode 100644 index 0000000..f290390 --- /dev/null +++ b/src/tools/task/splitTasksRaw.ts @@ -0,0 +1,285 @@ +import { z } from "zod"; +import { + getAllTasks, + batchCreateOrUpdateTasks, + clearAllTasks as modelClearAllTasks, +} from "../../models/taskModel.js"; +import { RelatedFileType, Task } from "../../types/index.js"; +import { getSplitTasksPrompt } from "../../prompts/index.js"; + +// 拆分任務工具 +export const splitTasksRawSchema = z.object({ + updateMode: z + .enum(["append", "overwrite", "selective", "clearAllTasks"]) + .describe( + "任務更新模式選擇:'append'(保留所有現有任務並添加新任務)、'overwrite'(清除所有未完成任務並完全替換,保留已完成任務)、'selective'(智能更新:根據任務名稱匹配更新現有任務,保留不在列表中的任務,推薦用於任務微調)、'clearAllTasks'(清除所有任務並創建備份)。\n預設為'clearAllTasks'模式,只有用戶要求變更或修改計劃內容才使用其他模式" + ), + tasksRaw: z + .string() + .describe( + "結構化的任務清單,每個任務應保持原子性且有明確的完成標準,避免過於簡單的任務,簡單修改可與其他任務整合,避免任務過多,範例:[{name: '簡潔明確的任務名稱,應能清晰表達任務目的', description: '詳細的任務描述,包含實施要點、技術細節和驗收標準', implementationGuide: '此特定任務的具體實現方法和步驟,請參考之前的分析結果提供精簡pseudocode', notes: '補充說明、特殊處理要求或實施建議(選填)', dependencies: ['此任務依賴的前置任務完整名稱'], relatedFiles: [{path: '文件路徑', type: '文件類型 (TO_MODIFY: 待修改, REFERENCE: 參考資料, CREATE: 待建立, DEPENDENCY: 依賴文件, OTHER: 其他)', description: '文件描述', lineStart: 1, lineEnd: 100}], verificationCriteria: '此特定任務的驗證標準和檢驗方法'}, {name: '任務2', description: '任務2描述', implementationGuide: '任務2實現方法', notes: '補充說明、特殊處理要求或實施建議(選填)', dependencies: ['任務1'], relatedFiles: [{path: '文件路徑', type: '文件類型 (TO_MODIFY: 待修改, REFERENCE: 參考資料, CREATE: 待建立, DEPENDENCY: 依賴文件, OTHER: 其他)', description: '文件描述', lineStart: 1, lineEnd: 100}], verificationCriteria: '此特定任務的驗證標準和檢驗方法'}]" + ), + globalAnalysisResult: z + .string() + .optional() + .describe( + "全局分析結果:來自 reflect_task 的完整分析結果,適用於所有任務的通用部分" + ), +}); + +const tasksSchema = z + .array( + z.object({ + name: z + .string() + .max(100, { + message: "任務名稱過長,請限制在100個字符以內", + }) + .describe("簡潔明確的任務名稱,應能清晰表達任務目的"), + description: z + .string() + .min(10, { + message: "任務描述過短,請提供更詳細的內容以確保理解", + }) + .describe("詳細的任務描述,包含實施要點、技術細節和驗收標準"), + implementationGuide: z + .string() + .describe( + "此特定任務的具體實現方法和步驟,請參考之前的分析結果提供精簡pseudocode" + ), + dependencies: z + .array(z.string()) + .optional() + .describe( + "此任務依賴的前置任務ID或任務名稱列表,支持兩種引用方式,名稱引用更直觀,是一個字串陣列" + ), + notes: z + .string() + .optional() + .describe("補充說明、特殊處理要求或實施建議(選填)"), + relatedFiles: z + .array( + z.object({ + path: z + .string() + .min(1, { + message: "文件路徑不能為空", + }) + .describe("文件路徑,可以是相對於項目根目錄的路徑或絕對路徑"), + type: z + .nativeEnum(RelatedFileType) + .describe( + "文件類型 (TO_MODIFY: 待修改, REFERENCE: 參考資料, CREATE: 待建立, DEPENDENCY: 依賴文件, OTHER: 其他)" + ), + description: z + .string() + .min(1, { + message: "文件描述不能為空", + }) + .describe("文件描述,用於說明文件的用途和內容"), + lineStart: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的起始行(選填)"), + lineEnd: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的結束行(選填)"), + }) + ) + .optional() + .describe( + "與任務相關的文件列表,用於記錄與任務相關的代碼文件、參考資料、要建立的文件等(選填)" + ), + verificationCriteria: z + .string() + .optional() + .describe("此特定任務的驗證標準和檢驗方法"), + }) + ) + .min(1, { + message: "請至少提供一個任務", + }) + .describe( + "結構化的任務清單,每個任務應保持原子性且有明確的完成標準,避免過於簡單的任務,簡單修改可與其他任務整合,避免任務過多" + ); + +export async function splitTasksRaw({ + updateMode, + tasksRaw, + globalAnalysisResult, +}: z.infer) { + let tasks: Task[] = []; + try { + tasks = JSON.parse(tasksRaw); + } catch (error) { + return { + content: [ + { + type: "text" as const, + text: + "tasks 參數格式錯誤,請確保格式正確,錯誤訊息:" + + (error instanceof Error ? error.message : String(error)), + }, + ], + }; + } + + // 使用 tasksSchema 驗證 tasks + const tasksResult = tasksSchema.safeParse(tasks); + if (!tasksResult.success) { + // 返回錯誤訊息 + return { + content: [ + { + type: "text" as const, + text: + "tasks 參數格式錯誤,請確保格式正確,錯誤訊息:" + + tasksResult.error.message, + }, + ], + }; + } + + try { + // 檢查 tasks 裡面的 name 是否有重複 + const nameSet = new Set(); + for (const task of tasks) { + if (nameSet.has(task.name)) { + return { + content: [ + { + type: "text" as const, + text: "tasks 參數中存在重複的任務名稱,請確保每個任務名稱是唯一的", + }, + ], + }; + } + nameSet.add(task.name); + } + + // 根據不同的更新模式處理任務 + let message = ""; + let actionSuccess = true; + let backupFile = null; + let createdTasks: Task[] = []; + let allTasks: Task[] = []; + + // 將任務資料轉換為符合batchCreateOrUpdateTasks的格式 + const convertedTasks = tasks.map((task) => ({ + name: task.name, + description: task.description, + notes: task.notes, + dependencies: task.dependencies as unknown as string[], + implementationGuide: task.implementationGuide, + verificationCriteria: task.verificationCriteria, + relatedFiles: task.relatedFiles?.map((file) => ({ + path: file.path, + type: file.type as RelatedFileType, + description: file.description, + lineStart: file.lineStart, + lineEnd: file.lineEnd, + })), + })); + + // 處理 clearAllTasks 模式 + if (updateMode === "clearAllTasks") { + const clearResult = await modelClearAllTasks(); + + if (clearResult.success) { + message = clearResult.message; + backupFile = clearResult.backupFile; + + try { + // 清空任務後再創建新任務 + createdTasks = await batchCreateOrUpdateTasks( + convertedTasks, + "append", + globalAnalysisResult + ); + message += `\n成功創建了 ${createdTasks.length} 個新任務。`; + } catch (error) { + actionSuccess = false; + message += `\n創建新任務時發生錯誤: ${ + error instanceof Error ? error.message : String(error) + }`; + } + } else { + actionSuccess = false; + message = clearResult.message; + } + } else { + // 對於其他模式,直接使用 batchCreateOrUpdateTasks + try { + createdTasks = await batchCreateOrUpdateTasks( + convertedTasks, + updateMode, + globalAnalysisResult + ); + + // 根據不同的更新模式生成消息 + switch (updateMode) { + case "append": + message = `成功追加了 ${createdTasks.length} 個新任務。`; + break; + case "overwrite": + message = `成功清除未完成任務並創建了 ${createdTasks.length} 個新任務。`; + break; + case "selective": + message = `成功選擇性更新/創建了 ${createdTasks.length} 個任務。`; + break; + } + } catch (error) { + actionSuccess = false; + message = `任務創建失敗:${ + error instanceof Error ? error.message : String(error) + }`; + } + } + + // 獲取所有任務用於顯示依賴關係 + try { + allTasks = await getAllTasks(); + } catch (error) { + allTasks = [...createdTasks]; // 如果獲取失敗,至少使用剛創建的任務 + } + + // 使用prompt生成器獲取最終prompt + const prompt = getSplitTasksPrompt({ + updateMode, + createdTasks, + allTasks, + }); + + return { + content: [ + { + type: "text" as const, + text: prompt, + }, + ], + ephemeral: { + taskCreationResult: { + success: actionSuccess, + message, + backupFilePath: backupFile, + }, + }, + }; + } catch (error) { + return { + content: [ + { + type: "text" as const, + text: + "執行任務拆分時發生錯誤: " + + (error instanceof Error ? error.message : String(error)), + }, + ], + }; + } +}