内容
# TaskForge
**为人类和 AI 代理构建的多用户任务跟踪器,支持在同一任务板上协作。**
TaskForge 是一个全栈任务管理应用程序,具有三种接口 — REST API、MCP 服务器(用于 AI 代理)和 Kanban 单页应用 — 所有接口都在单个 NestJS 后端运行。它的设计允许任何与 MCP 兼容的代理(Claude Code、Cursor、GitHub Copilot 等)执行与人类相同的操作:创建任务板、移动任务、分配工作、评论、搜索等。
## 功能
- **身份验证** — 邮箱/密码登录、会话令牌、仅限邀请注册、代理令牌、管理员/成员角色
- **引导** — 首次运行设置创建管理员账户和实例标题
- **Kanban 任务板** — 拖拽列,包含待办事项 → 进行中 → 评审 → 已完成
- **列表视图** — 表格视图,快速扫描所有列表
- **任务详情** — 编辑标题、描述、优先级、分配者、截止日期、标签;每个任务都有专属路由
- **子任务** — 在父任务下嵌套任务
- **任务关系** — 使用 `blocks` / `related_to` 关系链接任务
- **每个任务板的任务编号** — 每个任务都有一个顺序的任务板范围内的 ID(例如 `TF-12`)
- **评论** — 任务上的线程式讨论,归属于已认证的用户
- **标签** — 按任务板划分的色彩标签
- **活动日志** — 每个任务和任务板的完整审计跟踪
- **实时更新** — WebSocket 事件实时将更改推送到所有连接的客户端(需要身份验证)
- **全文搜索** — 搜索任务标题和描述,或按任务编号搜索
- **MCP 协议** — AI 代理通过 Streamable HTTP 传输连接,执行与人类相同的操作
- **优先级系统** — 低 / 中 / 高 / 紧急,带有视觉指示器
- **WIP 限制** — 可选的每个列表的工作进行中限制
- **软删除** — 任务归档而不是硬删除
- **单个容器** — 所有内容(API + SPA + WebSocket)都在一个 Docker 镜像中
## 截图
| 首页 | Kanban 任务板 |
|-----------|-------------|
|  |  |
| 列表视图 | 任务详情 |
|-----------|-------------|
|  |  |
## 架构
```
┌─────────────────────────────────────────────────────┐
│ TaskForge 容器 │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ NestJS 后端 (:3000) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ │
│ │ │ REST API │ │ MCP API │ │ WebSocket │ │ │
│ │ │ /api/* │ │ /api/mcp │ │ /ws │ │ │
│ │ └──────────┘ └──────────┘ └────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ 身份验证守护进程(Bearer 会话令牌,│ │ │
│ │ │ 公共异常,管理员路由) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ Prisma ORM → SQLite │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ React SPA(作为静态资源提供) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
```
### 数据模型
```
用户
├── 会话(Bearer 令牌;机器人会话标记)
├── 邀请令牌(由管理员创建,一次性使用)
└── 成员资格(按任务板角色:管理员/成员/查看者)
设置(单例 — 实例标题,已启用标志)
任务板
├── 标识符(任务编号的前缀,例如 TF)
├── nextTaskNum(顺序计数器)
├── 列表(按位置排序)
│ ├── 任务(按位置排序,任务板范围内的编号)
│ │ ├── 评论(归属于用户)
│ │ ├── 活动(审计日志,归属于用户)
│ │ ├── 标签(多对多通过 TaskLabel)
│ │ ├── 子任务(通过 parentId 自相关)
│ │ └── 关系(通过 TaskRelation 阻塞/相关)
│ └── WIP 限制(可选)
├── 标签(任务板级别)
└── 成员(任务板级别)
```
## 快速开始
### 先决条件
- **Node.js** >= 20
- **pnpm** >= 10(使用 `corepack enable && corepack prepare pnpm@10.12.1 --activate` 安装)
### Docker(最快)
```bash
docker run -d --name taskforge -p 3000:3000 -v taskforge-data:/data emreyc/taskforge:latest
```
在 **http://localhost:3000** 打开并按照引导提示创建管理员账户。SQLite 数据库在 `/data/taskforge.db` 处持久化存储在 `taskforge-data` 卷中。
### 本地开发
```bash
# 克隆
git clone https://github.com/emreycolakoglu/taskforge.git
cd taskforge
# 安装依赖
pnpm install
# 生成 Prisma 客户端并应用迁移
cd apps/api
pnpm prisma:generate
pnpm prisma:migrate
cd ../..
# 启动开发服务器(API 在 :3000,Web 在 :5173,带代理)
pnpm dev
```
API 在 `http://localhost:3000` 运行,Vite 开发服务器在 `http://localhost:5173` 运行(代理到 API)。首次访问时,SPA 重定向到 `/onboarding` 以创建管理员账户。
### Docker(生产环境,从源码)
```bash
# 构建并运行(默认情况下通过 docker-compose 暴露 :4321)
docker compose up --build
# 或手动构建
docker build -t taskforge .
docker run -p 3000:3000 -v taskforge-data:/data taskforge
```
然后在浏览器中打开 **http://localhost:4321**(docker-compose)或 **http://localhost:3000**(手动运行)。首次访问触发引导。
## 配置
所有配置均通过环境变量进行:
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `PORT` | `3000` | HTTP 服务器端口 |
| `DATABASE_URL` | `file:./prisma/dev.db` | SQLite 数据库路径。在 Docker 中,使用 `file:/data/taskforge.db` 进行持久化 |
| `CORS_ORIGIN` | `*` | 允许的 CORS 源 |
| `MCP_ALLOWED_ORIGINS` | `http://localhost:3000,http://localhost:5173,http://127.0.0.1:3000,http://127.0.0.1:5173` | 浏览器 MCP 请求的允许来源(防止 DNS 劫持) |
| `MCP_REQUIRE_ORIGIN` | `1` | 设置为 `0` 以完全跳过源检查 |
| `NODE_ENV` | `development` | 设置为 `production` 以启用生产模式 |
### .env 文件
```env
PORT=3000
DATABASE_URL=file:./prisma/dev.db
CORS_ORIGIN=*
```
## 身份验证
所有 REST 端点(少数 `@Public` 端点除外)都需要 `Authorization: Bearer <token>` 头。会话是 UUID 令牌,存储在数据库中,过期时间为 90 天(机器人令牌为 365 天)。
### 身份验证端点(`/api/auth`)
| 方法 | 端点 | 身份验证 | 描述 |
|--------|----------|------|-------------|
| `GET` | `/api/auth/status` | 公共 | 实例是否已引导 + 实例标题 |
| `POST` | `/api/auth/onboard` | 公共 | 首次运行设置;创建管理员用户 + 设置 |
| `POST` | `/api/auth/login` | 公共 | 使用邮箱/密码登录,返回会话令牌 |
| `POST` | `/api/auth/logout` | 已身份验证 | 撤销当前会话 |
| `POST` | `/api/auth/invite` | 管理员 | 创建一次性邀请令牌(7 天有效期) |
| `POST` | `/api/auth/signup/:token` | 公共 | 通过邀请令牌注册;返回会话 |
| `POST` | `/api/auth/bot-token` | 管理员 | 创建长期机器人会话令牌,用于代理 |
| `GET` | `/api/auth/me` | 已身份验证 | 当前用户 |
| `PATCH` | `/api/auth/me` | 已身份验证 | 更新显示名称 / 更改密码 |
| `GET` | `/api/auth/users` | 管理员 | 列出所有用户 |
| `GET` | `/api/auth/invites` | 管理员 | 列出所有邀请令牌 |
| `DELETE` | `/api/auth/invites/:id` | 管理员 | 撤销邀请令牌 |
## REST API
所有端点均位于 `/api` 下,需要 Bearer 令牌(参见 [身份验证](#authentication))。请求和响应正文为 JSON。
### 任务板
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/boards` | 列出所有任务板 |
| `GET` | `/api/boards/:id` | 获取任务板及其列表和标签 |
| `GET` | `/api/boards/:id/full` | 获取任务板及其列表、任务、标签和成员 |
| `POST` | `/api/boards` | 创建任务板(自动创建 5 个默认列表) |
| `PUT` | `/api/boards/:id` | 更新任务板名称/_slug/标识符/描述 |
| `DELETE` | `/api/boards/:id` | 删除任务板及其所有数据 |
**创建任务板:**
```json
{ "name": "我的项目", "slug": "my-project", "identifier": "MYP", "description": "可选" }
```
> `identifier` 是一个 3 个字母的大写前缀,用于每个任务板的任务编号(例如 `MYP-1`)。
### 列表
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/lists/board/:boardId` | 列出任务板中的所有列表 |
| `GET` | `/api/lists/:id` | 获取单个列表 |
| `POST` | `/api/lists` | 创建列表 |
| `PUT` | `/api/lists/:id` | 更新列表名称/颜色/wipLimit/位置 |
| `PUT` | `/api/lists/reorder` | 重新排序列表 |
| `DELETE` | `/api/lists/:id` | 删除列表及其任务 |
**创建列表:**
```json
{ "boardId": "...", "name": "进行中", "color": "#f59e0b", "wipLimit": 5 }
```
### 任务
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/tasks/board/:boardId` | 列出任务板中的任务(`?include=all\|top\|sub`, `?parentId=`) |
| `GET` | `/api/tasks/list/:listId` | 列出特定列表中的任务(相同的查询参数) |
| `GET` | `/api/tasks/search?q=query` | 对任务进行全文搜索(也匹配任务编号,如 `TF-12`) |
| `GET` | `/api/tasks/:id` | 获取任务及其评论、活动、标签、子任务、关系 |
| `POST` | `/api/tasks` | 创建任务 |
| `PUT` | `/api/tasks/:id` | 更新任务字段 |
| `PUT` | `/api/tasks/:id/move` | 将任务移动到另一个列表 |
| `PUT` | `/api/tasks/reorder` | 在列表中重新排序任务 |
| `POST` | `/api/tasks/:taskId/labels/:labelId` | 附加标签到任务 |
| `DELETE` | `/api/tasks/:taskId/labels/:labelId` | 从任务中分离标签 |
| `DELETE` | `/api/tasks/:id` | 存档任务(软删除) |
**创建任务:**
```json
{
"listId": "...",
"title": "实现登录页面",
"description": "添加邮箱/密码和 OAuth 登录",
"priority": "high",
"assigneeId": "user-id",
"dueDate": "2026-07-01T00:00:00Z",
"parentId": "parent-task-id",
"labelIds": ["label-id-1", "label-id-2"],
"metadata": "任意 JSON 字符串"
}
```
**移动任务:**
```json
{ "listId": "new-list-id", "position": 0 }
```
### 任务关系
关系在任务下作用域。`blocks` 是有方向的;`related_to` 是无方向的(规范化)。
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/tasks/:taskId/relations` | 列出任务的关系 |
| `POST` | `/api/tasks/:taskId/relations` | 创建关系 |
| `DELETE` | `/api/tasks/:taskId/relations/:relationId` | 删除关系 |
**创建关系:**
```json
{ "otherTaskId": "other-task-id", "type": "blocks", "direction": "source" }
>
> `direction: "source"` 表示路径任务阻塞其他任务;`"target"` 表示路径任务被其他任务阻塞。默认为 `"source"`。对 `related_to` 忽略。
### 评论
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/comments/task/:taskId` | 列出任务上的评论 |
| `POST` | `/api/comments` | 添加评论(归属于认证用户) |
| `DELETE` | `/api/comments/:id` | 删除评论 |
**添加评论:**
```json
{ "taskId": "...", "body": "看起来不错!" }
### 列表
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/lists/board/:boardId` | 列出任务板上的标签 |
| `POST` | `/api/lists/:boardId/labels` | 创建标签 |
| `PATCH` | `/api/labels/:id" | 更新标签名称/颜色 |
| `DELETE` | `/api/labels/:id` | 删除标签 |
**创建标签:**
```json
{ "name": "bug", "color": "#ef4444" }
```
### 活动
| 方法 | 端点 | 描述 |
|--------|----------|-------------|
| `GET` | `/api/activity/task/:taskId` | 任务活动日志 |
| `GET` | `/api/activity/board/:boardId` | 整个任务板的活动日志 |
## MCP 服务器(AI 代理接口)
TaskForge 在 `POST /api/mcp` 处通过 **Streamable HTTP 传输**(2025-03-26 规范)实现了 **MCP(模型上下文协议)**。任何与 MCP 兼容的代理(Claude Code、Cursor、GitHub Copilot、opencode 等)都可以连接并执行与人类相同的操作。
### 身份验证
MCP 端点位于全局 `AuthGuard` 后。代理必须发送 Bearer 会话令牌。该令牌可以是:
- 用户会话令牌(来自 `POST /api/auth/login`)
- 管理员通过 `POST /api/auth/bot-token` 创建的机器人令牌(365 天有效期,推荐用于代理)
```bash
# 管理员创建机器人令牌
curl -X POST http://localhost:3000/api/auth/bot-token \
-H "Authorization: Bearer <admin-token>"
# → { "id": "...", "token": "bot-uuid", "expiresAt": "..." }
```
### 代理如何连接
**Claude Code / Cursor / Copilot** — 添加到您的 MCP 配置:
```json
{
"mcpServers": {
"taskforge": {
"url": "http://localhost:3000/api/mcp",
"headers": { "Authorization": "Bearer <bot-token>" }
}
}
}
```
Streamable HTTP 传输需要一个 `initialize` 握手,返回一个 `Mcp-Session-Id` 头;后续请求必须包含该头。大多数 MCP 客户端自动处理此功能。
### 可用的 MCP 工具
#### 任务板
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `boards_list` | `{}` | 列出所有任务板及其列表和成员计数 |
| `boards_get` | `{ id }` | 获取任务板及其列表、任务和标签 |
| `boards_create` | `{ name, slug, identifier?, description? }` | 创建任务板及 5 个默认列表 |
| `boards_delete` | `{ id }` | 删除任务板 |
#### 列表
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `lists_list` | `{ boardId }` | 列出任务板中的所有列表 |
| `lists_create` | `{ boardId, name, position?, color?, wipLimit? }` | 创建列表 |
| `lists_update` | `{ id, name?, color?, wipLimit? }` | 更新列表 |
| `lists_delete` | `{ id }` | 删除列表 |
#### 任务
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `tasks_list` | `{ boardId?, listId?, assigneeId?, status?, parentId?, include?, limit? }` | 列出任务并筛选 |
| `tasks_get` | `{ id }` | 获取任务及其评论、活动、标签、子任务和关系 |
| `tasks_search` | `{ query }` | 全文搜索或任务编号查找(例如 `TF-12`) |
| `tasks_create` | `{ listId, title, description?, priority?, assigneeId?, dueDate?, parentId?, labelIds?, position?, metadata? }` | 创建任务(默认为调用者分配) |
| `tasks_update` | `{ id, title?, description?, priority?, status?, assigneeId?, dueDate?, listId?, position?, parentId?, labelIds? }` | 更新任务(`parentId: null` 取消嵌套) |
| `tasks_move` | `{ id, listId, position? }` | 将任务移动到另一个列表 |
| `tasks_delete` | `{ id }` | 存档任务 |
#### 评论
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `comments_list` | `{ taskId }` | 列出任务上的评论 |
| `comments_create` | `{ taskId, body }` | 添加评论(归属于调用者) |
#### 标签
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `labels_list` | `{ boardId }` | 列出任务板上的标签 |
| `labels_create` | `{ boardId, name, color? }` | 创建标签 |
| `labels_delete` | `{ id }` | 删除标签 |
#### 活动
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `activity_list` | `{ taskId?, boardId?, limit? }` | 获取活动日志 |
#### 关系
| 工具 | 参数 | 描述 |
|------|--------|-------------|
| `relations_list` | `{ taskId }` | 列出任务的阻塞/阻塞或相关关系 |
| `relations_create` | `{ taskId, otherTaskId, type, direction? }` | 创建 `blocks` 或 `related_to` 关系 |
| `relations_delete` | `{ relationId }` | 删除关系 |
### 示例:代理创建任务板和任务
```json
// 1. 创建任务板
→ {"method":"tools/call","params":{"name":"boards_create","arguments":{"name":"Sprint 24","slug":"sprint-24","identifier":"SPR"}},"id":1}
← {"jsonrpc":"2.0","id":1,"result":{...}}
// 2. 在 "To Do" 列表中创建一个任务
→ {"method":"tools/call","params":{"name":"tasks_create","arguments":{"listId":"...","title":"设计 API 架构","priority":"high","assigneeId":"alice-id"}},"id":2}
← {"jsonrpc":"2.0","id":2,"result":{...}}
// 3. 将任务移动到 "In Progress"
→ {"method":"tools/call","params":{"name":"tasks_move","arguments":{"id":"...","listId":"in-progress-list-id"}},"id":3}
← {"jsonrpc":"2.0","id":3,"result":{...}}
// 4. 搜索任务
→ {"method":"tools/call","params":{"name":"tasks_search","arguments":{"query":"API"}},"id":4}
← {"jsonrpc":"2.0","id":4,"result":[{...}]}
```
## WebSocket 事件
`/ws` 处的 WebSocket 服务器向连接的客户端推送实时事件。**身份验证是必需的** - 客户端必须在连接后 5 秒内使用会话令牌发出 `auth` 消息:
```javascript
const ws = new WebSocket('ws://localhost:3000/ws');
ws.onopen = () => {
ws.send(JSON.stringify({ event: 'auth', data: { token: '<session-token>', boardId: 'board-123' } }));
};
ws.onmessage = (event) => {
const { event: name, data } = JSON.parse(event.data);
console.log(name, data);
};
```
身份验证成功后,服务器发出 `auth_success`;身份验证失败后,服务器发出 `auth_error` 并断开连接。提供 `boardId` 可加入该板的事件室。
### 事件类型
| 事件 | 载荷 | 何时发生 |
|-------|---------|------|
| `board:created` | 板对象 | 创建新板 |
| `board:updated` | 板对象 | 板被重命名/更新 |
| `board:deleted` | `{ id }` | 板被删除 |
| `list:created` | 列表对象 | 添加新列表 |
| `list:updated` | 列表对象 | 列表被重命名/重新着色 |
| `list:reordered` | 重新排序结果 | 列表被重新排序 |
| `list:deleted` | `{ id }` | 列表被删除 |
| `task:created` | 任务对象 | 创建新任务 |
| `task:updated` | 任务对象 | 任务被编辑 |
| `task:moved` | 任务对象 | 任务被移动到另一个列表 |
| `task:deleted` | `{ id }` | 任务被存档 |
| `task.label.attached` | 任务对象 | 标签被附加到任务 |
| `task.label.detached` | 任务对象 | 标签从任务中分离 |
| `comment:created` | 评论对象 | 添加评论 |
| `comment:deleted` | `{ id }` | 评论被删除 |
| `label:created` | 标签对象 | 创建新标签 |
| `label:updated` | 标签对象 | 标签被重命名/重新着色 |
| `label:deleted` | `{ id }` | 标签被删除 |
| `relation:created` | 关系对象 | 创建任务关系 |
| `relation:deleted` | `{ id }` | 任务关系被删除 |
---
## 前端(SPA)
React SPA 在生产环境中由 NestJS 后端提供服务。在开发环境中,Vite 代理 API 和 WebSocket 请求到后端。
### 路由
| 路由 | 视图 |
|-------|------|
| `/onboarding` | 首次运行管理员设置 |
| `/login` | 登录表单 |
| `/signup/:token` | 基于邀请的注册 |
| `/` | 首页 - 板列表,可创建/删除 |
| `/board/:id` | Kanban 板,包含任务卡、标签、优先级指示器、分配者 |
| `/board/:id/settings` | 板设置(标签、成员) |
| `/board/:boardId/task/:taskId` | 任务详情页(编辑字段、活动日志、评论、子任务、关系) |
| `/tasks` | 列表视图 - 所有任务的可排序表格 |
| `/settings` | 管理员设置 |
| `/account` | 账户设置(显示名称、密码) |
### 技术栈
- React 19 + TypeScript(严格模式)
- React Router 7
- Vite 6(开发服务器,带代理)
- Tailwind CSS 4 + shadcn/ui(Radix 原语)
- TanStack Query(服务器状态)
- `@hello-pangea/dnd`(拖拽和释放)
- Socket.IO 客户端(实时事件)
- Sonner(通知),Lucide 图标
> SPA 遵循暗色“午夜指挥中心”设计系统,带有单个 Acid Lime 强调色。在进行任何前端更改之前,请参阅 [`design.md`](./design.md)。
---
## Docker
### 构建
```bash
# 使用 docker-compose(推荐 - 暴露 :4321)
docker compose up --build
# 手动构建
docker build -t taskforge .
```
Dockerfile 使用多阶段构建:
1. **base** - Node 23 Alpine + pnpm
2. **deps** - 安装所有依赖项
3. **builder** - 生成 Prisma 客户端,构建 API 和 SPA
4. **runner** - 带有 SQLite 持久性的最小生产镜像,存储在 `/data`
### 卷
> **数据丢失警告:** SQLite 数据库存储在容器内的 `/data/taskforge.db`。如果未在 `/data` 处挂载持久卷,**每次重新部署都会重新创建容器并清除数据库**。这适用于 `docker run`、`docker compose up`、Coolify 和任何容器编排器。
在 `/data` 处挂载卷以持久化 SQLite 数据库:
```yaml
volumes:
- taskforge-data:/data
```
Dockerfile 声明 `VOLUME ["/data"]`,因此在 `docker run` 时会自动创建匿名 Docker 存储 - 但匿名卷是每个容器独立的,不会在 `docker rm` 或镜像重新部署时存活。为了真正的持久性,请使用**命名卷**或**绑定挂载**:
```bash
# 命名卷(推荐)
docker run -d --name taskforge -p 3000:3000 -v taskforge-data:/data emreyc/taskforge:latest
# 绑定挂载(用于备份/主机访问)
docker run -d --name taskforge -p 3000:3000 -v /opt/taskforge-data:/data emreyc/taskforge:latest
```
### 健康检查
容器包含一个健康检查,每 30 秒 ping 一次 `GET /api/settings/initialized`。
### 启动时的迁移
`docker-entrypoint.sh` 在启动应用程序之前运行 `prisma migrate deploy`,因此架构更改会随镜像一起发布。要发布架构更改:请在本地运行 `pnpm --filter @taskforge/api prisma:migrate -- --name <desc>`,提交新的迁移文件,然后推送。
---
## 开发
### 项目结构
```
taskforge/
├── apps/
│ ├── api/ # NestJS 后端(CommonJS)
│ │ ├── prisma/
│ │ │ ├── schema.prisma # 数据库架构
│ │ │ └── migrations/ # Prisma 迁移
│ │ ├── docker-entrypoint.sh # 运行迁移然后启动节点
│ │ └── src/
│ │ ├── main.ts # 入口(SPA 服务 + CORS + 验证)
│ │ ├── app.module.ts # 根模块
│ │ ├── prisma/ # Prisma 客户端服务(@Global)
│ │ ├── auth/ # 用户,会话,邀请,机器人令牌,AuthGuard
│ │ ├── settings/ # 实例设置(单例)
│ │ ├── boards/ # 板模块(REST)
│ │ ├── lists/ # 列表模块(REST)
│ │ ├── tasks/ # 任务模块(REST)
│ │ ├── relations/ # 任务关系(blocks / related_to)
│ │ ├── comments/ # 评论模块(REST)
│ │ ├── labels/ # 标签模块(REST)
│ │ ├── activity/ # 活动日志模块(REST)
│ │ ├── events/ # WebSocket 网关 + 事件总线
│ │ └── mcp/ # MCP Streamable HTTP 服务器 + 工具定义
│ └── web/ # React SPA(ESM,严格模式)
│ └── src/
│ ├── app.tsx # 路由
│ ├── contexts/ # AuthContext
│ ├── pages/ # 路由组件
│ ├── components/ # KanbanBoard, TaskCard, TaskDetail, 对话框,UI 原语
│ ├── hooks/ # api.ts, use-auth, use-socket, use-tasks, use-relations, ...
│ ├── lib/ # 常量,实用工具
│ └── types/ # TypeScript 接口
├── Dockerfile
├── docker-compose.yml
├── package.json # 根工作区配置
├── pnpm-workspace.yaml
└── turbo.json # Turborepo 管道
```
### 命令
```bash
pnpm dev # 以开发模式启动 API 和 web
pnpm build # 构建两个应用程序
pnpm lint # 校验所有应用程序(注意:未安装 eslint - 请参阅 AGENTS.md)
pnpm clean # 清理构建工件
# 数据库
pnpm db:generate # 生成 Prisma 客户端
pnpm db:migrate # 运行 Prisma 迁移(添加 -- --name <desc> 以创建迁移)
# 测试
pnpm --filter @taskforge/api test # API(Jest)
pnpm --filter @taskforge/web test # Web(Vitest)
# Docker
pnpm docker:build # 构建 Docker 镜像
pnpm docker:run # 运行 Docker 容器
```
### 添加新模块
1. 在 `apps/api/src/<module>/` 处创建具有控制器、服务、模块和 DTO 文件的新模块
2. 在 `apps/api/src/app.module.ts` 中注册模块
3. 在 `apps/api/src/mcp/tool-definitions.ts` 中添加 MCP 工具定义,并在 `mcp.service.ts` 中添加处理程序
4. 在 `apps/web/src/hooks/api.ts` 中添加 API 客户端方法
5. 在 `apps/web/src/hooks/use-socket.ts` 中添加 WebSocket 事件处理
---
## 用例
### 独自开发者
在本地使用 SQLite 运行。每天使用 SPA 进行工作,使用 MCP 服务器通过机器人令牌让 AI 编码代理自动创建和管理任务。
### 小团队
在单个 VPS 上使用 Docker 部署。管理员创建邀请令牌;成员注册并使用 SPA。CI/CD 管道使用 REST API(通过机器人令牌)创建发布任务。AI 代理加入站会并更新板块。
### 代理优先工作流
您的 AI 代理管理整个板块。代理从 PR 描述中创建任务,将它们移动到审查阶段,分配审查者,链接阻止者,并在完成后存档工作 - 所有操作均通过 MCP 进行。人类根据需要通过 SPA 检查。
### 混合
人类使用 Kanban 板。AI 代理使用 MCP:
- 从错误报告中创建任务
- 将任务移动到管道阶段
- 根据团队容量分配工作
- 搜索和报告任务状态
- 添加包含分析结果的评论
- 链接阻止关系
---
## 常见问题
**问:我可以使用不同的数据库吗?**
答:是的。将 `prisma/schema.prisma` 中的 `provider` 从 `sqlite` 更改为 `postgresql` 或 `mysql`,更新 `DATABASE_URL`,然后运行 `pnpm db:migrate`。Prisma 处理其余部分。
**问:代理如何进行身份验证?**
答:管理员通过 `POST /api/auth/bot-token` 创建机器人令牌(365 天有效期)。代理在每个 REST 和 MCP 请求上发送它作为 `Authorization: Bearer <token>`,并在 `auth` WebSocket 消息中发送。用户会话令牌(90 天有效期)也有效。
**问:我可以部署到 Fly.io / Railway / Render 吗?**
答:是的。Docker 镜像是自包含的。设置 `DATABASE_URL` 为持久卷路径。对于 SQLite,请确保卷在重启时持续存在。对于生产环境,请考虑使用 PostgreSQL。
**问:如何为任务添加自定义字段?**
答:使用 `metadata` 字段 - 它是一个 JSON 字符串,接受任意数据。在前端或代理逻辑中解析它。
**问:多个代理可以同时连接吗?**
答:是的。MCP 端点是基于会话的,并处理并发请求。WebSocket 事件广播到所有连接的客户端。
**问:任务编号如何分配?**
答:每个板块都有一个 `identifier`(3 个字母的前缀)和一个 `nextTaskNum` 计数器。每个新任务都会获得下一个编号(例如 `TF-1`、`TF-2`),并显示和搜索为 `TF-12`。
---
## 许可证
MIT
---
## 贡献
1. 复刻仓库
2. 创建一个功能分支 (`git checkout -b feature/amazing`)
3. 提交您的更改 (`git commit -m 'Add amazing feature'`)
4. 推送到该分支 (`git push origin feature/amazing`)
5. 打开一个拉取请求
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器