diff --git a/.gitignore b/.gitignore index e90cfa6..16c08be 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ coverage/ *.sqlite data/*.json data/*/*.json -!data/example.json +data/* # Cursor AI 生成的暫存檔案 .cursor/tmp/ \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 777aa6c..bd8e461 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,6 @@ import "dotenv/config"; 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 { @@ -42,6 +40,12 @@ import { processThoughtSchema, } from "./tools/thoughtChainTools.js"; +// 導入專案工具 +import { + initProjectRules, + initProjectRulesSchema, +} from "./tools/projectTools.js"; + async function main() { try { console.log("啟動蝦米任務管理器服務..."); @@ -187,13 +191,23 @@ async function main() { // 註冊思維鏈工具 server.tool( "process_thought", - "你可以透過靈活的、可適應和發展的思考過程來分析問題,隨著理解的加深,每個想法都可以建立、質疑或修改先前的見解。你可以質疑想法、假設想法、驗證想法,並且可以建立新的想法。你將重複這個過程,直到你對問題有足夠的理解,並且能夠提出有效的解決方案。如果你覺得思考已經充分可以把 nextThoughtNeeded 設為 false 並且停止思考。", + "任何需要思考或分析的時候,透過該工具進行靈活的、可適應和發展的思考過程來分析問題,隨著理解的加深,每個想法都可以建立、質疑或修改先前的見解。你可以質疑想法、假設想法、驗證想法,並且可以建立新的想法。你將重複這個過程,直到你對問題有足夠的理解,並且能夠提出有效的解決方案。如果你覺得思考已經充分可以把 nextThoughtNeeded 設為 false 並且停止思考,如果你覺得需要更多的思考你可以隨時變更 total_thoughts 來增加步驟。", processThoughtSchema.shape, async (args) => { return await processThought(args); } ); + // 註冊初始化專案規範工具 + server.tool( + "init_project_rules", + "初始化專案規範,當用戶要求產生或初始化專案規範文件時呼叫該工具,如果用戶要求變更或更新專案規範也呼叫該工具", + initProjectRulesSchema.shape, + async () => { + return await initProjectRules(); + } + ); + // 建立連接 const transport = new StdioServerTransport(); await server.connect(transport); diff --git a/src/prompts/generators/initProjectRules.ts b/src/prompts/generators/initProjectRules.ts new file mode 100644 index 0000000..35b79af --- /dev/null +++ b/src/prompts/generators/initProjectRules.ts @@ -0,0 +1,32 @@ +/** + * initProjectRules prompt 生成器 + * 負責將模板和參數組合成最終的 prompt + */ + +import { loadPrompt, generatePrompt } from "../loader.js"; +import { initProjectRulesTemplate } from "../templates/initProjectRules.js"; +import { getRulesFilePath } from "../../utils/pathUtils.js"; +/** + * initProjectRules prompt 參數介面 + */ +export interface InitProjectRulesPromptParams { + // 目前沒有額外參數,未來可按需擴展 +} + +/** + * 獲取 initProjectRules 的完整 prompt + * @param params prompt 參數(可選) + * @returns 生成的 prompt + */ +export function getInitProjectRulesPrompt( + params?: InitProjectRulesPromptParams +): string { + // 使用基本模板 + const rulesPath = getRulesFilePath(); + const basePrompt = generatePrompt(initProjectRulesTemplate, { + rulesPath, + }); + + // 載入可能的自定義 prompt (通過環境變數覆蓋或追加) + return loadPrompt(basePrompt, "INIT_PROJECT_RULES"); +} diff --git a/src/prompts/generators/planTask.ts b/src/prompts/generators/planTask.ts index 5a48205..4f433c9 100644 --- a/src/prompts/generators/planTask.ts +++ b/src/prompts/generators/planTask.ts @@ -5,7 +5,8 @@ import { loadPrompt, generatePrompt } from "../loader.js"; import * as templates from "../templates/planTask.js"; -import { TaskStatus, Task, TaskDependency } from "../../types/index.js"; +import { getRulesFilePath } from "../../utils/pathUtils.js"; +import { Task, TaskDependency } from "../../types/index.js"; /** * planTask prompt 參數介面 @@ -178,8 +179,11 @@ export function getPlanTaskPrompt(params: PlanTaskPromptParams): string { basePrompt += templates.infoCollectionGuideItems2; basePrompt += templates.infoCollectionGuideItems3; + const rulesPath = getRulesFilePath(); // 添加下一步指導 - basePrompt += templates.nextStepsTemplate; + basePrompt += generatePrompt(templates.nextStepsTemplate, { + rulesPath, + }); basePrompt += templates.nextStepsContent1; basePrompt += templates.nextStepsContent1Detail; diff --git a/src/prompts/index.ts b/src/prompts/index.ts index 5a31582..7d515ae 100644 --- a/src/prompts/index.ts +++ b/src/prompts/index.ts @@ -18,4 +18,8 @@ export { getCompleteTaskPrompt } from "./generators/completeTask.js"; export { getListTasksPrompt } from "./generators/listTasks.js"; export { getQueryTaskPrompt } from "./generators/queryTask.js"; export { getGetTaskDetailPrompt } from "./generators/getTaskDetail.js"; +export { getInitProjectRulesPrompt } from "./generators/initProjectRules.js"; // 等等 + +// 導入初始化專案規範模板 +export { initProjectRulesTemplate } from "./templates/initProjectRules.js"; diff --git a/src/prompts/templates/initProjectRules.ts b/src/prompts/templates/initProjectRules.ts new file mode 100644 index 0000000..4530157 --- /dev/null +++ b/src/prompts/templates/initProjectRules.ts @@ -0,0 +1,75 @@ +/** + * 初始化專案規範 prompt 模板 + * 包含指導如何產生專案規範文件的提示詞 + */ + +// 基本提示詞模板 +export const initProjectRulesTemplate = `請用 「process_thought」 工具思考以下問題 + +# 專案規範初始化指南 + +## 目的 + +**此文件專為 AI Agent 設計,非一般開發者文檔。** +**必須生成一個專屬於 AI Agent 操作使用的專案規範文件(rules.md)。** + +**必須專注於以下關鍵目標:** +- 明確專案特定規則與限制,禁止包含通用開發知識 +- 提供 AI 執行任務時所需的專案特定資訊 +- 為 AI 決策過程提供明確指導 + +**強制規定:** +- 完成的規範必須使 AI Agent 能立即理解哪些檔案必須參考或修改 +- 明確指示多檔案連動修改要求(例如修改 README.md 時必須同步修改 /docs/zh/README.md) +- 使用命令式語言定義規則,避免解釋性內容 +- 不要進行專案的功能解釋,而是如何修改功能或增加功能 +- 請提供範例什麼事可以做的,什麼事不可以做的 + +**嚴重禁止:** +- 禁止包含通用開發知識 +- 禁止包含 LLM 已知的通用開發知識 +- 進行專案功能解釋 + +## 建議結構 + +請使用以下結構建立規範文件: + +\`\`\`markdown +# 開發守則 +## 標題 +### 副標題 +- 規則一 +- 規則二 +\`\`\` + +## 內容指南 + +規範文件應包含但不限於以下內容: + +1. **專案概述** - 簡要描述專案的目的、技術棧和核心功能 +2. **專案架構** - 說明主要目錄結構和模塊劃分 +3. **代碼規範** - 包括命名規範、格式要求、註釋規則等 +4. **功能實作規範** - 主要解釋如何實作功能及應該注意事項 +5. **框架/插件/第三方庫使用規範** - 外部依賴的使用規範 +6. **工作流程規範** - 工作流程指南,包含工作流程圖或資料流 +7. **關鍵檔案交互規範** - 關鍵檔案的交互規範,修改哪些檔案需要同步修改 +8. **AI 決策規範** - 提供處理模糊情況的決策樹和優先級判斷標準 +9. **禁止事項** - 明確列出哪些做法是禁止的 + +## 注意事項 + +1. **面向 AI 優化** - 文件將作為 prompt 提供給 Coding Agent AI,應對 prompt 最佳化 +2. **專注於開發指導** - 提供持續開發的規則,而非使用教學 +3. **具體示例** - 盡可能提供「應該做什麼」和「不應該做什麼」的具體示例 +4. **使用命令式語言** - 必須使用直接指令而非描述性語言,減少解釋內容 +5. **結構化呈現** - 所有內容必須以列表、表格等結構化形式呈現,便於 AI 解析 +6. **突出重點標記** - 使用粗體、警告標記等突出關鍵規則和禁忌 +7. **移除通用知識** - 禁止包含 LLM 已知的通用開發知識,僅包含專案特定規則 + +請根據以上指南,創建一個名為 rules.md 的文件並存放於: {rulesPath} + +**現在開始呼叫 「process_thought」 工具思考如何撰寫出教導 Coding Agent 規範文件** +**思考完畢後請立即編輯 rules.md 文件,禁止呼叫「analyze_task」工具** +**如果檔案已經存在或用戶要求更新,請思考規範是否已經過時,是否需要補充更新** +**如果是更新模式,除非必要否則你應該保持現有的規範,以最小變更為原則的修改** +`; diff --git a/src/prompts/templates/planTask.ts b/src/prompts/templates/planTask.ts index 80a4768..ff9517e 100644 --- a/src/prompts/templates/planTask.ts +++ b/src/prompts/templates/planTask.ts @@ -87,7 +87,7 @@ export const infoCollectionGuideItems2 = `2. **查詢記憶** - 使用「query_t export const infoCollectionGuideItems3 = `3. **網路搜索** - 當出現你不理解的名詞或概念時,使用網路搜尋工具找尋答案\n\n`; // 下一步模板 -export const nextStepsTemplate = `## 下一步\n\n`; +export const nextStepsTemplate = `## 下一步\n\n⚠️ 重要:請先閱讀 {rulesPath} 規則再進行任何分析或設計 ⚠️\n\n`; export const nextStepsContent1 = `**第一步:根據任務描述決定是否查詢記憶**\n`; export const nextStepsContent1Detail = `- 判斷任務是否屬於必查情境,若是,請先使用「query_task」查詢過往記錄;否則,可直接進行分析。\n\n`; export const nextStepsContent2 = `**第二步:使用 analyze_task 提交分析結果**\n`; diff --git a/src/tools/projectTools.ts b/src/tools/projectTools.ts new file mode 100644 index 0000000..06e9942 --- /dev/null +++ b/src/tools/projectTools.ts @@ -0,0 +1,44 @@ +import { z } from "zod"; +import { getInitProjectRulesPrompt } from "../prompts/index.js"; +import { getRulesFilePath, ensureRulesFileExists } from "../utils/pathUtils.js"; + +// 定義schema +export const initProjectRulesSchema = z.object({}); + +/** + * 初始化專案規範工具函數 + * 提供建立規範文件的指導 + */ +export async function initProjectRules() { + try { + // 從生成器獲取提示詞 + const promptContent = getInitProjectRulesPrompt(); + + // 確保 DATA_DIR 目錄中存在 rules.md 文件 + await ensureRulesFileExists(); + + // 輸出規則文件的路徑,幫助用戶找到文件 + const rulesPath = getRulesFilePath(); + + // 返回成功響應 + return { + content: [ + { + type: "text" as const, + text: promptContent + `\n\n規則文件將位於: ${rulesPath}`, + }, + ], + }; + } catch (error) { + // 錯誤處理 + const errorMessage = error instanceof Error ? error.message : "未知錯誤"; + return { + content: [ + { + type: "text" as const, + text: `初始化專案規範時發生錯誤: ${errorMessage}`, + }, + ], + }; + } +} diff --git a/src/tools/thoughtChainTools.ts b/src/tools/thoughtChainTools.ts index 41bb292..668ae19 100644 --- a/src/tools/thoughtChainTools.ts +++ b/src/tools/thoughtChainTools.ts @@ -75,7 +75,7 @@ export const processThoughtSchema = z.object({ .positive({ message: "總思維數必須是正整數", }) - .describe("預計總思維數量"), + .describe("預計總思維數量,如果需要更多的思考可以隨時變更"), next_thought_needed: z.boolean().describe("是否需要下一步思維"), stage: z .string() diff --git a/src/utils/pathUtils.ts b/src/utils/pathUtils.ts new file mode 100644 index 0000000..95fd525 --- /dev/null +++ b/src/utils/pathUtils.ts @@ -0,0 +1,41 @@ +import fs from "fs/promises"; +import path from "path"; +import { fileURLToPath } from "url"; + +// 獲取項目根目錄路徑 +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const PROJECT_ROOT = path.resolve(__dirname, "../.."); + +// 獲取數據目錄路徑 +const DATA_DIR = process.env.DATA_DIR || path.join(PROJECT_ROOT, "data"); + +/** + * 獲取規則文件路徑 + * @returns 規則文件的完整路徑 + */ +export function getRulesFilePath(): string { + return path.join(DATA_DIR, "rules.md"); +} + +/** + * 確保規則文件存在 + * 如果文件不存在,會嘗試從根目錄複製,或創建空文件 + */ +export async function ensureRulesFileExists(): Promise { + const dataRulesPath = getRulesFilePath(); + + try { + // 檢查 DATA_DIR 目錄中是否存在規則文件 + await fs.access(dataRulesPath); + } catch (error) { + // DATA_DIR 目錄中不存在規則文件 + await fs.mkdir(path.dirname(dataRulesPath), { recursive: true }); + await fs.writeFile( + dataRulesPath, + "# 開發守則\n\n請在此文件中定義專案規範。", + "utf-8" + ); + console.log(`已在 ${dataRulesPath} 創建空規則文件`); + } +}