新增網頁圖形介面功能,透過環境變數 ENABLE_GUI 控制

This commit is contained in:
siage 2025-04-29 19:40:20 +08:00
parent e31b87327e
commit bf5f3678ba
11 changed files with 2277 additions and 13 deletions

View File

@ -19,6 +19,12 @@ ENABLE_THOUGHT_CHAIN=true
# (e.g., to 'my_templates'), and set TEMPLATES_USE to the new directory name (e.g., 'my_templates'). # (e.g., to 'my_templates'), and set TEMPLATES_USE to the new directory name (e.g., 'my_templates').
TEMPLATES_USE=en TEMPLATES_USE=en
# Enable GUI interface
# If set to true, a file named 'WebGUI.md' will be created in the DATA_DIR.
# This file will contain a local web address you can open in your browser to access the GUI.
# Default is false.
ENABLE_GUI=false
# ============================ # ============================
# Prompt Customization # Prompt Customization
# ============================ # ============================

View File

@ -7,6 +7,7 @@
- [🧠 Task Memory Function](#task-memory-function) - [🧠 Task Memory Function](#task-memory-function)
- [🤔 Thought Chain Process](#thought-chain) - [🤔 Thought Chain Process](#thought-chain)
- [📋 Project Rules Initialization](#project-rules) - [📋 Project Rules Initialization](#project-rules)
- [🌐 Web GUI](#web-gui)
- [📚 Documentation Resources](#documentation) - [📚 Documentation Resources](#documentation)
- [🔧 Installation and Usage](#installation) - [🔧 Installation and Usage](#installation)
- [🔌 Using with MCP-Compatible Clients](#clients) - [🔌 Using with MCP-Compatible Clients](#clients)
@ -39,6 +40,7 @@ Shrimp Task Manager guides Agents through structured workflows for systematic pr
- **Task Memory Function**: Automatically backup task history, providing long-term memory and reference capabilities - **Task Memory Function**: Automatically backup task history, providing long-term memory and reference capabilities
- **Thought Chain Process**: Step-by-step reasoning to analyze complex problems systematically - **Thought Chain Process**: Step-by-step reasoning to analyze complex problems systematically
- **Project Rules Initialization**: Define project standards and rules to maintain consistency across large projects - **Project Rules Initialization**: Define project standards and rules to maintain consistency across large projects
- **<a id="web-gui"></a>Web GUI**: Provides an optional web-based graphical user interface for task management. Enable by setting `ENABLE_GUI=true` in your `.env` file. When enabled, a `WebGUI.md` file containing the access address will be created in your `DATA_DIR`.
## 🧭 <a id="usage-guide"></a>Usage Guide ## 🧭 <a id="usage-guide"></a>Usage Guide
@ -178,7 +180,10 @@ Shrimp Task Manager offers two configuration methods: global configuration and p
"command": "node", "command": "node",
"args": ["/mcp-shrimp-task-manager/dist/index.js"], "args": ["/mcp-shrimp-task-manager/dist/index.js"],
"env": { "env": {
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑 "DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -193,7 +198,10 @@ or
"command": "npx", "command": "npx",
"args": ["-y", "mcp-shrimp-task-manager"], "args": ["-y", "mcp-shrimp-task-manager"],
"env": { "env": {
"DATA_DIR": "/mcp-shrimp-task-manager/data" "DATA_DIR": "/mcp-shrimp-task-manager/data",
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -216,7 +224,10 @@ You can also set up dedicated configurations for each project to use independent
"command": "node", "command": "node",
"args": ["/path/to/mcp-shrimp-task-manager/dist/index.js"], "args": ["/path/to/mcp-shrimp-task-manager/dist/index.js"],
"env": { "env": {
"DATA_DIR": "/path/to/project/data" // Must use absolute path "DATA_DIR": "/path/to/project/data", // Must use absolute path
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -231,7 +242,10 @@ or
"command": "npx", "command": "npx",
"args": ["-y", "mcp-shrimp-task-manager"], "args": ["-y", "mcp-shrimp-task-manager"],
"env": { "env": {
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑 "DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -264,7 +278,8 @@ Shrimp Task Manager supports customizing prompt behavior through environment var
"MCP_PROMPT_PLAN_TASK": "Custom planning guidance...", "MCP_PROMPT_PLAN_TASK": "Custom planning guidance...",
"MCP_PROMPT_EXECUTE_TASK_APPEND": "Additional execution instructions...", "MCP_PROMPT_EXECUTE_TASK_APPEND": "Additional execution instructions...",
"ENABLE_THOUGHT_CHAIN": "true", "ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en" "TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }

View File

@ -7,6 +7,7 @@
- [🧠 任務記憶功能](#任務記憶功能) - [🧠 任務記憶功能](#任務記憶功能)
- [🤔 思維鏈過程](#思維鏈過程) - [🤔 思維鏈過程](#思維鏈過程)
- [📋 專案規範初始化](#專案規範初始化) - [📋 專案規範初始化](#專案規範初始化)
- [🌐 網頁圖形介面](#網頁圖形介面)
- [📚 文件資源](#文件資源) - [📚 文件資源](#文件資源)
- [🔧 安裝與使用](#安裝與使用) - [🔧 安裝與使用](#安裝與使用)
- [🔌 在支援 MCP 的客戶端中使用](#客戶端中使用) - [🔌 在支援 MCP 的客戶端中使用](#客戶端中使用)
@ -35,6 +36,7 @@
- **任務記憶功能**:自動備份任務歷史記錄,提供長期記憶和參考能力 - **任務記憶功能**:自動備份任務歷史記錄,提供長期記憶和參考能力
- **思維鏈過程**:通過步驟化的推理系統性地分析複雜問題 - **思維鏈過程**:通過步驟化的推理系統性地分析複雜問題
- **專案規範初始化**:定義專案標準和規則,維持大型專案的一致性 - **專案規範初始化**:定義專案標準和規則,維持大型專案的一致性
- **<a id="網頁圖形介面"></a>網頁圖形介面**:提供選用的網頁圖形化使用者介面來管理任務。透過在您的 `.env` 檔案中設定 `ENABLE_GUI=true` 來啟用。啟用後,將會在您的 `DATA_DIR` 中建立一個包含存取網址的 `WebGUI.md` 檔案。
## 🧭 <a id="使用指南"></a>使用指南 ## 🧭 <a id="使用指南"></a>使用指南
@ -174,7 +176,10 @@ npm run build
"command": "node", "command": "node",
"args": ["/mcp-shrimp-task-manager/dist/index.js"], "args": ["/mcp-shrimp-task-manager/dist/index.js"],
"env": { "env": {
"DATA_DIR": "/mcp-shrimp-task-manager/data" "DATA_DIR": "/mcp-shrimp-task-manager/data",
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -188,7 +193,10 @@ or
"command": "npx", "command": "npx",
"args": ["-y", "mcp-shrimp-task-manager"], "args": ["-y", "mcp-shrimp-task-manager"],
"env": { "env": {
"DATA_DIR": "/mcp-shrimp-task-manager/data" "DATA_DIR": "/mcp-shrimp-task-manager/data",
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -211,7 +219,10 @@ or
"command": "node", "command": "node",
"args": ["/path/to/mcp-shrimp-task-manager/dist/index.js"], "args": ["/path/to/mcp-shrimp-task-manager/dist/index.js"],
"env": { "env": {
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑 "DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -225,7 +236,10 @@ or
"command": "npx", "command": "npx",
"args": ["-y", "mcp-shrimp-task-manager"], "args": ["-y", "mcp-shrimp-task-manager"],
"env": { "env": {
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑 "DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
"ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }
@ -258,7 +272,8 @@ or
"MCP_PROMPT_PLAN_TASK": "自定義規劃指導...", "MCP_PROMPT_PLAN_TASK": "自定義規劃指導...",
"MCP_PROMPT_EXECUTE_TASK_APPEND": "附加執行說明...", "MCP_PROMPT_EXECUTE_TASK_APPEND": "附加執行說明...",
"ENABLE_THOUGHT_CHAIN": "true", "ENABLE_THOUGHT_CHAIN": "true",
"TEMPLATES_USE": "en" "TEMPLATES_USE": "en",
"ENABLE_GUI": "false"
} }
} }
} }

197
package-lock.json generated
View File

@ -1,16 +1,18 @@
{ {
"name": "mcp-shrimp-task-manager", "name": "mcp-shrimp-task-manager",
"version": "1.0.10", "version": "1.0.11",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mcp-shrimp-task-manager", "name": "mcp-shrimp-task-manager",
"version": "1.0.10", "version": "1.0.11",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0", "@modelcontextprotocol/sdk": "^1.0.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express": "^5.1.0",
"get-port": "^7.1.0",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"zod": "^3.22.4", "zod": "^3.22.4",
"zod-to-json-schema": "^3.24.5" "zod-to-json-schema": "^3.24.5"
@ -19,6 +21,7 @@
"mcp-shrimp-task-manager": "dist/index.js" "mcp-shrimp-task-manager": "dist/index.js"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.1",
"@types/node": "^20.8.2", "@types/node": "^20.8.2",
"@types/uuid": "^9.0.6", "@types/uuid": "^9.0.6",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
@ -107,6 +110,60 @@
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true "dev": true
}, },
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"dev": true
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.17.30", "version": "20.17.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
@ -116,6 +173,39 @@
"undici-types": "~6.19.2" "undici-types": "~6.19.2"
} }
}, },
"node_modules/@types/qs": {
"version": "6.9.18",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
"dev": true
},
"node_modules/@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"dev": true
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dev": true,
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"node_modules/@types/serve-static": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
"dev": true,
"dependencies": {
"@types/http-errors": "*",
"@types/node": "*",
"@types/send": "*"
}
},
"node_modules/@types/uuid": { "node_modules/@types/uuid": {
"version": "9.0.8", "version": "9.0.8",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
@ -677,6 +767,17 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/get-port": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz",
"integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-proto": { "node_modules/get-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@ -1664,6 +1765,60 @@
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true "dev": true
}, },
"@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"dev": true
},
"@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true
},
"@types/node": { "@types/node": {
"version": "20.17.30", "version": "20.17.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
@ -1673,6 +1828,39 @@
"undici-types": "~6.19.2" "undici-types": "~6.19.2"
} }
}, },
"@types/qs": {
"version": "6.9.18",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
"dev": true
},
"@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"dev": true
},
"@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dev": true,
"requires": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"@types/serve-static": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
"dev": true,
"requires": {
"@types/http-errors": "*",
"@types/node": "*",
"@types/send": "*"
}
},
"@types/uuid": { "@types/uuid": {
"version": "9.0.8", "version": "9.0.8",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
@ -2074,6 +2262,11 @@
"math-intrinsics": "^1.1.0" "math-intrinsics": "^1.1.0"
} }
}, },
"get-port": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz",
"integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="
},
"get-proto": { "get-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",

View File

@ -16,7 +16,7 @@
"mcp-shrimp-task-manager": "./dist/index.js" "mcp-shrimp-task-manager": "./dist/index.js"
}, },
"scripts": { "scripts": {
"build": "tsc && copyfiles -u 1 './src/**/*.md' dist && node scripts/add-shebang.js", "build": "tsc && copyfiles -u 1 './src/**/*.md' dist && copyfiles -u 1 './src/public/**/*' dist && node scripts/add-shebang.js",
"dev": "ts-node src/index.ts", "dev": "ts-node src/index.ts",
"start": "node dist/index.js", "start": "node dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@ -32,11 +32,14 @@
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0", "@modelcontextprotocol/sdk": "^1.0.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express": "^5.1.0",
"get-port": "^7.1.0",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"zod": "^3.22.4", "zod": "^3.22.4",
"zod-to-json-schema": "^3.24.5" "zod-to-json-schema": "^3.24.5"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^5.0.1",
"@types/node": "^20.8.2", "@types/node": "^20.8.2",
"@types/uuid": "^9.0.6", "@types/uuid": "^9.0.6",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",

View File

@ -8,6 +8,12 @@ import {
CallToolRequestSchema, CallToolRequestSchema,
ListToolsRequestSchema, ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js"; } from "@modelcontextprotocol/sdk/types.js";
import express, { Request, Response, NextFunction } from "express";
import getPort from "get-port";
import path from "path";
import fs from "fs";
import fsPromises from "fs/promises";
import { fileURLToPath } from "url";
// 導入工具函數 // 導入工具函數
import { import {
@ -54,6 +60,136 @@ import {
async function main() { async function main() {
try { try {
console.log("Starting Shrimp Task Manager service..."); console.log("Starting Shrimp Task Manager service...");
const ENABLE_GUI = process.env.ENABLE_GUI === "true";
if (ENABLE_GUI) {
// 創建 Express 應用
const app = express();
// 儲存 SSE 客戶端的列表
let sseClients: Response[] = [];
// 發送 SSE 事件的輔助函數
function sendSseUpdate() {
console.log("Tasks changed, sending update to clients...");
sseClients.forEach((client) => {
// 檢查客戶端是否仍然連接
if (!client.writableEnded) {
client.write(
`event: update\ndata: ${JSON.stringify({
timestamp: Date.now(),
})}\n\n`
);
}
});
// 清理已斷開的客戶端 (可選,但建議)
sseClients = sseClients.filter((client) => !client.writableEnded);
}
// 設置靜態文件目錄
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const publicPath = path.join(__dirname, "public");
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, "data");
const TASKS_FILE_PATH = path.join(DATA_DIR, "tasks.json"); // 提取檔案路徑
app.use(express.static(publicPath));
// 設置 API 路由
app.get("/api/tasks", async (req: Request, res: Response) => {
try {
// 使用 fsPromises 保持異步讀取
const tasksData = await fsPromises.readFile(TASKS_FILE_PATH, "utf-8");
res.json(JSON.parse(tasksData));
} catch (error) {
console.error("Error reading tasks.json:", error);
// 確保檔案不存在時返回空任務列表
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
res.json({ tasks: [] });
} else {
res.status(500).json({ error: "Failed to read tasks data" });
}
}
});
// 新增SSE 端點
app.get("/api/tasks/stream", (req: Request, res: Response) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
// 可選: CORS 頭,如果前端和後端不在同一個 origin
// "Access-Control-Allow-Origin": "*",
});
// 發送一個初始事件或保持連接
res.write("data: connected\n\n");
// 將客戶端添加到列表
sseClients.push(res);
// 當客戶端斷開連接時,將其從列表中移除
req.on("close", () => {
sseClients = sseClients.filter((client) => client !== res);
});
});
// 獲取可用埠
const port = await getPort();
// 啟動 HTTP 伺服器
const httpServer = app.listen(port, () => {
// 在伺服器啟動後開始監聽檔案變化
try {
// 檢查檔案是否存在,如果不存在則不監聽 (避免 watch 報錯)
if (fs.existsSync(TASKS_FILE_PATH)) {
fs.watch(TASKS_FILE_PATH, (eventType, filename) => {
if (
filename &&
(eventType === "change" || eventType === "rename")
) {
// 稍微延遲發送,以防短時間內多次觸發 (例如編輯器保存)
// debounce sendSseUpdate if needed
sendSseUpdate();
}
});
} else {
console.warn(
`${TASKS_FILE_PATH} does not exist. File watching not started. It will start if the file is created later by the application.`
);
// 可以考慮在這裡也設置一個 watcher 監聽目錄創建或檔案創建
}
} catch (watchError) {
console.error(
`Error setting up file watch on ${TASKS_FILE_PATH}:`,
watchError
);
}
});
// 將 URL 寫入 ebGUI.md
try {
const websiteUrl = `[Task Manager UI](http://localhost:${port})`;
const websiteFilePath = path.join(DATA_DIR, "WebGUI.md");
await fsPromises.writeFile(websiteFilePath, websiteUrl, "utf-8");
} catch (error) {
console.error("Error writing website URL to file:", error);
}
// 設置進程終止事件處理 (確保移除 watcher)
const shutdownHandler = async () => {
// 關閉所有 SSE 連接
sseClients.forEach((client) => client.end());
sseClients = [];
// 關閉 HTTP 伺服器
await new Promise<void>((resolve) => httpServer.close(() => resolve()));
process.exit(0);
};
process.on("SIGINT", shutdownHandler);
process.on("SIGTERM", shutdownHandler);
}
// 創建MCP服務器 // 創建MCP服務器
const server = new Server( const server = new Server(

139
src/public/index.html Normal file
View File

@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title data-i18n-key="app_title">Shrimp Task Manager</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<header>
<h1 data-i18n-key="app_title">Shrimp Task Manager</h1>
<div class="header-controls">
<div class="status-bar">
<div class="status-indicator"></div>
<span data-i18n-key="status_indicator_online">ONLINE</span>
</div>
<select id="lang-switcher" aria-label="Select Language">
<option value="en">English</option>
<option value="zh-TW">繁體中文</option>
</select>
</div>
</header>
<div class="progress-indicator" id="progress-indicator">
<div class="progress-bar-container">
<div
class="progress-segment progress-completed"
id="progress-completed"
></div>
<div
class="progress-segment progress-in-progress"
id="progress-in-progress"
></div>
<div
class="progress-segment progress-pending"
id="progress-pending"
></div>
</div>
<div class="progress-labels" id="progress-labels">
<!-- Labels will be generated by JS -->
</div>
</div>
<main>
<div class="dependency-view">
<div class="panel-header">
<h2 data-i18n-key="dependency_view_title">Dependency View</h2>
</div>
<div id="dependency-graph" class="dependency-graph">
<p class="placeholder"></p>
</div>
</div>
<div class="bottom-panels">
<div class="task-panel">
<div class="panel-header">
<h2 data-i18n-key="task_list_title">Task List</h2>
<div class="filters">
<input
type="text"
id="search-input"
placeholder="Search tasks..."
data-i18n-key="search_placeholder"
/>
<select id="sort-options">
<option
value="date-desc"
data-i18n-key="sort_option_date_desc"
>
Creation Time (New-Old)
</option>
<option value="date-asc" data-i18n-key="sort_option_date_asc">
Creation Time (Old-New)
</option>
<option value="name-asc" data-i18n-key="sort_option_name_asc">
Name (A-Z)
</option>
<option
value="name-desc"
data-i18n-key="sort_option_name_desc"
>
Name (Z-A)
</option>
<option value="status" data-i18n-key="sort_option_status">
Status
</option>
</select>
<select id="status-filter">
<option value="all" data-i18n-key="status_filter_all">
All Statuses
</option>
<option value="pending" data-i18n-key="status_filter_pending">
Pending
</option>
<option
value="in_progress"
data-i18n-key="status_filter_in_progress"
>
In Progress
</option>
<option
value="completed"
data-i18n-key="status_filter_completed"
>
Completed
</option>
</select>
</div>
</div>
<div class="task-list" id="task-list">
<div class="loading"></div>
</div>
</div>
<div class="task-details">
<div class="panel-header">
<h2 data-i18n-key="task_details_title">Task Details</h2>
</div>
<div id="task-details-content">
<p class="placeholder"></p>
</div>
</div>
</div>
</main>
<footer>
<p>
© 2023 Shrimp Task Manager - Current time:
<span id="current-time"></span>
</p>
</footer>
</div>
<script src="script.js"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,55 @@
{
"app_title": "Shrimp Task Manager",
"status_indicator_online": "ONLINE",
"dependency_view_title": "Dependency View",
"dependency_graph_placeholder": "Dependency relationship for all tasks",
"task_list_title": "Task List",
"search_placeholder": "Search tasks...",
"sort_option_date_desc": "Creation Time (New-Old)",
"sort_option_date_asc": "Creation Time (Old-New)",
"sort_option_name_asc": "Name (A-Z)",
"sort_option_name_desc": "Name (Z-A)",
"sort_option_status": "Status",
"status_filter_all": "All Statuses",
"status_filter_pending": "Pending",
"status_filter_in_progress": "In Progress",
"status_filter_completed": "Completed",
"task_list_loading": "Loading...",
"task_list_empty": "No matching tasks",
"task_details_title": "Task Details",
"task_details_placeholder": "Select a task to view details",
"footer_copyright": "© 2023 Shrimp Task Manager - Current time: ",
"status_pending": "Pending",
"status_in_progress": "In Progress",
"status_completed": "Completed",
"task_detail_status_label": "Status:",
"task_detail_summary_title": "Completion Summary",
"task_detail_description_title": "Task Description",
"task_detail_implementation_guide_title": "Implementation Guide",
"task_detail_verification_criteria_title": "Verification Criteria",
"task_detail_dependencies_title": "Dependencies (Prerequisites)",
"task_detail_related_files_title": "Related Files",
"task_detail_notes_title": "Notes",
"task_detail_no_summary": "No summary provided.",
"task_detail_no_description": "No description",
"task_detail_no_implementation_guide": "No implementation guide",
"task_detail_no_verification_criteria": "No verification criteria",
"task_detail_no_dependencies": "No dependencies",
"task_detail_no_related_files": "No related files",
"task_detail_no_notes": "No notes",
"task_detail_unknown_dependency": "Unknown Task",
"progress_completed": "Completed",
"progress_in_progress": "In Progress",
"progress_pending": "Pending",
"progress_total": "Total",
"global_analysis_title": "Goal",
"error_loading_tasks": "Failed to load tasks: {message}",
"error_updating_tasks": "Failed to update tasks: {message}",
"error_loading_graph": "Failed to load dependency graph",
"error_task_not_found": "Task not found"
}

View File

@ -0,0 +1,55 @@
{
"app_title": "Shrimp Task Manager",
"status_indicator_online": "在線",
"dependency_view_title": "依賴關係",
"dependency_graph_placeholder": "所有任務的依賴關係",
"task_list_title": "任務列表",
"search_placeholder": "搜索任務...",
"sort_option_date_desc": "創建時間 (新-舊)",
"sort_option_date_asc": "創建時間 (舊-新)",
"sort_option_name_asc": "名稱 (A-Z)",
"sort_option_name_desc": "名稱 (Z-A)",
"sort_option_status": "狀態",
"status_filter_all": "所有狀態",
"status_filter_pending": "等待中",
"status_filter_in_progress": "進行中",
"status_filter_completed": "已完成",
"task_list_loading": "載入中...",
"task_list_empty": "沒有符合條件的任務",
"task_details_title": "任務詳情",
"task_details_placeholder": "選擇一個任務查看詳情",
"footer_copyright": "© 2023 Shrimp Task Manager - 當前時間: ",
"status_pending": "等待中",
"status_in_progress": "進行中",
"status_completed": "已完成",
"task_detail_status_label": "狀態:",
"task_detail_summary_title": "完成摘要",
"task_detail_description_title": "任務描述",
"task_detail_implementation_guide_title": "實現指南",
"task_detail_verification_criteria_title": "驗證標準",
"task_detail_dependencies_title": "依賴項 (前置任務)",
"task_detail_related_files_title": "相關文件",
"task_detail_notes_title": "備註",
"task_detail_no_summary": "沒有提供摘要。",
"task_detail_no_description": "無描述",
"task_detail_no_implementation_guide": "無實現指南",
"task_detail_no_verification_criteria": "無驗證標準",
"task_detail_no_dependencies": "無依賴項",
"task_detail_no_related_files": "無相關文件",
"task_detail_no_notes": "無備註",
"task_detail_unknown_dependency": "未知任務",
"progress_completed": "已完成",
"progress_in_progress": "進行中",
"progress_pending": "待處理",
"progress_total": "總計",
"global_analysis_title": "目標",
"error_loading_tasks": "載入任務失敗: {message}",
"error_updating_tasks": "更新任務失敗: {message}",
"error_loading_graph": "載入依賴圖失敗",
"error_task_not_found": "任務未找到"
}

1186
src/public/script.js Normal file

File diff suppressed because it is too large Load Diff

461
src/public/style.css Normal file
View File

@ -0,0 +1,461 @@
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--background-color: #1a1a2e;
--panel-color: #16213e;
--text-color: #f0f0f0;
--accent-color: #4cd137;
--danger-color: #e74c3c;
--border-radius: 8px;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.6;
}
.container {
display: flex;
flex-direction: column;
min-height: 100vh;
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 20px;
}
h1 {
font-size: 1.8rem;
font-weight: 600;
color: var(--primary-color);
}
.status-bar {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.9rem;
font-weight: 500;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: var(--accent-color);
box-shadow: 0 0 8px var(--accent-color);
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
main {
display: grid;
grid-template-rows: auto 1fr;
grid-template-areas:
"dependency"
"bottom";
gap: 20px;
flex-grow: 1;
}
.task-panel,
.task-details {
background-color: var(--panel-color);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
overflow: hidden;
display: flex;
flex-direction: column;
}
.dependency-view {
grid-area: dependency;
background-color: var(--panel-color);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
overflow: hidden;
display: flex;
flex-direction: column;
}
.bottom-panels {
grid-area: bottom;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
overflow: hidden;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background-color: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
h2 {
font-size: 1.2rem;
font-weight: 500;
}
select {
background-color: rgba(0, 0, 0, 0.3);
color: var(--text-color);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 5px 10px;
border-radius: 4px;
outline: none;
}
.task-list,
.dependency-graph,
#task-details-content {
padding: 15px;
flex-grow: 1;
overflow-y: auto;
}
.dependency-graph {
min-height: 300px;
}
.task-item {
padding: 15px;
margin-bottom: 15px;
background-color: rgba(0, 0, 0, 0.2);
border-radius: var(--border-radius);
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.task-item::before {
content: "";
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 4px;
background-color: transparent;
transition: background-color 0.3s;
}
.task-item.status-pending::before {
background-color: #f1c40f;
}
.task-item.status-in-progress::before {
background-color: var(--primary-color);
}
.task-item.status-completed::before {
background-color: var(--secondary-color);
}
.task-item:hover {
transform: translateY(-3px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}
.task-item.selected {
background-color: rgba(52, 152, 219, 0.15);
box-shadow: 0 0 0 2px var(--primary-color);
}
.task-item h3 {
font-size: 1.1rem;
margin-bottom: 5px;
}
.task-meta {
display: flex;
justify-content: space-between;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.7);
}
.task-status {
padding: 2px 8px;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 500;
}
.status-pending {
background-color: rgba(241, 196, 15, 0.2);
color: #f1c40f;
}
.status-in-progress {
background-color: rgba(52, 152, 219, 0.2);
color: #3498db;
}
.status-completed {
background-color: rgba(46, 204, 113, 0.2);
color: #2ecc71;
}
.placeholder {
text-align: center;
color: rgba(255, 255, 255, 0.5);
padding: 50px 0;
}
.loading {
text-align: center;
padding: 20px;
color: rgba(255, 255, 255, 0.7);
animation: fadeInOut 1.5s infinite;
}
@keyframes fadeInOut {
0%,
100% {
opacity: 0.5;
}
50% {
opacity: 1;
}
}
.task-details-header {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.task-details-header h3 {
font-size: 1.3rem;
margin-bottom: 5px;
}
.task-details-section {
margin-bottom: 20px;
}
.task-details-section h4 {
font-size: 1rem;
color: var(--primary-color);
margin-bottom: 10px;
}
.dependencies,
.related-files {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.dependency-tag,
.file-tag {
background-color: rgba(0, 0, 0, 0.3);
border-radius: 4px;
padding: 5px 10px;
font-size: 0.8rem;
}
pre {
background-color: rgba(0, 0, 0, 0.3);
padding: 15px;
border-radius: var(--border-radius);
overflow-x: auto;
margin: 10px 0;
font-family: "Consolas", "Monaco", monospace;
font-size: 0.9rem;
}
footer {
margin-top: 20px;
text-align: center;
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
padding: 10px 0;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
/* 進度指示器樣式 */
.progress-indicator {
margin-bottom: 20px;
padding: 15px;
background-color: var(--panel-color);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
}
.progress-bar-container {
display: flex;
height: 10px;
border-radius: 5px;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.3);
}
.progress-segment {
height: 100%;
transition: width 0.5s ease-in-out;
}
.progress-completed {
background-color: var(--secondary-color);
}
.progress-in-progress {
background-color: var(--primary-color);
}
.progress-pending {
background-color: #f1c40f; /* 與status-pending一致 */
}
.progress-labels {
display: flex;
justify-content: space-between;
margin-top: 10px;
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.7);
}
.progress-labels span {
padding: 2px 5px;
}
.label-completed {
color: var(--secondary-color);
}
.label-in-progress {
color: var(--primary-color);
}
.label-pending {
color: #f1c40f;
}
/* 依賴關係圖樣式 */
.dependency-graph svg {
display: block; /* 避免底部多餘空間 */
width: 100%;
height: 100%;
}
.nodes g.node-item circle {
transition: r 0.2s, stroke 0.2s;
/* 根據類型設置基礎顏色 */
}
.nodes g.type-current circle {
fill: var(--primary-color); /* 當前選中任務 */
}
.nodes g.type-dependency circle {
fill: var(--danger-color); /* 前置依賴任務 */
}
.nodes g.type-dependent circle {
fill: var(--secondary-color); /* 後置依賴任務 */
}
.nodes g.type-unknown circle {
fill: #7f8c8d; /* 未知任務 */
}
/* 根據狀態調整節點透明度或邊框 */
.nodes g.status-completed circle {
opacity: 0.6;
}
.nodes g.status-in-progress circle {
/* 可以添加特殊效果,如描邊動畫 */
stroke: var(--accent-color);
stroke-width: 2px;
}
.nodes g.node-item:hover circle {
r: 14; /* 懸停時放大 */
stroke: #fff;
stroke-width: 2.5px;
}
.nodes g.node-item text {
fill: var(--text-color);
font-size: 10px;
pointer-events: none; /* 避免文本干擾點擊 */
}
/* 新增:高亮節點樣式 */
g.node-item.highlighted circle {
stroke: var(--accent-color) !important; /* 使用重要標誌確保覆蓋 */
stroke-width: 3px !important;
}
@media (max-width: 768px) {
main {
grid-template-rows: auto auto; /* Stack dependency and bottom panels */
grid-template-areas:
"dependency"
"bottom";
}
.bottom-panels {
grid-template-columns: 1fr; /* Stack task list and details */
grid-template-rows: auto auto; /* Or let them take natural height */
}
.task-panel,
.dependency-view,
.task-details {
/* grid-column: 1 / -1; No longer needed */
min-height: 300px; /* Ensure panels have some height */
}
}
/* 過濾器區域樣式 */
.filters {
display: flex;
gap: 10px;
}
.filters input[type="text"],
.filters select {
background-color: rgba(0, 0, 0, 0.3);
color: var(--text-color);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 5px 10px;
border-radius: 4px;
outline: none;
font-size: 0.9rem;
}
.filters input[type="text"] {
flex-grow: 1; /* 讓搜索框佔據更多空間 */
min-width: 150px;
}