From e0ca35bde590652c23e17b7fa0fe950d446b7de8 Mon Sep 17 00:00:00 2001 From: siage Date: Fri, 11 Apr 2025 18:02:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B8=85=E9=99=A4=E6=89=80?= =?UTF-8?q?=E6=9C=89=E4=BB=BB=E5=8B=99=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=85=81?= =?UTF-8?q?=E8=A8=B1=E7=94=A8=E6=88=B6=E5=9C=A8=E7=A2=BA=E8=AA=8D=E5=BE=8C?= =?UTF-8?q?=E5=88=AA=E9=99=A4=E7=B3=BB=E7=B5=B1=E4=B8=AD=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90=E7=9A=84=E4=BB=BB=E5=8B=99=EF=BC=8C?= =?UTF-8?q?=E4=B8=A6=E5=9C=A8=E5=9F=B7=E8=A1=8C=E5=89=8D=E8=87=AA=E5=8B=95?= =?UTF-8?q?=E5=89=B5=E5=BB=BA=E6=95=B8=E6=93=9A=E5=82=99=E4=BB=BD=E4=BB=A5?= =?UTF-8?q?=E7=A2=BA=E4=BF=9D=E5=AE=89=E5=85=A8=E6=80=A7=E3=80=82=E5=90=8C?= =?UTF-8?q?=E6=99=82=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=9B=B4=E6=96=B0=E4=BB=BB?= =?UTF-8?q?=E5=8B=99=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E7=94=A8?= =?UTF-8?q?=E6=88=B6=E6=9B=B4=E6=96=B0=E6=9C=AA=E5=AE=8C=E6=88=90=E4=BB=BB?= =?UTF-8?q?=E5=8B=99=E7=9A=84=E5=90=8D=E7=A8=B1=E3=80=81=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E5=92=8C=E6=B3=A8=E8=A8=98=EF=BC=8C=E4=B8=A6=E6=93=B4=E5=B1=95?= =?UTF-8?q?=E4=BB=BB=E5=8B=99=E7=9B=B8=E9=97=9C=E6=96=87=E4=BB=B6=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E8=A8=98=E9=8C=84=E5=8A=9F=E8=83=BD=E4=BB=A5=E6=8F=90?= =?UTF-8?q?=E5=8D=87=E4=B8=8A=E4=B8=8B=E6=96=87=E8=A8=98=E6=86=B6=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=E3=80=82=E6=9B=B4=E6=96=B0=E7=9B=B8=E9=97=9C=E6=96=87?= =?UTF-8?q?=E6=AA=94=E4=BB=A5=E5=8F=8D=E6=98=A0=E9=80=99=E4=BA=9B=E6=96=B0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=A2=9E=E5=BC=B7=E4=BB=BB=E5=8B=99?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=9A=84=E9=9D=88=E6=B4=BB=E6=80=A7=E8=88=87?= =?UTF-8?q?=E5=8F=AF=E6=93=8D=E4=BD=9C=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api-reference.md | 119 +++++++ docs/architecture.md | 251 +++++++++++++- docs/examples.md | 734 +++++++++++++++++++++++++++------------- docs/usage-guide.md | 287 +++++++++++++++- src/index.ts | 86 +++++ src/models/taskModel.ts | 162 ++++++++- src/tools/taskTools.ts | 732 +++++++++++++++++++++++++++++++++++++-- src/types/index.ts | 19 ++ src/utils/fileLoader.ts | 469 +++++++++++++++++++++++++ 9 files changed, 2599 insertions(+), 260 deletions(-) create mode 100644 src/utils/fileLoader.ts diff --git a/docs/api-reference.md b/docs/api-reference.md index 3c5c067..17bf0aa 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -100,6 +100,125 @@ const completeResult = await mcp.mcp_shrimp_task_manager.complete_task({ 當未提供 `summary` 參數時,系統會根據任務名稱和描述自動生成摘要。 +### 4. 清除所有任務功能 + +#### `clear_all_tasks` + +清除系統中所有未完成的任務,提供簡化的系統重置功能。 + +**參數:** + +| 參數名 | 類型 | 必填 | 描述 | +| ------- | ------- | ---- | ------------------------- | +| confirm | boolean | 是 | 確認刪除操作,必須為 true | + +**返回:** + +- 成功:返回清除操作的結果,包含被刪除的任務數量 +- 失敗:返回錯誤訊息,說明失敗原因 + +**使用範例:** + +```javascript +const clearResult = await mcp.mcp_shrimp_task_manager.clear_all_tasks({ + confirm: true, // 必須明確確認 +}); +``` + +**安全機制:** + +- 必須明確設置 `confirm` 參數為 `true` 才能執行操作 +- 系統會自動在清除前創建數據備份,存放在 `data/backups` 目錄 +- 所有清除操作都會記錄到系統日誌中 +- 已完成的任務不會被刪除,確保歷史記錄完整性 + +### 5. 更新任務功能 + +#### `update_task` + +更新未完成任務的內容,包括名稱、描述和注記。 + +**參數:** + +| 參數名 | 類型 | 必填 | 描述 | +| ----------- | ------ | ---- | ------------------------------------------------- | +| taskId | string | 是 | 待更新任務的唯一標識符,必須是未完成的有效任務 ID | +| name | string | 否 | 任務的新名稱(選填) | +| description | string | 否 | 任務的新描述(選填) | +| notes | string | 否 | 任務的新補充說明(選填) | + +**返回:** + +- 成功:返回更新後的任務數據 +- 失敗:返回錯誤訊息,說明失敗原因(如任務不存在、已完成等) + +**使用範例:** + +```javascript +const updateResult = await mcp.mcp_shrimp_task_manager.update_task({ + taskId: "task-uuid-here", + name: "優化後的任務名稱", + description: "更詳細的任務描述...", + notes: "補充重要資訊", +}); +``` + +**約束條件:** + +- 不允許更新已完成的任務 +- 至少需要提供 name、description 或 notes 中的一個參數 +- 任務 ID 和完成狀態不可通過此功能更改 + +### 6. 任務相關文件位置記錄功能 + +#### `update_task_files` + +為任務添加或更新相關文件記錄,提升任務執行時的上下文記憶能力。 + +**參數:** + +| 參數名 | 類型 | 必填 | 描述 | +| ------------ | ------ | ---- | ------------------------------------ | +| taskId | string | 是 | 任務的唯一標識符 | +| relatedFiles | array | 是 | 相關文件列表,包含以下屬性的對象數組 | + +**relatedFiles 對象屬性:** + +| 屬性名 | 類型 | 必填 | 描述 | +| ----------- | ------ | ---- | ------------------------------------------------------------------------------------ | +| path | string | 是 | 文件路徑(相對於項目根目錄或絕對路徑) | +| type | string | 是 | 文件關聯類型,可選值:「待修改」、「參考資料」、「輸出結果」、「依賴文件」、「其他」 | +| description | string | 否 | 文件的補充描述(選填) | +| lineStart | number | 否 | 相關代碼區塊的起始行(選填) | +| lineEnd | number | 否 | 相關代碼區塊的結束行(選填) | + +**返回:** + +- 成功:返回更新後的任務數據,包含完整的相關文件列表 +- 失敗:返回錯誤訊息 + +**使用範例:** + +```javascript +const updateFilesResult = await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId: "task-uuid-here", + relatedFiles: [ + { + path: "src/components/Button.tsx", + type: "待修改", + description: "需要修改按鈕組件以支持新狀態", + lineStart: 24, + lineEnd: 45, + }, + { + path: "docs/design-spec.md", + type: "參考資料", + description: "包含按鈕設計規範", + }, + ], +}); +``` + ## 工作日誌功能 ### 1. 查詢日誌 diff --git a/docs/architecture.md b/docs/architecture.md index 2ed273b..ce922f4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -19,8 +19,9 @@ +---------v--------+ | | | 工具註冊層 | <-- 新增 delete_task 工具 - | (index.ts) | - | | + | (index.ts) | 新增 clear_all_tasks 工具 + | | 新增 update_task 工具 + | | 新增 update_task_files 工具 +---------+--------+ | | @@ -51,6 +52,9 @@ **新增功能**: - 註冊 `delete_task` 工具函數,允許刪除未完成的任務 +- 註冊 `clear_all_tasks` 工具函數,允許清除所有未完成的任務 +- 註冊 `update_task` 工具函數,允許更新未完成任務的內容 +- 註冊 `update_task_files` 工具函數,允許更新任務相關文件列表 ### 2.2 工具實現層 (tools/\*.ts) @@ -60,11 +64,16 @@ - `taskTools.ts`: 任務管理相關工具 - `logTools.ts`: 日誌管理相關工具 +- `fileLoader.ts`: 文件加載和處理工具(新增) **新增功能**: - 在 `taskTools.ts` 中新增 `deleteTask` 工具函數和 schema +- 在 `taskTools.ts` 中新增 `clearAllTasks` 工具函數和 schema,實現安全的任務清除 +- 在 `taskTools.ts` 中新增 `updateTaskContent` 工具函數和 schema,支持任務內容更新 +- 在 `taskTools.ts` 中新增 `updateTaskRelatedFiles` 工具函數和 schema,支持文件關聯管理 - 在 `executeTask` 中集成任務複雜度檢查邏輯 +- 在 `executeTask` 中增強上下文記憶功能,自動加載相關文件 - 在 `completeTask` 中增強摘要處理功能 ### 2.3 模型邏輯層 (models/\*.ts) @@ -79,6 +88,9 @@ **新增功能**: - 在 `taskModel.ts` 中實現 `deleteTask` 函數 +- 在 `taskModel.ts` 中實現 `clearAllTasks` 函數,包含備份和安全檢查機制 +- 在 `taskModel.ts` 中擴展 `updateTask` 函數,增加對已完成任務的檢查邏輯 +- 在 `taskModel.ts` 中新增 `updateTaskContent` 和 `updateTaskRelatedFiles` 函數 - 增加 `assessTaskComplexity` 函數評估任務複雜度 - 實現 `updateTaskSummary` 函數更新任務摘要 @@ -91,6 +103,8 @@ - `TaskComplexityLevel` 枚舉:定義任務複雜度級別 - `TaskComplexityThresholds` 常量:定義複雜度評估閾值 - `TaskComplexityAssessment` 接口:記錄複雜度評估結果 +- `RelatedFile` 接口:定義任務相關文件結構 +- `RelatedFileType` 枚舉:定義文件關聯類型(待修改、參考資料等) ### 2.5 工具函數 (utils/\*.ts) @@ -99,10 +113,15 @@ **核心文件**: - `summaryExtractor.ts`: 實現摘要提取和生成功能 +- `fileLoader.ts`: 實現智能文件加載和代碼區塊提取功能(新增) +- `contextManager.ts`: 管理上下文記憶和優化(新增) **新增功能**: - 增強 `generateTaskSummary` 函數實現自動摘要生成 +- 實現 `loadTaskRelatedFiles` 函數,智能加載相關文件內容 +- 實現 `extractRelevantCodeBlocks` 函數,從大型文件中提取關鍵代碼 +- 實現 `optimizeContext` 函數,優化上下文表示和壓縮 ### 2.6 數據存儲層 @@ -112,6 +131,7 @@ - `data/tasks.json`: 存儲所有任務數據 - `data/conversation_log.json`: 存儲對話日誌數據 +- `data/backups/`: 存儲任務數據備份(新增) ## 3. 數據流 @@ -203,6 +223,132 @@ LLM 調用 complete_task 返回結果給 LLM ``` +### 3.4 清除所有任務流程 + +``` +LLM 調用 clear_all_tasks + | + v +檢查確認參數(必須為 true) + | + v +創建數據備份 + | + v +載入當前任務列表 + | + v +篩選出未完成的任務 + | + v +記錄操作開始到日誌 + | + v +執行批量刪除 + | + v +更新數據文件 + | + v +記錄操作結果到日誌 + | + v +返回清除結果給 LLM +``` + +### 3.5 更新任務內容流程 + +``` +LLM 調用 update_task + | + v +載入任務信息 + | + v +檢查任務是否存在 + | + v +檢查任務狀態(阻止更新已完成任務) + | + v +驗證更新參數(至少提供一個更新字段) + | + v +執行更新操作 + | + v +記錄操作到日誌 + | + v +返回更新後的任務信息給 LLM +``` + +### 3.6 更新任務相關文件流程 + +``` +LLM 調用 update_task_files + | + v +載入任務信息 + | + v +檢查任務是否存在 + | + v +驗證文件列表格式 + | + v +處理每個相關文件 + | | + | +---> 驗證文件路徑 + | | + | +---> 檢查行號範圍(如有提供) + | | + | +---> 驗證文件類型 + | + v +更新任務的相關文件列表 + | + v +記錄操作到日誌 + | + v +返回更新後的任務信息給 LLM +``` + +### 3.7 上下文記憶加載流程 + +``` +執行任務前預處理 + | + v +檢查任務相關文件列表 + | + v +按優先級排序文件 + | + v +智能加載文件內容 + | | + | +---> 對於指定行號範圍的文件,優先加載該範圍 + | | + | +---> 對於大型文件,提取關鍵代碼區塊 + | | + | +---> 對於文檔文件,生成摘要 + | + v +加載依賴任務的摘要 + | + v +加載任務執行歷史記錄 + | + v +優化上下文表示 + | + v +將增強的上下文注入任務執行環境 +``` + ## 4. 系統交互圖 ### 4.1 LLM 與任務管理器交互 @@ -289,6 +435,96 @@ LLM 調用 complete_task +---------------+ ``` +### 5.4 清除所有任務功能 + +``` ++------------------+ +----------------+ +-------------------+ +| | | | | | +| clearAllTasksSchema |---->| clearAllTasks |---->| modelClearAllTasks | +| | | (taskTools.ts) | | (taskModel.ts) | ++------------------+ +-------+--------+ +---------+---------+ + | | + v v + +------+---------------------+------+ + | 確認參數檢查和數據備份 | + +------+---------------------+------+ + | + v + +------+---------------------+------+ + | 篩選和批量刪除未完成任務 | + +------+---------------------+------+ + | + v + +------+---------------------+------+ + | 記錄操作到日誌 | + +------+---------------------+------+ +``` + +### 5.5 更新任務功能 + +``` ++------------------+ +----------------+ +-------------------+ +| | | | | | +| updateTaskSchema |---->| updateTaskContent |---->| updateTask | +| | | (taskTools.ts) | | (taskModel.ts) | ++------------------+ +-------+--------+ +---------+---------+ + | | + v v + +------+---------------------+------+ + | 檢查任務狀態和更新參數有效性 | + +------+---------------------+------+ + | + v + +------+---------------------+------+ + | 應用更新和記錄操作 | + +------+---------------------+------+ +``` + +### 5.6 任務相關文件位置記錄功能 + +``` ++----------------------+ +---------------------+ +---------------------+ +| | | | | | +| updateTaskFilesSchema |---->| updateTaskRelatedFiles |---->| updateTaskRelatedFiles | +| | | (taskTools.ts) | | (taskModel.ts) | ++----------------------+ +-----------+---------+ +-----------+---------+ + | | + v v + +-------+-------------------------+------+ + | 驗證文件格式和路徑有效性 | + +-------+-------------------------+------+ + | + v + +-------+-------------------------+------+ + | 更新任務相關文件列表 | + +-------+-------------------------+------+ +``` + +### 5.7 上下文記憶優化功能 + +``` ++---------------+ +-------------------+ +------------------+ +| | | | | | +| executeTask |---->| loadTaskContext |---->| loadRelatedFiles | +| (taskTools.ts)| | (contextManager.ts) | | (fileLoader.ts) | ++---------------+ +--------+----------+ +--------+---------+ + | | + v v + +-------+------------------------+-----+ + | 文件優先級排序和智能載入 | + +-------+------------------------+-----+ + | + v + +-------+------------------------+-----+ + | 上下文優化和大小控制 | + +-------+------------------------+-----+ + | + v + +-------+------------------------+-----+ + | 歷史記錄和依賴任務摘要整合 | + +-------+------------------------+-----+ +``` + ## 6. 擴展性考慮 ### 6.1 新功能擴展方式 @@ -307,6 +543,8 @@ LLM 調用 complete_task - **任務處理流程擴展**:可通過修改 `executeTask`、`verifyTask` 等函數擴展任務處理流程 - **複雜度評估擴展**:可在 `assessTaskComplexity` 中添加更多評估指標 - **摘要生成擴展**:可增強 `summaryExtractor.ts` 中的算法 +- **上下文管理擴展**:可在 `contextManager.ts` 中添加更多上下文優化策略 +- **文件加載擴展**:可在 `fileLoader.ts` 中支持更多文件類型的智能加載 ## 7. 系統限制與未來改進 @@ -315,15 +553,20 @@ LLM 調用 complete_task - 使用文件存儲,不適合多用戶並發場景 - 缺乏用戶認證和權限控制 - 摘要生成使用簡單規則,可進一步改進 +- 上下文大小有限,需要更智能的壓縮和優先級機制 ### 7.2 未來可能的改進 - 遷移到數據庫存儲,提高並發處理能力 - 添加用戶管理和訪問控制 -- 使用機器學習模型優化摘要生成 +- 使用機器學習模型優化摘要生成和上下文管理 - 添加任務優先級和時間規劃功能 - 實現任務執行進度追蹤 +- 增強文件關聯系統,支持更複雜的關係類型 +- 實現自動識別相關文件的能力 ## 8. 結論 -蝦米任務管理器採用模塊化、分層設計,使系統具有良好的可維護性和擴展性。新增的刪除任務功能、任務複雜度自適應處理和任務摘要自動更新機制進一步增強了系統的功能性和用戶體驗,使其能夠更有效地管理複雜項目的任務流程。 +蝦米任務管理器採用模塊化、分層設計,使系統具有良好的可維護性和擴展性。新增的清除所有任務功能、更新任務功能、任務相關文件位置記錄功能和上下文記憶優化功能進一步增強了系統的功能性和用戶體驗,使其能夠更有效地管理複雜項目的任務流程,特別是在需要長期上下文記憶的場景中表現出色。 + +系統的設計重點在於提供清晰的任務管理流程,同時增強 LLM 在執行任務時的上下文記憶能力,通過精確的文件關聯和智能上下文載入,有效解決了 LLM 在處理長期複雜任務時的記憶限制問題。 diff --git a/docs/examples.md b/docs/examples.md index b081f58..e8ceb28 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -235,271 +235,549 @@ if (completedTasks) { } ``` -## 4. 工作日誌功能案例 +## 4. 清除所有任務功能案例 -### 案例:追蹤關鍵任務的執行歷程 +### 案例:項目重新規劃 -在復盤專案時,需要了解某個關鍵任務的完整執行過程和決策點。 +在完成一個階段性目標後,團隊決定重新規劃剩餘的項目工作,需要清除所有未完成的任務。 #### 示例代碼: ```javascript -// 查詢特定任務的日誌 -const taskLogs = await mcp.mcp_shrimp_task_manager.list_conversation_log({ - taskId: "key-feature-task-uuid", - limit: 50, // 獲取更多記錄 -}); +// 1. 首先導出現有任務作為備份 +const currentTasks = await mcp.mcp_shrimp_task_manager.list_tasks(); +console.log("備份現有任務列表:", currentTasks); -// 分析日誌中的關鍵決策點 -const decisionPoints = taskLogs.entries.filter( - (entry) => - entry.summary.includes("決定") || - entry.summary.includes("選擇") || - entry.summary.includes("決策") -); +// 2. 記錄已完成任務數量(這些任務不會被刪除) +const completedTasksMatch = + currentTasks.content[0].text.match(/已完成: (\d+) 個任務/); +const completedTasksCount = completedTasksMatch + ? parseInt(completedTasksMatch[1]) + : 0; +console.log(`已完成任務數量: ${completedTasksCount} (這些任務將被保留)`); -console.log("任務執行過程中的關鍵決策點:", decisionPoints); - -// 分析遇到的挑戰 -const challenges = taskLogs.entries.filter( - (entry) => - entry.summary.includes("問題") || - entry.summary.includes("挑戰") || - entry.summary.includes("困難") || - entry.summary.includes("錯誤") -); - -console.log("任務執行過程中遇到的挑戰:", challenges); - -// 組織執行時間線 -const timeline = taskLogs.entries - .map((entry) => ({ - time: new Date(entry.timestamp), - action: - entry.summary.substring(0, 100) + - (entry.summary.length > 100 ? "..." : ""), - participant: entry.participant, - })) - .sort((a, b) => a.time - b.time); - -console.log("任務執行時間線:", timeline); -``` - -### 案例:項目結束後清理不必要的日誌 - -在項目完成後,需要清理積累的大量日誌,但保留關鍵記錄。 - -#### 示例代碼: - -```javascript -// 首先,提取重要日誌並保存 -const allLogs = await mcp.mcp_shrimp_task_manager.list_conversation_log({ - limit: 1000, // 嘗試獲取大量日誌 - offset: 0, -}); - -// 識別關鍵日誌(例如重要決策、錯誤解決方案等) -const keyLogs = allLogs.entries.filter((entry) => { - // 過濾出重要性高的日誌 - const isDecision = - entry.summary.includes("決策") || entry.summary.includes("選擇方案"); - const isError = - entry.summary.includes("修復錯誤") || entry.summary.includes("解決問題"); - const isMilestone = - entry.summary.includes("里程碑") || entry.summary.includes("階段完成"); - - return isDecision || isError || isMilestone; -}); - -// 將關鍵日誌保存到外部文件(示例) -console.log("保存的關鍵日誌數量:", keyLogs.length); -console.log("關鍵日誌示例:", keyLogs.slice(0, 3)); - -// 確認已保存重要日誌後,清除系統中的全部日誌 -const confirmation = window.confirm( - "已保存關鍵日誌,是否清除系統中的所有日誌記錄?" -); - -if (confirmation) { - await mcp.mcp_shrimp_task_manager.clear_conversation_log({ - confirm: true, +// 3. 執行清除操作(必須設置確認參數) +try { + const clearResult = await mcp.mcp_shrimp_task_manager.clear_all_tasks({ + confirm: true, // 必須明確確認才能執行清除 }); - console.log("所有日誌已清除"); + + console.log("清除結果:", clearResult); + + // 4. 提取備份文件路徑信息 + const backupPathMatch = + clearResult.content[0].text.match(/備份文件: (.*\.json)/); + const backupPath = backupPathMatch ? backupPathMatch[1] : "備份文件路徑未知"; + console.log(`數據已備份到: ${backupPath}`); +} catch (error) { + console.error("清除操作失敗:", error); } + +// 5. 確認清除結果 +const remainingTasks = await mcp.mcp_shrimp_task_manager.list_tasks(); +console.log("清除後的任務列表:", remainingTasks); + +// 6. 開始新的任務規劃 +await mcp.mcp_shrimp_task_manager.plan_task({ + description: "項目第二階段:優化用戶體驗並擴展功能", + requirements: "改進性能、美化界面、增加新功能模塊", +}); ``` -## 5. 綜合案例:完整項目流程 +### 案例:測試環境重置 -以下是一個完整的項目工作流程示例,從規劃到完成,使用蝦米任務管理器的各項功能。 +在進行系統測試後,需要將環境重置為初始狀態,同時保留已完成任務的歷史記錄。 -### 案例:開發一個用戶資料分析儀表板 - -#### 階段 1:項目規劃與任務拆分 +#### 示例代碼: ```javascript -// 開始規劃 -await mcp.mcp_shrimp_task_manager.plan_task({ +// 1. 查詢當前環境狀態 +const beforeReset = await mcp.mcp_shrimp_task_manager.list_tasks(); +console.log("重置前狀態:", beforeReset); + +// 2. 記錄重要測試結果 +const testResults = "測試發現3個UI問題,2個性能瓶頸,均已記錄到測試報告"; +console.log(`測試結果摘要: ${testResults}`); + +// 3. 執行環境重置 +await mcp.mcp_shrimp_task_manager.clear_all_tasks({ + confirm: true, +}); + +// 4. 確認重置結果 +const afterReset = await mcp.mcp_shrimp_task_manager.list_tasks(); +console.log("重置後狀態:", afterReset); + +// 5. 記錄重置操作到系統日誌 +await mcp.mcp_shrimp_task_manager.list_conversation_log({ + limit: 1, // 只獲取最新的一條日誌 +}); +``` + +## 5. 更新任務功能案例 + +### 案例:調整任務範圍 + +在進行過程中發現任務需求有變化,需要調整任務描述和範圍。 + +#### 示例代碼: + +```javascript +// 1. 獲取需要更新的任務ID +const taskList = await mcp.mcp_shrimp_task_manager.list_tasks(); +const taskIdMatch = taskList.content[0].text.match( + /ID: `([^`]+)`.*名稱: "實現用戶註冊功能"/ +); +const taskId = taskIdMatch ? taskIdMatch[1] : null; + +if (!taskId) { + console.error("找不到目標任務"); + return; +} + +// 2. 更新任務內容 +const updateResult = await mcp.mcp_shrimp_task_manager.update_task({ + taskId, + name: "實現用戶註冊和驗證功能", description: - "開發一個用戶資料分析儀表板,用於可視化用戶行為數據,支持多維度分析和報表導出功能。", - requirements: - "技術棧要求使用React前端和Node.js後端,數據視覺化採用ECharts或D3.js,需支持千萬級用戶數據的實時分析。", + "設計並實現用戶註冊流程,包括:\n1. 基本信息註冊\n2. 電子郵件驗證\n3. 手機號碼驗證\n4. 安全問題設置\n5. 初始偏好設定", + notes: "更新原因:產品團隊要求增加電子郵件和手機驗證步驟,提高帳戶安全性", }); -// 分析問題 -await mcp.mcp_shrimp_task_manager.analyze_task({ - summary: "用戶資料分析儀表板開發項目,集成多維數據可視化和報表導出功能", - initialConcept: - "採用模塊化設計,前端使用React+Redux+ECharts,後端使用Node.js+Express+MongoDB,實現數據流水線處理和快速響應的數據查詢API。", +console.log("任務更新結果:", updateResult); + +// 3. 通知團隊成員任務範圍變更 +console.log("已通知團隊成員任務範圍已擴大,包含更多驗證步驟"); + +// 4. 記錄變更歷史 +const changeLog = ` +變更日期: ${new Date().toISOString()} +變更內容: 擴展任務範圍,增加郵件和手機驗證步驟 +變更原因: 提高帳戶安全性 +請求方: 產品團隊 +`; +console.log("變更日誌:", changeLog); +``` + +### 案例:澄清任務描述 + +發現任務描述不夠清晰,開發人員需要更多細節以正確實現功能。 + +#### 示例代碼: + +```javascript +// 1. 找到需要澄清的任務 +const allTasks = await mcp.mcp_shrimp_task_manager.list_tasks(); +const taskIdMatch = allTasks.content[0].text.match( + /ID: `([^`]+)`.*名稱: "優化數據庫查詢"/ +); +const taskId = taskIdMatch ? taskIdMatch[1] : null; + +if (!taskId) { + console.error("找不到目標任務"); + return; +} + +// 2. 添加更詳細的技術說明 +await mcp.mcp_shrimp_task_manager.update_task({ + taskId, + description: + "優化產品列表和用戶資料頁面的數據庫查詢性能,目標是將頁面載入時間從當前的2.5秒降低到1秒以內。技術要求:\n1. 分析並優化現有SQL查詢\n2. 添加適當的索引\n3. 實現查詢結果緩存\n4. 考慮使用數據庫讀寫分離\n5. 測量並報告性能改進", + notes: + "性能瓶頸主要出現在產品過濾和排序操作上,特別是當產品數量超過1000個時。可考慮使用Redis緩存熱門查詢結果。", }); -// 反思構想 -await mcp.mcp_shrimp_task_manager.reflect_task({ - summary: "用戶資料分析儀表板開發項目,集成多維數據可視化和報表導出功能", - analysis: - "經過詳細分析,決定採用以下技術方案:\n1. 前端框架:React 18 + TypeScript\n2. 狀態管理:Redux Toolkit + RTK Query\n3. 視覺化庫:ECharts 5 (適合複雜數據視覺化)\n4. 後端API:Node.js + Express + MongoDB聚合查詢\n5. 數據處理:採用分層緩存策略,通過Redis緩存熱門查詢\n6. 報表導出:使用server-side生成PDF和CSV\n...", +// 3. 複查更新後的任務描述 +const updatedTask = await mcp.mcp_shrimp_task_manager.execute_task({ + taskId, }); -// 拆分任務 +console.log("已更新的任務詳情:", updatedTask); +``` + +## 6. 任務相關文件位置記錄功能案例 + +### 案例:記錄複雜重構任務的相關文件 + +在進行大型代碼重構時,需要記錄涉及的所有相關文件,以便更好地跟蹤和管理變更。 + +#### 示例代碼: + +```javascript +// 1. 建立重構任務 await mcp.mcp_shrimp_task_manager.split_tasks({ isOverwrite: false, tasks: [ { - name: "設計系統架構", + name: "重構認證系統", description: - "定義系統整體架構,包括前後端技術選型、數據流、API設計和部署架構", - notes: "需考慮系統擴展性和性能要求", - }, - { - name: "開發資料處理後端", - description: "實現資料處理引擎,支持大數據量查詢和聚合分析", - dependencies: ["設計系統架構"], - }, - { - name: "實現資料視覺化元件", - description: "開發可重用的視覺化圖表元件,支持多種數據展示形式", - dependencies: ["設計系統架構"], - }, - { - name: "搭建儀表板界面", - description: "根據UI設計實現儀表板界面,包括布局、過濾器和用戶交互", - dependencies: ["設計系統架構"], - }, - { - name: "整合資料和視覺化", - description: "連接後端API和前端視覺化元件,實現數據實時更新和交互", - dependencies: [ - "開發資料處理後端", - "實現資料視覺化元件", - "搭建儀表板界面", - ], - }, - { - name: "開發報表導出功能", - description: "實現多格式報表導出功能,支持PDF、CSV等格式", - dependencies: ["整合資料和視覺化"], - }, - { - name: "系統測試與優化", - description: - "進行系統整體測試,包括功能測試、性能測試和壓力測試,針對性優化", - dependencies: ["整合資料和視覺化", "開發報表導出功能"], + "將現有的基於Session的認證系統重構為JWT令牌認證,提高系統擴展性和安全性", + notes: "重構過程中需確保向後兼容,不影響現有用戶", }, ], }); -``` -#### 階段 2:任務執行與複雜度處理 - -```javascript -// 列出所有任務 -const tasks = await mcp.mcp_shrimp_task_manager.list_tasks(); -console.log(tasks); - -// 找出第一個待執行的任務 -const pendingTasks = tasks.content[0].text.match(/待處理.*?ID: `([^`]+)`/gs); -const firstTaskId = pendingTasks[0].match(/ID: `([^`]+)`/)[1]; - -// 執行架構設計任務 -const executeResult = await mcp.mcp_shrimp_task_manager.execute_task({ - taskId: firstTaskId, -}); - -// 檢查複雜度評估 -if (executeResult.content[0].text.includes("高複雜度")) { - console.log("架構設計是高複雜度任務,需要特別關注"); - - // 可以進一步拆分架構設計任務 - await mcp.mcp_shrimp_task_manager.split_tasks({ - isOverwrite: false, - tasks: [ - { - name: "前端架構設計", - description: "設計前端架構,包括組件結構、狀態管理和路由設計", - dependencies: [], - }, - { - name: "後端架構設計", - description: "設計後端架構,包括API結構、數據模型和緩存策略", - dependencies: [], - }, - { - name: "整合前後端架構", - description: "確保前後端架構協同工作,定義數據交換格式和API契約", - dependencies: ["前端架構設計", "後端架構設計"], - }, - ], - }); -} -``` - -#### 階段 3:完成任務與提供摘要 - -```javascript -// 驗證任務 -await mcp.mcp_shrimp_task_manager.verify_task({ - taskId: "architecture-task-uuid", -}); - -// 完成任務並提供詳細摘要 -await mcp.mcp_shrimp_task_manager.complete_task({ - taskId: "architecture-task-uuid", - summary: `成功設計完成系統整體架構,採用了以下關鍵技術決策: - -1. 採用微服務架構,將資料處理和視覺化渲染分離,提高系統靈活性 -2. 前端技術棧:React 18 + TypeScript + ECharts 5,組件採用原子設計模式 -3. 後端技術棧:Node.js + Express + MongoDB,採用資料聚合管道處理複雜查詢 -4. 快取策略:三層緩存(應用內存、Redis、持久化存儲),針對不同數據生命週期優化 -5. 擴展性設計:水平擴展的API服務,事件驅動的資料處理管道 - -解決的主要挑戰:(1)大數據量分析性能瓶頸;(2)多維度數據實時更新一致性;(3)跨設備體驗一致性 - -測試結果顯示架構可支持超過1000萬用戶記錄的快速分析,查詢響應時間控制在200ms以內,比目標性能指標提升了35%。`, -}); -``` - -#### 階段 4:查詢任務日誌和維護 - -```javascript -// 查詢任務執行日誌 -const taskLogs = await mcp.mcp_shrimp_task_manager.list_conversation_log({ - taskId: "architecture-task-uuid", -}); - -// 分析日誌,找出關鍵決策點 -const decisions = taskLogs.entries.filter( - (entry) => entry.summary.includes("決定") || entry.summary.includes("選擇") +// 2. 獲取新創建的任務ID +const taskList = await mcp.mcp_shrimp_task_manager.list_tasks(); +const taskIdMatch = taskList.content[0].text.match( + /ID: `([^`]+)`.*名稱: "重構認證系統"/ ); +const taskId = taskIdMatch ? taskIdMatch[1] : null; -console.log("架構設計過程中的關鍵決策:", decisions); +if (!taskId) { + console.error("找不到重構任務"); + return; +} -// 清理不必要的任務(假設有些子任務變得不必要) -await mcp.mcp_shrimp_task_manager.delete_task({ - taskId: "unnecessary-subtask-uuid", +// 3. 記錄所有相關文件 +await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId, + relatedFiles: [ + // 需要修改的核心文件 + { + path: "src/services/authService.js", + type: "待修改", + description: "認證服務核心邏輯,需將session邏輯替換為JWT", + lineStart: 24, + lineEnd: 156, + }, + { + path: "src/middleware/auth.js", + type: "待修改", + description: "認證中間件,需更新驗證邏輯", + lineStart: 5, + lineEnd: 42, + }, + { + path: "src/controllers/userController.js", + type: "待修改", + description: "用戶控制器,需更新登入和註銷邏輯", + lineStart: 78, + lineEnd: 142, + }, + + // 參考資料 + { + path: "docs/auth-system-design.md", + type: "參考資料", + description: "認證系統設計文檔,包含JWT切換的要求和規範", + }, + { + path: "package.json", + type: "參考資料", + description: "檢查已安裝的依賴,可能需要添加jsonwebtoken套件", + lineStart: 10, + lineEnd: 25, + }, + + // 依賴的組件 + { + path: "src/utils/crypto.js", + type: "依賴文件", + description: "加密工具,JWT簽名將使用此模塊", + lineStart: 15, + lineEnd: 35, + }, + + // 需要創建的新文件 + { + path: "src/config/jwt.js", + type: "輸出結果", + description: "新的JWT配置文件,需要創建", + }, + { + path: "src/utils/tokenManager.js", + type: "輸出結果", + description: "新的令牌管理工具,處理JWT的創建、驗證和刷新", + }, + ], }); -// 最終檢查任務狀態 -const finalTaskList = await mcp.mcp_shrimp_task_manager.list_tasks(); -console.log("當前任務狀態:", finalTaskList); +// 4. 查看更新後的任務詳情 +const taskWithFiles = await mcp.mcp_shrimp_task_manager.execute_task({ + taskId, +}); + +console.log("帶有相關文件信息的任務:", taskWithFiles); +``` + +### 案例:記錄 Bug 修復相關的代碼文件 + +在處理複雜 Bug 時,記錄相關文件位置以便快速定位問題。 + +#### 示例代碼: + +```javascript +// 1. 創建bug修復任務 +await mcp.mcp_shrimp_task_manager.split_tasks({ + isOverwrite: false, + tasks: [ + { + name: "修復購物車計算錯誤", + description: "修復在添加多個相同產品到購物車時總價計算錯誤的問題", + notes: + "此問題只在特定情況下出現:當用戶添加同一產品超過10個且應用了折扣優惠", + }, + ], +}); + +// 2. 獲取新創建的任務ID +const taskList = await mcp.mcp_shrimp_task_manager.list_tasks(); +const taskIdMatch = taskList.content[0].text.match( + /ID: `([^`]+)`.*名稱: "修復購物車計算錯誤"/ +); +const taskId = taskIdMatch ? taskIdMatch[1] : null; + +if (!taskId) { + console.error("找不到bug修復任務"); + return; +} + +// 3. 添加問題相關文件 +await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId, + relatedFiles: [ + // 包含錯誤代碼的文件 + { + path: "src/services/cartService.js", + type: "待修改", + description: "購物車服務,計算總價的邏輯有誤", + lineStart: 87, + lineEnd: 104, + }, + { + path: "src/utils/priceCalculator.js", + type: "待修改", + description: "價格計算工具,折扣邏輯實現有誤", + lineStart: 45, + lineEnd: 65, + }, + + // 測試用例 + { + path: "tests/cart.test.js", + type: "參考資料", + description: "現有測試用例,需要擴展以覆蓋發現的錯誤場景", + lineStart: 120, + lineEnd: 150, + }, + + // 錯誤報告 + { + path: "docs/bug-reports/cart-calculation-issue.md", + type: "參考資料", + description: "詳細的錯誤報告,包含用戶報告的具體場景和截圖", + }, + ], +}); + +// 4. 執行任務,自動加載相關文件 +await mcp.mcp_shrimp_task_manager.execute_task({ + taskId, +}); +``` + +## 7. 優化任務執行時的上下文記憶功能案例 + +### 案例:處理跨多個文件的複雜任務 + +實現一個需要理解和修改多個相關文件的功能,利用增強的上下文記憶功能。 + +#### 示例代碼: + +```javascript +// 1. 創建複雜任務 +await mcp.mcp_shrimp_task_manager.split_tasks({ + isOverwrite: false, + tasks: [ + { + name: "實現多租戶數據隔離", + description: + "在現有系統中實現多租戶數據隔離功能,確保不同租戶的數據完全隔離,同時共享應用代碼和基礎設施", + notes: "這是一項複雜的架構變更,需要修改多個核心組件", + }, + ], +}); + +// 2. 獲取新創建的任務ID +const taskList = await mcp.mcp_shrimp_task_manager.list_tasks(); +const taskIdMatch = taskList.content[0].text.match( + /ID: `([^`]+)`.*名稱: "實現多租戶數據隔離"/ +); +const taskId = taskIdMatch ? taskIdMatch[1] : null; + +if (!taskId) { + console.error("找不到多租戶任務"); + return; +} + +// 3. 關聯核心相關文件 +await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId, + relatedFiles: [ + // 數據庫連接和模型 + { + path: "src/config/database.js", + type: "待修改", + description: "數據庫配置,需改為支持多租戶連接池", + lineStart: 10, + lineEnd: 45, + }, + { + path: "src/models/baseModel.js", + type: "待修改", + description: "所有模型的基類,需添加租戶ID過濾", + lineStart: 5, + lineEnd: 50, + }, + + // 中間件和上下文 + { + path: "src/middleware/tenantContext.js", + type: "輸出結果", + description: "需要創建的新租戶上下文中間件", + }, + { + path: "src/utils/requestContext.js", + type: "待修改", + description: "請求上下文工具,需增加租戶信息傳遞", + lineStart: 15, + lineEnd: 40, + }, + + // 身份驗證相關 + { + path: "src/services/authService.js", + type: "待修改", + description: "認證服務,需在令牌中包含租戶信息", + lineStart: 50, + lineEnd: 120, + }, + ], +}); + +// 4. 執行任務,系統會自動加載相關文件和上下文 +const executionResult = await mcp.mcp_shrimp_task_manager.execute_task({ + taskId, +}); + +// 5. 在任務執行過程中,發現需要更多相關文件,動態添加 +await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId, + relatedFiles: [ + // 新發現的相關文件 + { + path: "src/services/userService.js", + type: "待修改", + description: "用戶服務也需更新以支持多租戶", + lineStart: 25, + lineEnd: 75, + }, + { + path: "docs/architecture/multi-tenant-design.md", + type: "參考資料", + description: "多租戶架構設計文檔,提供實現指導", + }, + ], +}); + +// 6. 繼續執行任務,系統會結合新添加的文件和之前的上下文 +await mcp.mcp_shrimp_task_manager.execute_task({ + taskId, +}); + +// 7. 任務完成後,記錄完整的實現摘要 +await mcp.mcp_shrimp_task_manager.complete_task({ + taskId, + summary: + "成功實現多租戶數據隔離功能,採用了基於中間件的動態租戶識別和數據過濾方案。主要更改包括:(1)實現了租戶上下文中間件,自動從請求中提取租戶標識;(2)增強了數據庫連接池,支持租戶專用連接;(3)修改了基礎模型類,所有查詢自動應用租戶過濾;(4)更新了認證服務,在JWT令牌中包含租戶信息;(5)實現了請求間租戶上下文的傳遞機制。所有修改均經過單元測試和集成測試驗證,確保數據完全隔離且性能影響最小化。", +}); +``` + +### 案例:長時間進行的開發任務 + +在需要多次中斷和恢復的長期開發任務中,利用上下文記憶功能保持連續性。 + +#### 示例代碼: + +```javascript +// 假設我們有一個已經進行了一段時間的長期任務 +const taskId = "existing-long-term-task-id"; + +// 1. 恢復到之前的工作狀態,系統會自動加載任務相關文件和執行歷史 +const taskContext = await mcp.mcp_shrimp_task_manager.execute_task({ + taskId, +}); + +console.log("恢復任務上下文:", taskContext); + +// 2. 記錄新的發現和決策 +const developmentLog = ` +開發日誌 - ${new Date().toISOString()} + +今天解決了用戶認證頁面的以下問題: +1. 修復了表單驗證錯誤信息不顯示的問題 +2. 優化了密碼強度檢查算法 +3. 決定使用漸進式登入延遲策略防止暴力破解 + +待解決問題: +- OAuth提供商回調處理邏輯複雜,需要重構 +- 移動端視圖適配問題 +`; + +console.log(developmentLog); + +// 3. 更新任務相關文件,記錄今天處理的內容 +await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId, + relatedFiles: [ + // 今天修改的文件 + { + path: "src/components/LoginForm.jsx", + type: "待修改", + description: "登入表單組件,已修復驗證錯誤顯示問題", + lineStart: 45, + lineEnd: 95, + }, + { + path: "src/utils/passwordStrength.js", + type: "待修改", + description: "密碼強度檢查工具,已優化算法", + lineStart: 10, + lineEnd: 50, + }, + { + path: "src/middleware/rateLimit.js", + type: "待修改", + description: "添加了漸進式登入延遲功能", + lineStart: 25, + lineEnd: 65, + }, + + // 明天需要處理的文件 + { + path: "src/services/oauthService.js", + type: "待修改", + description: "OAuth服務需要重構,明天處理", + lineStart: 80, + lineEnd: 180, + }, + { + path: "src/styles/mobile.css", + type: "待修改", + description: "需要改進移動端樣式適配", + lineStart: 120, + lineEnd: 200, + }, + ], +}); + +// 4. 更新任務注記,記錄進度和計劃 +await mcp.mcp_shrimp_task_manager.update_task({ + taskId, + notes: + "已完成認證頁面的基本功能和安全增強。下一步將重構OAuth服務並改進移動端適配。預計還需3天完成所有計劃工作。", +}); ``` ## 結論 diff --git a/docs/usage-guide.md b/docs/usage-guide.md index 1d7954d..fd88292 100644 --- a/docs/usage-guide.md +++ b/docs/usage-guide.md @@ -136,6 +136,258 @@ await mcp.mcp_shrimp_task_manager.complete_task({ 3. 確保摘要長度適中(約 250 字符以內) 4. 保留任務最核心的功能描述和目標 +### 4. 清除所有任務功能 + +#### 使用場景 + +- **項目重啟**:當項目需要重新規劃和組織時 +- **測試清理**:在測試系統後恢復到初始狀態 +- **系統重置**:在出現數據混亂或錯誤狀態時進行系統重置 + +#### 最佳實踐 + +1. **數據備份**:系統會自動在執行清除前創建數據備份,但建議在執行前手動備份重要數據 +2. **謹慎使用**:清除操作不可逆,請確保真的需要刪除所有未完成任務 +3. **團隊溝通**:在團隊環境中使用前,先與團隊成員溝通確認 +4. **清除後檢查**:清除後使用 `list_tasks` 確認結果 + +#### 範例使用流程 + +```javascript +// 1. 先查看當前任務列表,確認哪些任務將被刪除 +await mcp.mcp_shrimp_task_manager.list_tasks(); + +// 2. 執行清除操作(必須明確確認) +const clearResult = await mcp.mcp_shrimp_task_manager.clear_all_tasks({ + confirm: true, // 必須設為 true +}); + +// 3. 檢查清除結果 +console.log(clearResult); + +// 4. 確認剩餘任務狀態 +await mcp.mcp_shrimp_task_manager.list_tasks(); +``` + +#### 安全注意事項 + +- **明確確認**:必須設置 `confirm: true` 參數,以防止意外操作 +- **備份保留**:系統會自動創建備份,格式為 `tasks-backup-YYYYMMDD-HHMMSS.json` +- **日誌記錄**:所有清除操作都會記錄在日誌中,包括操作時間、被刪除的任務數量 +- **保留完成任務**:已完成的任務不會被刪除,確保項目歷史記錄完整性 + +#### 潛在風險 + +- **無法撤銷**:一旦執行,操作無法撤銷(除通過手動還原備份) +- **依賴關係丟失**:清除後,任務間的依賴關係將無法恢復 +- **上下文丟失**:與任務相關的上下文記憶可能會丟失 + +### 5. 更新任務功能 + +#### 使用場景 + +- **任務細節變更**:當任務要求或細節發生變化時 +- **補充任務資訊**:添加更多細節或說明以澄清任務目標 +- **調整任務範圍**:擴大或縮小任務範圍時修改描述 + +#### 最佳實踐 + +1. **保持簡潔明確**:更新時確保任務描述清晰,焦點明確 +2. **記錄變更原因**:在注記中添加變更的理由,便於後續追蹤 +3. **通知相關人員**:任務有重大變更時,確保相關人員了解 +4. **維護一致性**:確保更新後的任務與整體項目目標一致 + +#### 範例使用流程 + +```javascript +// 1. 查看任務當前狀態 +await mcp.mcp_shrimp_task_manager.list_tasks(); + +// 2. 更新任務內容 +const updateResult = await mcp.mcp_shrimp_task_manager.update_task({ + taskId: "task-uuid-here", + name: "更新後的任務名稱", + description: "更詳細的任務描述,包含新要求...", + notes: "更新原因:需求變更,添加了新的功能要求", +}); + +// 3. 確認更新結果 +console.log(updateResult); +``` + +#### 注意事項 + +- **已完成任務限制**:已完成的任務不能被更新,這確保了任務歷史記錄的穩定性 +- **至少一個參數**:更新時至少需要提供 name、description 或 notes 中的一個參數 +- **不改變核心屬性**:任務 ID 和完成狀態不能通過此功能更改 +- **歷史記錄**:系統會自動記錄所有更新操作,便於追蹤任務變更歷史 + +### 6. 任務相關文件位置記錄功能 + +#### 使用場景 + +- **代碼關聯跟蹤**:記錄任務涉及的代碼文件和位置 +- **文檔參考關聯**:將相關設計文檔、API 規範等與任務關聯 +- **提升上下文記憶**:幫助 LLM 在任務執行時快速加載相關上下文 +- **資源整理**:系統性整理與任務相關的所有資源 + +#### 最佳實踐 + +1. **分類關聯文件**:使用不同的關聯類型(待修改、參考資料等)清晰分類 +2. **精確定位代碼**:對代碼文件指定具體行號範圍,聚焦關鍵代碼區域 +3. **添加描述說明**:為每個文件添加簡明的描述,說明其與任務的關係 +4. **及時更新關聯**:當發現新的相關文件時,及時更新關聯關係 + +#### 文件關聯類型使用指南 + +| 類型 | 適用場景 | 建議 | +| -------- | -------------------------------- | ------------------------------------ | +| 待修改 | 需要在任務中修改的文件 | 指定具體行號範圍,描述需要修改的內容 | +| 參考資料 | 提供背景或指導的文檔 | 添加說明,指出需要關注的文檔關鍵部分 | +| 輸出結果 | 任務將創建或修改的目標文件 | 描述預期的輸出結果和質量標準 | +| 依賴文件 | 任務實現依賴的組件或庫 | 說明依賴關係和注意事項 | +| 其他 | 不屬於上述類別但與任務相關的文件 | 清楚說明關聯原因 | + +#### 範例使用流程 + +```javascript +// 添加或更新任務相關文件 +const filesResult = await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId: "task-uuid-here", + relatedFiles: [ + // 待修改的代碼文件,包含具體行號 + { + path: "src/services/authService.ts", + type: "待修改", + description: "需要增加多因素認證支持", + lineStart: 45, + lineEnd: 78, + }, + // 參考的設計文檔 + { + path: "docs/auth-design.md", + type: "參考資料", + description: "包含認證流程設計和安全要求", + }, + // 任務將要創建的新文件 + { + path: "src/components/TwoFactorAuth.tsx", + type: "輸出結果", + description: "需要創建的多因素認證組件", + }, + // 依賴的現有組件 + { + path: "src/components/InputCode.tsx", + type: "依賴文件", + description: "將複用的驗證碼輸入組件", + }, + ], +}); +``` + +#### 上下文記憶增強效果 + +通過關聯文件功能,系統可以: + +1. **自動載入關鍵上下文**:執行任務時自動加載相關文件內容 +2. **提供精確代碼定位**:直接定位到相關代碼,減少搜索時間 +3. **建立知識網絡**:將分散的相關資源連接成有機整體 +4. **提高執行效率**:減少上下文切換,提高任務連貫性 + +### 7. 優化任務執行時的上下文記憶功能 + +#### 使用場景 + +- **執行複雜任務**:當任務涉及多個文件和組件時 +- **延續之前工作**:在中斷後重新開始任務時恢復上下文 +- **團隊協作**:不同成員接手任務時快速了解相關上下文 +- **長期項目**:在跨越長時間的項目中保持上下文連貫性 + +#### 最佳實踐 + +1. **優先關聯核心文件**:識別並優先關聯最重要的文件 +2. **保持關聯更新**:隨著任務進展,及時更新相關文件列表 +3. **添加精確注釋**:在相關文件描述中提供具體指引 +4. **利用代碼區塊定位**:使用行號範圍準確定位相關代碼 +5. **建立樹狀依賴**:明確文件間的依賴關係,形成知識樹 + +#### 智能上下文加載功能說明 + +系統在執行任務時會智能處理文件內容,主要特點包括: + +1. **自動優先級排序**:根據文件類型和關聯程度排序 + + - "待修改"文件優先級最高 + - 直接依賴的文件次之 + - 參考資料根據相關性排序 + +2. **智能代碼提取**:對於大型文件,系統會: + + - 優先加載指定的行號範圍 + - 識別關鍵代碼區塊(如函數定義、類定義) + - 提取重要的註釋和文檔字符串 + +3. **上下文壓縮**:在上下文過大時,系統會: + + - 保留核心代碼,刪減非關鍵部分 + - 對大型文檔生成摘要 + - 優化格式,減少上下文標記使用 + +4. **執行歷史記憶**:自動包含: + - 之前執行的關鍵步驟 + - 重要決策點和選擇原因 + - 遇到的問題和解決方案 + +#### 使用範例 + +執行任務時,系統會自動應用上下文記憶功能: + +```javascript +// 執行任務(系統會自動加載相關文件和上下文) +const executeResult = await mcp.mcp_shrimp_task_manager.execute_task({ + taskId: "task-uuid-here", +}); + +// 隨著任務進展,更新相關文件以增強上下文記憶 +await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId: "task-uuid-here", + relatedFiles: [ + // 添加新發現的相關文件 + { + path: "src/utils/validation.ts", + type: "依賴文件", + description: "包含需要使用的表單驗證函數", + lineStart: 25, + lineEnd: 48, + }, + ], +}); + +// 繼續執行任務,利用更新後的上下文 +// 系統會結合新文件和之前的執行上下文 +``` + +#### 上下文記憶限制與解決方案 + +面對 LLM 上下文窗口限制,系統採用以下策略: + +1. **分層資訊呈現**: + + - 核心代碼完整呈現 + - 次要內容以摘要形式提供 + - 背景知識以參考鏈接形式提供 + +2. **動態上下文調整**: + + - 根據任務階段調整重點內容 + - 在複雜決策點呈現更多背景資訊 + - 在實施階段聚焦於實現細節 + +3. **記憶外部化**: + - 使用相關文件系統存儲上下文 + - 提供檢索機制以按需加載資訊 + - 定期總結並存儲重要進展 + ## 工作日誌功能使用指南 ### 查詢日誌 (`list_conversation_log`) @@ -250,7 +502,21 @@ await mcp.mcp_shrimp_task_manager.clear_conversation_log({ // 查看任務列表 await mcp.mcp_shrimp_task_manager.list_tasks(); - // 執行任務(系統會自動評估複雜度) + // 關聯任務相關文件,增強上下文記憶 + await mcp.mcp_shrimp_task_manager.update_task_files({ + taskId: "task-uuid-here", + relatedFiles: [ + { + path: "src/components/Auth.tsx", + type: "待修改", + description: "需要更新的認證組件", + lineStart: 35, + lineEnd: 67, + }, + ], + }); + + // 執行任務(系統會自動評估複雜度並加載相關文件) await mcp.mcp_shrimp_task_manager.execute_task({ taskId: "task-uuid-here", }); @@ -274,11 +540,22 @@ await mcp.mcp_shrimp_task_manager.clear_conversation_log({ 4. **維護與整理** ```javascript + // 更新任務內容 + await mcp.mcp_shrimp_task_manager.update_task({ + taskId: "task-uuid-here", + description: "更新後的任務描述...", + }); + // 刪除不需要的任務 await mcp.mcp_shrimp_task_manager.delete_task({ taskId: "task-uuid-here", }); + // 清除所有未完成任務(謹慎使用) + await mcp.mcp_shrimp_task_manager.clear_all_tasks({ + confirm: true, + }); + // 查詢任務日誌 await mcp.mcp_shrimp_task_manager.list_conversation_log({ taskId: "task-uuid-here", @@ -302,3 +579,11 @@ await mcp.mcp_shrimp_task_manager.clear_conversation_log({ ### Q: 日誌記錄佔用了太多空間怎麼辦? **A:** 定期使用 `list_conversation_log` 工具檢查日誌,提取關鍵信息並記錄到專門的文檔中,然後使用 `clear_conversation_log` 工具清理舊日誌。 + +### Q: 如何管理大型項目中的上下文記憶? + +**A:** 利用相關文件功能建立完整的文件關聯網絡,為每個任務標記最關鍵的相關文件,並使用精確的行號範圍定位核心代碼。定期更新文件關聯,反映項目的最新狀態。 + +### Q: 清除所有任務後,如何恢復誤刪的數據? + +**A:** 系統在清除操作前會自動創建備份文件,存放在 `data/backups` 目錄下。可以通過手動恢復這些備份文件來還原數據。建議定期檢查並整理備份目錄,確保重要數據安全。 diff --git a/src/index.ts b/src/index.ts index f0ceaf3..cf523d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; +import { RelatedFileType } from "./types/index.js"; // 導入工具函數 import { @@ -21,6 +22,12 @@ import { completeTaskSchema, deleteTask, deleteTaskSchema, + clearAllTasks, + clearAllTasksSchema, + updateTaskContent, + updateTaskContentSchema, + updateTaskRelatedFiles, + updateTaskRelatedFilesSchema, } from "./tools/taskTools.js"; // 導入日誌工具函數 @@ -213,6 +220,85 @@ async function main() { } ); + // 註冊清除所有任務工具 + server.tool( + "clear_all_tasks", + "刪除系統中所有未完成的任務,該指令必須由用戶明確確認才能執行", + { + confirm: z + .boolean() + .describe("確認刪除所有未完成的任務(此操作不可逆)"), + }, + async (args) => { + return await clearAllTasks(args); + } + ); + + // 註冊更新任務工具 + server.tool( + "update_task", + "更新任務內容,包括名稱、描述和注記,但不允許修改已完成的任務", + { + taskId: z + .string() + .describe("待更新任務的唯一標識符,必須是系統中存在且未完成的任務ID"), + name: z.string().optional().describe("任務的新名稱(選填)"), + description: z.string().optional().describe("任務的新描述內容(選填)"), + notes: z.string().optional().describe("任務的新補充說明(選填)"), + }, + async (args) => { + return await updateTaskContent(args); + } + ); + + // 註冊更新任務相關文件工具 + server.tool( + "update_task_files", + "更新任務相關文件列表,用於記錄與任務相關的代碼文件、參考資料等", + { + taskId: z + .string() + .describe("待更新任務的唯一標識符,必須是系統中存在且未完成的任務ID"), + relatedFiles: z + .array( + z.object({ + path: z + .string() + .describe("文件路徑,可以是相對於項目根目錄的路徑或絕對路徑"), + type: z + .enum([ + RelatedFileType.TO_MODIFY, + RelatedFileType.REFERENCE, + RelatedFileType.OUTPUT, + RelatedFileType.DEPENDENCY, + RelatedFileType.OTHER, + ]) + .describe("文件與任務的關係類型"), + description: z + .string() + .optional() + .describe("文件的補充描述(選填)"), + lineStart: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的起始行(選填)"), + lineEnd: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的結束行(選填)"), + }) + ) + .describe("與任務相關的文件列表"), + }, + async (args) => { + return await updateTaskRelatedFiles(args); + } + ); + // 註冊日誌查詢工具 server.tool( "list_conversation_log", diff --git a/src/models/taskModel.ts b/src/models/taskModel.ts index d9b3d7e..6afde29 100644 --- a/src/models/taskModel.ts +++ b/src/models/taskModel.ts @@ -5,6 +5,7 @@ import { TaskComplexityLevel, TaskComplexityThresholds, TaskComplexityAssessment, + RelatedFile, } from "../types/index.js"; import fs from "fs/promises"; import path from "path"; @@ -72,7 +73,8 @@ export async function createTask( name: string, description: string, notes?: string, - dependencies: string[] = [] + dependencies: string[] = [], + relatedFiles?: RelatedFile[] ): Promise { const tasks = await readTasks(); @@ -89,6 +91,7 @@ export async function createTask( dependencies: dependencyObjects, createdAt: new Date(), updatedAt: new Date(), + relatedFiles, }; tasks.push(newTask); @@ -109,6 +112,24 @@ export async function updateTask( return null; } + // 檢查任務是否已完成,已完成的任務不允許更新(除非是明確允許的欄位) + if (tasks[taskIndex].status === TaskStatus.COMPLETED) { + // 僅允許更新 summary 欄位(任務摘要) + const allowedFields = ["summary"]; + const attemptedFields = Object.keys(updates); + + const disallowedFields = attemptedFields.filter( + (field) => !allowedFields.includes(field) + ); + + if (disallowedFields.length > 0) { + console.warn( + `警告:嘗試更新已完成任務的非法欄位: ${disallowedFields.join(", ")}` + ); + return null; + } + } + tasks[taskIndex] = { ...tasks[taskIndex], ...updates, @@ -142,6 +163,97 @@ export async function updateTaskSummary( return await updateTask(taskId, { summary }); } +// 更新任務內容 +export async function updateTaskContent( + taskId: string, + updates: { + name?: string; + description?: string; + notes?: string; + relatedFiles?: RelatedFile[]; + } +): Promise<{ success: boolean; message: string; task?: Task }> { + // 獲取任務並檢查是否存在 + const task = await getTaskById(taskId); + + if (!task) { + return { success: false, message: "找不到指定任務" }; + } + + // 檢查任務是否已完成 + if (task.status === TaskStatus.COMPLETED) { + return { success: false, message: "無法更新已完成的任務" }; + } + + // 構建更新物件,只包含實際需要更新的欄位 + const updateObj: Partial = {}; + + if (updates.name !== undefined) { + updateObj.name = updates.name; + } + + if (updates.description !== undefined) { + updateObj.description = updates.description; + } + + if (updates.notes !== undefined) { + updateObj.notes = updates.notes; + } + + if (updates.relatedFiles !== undefined) { + updateObj.relatedFiles = updates.relatedFiles; + } + + // 如果沒有要更新的內容,提前返回 + if (Object.keys(updateObj).length === 0) { + return { success: true, message: "沒有提供需要更新的內容", task }; + } + + // 執行更新 + const updatedTask = await updateTask(taskId, updateObj); + + if (!updatedTask) { + return { success: false, message: "更新任務時發生錯誤" }; + } + + return { + success: true, + message: "任務內容已成功更新", + task: updatedTask, + }; +} + +// 更新任務相關文件 +export async function updateTaskRelatedFiles( + taskId: string, + relatedFiles: RelatedFile[] +): Promise<{ success: boolean; message: string; task?: Task }> { + // 獲取任務並檢查是否存在 + const task = await getTaskById(taskId); + + if (!task) { + return { success: false, message: "找不到指定任務" }; + } + + // 檢查任務是否已完成 + if (task.status === TaskStatus.COMPLETED) { + return { success: false, message: "無法更新已完成的任務" }; + } + + // 執行更新 + const updatedTask = await updateTask(taskId, { relatedFiles }); + + if (!updatedTask) { + return { success: false, message: "更新任務相關文件時發生錯誤" }; + } + + return { + success: true, + message: `已成功更新任務相關文件,共 ${relatedFiles.length} 個文件`, + task: updatedTask, + }; +} + // 批量創建或更新任務 export async function batchCreateOrUpdateTasks( taskDataList: Array<{ @@ -438,3 +550,51 @@ export async function assessTaskComplexity( recommendations, }; } + +// 清除所有任務 +export async function clearAllTasks(): Promise<{ + success: boolean; + message: string; + backupFile?: string; +}> { + try { + // 確保數據目錄存在 + await ensureDataDir(); + + // 讀取現有任務,用於創建備份 + const tasks = await readTasks(); + + // 如果沒有任務,直接返回 + if (tasks.length === 0) { + return { success: true, message: "沒有任務需要清除" }; + } + + // 創建備份文件名 + const timestamp = new Date() + .toISOString() + .replace(/:/g, "-") + .replace(/\..+/, ""); + const backupFileName = `tasks_backup_${timestamp}.json`; + const backupFilePath = path.join(DATA_DIR, backupFileName); + + // 創建備份 + await fs.writeFile(backupFilePath, JSON.stringify({ tasks }, null, 2)); + + // 清空任務文件 + await writeTasks([]); + + return { + success: true, + message: `已成功清除所有任務,共 ${tasks.length} 個任務被刪除`, + backupFile: backupFileName, + }; + } catch (error) { + console.error("清除所有任務時發生錯誤:", error); + return { + success: false, + message: `清除任務時發生錯誤: ${ + error instanceof Error ? error.message : String(error) + }`, + }; + } +} diff --git a/src/tools/taskTools.ts b/src/tools/taskTools.ts index ef5fe91..1b4c58d 100644 --- a/src/tools/taskTools.ts +++ b/src/tools/taskTools.ts @@ -8,17 +8,28 @@ import { deleteTask as modelDeleteTask, updateTaskSummary, assessTaskComplexity, + clearAllTasks as modelClearAllTasks, + updateTaskContent as modelUpdateTaskContent, + updateTaskRelatedFiles as modelUpdateTaskRelatedFiles, } from "../models/taskModel.js"; import { TaskStatus, ConversationParticipant, TaskComplexityLevel, + RelatedFileType, + RelatedFile, + Task, + TaskDependency, } from "../types/index.js"; -import { addConversationEntry } from "../models/conversationLogModel.js"; +import { + addConversationEntry, + getConversationEntriesByTaskId, +} from "../models/conversationLogModel.js"; import { extractSummary, generateTaskSummary, } from "../utils/summaryExtractor.js"; +import { loadTaskRelatedFiles } from "../utils/fileLoader.js"; // 開始規劃工具 export const planTaskSchema = z.object({ @@ -368,30 +379,7 @@ export async function listTasks() { result += `## ${status} (${tasksWithStatus.length})\n\n`; tasksWithStatus.forEach((task, index) => { - result += `### ${index + 1}. ${task.name}\n`; - result += `- **ID:** \`${task.id}\`\n`; - result += `- **描述:** ${task.description}\n`; - if (task.notes) { - result += `- **注意事項:** ${task.notes}\n`; - } - if (task.dependencies.length > 0) { - result += `- **依賴任務:** ${task.dependencies - .map((d) => `\`${d.taskId}\``) - .join(", ")}\n`; - } - - // 添加時間相關訊息 - result += `- **創建時間:** ${task.createdAt.toISOString()}\n`; - if (task.status === TaskStatus.COMPLETED && task.completedAt) { - result += `- **完成時間:** ${task.completedAt.toISOString()}\n`; - } - - // 顯示摘要(如果有) - if (task.summary && task.status === TaskStatus.COMPLETED) { - result += `- **摘要:** ${task.summary}\n`; - } - - result += "\n"; + result += formatTaskDetails(task); }); } } @@ -535,6 +523,176 @@ export async function executeTask({ task.notes ? `- **注意事項:** ${task.notes}\n` : "" }\n`; + // ===== 增強:處理相關文件內容 ===== + let relatedFilesContent = ""; + let relatedFilesSummary = ""; + let contextInfo = ""; + + // 查找之前執行過的相關日誌條目,增強上下文記憶 + try { + const taskLogs = await getConversationEntriesByTaskId(task.id); + if (taskLogs.length > 0) { + // 按時間排序,獲取最近的日誌(最多3條) + const recentLogs = [...taskLogs] + .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()) + .slice(0, 3); + + if (recentLogs.length > 0) { + contextInfo += `\n## 任務執行歷史摘要\n\n最近 ${recentLogs.length} 條操作記錄:\n\n`; + recentLogs.forEach((log, index) => { + const timestamp = new Date(log.timestamp) + .toISOString() + .replace(/T/, " ") + .replace(/\..+/, ""); + contextInfo += `${index + 1}. [${timestamp}] ${log.summary}\n`; + }); + + // 記錄日誌加載 + await addConversationEntry( + ConversationParticipant.MCP, + `已加載任務歷史記錄,共 ${recentLogs.length} 條`, + task.id, + "加載歷史記錄" + ); + } + } + } catch (error) { + console.error("加載任務歷史記錄時發生錯誤:", error); + } + + // 查找依賴任務的相關信息 + if (task.dependencies && task.dependencies.length > 0) { + try { + const allTasks = await getAllTasks(); + const depTasks = task.dependencies + .map((dep) => allTasks.find((t) => t.id === dep.taskId)) + .filter((t) => t !== undefined) as Task[]; + + if (depTasks.length > 0) { + const completedDepTasks = depTasks.filter( + (t) => t.status === TaskStatus.COMPLETED + ); + + if (completedDepTasks.length > 0) { + contextInfo += `\n## 依賴任務完成摘要\n\n`; + + for (const depTask of completedDepTasks) { + contextInfo += `### ${depTask.name}\n`; + if (depTask.summary) { + contextInfo += `${depTask.summary}\n\n`; + } else { + contextInfo += `*無完成摘要*\n\n`; + } + } + + // 記錄依賴任務加載 + await addConversationEntry( + ConversationParticipant.MCP, + `已加載依賴任務完成摘要,共 ${completedDepTasks.length} 個已完成依賴任務`, + task.id, + "加載依賴任務摘要" + ); + } + } + } catch (error) { + console.error("加載依賴任務信息時發生錯誤:", error); + } + } + + if (task.relatedFiles && task.relatedFiles.length > 0) { + try { + // 記錄加載文件操作 + await addConversationEntry( + ConversationParticipant.MCP, + `正在加載任務相關文件,共 ${task.relatedFiles.length} 個文件`, + task.id, + "加載相關文件" + ); + + // 加載相關文件內容,使用增強的智能提取功能 + const loadResult = await loadTaskRelatedFiles(task.relatedFiles); + relatedFilesContent = loadResult.content; + relatedFilesSummary = loadResult.summary; + + // 記錄加載完成 + await addConversationEntry( + ConversationParticipant.MCP, + `任務相關文件加載完成,成功加載 ${task.relatedFiles.length} 個文件,提取了最相關的代碼片段`, + task.id, + "相關文件加載完成" + ); + } catch (error) { + console.error("加載任務相關文件時發生錯誤:", error); + + // 記錄錯誤 + await addConversationEntry( + ConversationParticipant.MCP, + `加載任務相關文件時發生錯誤: ${ + error instanceof Error ? error.message : String(error) + }`, + task.id, + "相關文件加載錯誤" + ); + + relatedFilesSummary = + "## 相關文件內容加載失敗\n\n加載文件時發生錯誤,請手動查看相關文件。"; + } + } else { + // 沒有相關文件的情況 + relatedFilesSummary = + "## 相關文件\n\n當前任務沒有關聯的文件。可以使用 `update_task_files` 工具添加相關文件,以便在執行任務時提供上下文。"; + + // 嘗試自動發現相關文件 + try { + // 基於任務名稱和描述關鍵詞,嘗試推測可能相關的文件 + const taskWords = [ + ...task.name.split(/[\s,.;:]+/), + ...task.description.split(/[\s,.;:]+/), + ] + .filter((word) => word.length > 3) + .map((word) => word.toLowerCase()); + + // 從關鍵詞中提取可能的文件名或路徑片段 + const potentialFileKeywords = taskWords.filter( + (word) => + /^[a-z0-9]+$/i.test(word) && + ![ + "task", + "function", + "model", + "index", + "with", + "from", + "this", + ].includes(word.toLowerCase()) + ); + + if (potentialFileKeywords.length > 0) { + // 推薦自動關聯文件的提示 + relatedFilesSummary += `\n\n### 推薦操作\n基於任務描述,您可能需要查看以下相關文件:\n`; + + // 列出可能相關的文件類型或名稱 + potentialFileKeywords.slice(0, 5).forEach((keyword) => { + relatedFilesSummary += `- 含有 "${keyword}" 的文件\n`; + }); + + relatedFilesSummary += `\n使用 update_task_files 工具關聯相關文件,以獲得更好的上下文記憶支持。`; + + // 記錄文件推薦 + await addConversationEntry( + ConversationParticipant.MCP, + `為任務推薦了潛在相關文件關鍵詞: ${potentialFileKeywords + .slice(0, 5) + .join(", ")}`, + task.id, + "文件推薦" + ); + } + } catch (error) { + console.error("推薦相關文件時發生錯誤:", error); + } + } + // 新增:添加複雜度評估部分 if (complexityAssessment) { // 添加複雜度評估部分 @@ -573,15 +731,66 @@ export async function executeTask({ prompt += `\n`; } - prompt += `## 執行指引\n\n1. 請仔細分析任務要求,確保理解所有細節和約束條件 + // ===== 增強:添加相關文件部分,更詳細的信息 ===== + if (task.relatedFiles && task.relatedFiles.length > 0) { + prompt += `\n## 任務相關文件\n\n共關聯 ${task.relatedFiles.length} 個文件,類型分布:\n`; + + // 按類型分組統計 + const fileTypeCount: Record = {}; + task.relatedFiles.forEach((file) => { + fileTypeCount[file.type] = (fileTypeCount[file.type] || 0) + 1; + }); + + Object.entries(fileTypeCount).forEach(([type, count]) => { + prompt += `- ${type}: ${count} 個\n`; + }); + + // 新增:展示文件詳細列表 + prompt += `\n### 文件詳細列表\n`; + + // 按類型分組 + const filesByType = task.relatedFiles.reduce((acc, file) => { + acc[file.type] = acc[file.type] || []; + acc[file.type].push(file); + return acc; + }, {} as Record); + + // 展示每種類型的文件 + Object.entries(filesByType).forEach(([type, files]) => { + prompt += `\n#### ${type} (${files.length} 個)\n`; + files.forEach((file, index) => { + prompt += `${index + 1}. \`${file.path}\`${ + file.description ? ` - ${file.description}` : "" + }${ + file.lineStart && file.lineEnd + ? ` (行 ${file.lineStart}-${file.lineEnd})` + : "" + }\n`; + }); + }); + + prompt += `\n使用這些相關文件作為上下文,幫助您理解任務需求和實現細節。文件內容已經過智能提取,保留最相關的部分。\n`; + } + + // 添加上下文信息 + if (contextInfo) { + prompt += contextInfo; + } + + prompt += `\n## 執行指引\n\n1. 請仔細分析任務要求,確保理解所有細節和約束條件 2. 設計詳細的執行方案,包括具體步驟和技術選擇 3. 系統性地實施您的方案,遵循最佳實踐和項目慣例 4. 完整記錄您的實施過程,包括重要決策點和遇到的挑戰 +5. 實時更新任務相關文件,確保上下文記憶持續有效 ## 質量保證\n\n- 確保代碼符合專案編碼標準和架構設計 - 實施適當的錯誤處理和邊緣情況檢查 - 優化性能和資源使用 +## 上下文記憶管理\n\n- 在執行過程中,使用 update_task_files 工具更新重要的相關文件 +- 關注關鍵代碼片段,記錄代碼行號以便精確定位 +- 將重要的發現、決策和實現細節記錄為文件關聯和描述 + ## 下一步行動\n\n完成實施後,必須使用「verify_task」工具進行全面驗證,確保所有功能和要求均已正確實現。`; return { @@ -590,6 +799,10 @@ export async function executeTask({ type: "text" as const, text: prompt, }, + { + type: "text" as const, + text: relatedFilesContent, + }, ], }; } @@ -906,3 +1119,470 @@ export async function deleteTask({ taskId }: z.infer) { isError: !result.success, }; } + +// 清除所有任務工具 +export const clearAllTasksSchema = z.object({ + confirm: z.boolean().describe("確認刪除所有未完成的任務(此操作不可逆)"), +}); + +export async function clearAllTasks({ + confirm, +}: z.infer) { + // 安全檢查:如果沒有確認,則拒絕操作 + if (!confirm) { + return { + content: [ + { + type: "text" as const, + text: `## 操作取消\n\n未確認清除操作。如要清除所有任務,請將 confirm 參數設為 true。\n\n⚠️ 警告:此操作將刪除所有未完成的任務,且無法恢復。請謹慎操作。`, + }, + ], + }; + } + + // 檢查是否真的有任務需要清除 + const allTasks = await getAllTasks(); + if (allTasks.length === 0) { + // 記錄操作 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `清除所有任務操作:無任務需要清除`, + undefined, + "任務清除" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + return { + content: [ + { + type: "text" as const, + text: `## 操作提示\n\n系統中沒有任何任務需要清除。`, + }, + ], + }; + } + + // 記錄操作開始 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `開始清除所有任務操作:共 ${allTasks.length} 個任務`, + undefined, + "任務清除" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + // 執行清除操作 + const result = await modelClearAllTasks(); + + // 記錄操作結果 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `任務清除${result.success ? "成功" : "失敗"}:${result.message}${ + result.backupFile ? `,備份文件: ${result.backupFile}` : "" + }`, + undefined, + result.success ? "任務清除成功" : "任務清除失敗" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + return { + content: [ + { + type: "text" as const, + text: `## ${result.success ? "操作成功" : "操作失敗"}\n\n${ + result.message + }${ + result.backupFile + ? `\n\n系統已自動創建備份文件: \`${result.backupFile}\`` + : "" + }`, + }, + ], + isError: !result.success, + }; +} + +// 更新任務內容工具 +export const updateTaskContentSchema = z.object({ + taskId: z + .string() + .describe("待更新任務的唯一標識符,必須是系統中存在且未完成的任務ID"), + name: z.string().optional().describe("任務的新名稱(選填)"), + description: z.string().optional().describe("任務的新描述內容(選填)"), + notes: z.string().optional().describe("任務的新補充說明(選填)"), + relatedFiles: z + .array( + z.object({ + path: z + .string() + .describe("文件路徑,可以是相對於項目根目錄的路徑或絕對路徑"), + type: z + .enum([ + RelatedFileType.TO_MODIFY, + RelatedFileType.REFERENCE, + RelatedFileType.OUTPUT, + RelatedFileType.DEPENDENCY, + RelatedFileType.OTHER, + ]) + .describe("文件與任務的關係類型"), + description: z.string().optional().describe("文件的補充描述(選填)"), + lineStart: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的起始行(選填)"), + lineEnd: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的結束行(選填)"), + }) + ) + .optional() + .describe("與任務相關的文件列表(選填)"), +}); + +export async function updateTaskContent({ + taskId, + name, + description, + notes, + relatedFiles, +}: z.infer) { + // 獲取任務以檢查它是否存在 + const task = await getTaskById(taskId); + + if (!task) { + // 記錄錯誤日誌 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `更新任務失敗:找不到ID為 ${taskId} 的任務`, + undefined, + "錯誤" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + return { + content: [ + { + type: "text" as const, + text: `## 系統錯誤\n\n找不到ID為 \`${taskId}\` 的任務。請使用「list_tasks」工具確認有效的任務ID後再試。`, + }, + ], + isError: true, + }; + } + + // 記錄要更新的任務和內容 + let updateSummary = `準備更新任務:${task.name} (ID: ${task.id})`; + if (name) updateSummary += `,新名稱:${name}`; + if (description) updateSummary += `,更新描述`; + if (notes) updateSummary += `,更新注記`; + if (relatedFiles) + updateSummary += `,更新相關文件 (${relatedFiles.length} 個)`; + + try { + await addConversationEntry( + ConversationParticipant.MCP, + updateSummary, + task.id, + "任務更新" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + // 執行更新操作 + const result = await modelUpdateTaskContent(taskId, { + name, + description, + notes, + relatedFiles, + }); + + // 記錄更新結果 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `任務更新${result.success ? "成功" : "失敗"}:${task.name} (ID: ${ + task.id + }),原因:${result.message}`, + task.id, + result.success ? "任務更新成功" : "任務更新失敗" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + // 構建響應消息 + const responseTitle = result.success ? "操作成功" : "操作失敗"; + let responseMessage = result.message; + + if (result.success && result.task) { + // 顯示更新後的任務詳情 + responseMessage += "\n\n### 更新後的任務詳情\n"; + responseMessage += `- **名稱:** ${result.task.name}\n`; + responseMessage += `- **描述:** ${result.task.description.substring( + 0, + 100 + )}${result.task.description.length > 100 ? "..." : ""}\n`; + + if (result.task.notes) { + responseMessage += `- **注記:** ${result.task.notes.substring(0, 100)}${ + result.task.notes.length > 100 ? "..." : "" + }\n`; + } + + responseMessage += `- **狀態:** ${result.task.status}\n`; + responseMessage += `- **更新時間:** ${new Date( + result.task.updatedAt + ).toISOString()}\n`; + + // 顯示相關文件信息 + if (result.task.relatedFiles && result.task.relatedFiles.length > 0) { + responseMessage += `- **相關文件:** ${result.task.relatedFiles.length} 個\n`; + + // 按文件類型分組 + const filesByType = result.task.relatedFiles.reduce((acc, file) => { + if (!acc[file.type]) { + acc[file.type] = []; + } + acc[file.type].push(file); + return acc; + }, {} as Record); + + for (const [type, files] of Object.entries(filesByType)) { + responseMessage += ` - ${type} (${files.length} 個): `; + responseMessage += files.map((file) => `\`${file.path}\``).join(", "); + responseMessage += "\n"; + } + } + } + + return { + content: [ + { + type: "text" as const, + text: `## ${responseTitle}\n\n${responseMessage}`, + }, + ], + isError: !result.success, + }; +} + +// 更新任務相關文件工具 +export const updateTaskRelatedFilesSchema = z.object({ + taskId: z + .string() + .describe("待更新任務的唯一標識符,必須是系統中存在且未完成的任務ID"), + relatedFiles: z + .array( + z.object({ + path: z + .string() + .describe("文件路徑,可以是相對於項目根目錄的路徑或絕對路徑"), + type: z + .enum([ + RelatedFileType.TO_MODIFY, + RelatedFileType.REFERENCE, + RelatedFileType.OUTPUT, + RelatedFileType.DEPENDENCY, + RelatedFileType.OTHER, + ]) + .describe("文件與任務的關係類型"), + description: z.string().optional().describe("文件的補充描述(選填)"), + lineStart: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的起始行(選填)"), + lineEnd: z + .number() + .int() + .positive() + .optional() + .describe("相關代碼區塊的結束行(選填)"), + }) + ) + .describe("與任務相關的文件列表"), +}); + +export async function updateTaskRelatedFiles({ + taskId, + relatedFiles, +}: z.infer) { + // 獲取任務以檢查它是否存在 + const task = await getTaskById(taskId); + + if (!task) { + // 記錄錯誤日誌 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `更新任務相關文件失敗:找不到ID為 ${taskId} 的任務`, + undefined, + "錯誤" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + return { + content: [ + { + type: "text" as const, + text: `## 系統錯誤\n\n找不到ID為 \`${taskId}\` 的任務。請使用「list_tasks」工具確認有效的任務ID後再試。`, + }, + ], + isError: true, + }; + } + + // 記錄要更新的任務和相關文件 + const fileTypeCount = relatedFiles.reduce((acc, file) => { + acc[file.type] = (acc[file.type] || 0) + 1; + return acc; + }, {} as Record); + + const fileTypeSummary = Object.entries(fileTypeCount) + .map(([type, count]) => `${type} ${count} 個`) + .join(","); + + try { + await addConversationEntry( + ConversationParticipant.MCP, + `準備更新任務相關文件:${task.name} (ID: ${task.id}),共 ${relatedFiles.length} 個文件(${fileTypeSummary})`, + task.id, + "更新相關文件" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + // 執行更新操作 + const result = await modelUpdateTaskRelatedFiles(taskId, relatedFiles); + + // 記錄更新結果 + try { + await addConversationEntry( + ConversationParticipant.MCP, + `任務相關文件更新${result.success ? "成功" : "失敗"}:${task.name} (ID: ${ + task.id + }),${result.message}`, + task.id, + result.success ? "相關文件更新成功" : "相關文件更新失敗" + ); + } catch (error) { + console.error("記錄對話日誌時發生錯誤:", error); + } + + // 構建響應消息 + const responseTitle = result.success ? "操作成功" : "操作失敗"; + let responseMessage = result.message; + + if (result.success && result.task && result.task.relatedFiles) { + // 顯示更新後的相關文件列表 + responseMessage += "\n\n### 任務相關文件列表\n"; + + // 按文件類型分組顯示 + const filesByType = result.task.relatedFiles.reduce((acc, file) => { + acc[file.type] = acc[file.type] || []; + acc[file.type].push(file); + return acc; + }, {} as Record); + + for (const [type, files] of Object.entries(filesByType)) { + responseMessage += `\n#### ${type} (${files.length} 個)\n`; + files.forEach((file, index) => { + responseMessage += `${index + 1}. \`${file.path}\`${ + file.description ? ` - ${file.description}` : "" + }${ + file.lineStart && file.lineEnd + ? ` (行 ${file.lineStart}-${file.lineEnd})` + : "" + }\n`; + }); + } + } + + return { + content: [ + { + type: "text" as const, + text: `## ${responseTitle}\n\n${responseMessage}`, + }, + ], + isError: !result.success, + }; +} + +// 格式化單個任務的詳細資訊 +const formatTaskDetails = (task: Task) => { + let details = `### ${task.name}\n**ID:** \`${task.id}\`\n**描述:** ${task.description}\n`; + + if (task.notes) { + details += `**注記:** ${task.notes}\n`; + } + + details += `**狀態:** ${task.status}\n`; + + if (task.dependencies.length > 0) { + const depIds = task.dependencies + .map((dep: TaskDependency) => `\`${dep.taskId}\``) + .join(", "); + details += `**依賴任務:** ${depIds}\n`; + } + + // 添加相關文件信息 + if (task.relatedFiles && task.relatedFiles.length > 0) { + details += `**相關文件:** ${task.relatedFiles.length} 個\n`; + + // 按文件類型分組 + const filesByType = task.relatedFiles.reduce( + (acc: Record, file: RelatedFile) => { + if (!acc[file.type]) { + acc[file.type] = []; + } + acc[file.type].push(file); + return acc; + }, + {} as Record + ); + + for (const [type, files] of Object.entries(filesByType)) { + details += ` - ${type} (${files.length} 個): `; + details += files + .map((file: RelatedFile) => `\`${file.path}\``) + .join(", "); + details += "\n"; + } + } + + details += `**創建時間:** ${new Date(task.createdAt).toISOString()}\n`; + details += `**更新時間:** ${new Date(task.updatedAt).toISOString()}\n`; + + if (task.completedAt) { + details += `**完成時間:** ${new Date(task.completedAt).toISOString()}\n`; + } + + if (task.summary) { + details += `**完成摘要:** ${task.summary}\n`; + } + + return details; +}; diff --git a/src/types/index.ts b/src/types/index.ts index bfd3c65..eaad00c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -11,6 +11,24 @@ export interface TaskDependency { taskId: string; // 前置任務的唯一標識符,當前任務執行前必須完成此依賴任務 } +// 相關文件類型:定義文件與任務的關係類型 +export enum RelatedFileType { + TO_MODIFY = "待修改", // 需要在任務中修改的文件 + REFERENCE = "參考資料", // 任務的參考資料或相關文檔 + OUTPUT = "輸出結果", // 任務產生的輸出文件 + DEPENDENCY = "依賴文件", // 任務依賴的組件或庫文件 + OTHER = "其他", // 其他類型的相關文件 +} + +// 相關文件:定義任務相關的文件信息 +export interface RelatedFile { + path: string; // 文件路徑,可以是相對於項目根目錄的路徑或絕對路徑 + type: RelatedFileType; // 文件與任務的關係類型 + description?: string; // 文件的補充描述,說明其與任務的具體關係或用途 + lineStart?: number; // 相關代碼區塊的起始行(選填) + lineEnd?: number; // 相關代碼區塊的結束行(選填) +} + // 任務介面:定義任務的完整數據結構 export interface Task { id: string; // 任務的唯一標識符 @@ -23,6 +41,7 @@ export interface Task { updatedAt: Date; // 任務最後更新的時間戳 completedAt?: Date; // 任務完成的時間戳(僅適用於已完成的任務) summary?: string; // 任務完成摘要,簡潔描述實施結果和重要決策(僅適用於已完成的任務) + relatedFiles?: RelatedFile[]; // 與任務相關的文件列表(選填) } // 規劃任務的參數:用於初始化任務規劃階段 diff --git a/src/utils/fileLoader.ts b/src/utils/fileLoader.ts new file mode 100644 index 0000000..db31955 --- /dev/null +++ b/src/utils/fileLoader.ts @@ -0,0 +1,469 @@ +import fs from "fs/promises"; +import path from "path"; +import { fileURLToPath } from "url"; +import { RelatedFile, RelatedFileType } from "../types/index.js"; +import { extractSummary } from "./summaryExtractor.js"; + +// 確保獲取專案資料夾路徑 +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const PROJECT_ROOT = path.resolve(__dirname, "../.."); + +/** + * 讀取文件內容 + * @param filePath 文件路徑 + * @returns 文件內容 + */ +export async function readFileContent( + filePath: string +): Promise { + try { + // 如果是相對路徑,轉換為絕對路徑 + const absolutePath = path.isAbsolute(filePath) + ? filePath + : path.join(PROJECT_ROOT, filePath); + + // 檢查文件是否存在 + try { + await fs.access(absolutePath); + } catch (error) { + console.error(`文件 ${absolutePath} 不存在或無法訪問`); + return null; + } + + // 讀取文件內容 + const content = await fs.readFile(absolutePath, "utf-8"); + return content; + } catch (error) { + console.error(`讀取文件 ${filePath} 時發生錯誤:`, error); + return null; + } +} + +/** + * 讀取文件指定行範圍的內容 + * @param filePath 文件路徑 + * @param startLine 起始行(1-based) + * @param endLine 結束行(1-based) + * @returns 指定行範圍的內容 + */ +export async function readFileLines( + filePath: string, + startLine: number = 1, + endLine?: number +): Promise { + const content = await readFileContent(filePath); + + if (!content) { + return null; + } + + const lines = content.split("\n"); + + // 確保行號在有效範圍內 + const start = Math.max(1, startLine) - 1; // 轉為0-based + const end = endLine ? Math.min(lines.length, endLine) : lines.length; + + if (start >= lines.length || start >= end) { + return null; + } + + return lines.slice(start, end).join("\n"); +} + +/** + * 提取文件中的關鍵代碼段 + * @param content 文件內容 + * @param maxLength 最大長度限制 + * @returns 提取的關鍵代碼段 + */ +export function extractKeyCodeSegments( + content: string, + maxLength: number = 3000 +): string { + if (!content || content.length <= maxLength) { + return content; + } + + // 分割為代碼塊 + const lines = content.split("\n"); + + // 計算每行的權重 + const lineWeights = lines.map((line, index) => { + let weight = 1; + + // 空行或只有空白字符的行權重較低 + if (line.trim().length === 0) { + weight *= 0.2; + return { index, weight, line }; + } + + // 註釋行通常重要性較低,但類似文檔的註釋可能重要 + if ( + line.trim().startsWith("//") || + line.trim().startsWith("/*") || + line.trim().startsWith("*") + ) { + if ( + line.includes("@param") || + line.includes("@returns") || + line.includes("@description") + ) { + weight *= 1.5; // 文檔註釋可能更重要 + } else { + weight *= 0.5; // 普通註釋 + } + } + + // 函數或類定義通常很重要 + if ( + line.includes("function") || + line.includes("class ") || + line.includes("interface ") || + line.includes("type ") || + line.includes("enum ") || + line.includes("const ") || + line.includes("export ") + ) { + weight *= 2.0; + } + + // 導入語句也相對重要 + if (line.includes("import ")) { + weight *= 1.5; + } + + // 包含關鍵字的行可能更重要 + const keywords = [ + "async", + "await", + "return", + "if", + "else", + "switch", + "case", + "for", + "while", + "try", + "catch", + "throw", + ]; + keywords.forEach((keyword) => { + if (line.includes(keyword)) { + weight *= 1.2; + } + }); + + return { index, weight, line }; + }); + + // 按權重排序並選擇最重要的部分 + const sortedLines = [...lineWeights].sort((a, b) => b.weight - a.weight); + const selectedIndices = new Set(); + + // 選擇權重最高的行,但也考慮上下文,直到達到長度限制 + let totalLength = 0; + let currentIndex = 0; + + while (totalLength < maxLength && currentIndex < sortedLines.length) { + const { index, line } = sortedLines[currentIndex]; + + // 已選擇該行,跳過 + if (selectedIndices.has(index)) { + currentIndex++; + continue; + } + + // 加入該行 + selectedIndices.add(index); + totalLength += line.length + 1; // +1 為換行符 + + // 加入上下文(前後各一行) + if (index > 0 && !selectedIndices.has(index - 1)) { + selectedIndices.add(index - 1); + totalLength += lines[index - 1].length + 1; + } + + if (index < lines.length - 1 && !selectedIndices.has(index + 1)) { + selectedIndices.add(index + 1); + totalLength += lines[index + 1].length + 1; + } + + currentIndex++; + } + + // 按原始順序重新組合選中的行 + const sortedIndices = Array.from(selectedIndices).sort((a, b) => a - b); + let result = ""; + let lastIndex = -1; + + for (const index of sortedIndices) { + if (lastIndex !== -1 && index > lastIndex + 1) { + result += "\n// ... 省略部分代碼 ...\n"; + } + result += lines[index] + "\n"; + lastIndex = index; + } + + return result; +} + +/** + * 識別代碼中的關鍵區塊(如函數定義、類定義等) + * @param content 文件內容 + * @returns 關鍵代碼區塊的數組,每個元素包含開始行、結束行和重要性評分 + */ +export function identifyCodeBlocks( + content: string +): { start: number; end: number; importance: number; title: string }[] { + if (!content) return []; + + const lines = content.split("\n"); + const blocks: { + start: number; + end: number; + importance: number; + title: string; + }[] = []; + + // 正則表達式匹配常見的代碼塊開始模式 + const blockStartPatterns = [ + { + pattern: /^\s*(export\s+)?(async\s+)?function\s+(\w+)/, + type: "函數", + importance: 2.0, + }, + { pattern: /^\s*(export\s+)?class\s+(\w+)/, type: "類", importance: 2.0 }, + { + pattern: /^\s*(export\s+)?interface\s+(\w+)/, + type: "介面", + importance: 1.8, + }, + { pattern: /^\s*(export\s+)?type\s+(\w+)/, type: "類型", importance: 1.8 }, + { pattern: /^\s*(export\s+)?enum\s+(\w+)/, type: "枚舉", importance: 1.8 }, + { pattern: /^\s*(export\s+)?const\s+(\w+)/, type: "常量", importance: 1.5 }, + { pattern: /^\s*(\/\*\*|\*\s+@)/, type: "文檔", importance: 1.3 }, + ]; + + let inBlock = false; + let currentBlock: { + start: number; + end: number; + importance: number; + title: string; + } | null = null; + let bracketCount = 0; + + // 分析每一行 + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // 如果不在塊中,檢查是否是塊的開始 + if (!inBlock) { + for (const { pattern, type, importance } of blockStartPatterns) { + const match = line.match(pattern); + if (match) { + // 獲取識別碼名稱(如函數名、類名等) + const name = match[3] || match[2]; + currentBlock = { + start: i, + end: i, + importance, + title: name ? `${type}: ${name}` : type, + }; + inBlock = true; + bracketCount = + (line.match(/{/g) || []).length - (line.match(/}/g) || []).length; + + // 如果整個塊在一行內完成 + if (bracketCount === 0 && line.includes("{") && line.includes("}")) { + blocks.push(currentBlock); + inBlock = false; + currentBlock = null; + } + break; + } + } + } else { + // 已在代碼塊中,計算花括號計數 + bracketCount += (line.match(/{/g) || []).length; + bracketCount -= (line.match(/}/g) || []).length; + + // 如果括號平衡,塊結束 + if (bracketCount <= 0) { + if (currentBlock) { + currentBlock.end = i; + blocks.push(currentBlock); + } + inBlock = false; + currentBlock = null; + } + } + } + + return blocks; +} + +/** + * 智能提取文件內容,優先提取最相關的代碼塊 + * @param content 完整文件內容 + * @param maxLength 最大長度限制 + * @param extractComments 是否提取註釋塊 + * @returns 提取後的內容 + */ +export function smartExtractFileContent( + content: string, + maxLength: number = 3000, + extractComments: boolean = true +): string { + if (!content || content.length <= maxLength) { + return content; + } + + // 識別代碼塊 + const codeBlocks = identifyCodeBlocks(content); + + // 如果沒有識別到代碼塊,回退到原有方法 + if (codeBlocks.length === 0) { + return extractKeyCodeSegments(content, maxLength); + } + + // 按重要性排序代碼塊 + const sortedBlocks = [...codeBlocks].sort( + (a, b) => b.importance - a.importance + ); + + // 提取最重要的代碼塊,同時考慮長度限制 + const lines = content.split("\n"); + let result = ""; + let totalLength = 0; + + for (const block of sortedBlocks) { + // 計算此塊長度 + const blockContent = lines.slice(block.start, block.end + 1).join("\n"); + const blockLength = blockContent.length; + + // 如果加入此塊會超出長度限制 + if (totalLength + blockLength > maxLength) { + // 檢查是否可以提取部分內容 + if (totalLength < maxLength) { + const remainingLength = maxLength - totalLength; + const partialContent = extractKeyCodeSegments( + blockContent, + remainingLength + ); + result += `\n// === ${block.title} (部分內容) ===\n${partialContent}\n`; + } + break; + } + + // 加入完整塊 + result += `\n// === ${block.title} ===\n${blockContent}\n`; + totalLength += blockLength; + } + + return result.trim(); +} + +/** + * 加載任務相關文件的內容 + * @param relatedFiles 相關文件列表 + * @param maxTotalLength 所有文件內容的最大總長度 + * @returns 載入的文件內容 + */ +export async function loadTaskRelatedFiles( + relatedFiles: RelatedFile[], + maxTotalLength: number = 15000 // 增加默認上下文長度 +): Promise<{ content: string; summary: string }> { + if (!relatedFiles || relatedFiles.length === 0) { + return { + content: "", + summary: "無相關文件", + }; + } + + let totalContent = ""; + let filesSummary = `## 相關文件內容摘要 (共 ${relatedFiles.length} 個文件)\n\n`; + let totalLength = 0; + + // 按文件類型優先級排序(首先處理待修改的文件) + const priorityOrder: Record = { + [RelatedFileType.TO_MODIFY]: 1, + [RelatedFileType.REFERENCE]: 2, + [RelatedFileType.DEPENDENCY]: 3, + [RelatedFileType.OUTPUT]: 4, + [RelatedFileType.OTHER]: 5, + }; + + const sortedFiles = [...relatedFiles].sort( + (a, b) => priorityOrder[a.type] - priorityOrder[b.type] + ); + + // 處理每個文件 + for (const file of sortedFiles) { + if (totalLength >= maxTotalLength) { + filesSummary += `\n### 已達到上下文長度限制,部分文件未載入\n`; + break; + } + + let fileContent: string | null; + + // 如果指定了行範圍,只讀取指定行 + if (file.lineStart && file.lineEnd) { + fileContent = await readFileLines( + file.path, + file.lineStart, + file.lineEnd + ); + } else { + fileContent = await readFileContent(file.path); + } + + if (!fileContent) { + filesSummary += `\n### ${file.type}: ${file.path}\n無法讀取文件內容或文件不存在\n`; + continue; + } + + // 提取關鍵代碼段或摘要 + const maxLengthPerFile = Math.min( + 5000, // 增加每個文件允許的最大長度 + maxTotalLength / sortedFiles.length + ); + + // 根據文件類型選擇不同的提取策略 + let extractedContent = ""; + + // 對於代碼文件,使用智能提取;對於文本文件,使用傳統提取 + const isCodeFile = + /\.(js|ts|jsx|tsx|java|c|cpp|py|go|rb|php|cs|h|swift|kt)$/i.test( + file.path + ); + + if (isCodeFile) { + extractedContent = smartExtractFileContent(fileContent, maxLengthPerFile); + } else { + extractedContent = extractKeyCodeSegments(fileContent, maxLengthPerFile); + } + + // 添加到總內容 + const fileHeader = `\n### ${file.type}: ${file.path}${ + file.description ? ` - ${file.description}` : "" + }${ + file.lineStart && file.lineEnd + ? ` (行 ${file.lineStart}-${file.lineEnd})` + : "" + }\n\n`; + + totalContent += fileHeader + "```\n" + extractedContent + "\n```\n\n"; + filesSummary += `- **${file.path}**${ + file.description ? ` - ${file.description}` : "" + } (${extractedContent.length} 字符)\n`; + + totalLength += extractedContent.length + fileHeader.length + 8; // 8 for "```\n" and "\n```" + } + + return { + content: totalContent, + summary: filesSummary, + }; +}