Vibe Kanban — 项目学习指南
Vibe Kanban 是一个面向 AI 编程助手(Coding Agents)的任务编排和管理平台,帮助开发者高效地切换、管理和并行执行多个 AI 编程助手(如 Claude Code、Gemini CLI、Codex、Amp 等)来完成开发任务。
Vibe Kanban 项目学习指南
本指南旨在帮助新开发者快速上手并深入理解 Vibe Kanban 项目。建议按顺序阅读,逐步深入。
1. 项目简介
一句话说明
Vibe Kanban 是一个面向 AI 编程助手(Coding Agents)的任务编排和管理平台,帮助开发者高效地切换、管理和并行执行多个 AI 编程助手(如 Claude Code、Gemini CLI、Codex、Amp 等)来完成开发任务。
核心功能列表
- 多 Agent 切换与编排:支持在 Claude Code、Gemini、Codex、Cursor、Copilot 等主流 AI 编程助手间快速切换
- 并行任务执行:支持多个 AI Agent 并行或顺序执行任务
- 工作区管理:为每个任务创建独立的 Git 分支和工作区,隔离开发环境
- 任务看板:基于 Kanban 的任务跟踪系统,可视化任务状态
- MCP 配置中心:统一管理各 AI Agent 的 MCP(Model Context Protocol)服务器配置
- 远程开发支持:支持通过 SSH 远程连接开发环境
- 代码审查辅助:集成 PR 审查工具,辅助代码 Review
- 实时状态同步:使用 ElectricSQL 实现前后端实时数据同步
技术栈总览
后端技术栈
| 类别 | 技术 | 版本 | 说明 |
|---|---|---|---|
| 语言 | Rust | 2024 Edition | 主要后端语言 |
| Web 框架 | Axum | 0.8.4 | 异步 HTTP 服务框架 |
| 数据库 | SQLite (本地) / PostgreSQL (远程) | - | 使用 SQLx 进行数据库操作 |
| ORM | SQLx | 0.8.6 | 异步 SQL 数据库工具包 |
| 实时同步 | ElectricSQL | - | 基于 Postgres 逻辑复制的实时同步引擎 |
| 序列化 | serde/serde_json | 1.0 | Rust 序列化框架 |
| 类型生成 | ts-rs | git 版本 | Rust 到 TypeScript 类型生成 |
| 异步运行时 | Tokio | 1.0 | Rust 异步运行时 |
| 错误处理 | thiserror/anyhow | 2.0/1.0 | 错误处理库 |
| 日志 | tracing/tracing-subscriber | 0.1 | 结构化日志和追踪 |
前端技术栈
| 类别 | 技术 | 版本 | 说明 |
|---|---|---|---|
| 语言 | TypeScript | 5.9+ | 主要前端语言 |
| 框架 | React | 18.2 | UI 框架 |
| 路由 | TanStack Router | 1.161+ | 类型安全的路由库 |
| 状态管理 | TanStack DB + ElectricSQL | 0.1+ | 响应式数据库状态管理 |
| UI 组件 | Radix UI + shadcn | - | 无头组件库和设计系统 |
| 样式 | Tailwind CSS | 3.4+ | 原子化 CSS 框架 |
| 构建工具 | Vite | 7.3+ | 现代前端构建工具 |
| 表单 | React Hook Form + Zod | - | 表单验证 |
| 动画 | Framer Motion | 12+ | 动画库 |
| 国际化 | i18next | 25+ | 国际化框架 |
关键依赖
- AI Agent 集成:
codex-core(OpenAI Codex)、agent-client-protocol(Acp 协议) - Git 操作:
git2(libgit2 Rust 绑定) - MCP 协议:
rmcp(Rust MCP 服务器实现) - 容器化:Docker (远程部署)
2. 目录结构说明
项目根目录
vibe-kanban/
├── .cargo/ # Cargo 配置(Rust 工具链配置)
├── .github/ # GitHub Actions CI/CD 配置
├── .npmrc # npm/pnpm 配置
├── assets/ # 静态资源文件(打包到应用中)
├── crates/ # Rust workspace 成员(核心后端代码)
├── dev_assets_seed/ # 开发环境种子数据(初始数据库)
├── docs/ # 项目文档(Mintlify 格式)
├── npx-cli/ # npm CLI 包(npx vibe-kanban 入口)
├── packages/ # 前端 monorepo 包
├── scripts/ # 构建和开发脚本
├── shared/ # 共享类型定义(Rust 生成 TypeScript 类型)
├── AGENTS.md # 项目开发和代码规范指南
├── Cargo.toml # Rust workspace 根配置
├── package.json # pnpm workspace 根配置
├── pnpm-workspace.yaml # pnpm workspace 配置
├── README.md # 项目说明文档
└── Dockerfile # Docker 构建配置
crates/ 目录(Rust 后端)
crates/
├── api-types/ # 共享 API 类型定义(Rust + TypeScript)
│ ├── src/
│ │ ├── workspace.rs # Workspace 类型
│ │ ├── session.rs # Session 类型
│ │ ├── issue.rs # Issue 类型
│ │ └── ... # 其他共享类型
│ └── Cargo.toml
├── server/ # 本地服务器(主应用)
│ ├── src/
│ │ ├── main.rs # 应用入口
│ │ ├── lib.rs # 库导出
│ │ ├── routes/ # HTTP 路由
│ │ │ ├── mod.rs # 路由汇总
│ │ │ ├── workspaces.rs # 工作区路由
│ │ │ ├── sessions.rs # 会话路由
│ │ │ ├── task_attempts/ # 任务尝试路由
│ │ │ └── ... # 其他路由
│ │ ├── middleware/ # 中间件
│ │ │ ├── mod.rs
│ │ │ ├── origin.rs # Origin 验证中间件
│ │ │ └── model_loaders.rs
│ │ ├── preview_proxy/ # 预览代理(用于预览开发服务器)
│ │ └── error.rs # 错误定义
│ └── Cargo.toml
├── db/ # 数据库层(SQLx 模型 + 迁移)
│ ├── src/
│ │ ├── models/ # 数据模型
│ │ │ ├── mod.rs
│ │ │ ├── workspace.rs # Workspace 模型
│ │ │ ├── session.rs # Session 模型
│ │ │ ├── task.rs # Task 模型
│ │ │ └── ... # 其他模型
│ │ └── lib.rs # 数据库连接和迁移
│ ├── migrations/ # SQL 数据库迁移文件
│ └── Cargo.toml
├── executors/ # AI Agent 执行器(核心)
│ ├── src/
│ │ ├── executors/ # 各 Agent 实现
│ │ │ ├── mod.rs
│ │ │ ├── claude.rs # Claude Code 执行器
│ │ │ ├── codex.rs # Codex 执行器
│ │ │ ├── cursor.rs # Cursor 执行器
│ │ │ ├── copilot.rs # GitHub Copilot 执行器
│ │ │ └── ... # 其他 Agent
│ │ ├── actions/ # 可执行动作
│ │ │ ├── coding_agent_initial.rs # 初始执行
│ │ │ └── coding_agent_follow_up.rs # 后续执行
│ │ ├── mcp_config.rs # MCP 配置管理
│ │ ├── profile.rs # Agent 配置文件
│ │ └── lib.rs
│ └── Cargo.toml
├── services/ # 业务服务层
│ ├── src/
│ │ ├── services/
│ │ │ ├── mod.rs
│ │ │ ├── container.rs # 容器服务
│ │ │ ├── config.rs # 配置服务
│ │ │ └── ... # 其他服务
│ │ └── lib.rs
│ └── Cargo.toml
├── deployment/ # 部署抽象层
│ ├── src/
│ │ └── lib.rs # Deployment trait 定义
│ └── Cargo.toml
├── local-deployment/ # 本地部署实现
│ ├── src/
│ │ └── lib.rs # LocalDeployment 实现
│ └── Cargo.toml
├── remote/ # 远程服务器(云端版本)
│ ├── src/
│ │ ├── app.rs # 应用启动
│ │ ├── config.rs # 配置
│ │ ├── shapes.rs # ElectricSQL Shape 定义
│ │ ├── routes/ # 路由
│ │ │ ├── electric_proxy.rs # ElectricSQL 代理
│ │ │ └── ...
│ │ └── auth/ # 认证模块
│ └── Cargo.toml
├── git/ # Git 操作封装
├── git-host/ # Git 托管平台集成(GitHub/Azure)
├── mcp/ # MCP 服务器实现
├── review/ # PR 审查工具
├── utils/ # 通用工具函数
└── api-types/ # 共享类型(被 remote 和 server 依赖)
packages/ 目录(前端)
packages/
├── local-web/ # 本地 Web 应用(主前端)
│ ├── src/
│ │ ├── app/ # 应用入口和提供者
│ │ │ ├── entry/ # 启动入口
│ │ │ ├── providers/ # React Context 提供者
│ │ │ └── router/ # 路由配置
│ │ ├── routes/ # 路由页面(TanStack Router)
│ │ │ ├── _app.tsx # 应用布局
│ │ │ ├── _app.workspaces.tsx
│ │ │ ├── _app.workspaces_.$workspaceId.tsx
│ │ │ └── ... # 其他路由
│ │ ├── shared/ # 共享组件和工具
│ │ │ ├── components/ # 通用组件
│ │ │ ├── providers/ # 数据提供者
│ │ │ └── keyboard/ # 键盘快捷键
│ │ ├── features/ # 功能模块(按功能划分)
│ │ └── routeTree.gen.ts # 自动生成的路由树
│ ├── public/ # 静态资源
│ └── package.json
├── remote-web/ # 远程 Web 应用(云端前端)
│ ├── src/
│ └── package.json
├── web-core/ # 共享前端库(local + remote 共用)
│ ├── src/
│ │ ├── components/ # 共享组件
│ │ ├── hooks/ # 共享 Hooks
│ │ ├── utils/ # 工具函数
│ │ └── project-routes/ # 项目相关路由
│ └── package.json
└── ui/ # UI 组件库(基于 shadcn)
├── src/
│ └── components/ # UI 组件
└── package.json
shared/ 目录(类型共享)
shared/
├── types.ts # 本地类型定义(由 Rust 生成)
├── remote-types.ts # 远程类型定义(由 Rust 生成)
├── jwt.ts # JWT 工具
└── remote-types.ts # 远程类型定义
3. 架构设计
整体架构模式
Vibe Kanban 采用 前后端分离 + monorepo 架构,核心设计理念:
- Rust 后端 + React 前端:利用 Rust 的性能和类型安全,结合 React 的生态和开发体验
- 本地优先 + 云端同步:本地使用 SQLite 存储,远程版本使用 PostgreSQL + ElectricSQL 实时同步
- 插件化 Agent 执行器:通过 trait 抽象不同 AI Agent,支持热插拔
- 类型安全的全栈类型系统:使用
ts-rs从 Rust 自动生成 TypeScript 类型,保证前后端类型一致
架构分层
┌─────────────────────────────────────────────────────────┐
│ 前端层 (React + TypeScript) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ local-web │ │ remote-web │ │ web-core │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └────────────────┴────────────────┘ │
│ │ │
│ TanStack Router + ElectricSQL │
└──────────────────────────┼──────────────────────────────┘
│ HTTP/WebSocket
┌──────────────────────────┼──────────────────────────────┐
│ 后端层 (Rust + Axum) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ HTTP 路由层 (routes/) │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 业务服务层 (services/) │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ AI Agent 执行器层 (executors/) │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │
│ │ │Claude│ │Codex │ │Cursor│ │Copilot│ │ ... │ │ │
│ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 数据访问层 (db/) │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────┼──────────────────────────────┘
│
┌────────────┴────────────┐
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ SQLite │ │ PostgreSQL │
│ (本地) │ │ (远程) │
└─────────────┘ └──────┬──────┘
│
┌──────▼──────┐
│ ElectricSQL │
│ (实时同步) │
└─────────────┘
核心模块划分与职责
| 模块 | 职责 | 关键文件 |
|---|---|---|
| server | HTTP 服务器、路由、中间件、API 端点 | crates/server/src/main.rs, routes/mod.rs |
| db | 数据库连接、迁移、数据模型、CRUD 操作 | crates/db/src/models/, crates/db/migrations/ |
| executors | AI Agent 执行器抽象、各 Agent 实现、MCP 配置 | crates/executors/src/executors/mod.rs, mcp_config.rs |
| services | 业务逻辑封装、容器管理、配置服务 | crates/services/src/services/ |
| deployment | 部署策略抽象(本地/远程) | crates/deployment/src/lib.rs |
| local-deployment | 本地部署具体实现 | crates/local-deployment/src/lib.rs |
| remote | 远程服务器实现、ElectricSQL 集成、认证 | crates/remote/src/app.rs, shapes.rs |
| api-types | 前后端共享类型定义 | crates/api-types/src/ |
| git | Git 操作封装(分支、提交、工作树) | crates/git/src/ |
| mcp | MCP 服务器实现(任务服务器) | crates/mcp/src/ |
模块间调用关系与数据流向
典型请求流程(创建 Workspace)
1. 前端发起请求
POST /api/remote/workspaces
↓
2. HTTP 路由接收
crates/server/src/routes/remote/workspaces.rs
↓
3. 调用业务服务
crates/services/src/services/container.rs::ContainerService::create()
↓
4. 数据库操作
crates/db/src/models/workspace.rs::Workspace::create()
↓
5. 返回结果
Workspace { id, branch, container_ref, ... }
↓
6. 前端更新状态
ElectricSQL 同步到所有连接的客户端
Agent 执行流程
1. 用户触发 Agent 执行
↓
2. 创建 ExecutionProcess 记录
db::models::execution_process::ExecutionProcess::create()
↓
3. 选择执行器
executors::executors::get_executor(agent_type)
↓
4. 构建命令
executor.build_command(prompt, config)
↓
5. 生成进程
executor.spawn(current_dir, prompt, env)
↓
6. 流式输出日志
executor.stream_logs() → WebSocket → 前端
↓
7. 更新状态
执行完成 → 更新 ExecutionProcess.status
4. 核心流程解析
应用启动流程
后端启动(crates/server/src/main.rs)
// 1. 安装 rustls 加密提供者
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
// 2. 初始化 Sentry 错误追踪
sentry_utils::init_once(SentrySource::Backend);
// 3. 配置日志系统
let log_level = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string());
let filter_string = format!(
"warn,server={level},services={level},db={level},...",
level = log_level
);
let env_filter = EnvFilter::try_new(filter_string)...;
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer().with_filter(env_filter))
.with(sentry_layer())
.init();
// 4. 创建资源目录
if !asset_dir().exists() {
std::fs::create_dir_all(asset_dir())?;
}
// 5. 迁移数据库(旧版本 → 新版本)
let old_db = asset_dir().join("db.sqlite");
let new_db = asset_dir().join("db.v2.sqlite");
if !new_db.exists() && old_db.exists() {
std::fs::copy(&old_db, &new_db)?;
}
// 6. 初始化部署实例(核心)
let deployment = DeploymentImpl::new().await?;
// 内部完成:数据库连接、迁移、容器服务初始化等
// 7. 清理孤儿执行进程
deployment.container().cleanup_orphan_executions().await?;
// 8. 预加载执行器选项缓存
tokio::spawn(async move {
executors::executors::utils::preload_global_executor_options_cache().await;
});
// 9. 创建 Axum 路由
let app_router = routes::router(deployment.clone());
// 10. 绑定端口并启动服务
let main_listener = tokio::net::TcpListener::bind(format!("{host}:{port}")).await?;
let proxy_listener = tokio::net::TcpListener::bind(format!("{host}:{proxy_port}")).await?;
// 11. 启动两个服务器(主服务 + 预览代理)
let main_server = axum::serve(main_listener, app_router)...;
let proxy_server = axum::serve(proxy_listener, proxy_router)...;
// 12. 等待关闭信号
tokio::select! {
_ = shutdown_signal() => {},
_ = main_handle => {},
_ = proxy_handle => {},
}
// 13. 清理资源(终止所有运行中的容器进程)
perform_cleanup_actions(&deployment).await;
前端启动(packages/local-web/src/app/entry/App.tsx)
// 1. Bootstrap 组件加载配置
<Bootstrap>
// 2. 主题提供者
<ThemeProvider>
// 3. 配置提供者(加载全局设置)
<ConfigProvider>
// 4. 用户提供者(认证状态)
<UserProvider>
// 5. 终端提供者(xterm.js 实例管理)
<TerminalProvider>
// 6. 键盘快捷键提供者
<SequenceTrackerProvider>
// 7. 主应用布局
<SharedAppLayout />
</SequenceTrackerProvider>
</TerminalProvider>
</UserProvider>
</ConfigProvider>
</ThemeProvider>
</Bootstrap>
重要业务流程
流程 1:创建 Workspace 并启动 Agent
调用链路:
前端路由:packages/local-web/src/routes/_app.workspaces_.create.tsx
↓ POST /api/workspaces
后端路由:crates/server/src/routes/workspaces.rs::create_workspace()
↓
服务层:crates/services/src/services/container.rs::ContainerService::create_workspace()
↓
数据库:crates/db/src/models/workspace.rs::Workspace::create()
↓
返回 Workspace 对象
↓
前端:导航到 /workspaces/{workspaceId}
↓
用户:点击"Start Agent"
↓ POST /api/task_attempts
后端:crates/server/src/routes/task_attempts.rs::create_task_attempt()
↓
服务层:创建 ExecutionProcess
↓
执行器:crates/executors/src/executors/{agent}.rs::spawn()
↓
进程启动:tokio::process::Command::spawn()
↓
日志流:WebSocket → 前端终端组件
关键代码片段(crates/executors/src/executors/claude.rs):
#[async_trait]
impl StandardCodingAgentExecutor for ClaudeCode {
async fn spawn(
&self,
current_dir: &Path,
prompt: &str,
env: &ExecutionEnv,
) -> Result<SpawnedChild, ExecutorError> {
// 1. 构建命令
let mut builder = CommandBuilder::new("npx")
.args(["-y", "@anthropic-ai/claude-code"])
.args(["--verbose", "--output-format", "stream-json"])
.args(["--allowedTools", "Bash,Edit,Write,Glob,Grep"])
.arg("--append-prompt")
.arg(&self.append_prompt.combine_prompt(prompt));
// 2. 添加模型配置
if let Some(model) = &self.model {
builder = builder.extend_params(["--model", model]);
}
// 3. 应用覆盖配置
apply_overrides(builder, &self.cmd)
.build_initial()?
.spawn(current_dir, env)
}
}
流程 2:ElectricSQL 实时同步
工作流程:
1. 前端订阅 Shape
useElectricQuery(shapeDefinition)
↓
2. 发送 Shape 请求
GET /shape/{shapeName}?params=...
↓
3. 后端代理验证
crates/remote/src/routes/electric_proxy.rs
- 检查用户认证
- 验证组织/项目成员资格
↓
4. 转发到 ElectricSQL
POST http://electric:3000/v1/shape/{shapeName}
↓
5. ElectricSQL 返回数据
- 初始数据快照
- 建立 WebSocket 长连接
↓
6. 数据变更推送
Postgres WAL → ElectricSQL → 前端
↓
7. 前端更新 UI
TanStack DB 自动触发 React 重渲染
Shape 定义示例(crates/remote/src/shapes.rs):
define_shape!(
WORKSPACES_BY_PROJECT,
"workspaces_by_project",
Workspace,
r#"SELECT * FROM workspaces WHERE project_id = {project_id}"#,
["project_id"]
);
流程 3:MCP 配置管理
调用链路:
用户配置 MCP 服务器
↓
前端:设置页面 → 保存配置
↓ POST /api/config/mcp
后端:crates/server/src/routes/config.rs
↓
服务层:crates/services/src/services/config.rs
↓
读取 Agent 配置文件
- Claude: ~/.claude.json
- Cursor: ~/.cursor/mcp.json
- Codex: ~/.codex/config.json
↓
更新 MCP 服务器配置
crates/executors/src/mcp_config.rs::update_mcp_config()
↓
写入文件(保留注释)
write_jsonc_preserving_comments()
↓
重启 Agent(可选)
MCP 配置结构:
pub struct McpConfig {
pub servers: HashMap<String, serde_json::Value>, // MCP 服务器配置
pub servers_path: Vec<String>, // 搜索路径
pub template: serde_json::Value, // 默认模板
pub preconfigured: serde_json::Value, // 预配置服务器
pub is_toml_config: bool, // 是否 TOML 格式
}
流程 4:Git 工作区管理
Workspace 创建流程:
1. 接收创建请求
POST /api/workspaces
{ project_id, issue_id, ... }
↓
2. 生成唯一分支名
let branch = format!("vibe-kanban/{}", uuid::Uuid::new_v4());
↓
3. 创建 Git 分支
git::ops::create_branch(repo_path, &branch, base_branch)?;
↓
4. 记录到数据库
Workspace::create(pool, &data, id).await?;
↓
5. 返回工作区信息
{ id, branch, agent_working_dir, ... }
关键代码(crates/git/src/ops.rs):
pub fn create_branch(
repo_path: &Path,
branch_name: &str,
base_branch: &str,
) -> Result<(), GitError> {
let repo = Repository::open(repo_path)?;
// 查找基础分支
let base_ref = repo.find_reference(&format!("refs/heads/{}", base_branch))?;
let base_commit = base_ref.peel_to_commit()?;
// 创建新分支
repo.branch(branch_name, &base_commit, false)?;
Ok(())
}
5. 关键设计与实现
设计模式
1. 策略模式(Strategy Pattern)- Agent 执行器
应用场景:不同 AI Agent 的执行逻辑差异很大,但需要统一的接口
// Trait 定义(策略接口)
#[async_trait]
pub trait StandardCodingAgentExecutor: Send + Sync {
async fn spawn(
&self,
current_dir: &Path,
prompt: &str,
env: &ExecutionEnv,
) -> Result<SpawnedChild, ExecutorError>;
fn build_command_builder(&self) -> Result<CommandBuilder, CommandBuildError>;
}
// 具体策略实现(支持的 AI Agent)
pub struct ClaudeCode {
pub model: Option<String>,
pub approvals: Option<bool>,
pub plan: Option<bool>,
// ... 其他配置
}
pub struct Codex {
pub sandbox: Option<SandboxMode>,
pub ask_for_approval: Option<AskForApproval>,
pub model: Option<String>,
// ... 其他配置
}
pub struct CursorAgent { ... }
pub struct Copilot { ... }
pub struct Gemini { ... }
pub struct QwenCode { ... }
pub struct Amp { ... }
pub struct Opencode { ... }
pub struct Droid { ... }
// 上下文使用
pub fn get_executor(agent_type: &str) -> Box<dyn StandardCodingAgentExecutor> {
match agent_type {
"claude-code" => Box::new(ClaudeCode::default()),
"codex" => Box::new(Codex::default()),
"cursor" => Box::new(CursorAgent::default()),
_ => panic!("Unknown executor"),
}
}
优势:
- 新增 Agent 只需实现 trait,无需修改现有代码
- 运行时动态切换 Agent
- 统一的错误处理和日志记录
2. 工厂模式(Factory Pattern)- 部署实例
应用场景:根据编译目标(本地/远程)创建不同的部署实例
// crates/server/src/lib.rs
// #[cfg(feature = "cloud")]
// type DeploymentImpl = vibe_kanban_cloud::deployment::CloudDeployment;
// #[cfg(not(feature = "cloud"))]
pub type DeploymentImpl = local_deployment::LocalDeployment;
使用:
// main.rs
let deployment = DeploymentImpl::new().await?;
// 编译时决定使用 LocalDeployment 或 CloudDeployment
3. 构建者模式(Builder Pattern)- 命令构建
应用场景:构建复杂的命令行参数
pub struct CommandBuilder {
command: String,
args: Vec<String>,
env: HashMap<String, String>,
}
impl CommandBuilder {
pub fn new(cmd: &str) -> Self { ... }
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.args.extend(args.into_iter().map(Into::into));
self
}
pub fn param(mut self, key: &str, value: &str) -> Self {
self.args.push(format!("--{}={}", key, value));
self
}
pub fn build(self) -> Command { ... }
}
// 使用示例
let command = CommandBuilder::new("npx")
.args(["-y", "@anthropic-ai/claude-code"])
.param("model", "claude-sonnet-4-20250514")
.param("allowed-tools", "Bash,Edit,Write")
.build();
数据模型 / 数据库设计
核心表结构
核心数据表(基于 crates/db/migrations/20250617183714_init.sql 及后续迁移):
projects 表:
CREATE TABLE projects (
id BLOB PRIMARY KEY,
name TEXT NOT NULL,
git_repo_path TEXT NOT NULL DEFAULT '' UNIQUE,
setup_script TEXT DEFAULT '',
created_at TEXT NOT NULL DEFAULT (datetime('now', 'subsec')),
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'subsec'))
);
tasks 表:
CREATE TABLE tasks (
id BLOB PRIMARY KEY,
project_id BLOB NOT NULL,
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL DEFAULT 'todo'
CHECK (status IN ('todo','inprogress','done','cancelled','inreview')),
created_at TEXT NOT NULL DEFAULT (datetime('now', 'subsec')),
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'subsec')),
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
);
workspaces 表(后续迁移添加):
CREATE TABLE workspaces (
id BLOB PRIMARY KEY,
task_id BLOB,
container_ref TEXT, -- 容器引用/工作区路径
branch TEXT NOT NULL, -- Git 分支名
agent_working_dir TEXT,
setup_completed_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
archived INTEGER NOT NULL DEFAULT 0,
pinned INTEGER NOT NULL DEFAULT 0,
name TEXT
);
task_attempts 表:
CREATE TABLE task_attempts (
id BLOB PRIMARY KEY,
task_id BLOB NOT NULL,
worktree_path TEXT NOT NULL,
merge_commit TEXT,
executor TEXT,
stdout TEXT,
stderr TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
);
task_attempt_activities 表:
CREATE TABLE task_attempt_activities (
id BLOB PRIMARY KEY,
task_attempt_id BLOB NOT NULL,
status TEXT NOT NULL DEFAULT 'init'
CHECK (status IN (
'init','setuprunning','setupcomplete','setupfailed',
'executorrunning','executorcomplete','executorfailed','paused'
)),
note TEXT,
created_at TEXT NOT NULL,
FOREIGN KEY (task_attempt_id) REFERENCES task_attempts(id) ON DELETE CASCADE
);
sessions 表(后续迁移添加):
CREATE TABLE sessions (
id BLOB PRIMARY KEY,
workspace_id BLOB NOT NULL,
executor TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
execution_processes 表(后续迁移添加):
CREATE TABLE execution_processes (
id BLOB PRIMARY KEY,
session_id BLOB NOT NULL,
status TEXT NOT NULL, -- running/completed/failed/killed
run_reason TEXT NOT NULL,
executor_action JSON NOT NULL,
started_at TEXT,
completed_at TEXT,
created_at TEXT NOT NULL,
dropped INTEGER NOT NULL DEFAULT 0
);
数据库迁移
迁移文件位于 crates/db/migrations/,使用 SQLx 管理:
# 创建新迁移
sqlx migrate add <migration_name>
# 运行迁移
pnpm run prepare-db
# 检查迁移(CI)
pnpm run prepare-db:check
迁移文件示例(20250617183714_init.sql):
-- 创建 workspaces 表
CREATE TABLE IF NOT EXISTS workspaces (
id TEXT PRIMARY KEY,
task_id TEXT,
container_ref TEXT,
branch TEXT NOT NULL,
agent_working_dir TEXT,
setup_completed_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
archived INTEGER NOT NULL DEFAULT 0,
pinned INTEGER NOT NULL DEFAULT 0,
name TEXT
);
-- 创建索引
CREATE INDEX idx_workspaces_task_id ON workspaces(task_id);
CREATE INDEX idx_workspaces_archived ON workspaces(archived);
状态管理 / 数据流方案
前端状态管理(TanStack DB + ElectricSQL)
架构:
ElectricSQL (Postgres)
↓ HTTP
TanStack Electric Collection
↓ 响应式
TanStack React DB
↓ React Context
组件使用 useQuery()
使用示例:
// 定义 Shape
const workspacesShape = defineShape({
name: 'workspaces',
tableName: 'workspaces',
where: { projectId: '{projectId}' },
columns: ['id', 'name', 'branch', 'archived', ...],
});
// 组件中使用
function WorkspaceList({ projectId }) {
const workspaces = useQuery(
workspacesShape,
{ projectId },
{ orderBy: { updated_at: 'desc' } }
);
return (
<div>
{workspaces.map(ws => (
<WorkspaceCard key={ws.id} workspace={ws} />
))}
</div>
);
}
乐观更新:
// 创建 Workspace
const createWorkspace = useMutation({
mutationFn: async (data) => {
// 1. 乐观更新本地 DB
const optimisticId = uuid();
electricCollection.insert({
...data,
id: optimisticId,
pending: true,
});
// 2. 发送请求到后端
const response = await fetch('/api/workspaces', {
method: 'POST',
body: JSON.stringify(data),
});
const { data: workspace, txid } = await response.json();
// 3. 等待 ElectricSQL 同步(通过 txid)
await waitForTxid(txid);
// 4. 替换乐观更新为真实数据
electricCollection.update(optimisticId, {
...workspace,
pending: false,
});
},
});
错误处理与日志策略
后端错误处理
错误类型定义:
// crates/server/src/error.rs
#[derive(Debug, Error)]
pub enum VibeKanbanError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Sqlx(#[from] SqlxError),
#[error(transparent)]
Deployment(#[from] DeploymentError),
#[error(transparent)]
Other(#[from] AnyhowError),
}
// executors 错误
#[derive(Debug, Error)]
pub enum ExecutorError {
#[error("Executor not found: {0}")]
NotFound(String),
#[error("Command execution failed: {0}")]
CommandFailed(String),
#[error("MCP configuration error: {0}")]
McpConfigError(String),
#[error(transparent)]
Io(#[from] std::io::Error),
}
错误处理最佳实践:
// 1. 使用 ? 操作符传播错误
pub async fn create_workspace(
pool: &SqlitePool,
data: &CreateWorkspace,
) -> Result<Workspace, WorkspaceError> {
let workspace = Workspace::create(pool, data, id).await?;
Ok(workspace)
}
// 2. 使用 anyhow 处理非结构化错误
pub async fn complex_operation() -> anyhow::Result<()> {
let result = some_fallible_operation().await
.context("Failed to perform operation")?;
Ok(())
}
// 3. 使用 tracing 记录错误上下文
pub async fn spawn_agent(...) -> Result<SpawnedChild, ExecutorError> {
tracing::info!(
prompt_length = prompt.len(),
executor = %agent_type,
"Spawning coding agent"
);
match executor.spawn(current_dir, prompt, env).await {
Ok(child) => Ok(child),
Err(e) => {
tracing::error!(error = %e, "Failed to spawn agent");
Err(e)
}
}
}
日志策略
日志级别配置:
// main.rs
let filter_string = format!(
"warn,server={level},services={level},db={level},executors={level}",
level = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string())
);
// 开发环境:debug
RUST_LOG=debug pnpm run dev
// 生产环境:info
RUST_LOG=info cargo run --release
结构化日志:
tracing::info!(
workspace_id = %workspace.id,
branch = %workspace.branch,
"Workspace created successfully"
);
tracing::error!(
error = %e,
workspace_id = %workspace_id,
"Failed to create workspace"
);
前端日志:
// 使用 Sentry 进行错误追踪
import * as Sentry from '@sentry/react';
try {
await createWorkspace(data);
} catch (error) {
Sentry.captureException(error, {
tags: { workspace_type: 'local' },
extra: { data },
});
}
安全机制
认证与授权
本地版本:
- 无强制认证(本地使用)
- Origin 验证防止 CSRF
远程版本(crates/remote/src/auth/):
// JWT 认证
pub struct JwtClaims {
pub sub: String, // 用户 ID
pub email: String,
pub exp: usize, // 过期时间
}
// 中间件验证
pub async fn require_session(
auth_header: Option<TypedHeader<Authorization<Bearer>>>,
Extension(state): Extension<AppState>,
) -> Result<RequestContext, AuthError> {
let token = auth_header.ok_or(AuthError::MissingToken)?;
let claims = verify_jwt(token.token(), &state.jwt_secret)?;
// 检查用户是否存在
let user = db::users::find_by_id(&state.pool, &claims.sub).await?
.ok_or(AuthError::UserNotFound)?;
Ok(RequestContext { user, .. })
}
OAuth 提供商:
// 支持 GitHub 和 Google
pub enum OAuthProvider {
GitHub,
Google,
}
// 配置(至少需要一个)
let github_client_id = std::env::var("VIBEKANBAN_REMOTE_GITHUB_CLIENT_ID");
let google_client_id = std::env::var("VIBEKANBAN_REMOTE_GOOGLE_CLIENT_ID");
// 至少一个必须配置
if github_client_id.is_err() && google_client_id.is_err() {
panic!("At least one OAuth provider must be configured");
}
Origin 验证
防止 CSRF 攻击:
// crates/server/src/middleware/origin.rs
pub async fn validate_origin(
request: Parts,
next: Next,
) -> Result<Response, StatusCode> {
let origin = request.headers.get(http::header::ORIGIN);
let host = request.headers.get(http::header::HOST);
// 开发环境:允许 localhost
if cfg!(debug_assertions) {
if origin.map_or(false, |o| o == "http://localhost:3000") {
return Ok(next.run(request).await);
}
}
// 生产环境:检查 VK_ALLOWED_ORIGINS
let allowed_origins = std::env::var("VK_ALLOWED_ORIGINS")
.unwrap_or_default();
if allowed_origins.split(',').any(|allowed| {
origin.map_or(false, |o| o == allowed)
}) {
Ok(next.run(request).await)
} else {
Err(StatusCode::FORBIDDEN)
}
}
ElectricSQL 安全
Shape 访问控制:
// crates/remote/src/routes/electric_proxy.rs
pub async fn proxy_shape_request(
RequestContext { user, .. }: RequestContext,
Path(shape_name): Path<String>,
Query(params): Query<HashMap<String, String>>,
) -> Result<Response, ApiError> {
// 1. 验证 Shape 是否存在
let shape = get_shape_definition(&shape_name)
.ok_or(ApiError::ShapeNotFound)?;
// 2. 检查用户是否有权访问
match shape.scope {
ShapeScope::Organization(org_id) => {
check_org_membership(&user.id, &org_id).await?;
}
ShapeScope::Project(project_id) => {
check_project_membership(&user.id, &project_id).await?;
}
}
// 3. 转发请求到 ElectricSQL
let response = reqwest::Client::new()
.post(format!("{}/v1/shape/{}", state.electric_url, shape_name))
.json(¶ms)
.send()
.await?;
Ok(response)
}
6. 环境搭建与运行
环境依赖与前置条件
系统要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| 操作系统 | macOS / Linux / Windows | 推荐 macOS 或 Linux |
| Rust | 最新稳定版 | 使用 rustup 安装 |
| Node.js | >= 20 | LTS 版本 |
| pnpm | >= 8 | 包管理器 |
| Git | 最新 | 版本控制 |
可选工具
# Cargo watch(开发时自动重新编译)
cargo install cargo-watch
# SQLx CLI(数据库迁移)
cargo install sqlx-cli
# Docker(远程部署)
# 参考 https://docs.docker.com/get-docker/
安装步骤
1. 克隆仓库
git clone https://github.com/BloopAI/vibe-kanban.git
cd vibe-kanban
2. 安装 Rust
# 使用 rustup 安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
# 验证安装
rustc --version
cargo --version
3. 安装 Node.js 和 pnpm
# 使用 nvm 安装 Node.js(推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 20
nvm use 20
# 安装 pnpm
npm install -g pnpm
# 验证安装
node --version # 应该 >= 20
pnpm --version # 应该 >= 8
4. 安装依赖
# 安装 pnpm 依赖
pnpm install
# 安装 Rust 依赖(自动完成)
5. 准备数据库
# 初始化 SQLite 数据库(本地)
pnpm run prepare-db
# 如果是远程部署,准备 PostgreSQL
pnpm run remote:prepare-db
启动开发环境
启动本地开发服务器
# 一键启动(后端 + 前端)
pnpm run dev
这会:
- 自动分配端口(前端 3000,后端 3001)
- 启动 Rust 后端(带热重载)
- 启动 Vite 前端开发服务器
- 自动打开浏览器
分别启动
# 只启动后端(带热重载)
pnpm run backend:dev:watch
# 只启动前端
pnpm run local-web:dev
访问应用
打开浏览器访问:http://localhost:3000(端口可能自动变化,查看终端输出)
关键环境变量
本地开发环境变量
创建 .env 文件(可选):
# 端口配置(可选,默认自动分配)
FRONTEND_PORT=3000
BACKEND_PORT=3001
HOST=127.0.0.1
# MCP 服务器配置
MCP_HOST=127.0.0.1
MCP_PORT=3001
# PostHog 分析(可选)
POSTHOG_API_KEY=your_key
POSTHOG_API_ENDPOINT=https://app.posthog.com
# 允许的来源(自托管时必需)
VK_ALLOWED_ORIGINS=http://localhost:3000
# 禁用工作树清理(调试用)
DISABLE_WORKTREE_CLEANUP=1
远程部署环境变量
# JWT 密钥(必需)
VIBEKANBAN_REMOTE_JWT_SECRET=your-secret-key-here
# OAuth 配置(至少一个)
VIBEKANBAN_REMOTE_GITHUB_CLIENT_ID=github_client_id
VIBEKANBAN_REMOTE_GITHUB_CLIENT_SECRET=github_client_secret
VIBEKANBAN_REMOTE_GOOGLE_CLIENT_ID=google_client_id
VIBEKANBAN_REMOTE_GOOGLE_CLIENT_SECRET=google_client_secret
# 数据库配置
DATABASE_URL=postgresql://user:password@localhost:5432/vibekanban
# ElectricSQL 配置
ELECTRIC_URL=http://localhost:3000
# 前端 URL(构建时)
VITE_APP_BASE_URL=https://vibekanban.example.com
VITE_API_BASE_URL=https://api.vibekanban.example.com
构建生产版本
构建本地版本
# 构建 Rust 后端
cargo build --release
# 构建前端
cd packages/local-web
pnpm run build
# 构建 npx CLI
pnpm run build:npx
cd npx-cli
pnpm pack
Docker 构建(远程)
# 从 crates/remote 目录构建
cd crates/remote
docker compose --env-file .env.remote up --build
测试
# Rust 测试
cargo test --workspace
# 前端类型检查
pnpm run check
# 前端 Lint
pnpm run lint
# 后端 Lint
pnpm run backend:lint
格式化代码
# 格式化所有代码
pnpm run format
7. 推荐学习路线
第一阶段:入门(1-2 周)
目标
理解项目整体架构,能够运行和调试项目
阅读顺序
-
项目文档(第 1 天)
README.md- 项目概述AGENTS.md- 开发规范docs/getting-started.mdx- 用户入门指南
-
代码结构(第 2-3 天)
Cargo.toml- Rust workspace 配置package.json- pnpm workspace 配置crates/server/src/main.rs- 后端入口packages/local-web/src/app/entry/App.tsx- 前端入口
-
核心概念(第 4-5 天)
- Workspace 概念:
crates/db/src/models/workspace.rs - Session 概念:
crates/db/src/models/session.rs - Executor 概念:
crates/executors/src/executors/mod.rs
- Workspace 概念:
-
实践任务
- 在本地成功启动项目
- 创建一个 Workspace 并运行一次 Agent
- 修改前端的一个文本,验证热重载
关键理解点
- 理解 Workspace、Session、ExecutionProcess 的关系
- 理解前后端通信流程
- 理解 ElectricSQL 同步机制的基本原理
- 能够使用调试工具(tracing 日志、浏览器 DevTools)
第二阶段:进阶(2-4 周)
目标
深入理解核心模块,能够修改和扩展功能
学习模块
-
Executor 系统(第 1 周)
- 阅读:
crates/executors/src/executors/mod.rs - 深入一个具体执行器:
crates/executors/src/executors/claude.rs - 理解 MCP 配置:
crates/executors/src/mcp_config.rs - 实践:添加一个新的 Executor 配置选项
- 阅读:
-
数据库层(第 2 周)
- 阅读:
crates/db/src/models/mod.rs - 理解迁移系统:
crates/db/migrations/ - 学习 SQLx 使用模式
- 实践:添加一个新字段到 Workspace 表
- 阅读:
-
HTTP API 层(第 3 周)
- 阅读:
crates/server/src/routes/mod.rs - 深入一个路由:
crates/server/src/routes/workspaces.rs - 理解中间件:
crates/server/src/middleware/mod.rs - 实践:添加一个新的 API 端点
- 阅读:
-
前端架构(第 4 周)
- 阅读:
packages/local-web/src/routes/_app.tsx - 理解 TanStack Router:
packages/local-web/src/routeTree.gen.ts - 学习 ElectricSQL 集成:
packages/web-core/src/hooks/ - 实践:添加一个新的前端页面
- 阅读:
关键理解点
- 理解 Executor trait 设计和实现
- 掌握 SQLx 异步数据库操作
- 理解 Axum 路由和中间件机制
- 掌握 TanStack Router 和 ElectricSQL 集成
- 理解前后端类型生成机制(ts-rs)
第三阶段:精通(1-2 月)
目标
掌握高级主题,能够独立设计和实现新功能
深入学习
-
ElectricSQL 深度集成
- 阅读:
crates/remote/src/shapes.rs - 理解 Shape 定义和同步机制
- 学习 txid 握手机制
- 实践:添加一个新的 Shape 定义
- 阅读:
-
远程部署架构
- 阅读:
crates/remote/src/app.rs - 理解认证授权系统
- 学习 Docker 部署配置
- 实践:部署一个本地测试实例
- 阅读:
-
性能优化
- 学习缓存策略:
crates/executors/src/executors/utils.rs - 理解并发控制
- 分析性能瓶颈
- 实践:优化一个慢查询或接口
- 学习缓存策略:
-
测试与质量保证
- 学习 Rust 测试:
crates/*/src/**/*.rs中的#[cfg(test)] - 学习前端测试策略
- 实践:为新功能编写测试
- 学习 Rust 测试:
实践项目建议
-
添加新的 AI Agent 支持
- 实现一个新的 Executor
- 添加前端配置 UI
- 编写文档
-
增强 MCP 配置管理
- 添加 MCP 服务器市场
- 实现配置导入导出
- 添加配置验证
-
改进工作区管理
- 添加工作区模板
- 实现工作区克隆
- 添加批量操作
-
优化用户体验
- 添加键盘快捷键
- 改进错误提示
- 添加性能监控
8. 常见问题与注意事项
容易踩的坑
1. 端口冲突
问题:启动时报端口已被占用
解决方案:
# 方法 1:使用自动端口分配(默认)
unset FRONTEND_PORT BACKEND_PORT
pnpm run dev
# 方法 2:手动指定不同端口
export FRONTEND_PORT=3002
export BACKEND_PORT=3003
pnpm run dev
# 方法 3:查找占用端口的进程
lsof -i :3000 # macOS/Linux
netstat -ano | findstr :3000 # Windows
2. 数据库迁移失败
问题:pnpm run prepare-db 失败
可能原因:
- SQLx 离线模式数据过期
- 数据库文件损坏
解决方案:
# 删除旧数据库和缓存
rm -rf crates/db/.sqlx
rm -rf assets/db.v2.sqlite
# 重新准备数据库
pnpm run prepare-db
3. Rust 编译错误
问题:依赖编译失败或版本冲突
解决方案:
# 更新 Rust 工具链
rustup update
# 清理构建缓存
cargo clean
# 重新构建
cargo build
4. 前端类型错误
问题:TypeScript 类型不匹配
可能原因:
- Rust 类型变更后未重新生成 TypeScript 类型
解决方案:
# 重新生成类型
pnpm run generate-types
# 检查类型
pnpm run check
5. ElectricSQL 同步问题
问题:前端数据不同步或卡住
排查步骤:
- 检查 ElectricSQL 服务是否运行
- 查看浏览器控制台网络请求
- 检查 Shape 定义是否正确
- 验证 txid 握手机制
代码中的特殊约定
1. 命名规范
Rust:
// 文件名:snake_case
// crates/executors/src/standard_coding_agent.rs
// 模块名:snake_case
mod coding_agent;
// 类型名:PascalCase
pub struct CodingAgent;
// 函数名:snake_case
pub fn create_workspace() {}
// 常量:SCREAMING_SNAKE_CASE
const MAX_WORKSPACE_NAME_LEN: usize = 60;
TypeScript:
// 文件名:kebab-case 或 PascalCase(组件)
// routes/workspace-list.tsx
// components/WorkspaceCard.tsx
// 组件名:PascalCase
function WorkspaceCard() {}
// 函数/变量:camelCase
const createWorkspace = () => {};
// 类型:PascalCase
interface Workspace {
id: string;
name: string;
}
// 常量:UPPER_CASE
const MAX_NAME_LENGTH = 60;
2. 错误处理约定
// 库代码:使用具体错误类型
pub fn operation() -> Result<T, SpecificError> { ... }
// 应用代码:使用 anyhow
pub fn operation() -> anyhow::Result<T> { ... }
// 永远不要吞掉错误
// 错误做法:
if let Err(e) = operation() {
// 什么都不做
}
// 正确做法:
if let Err(e) = operation() {
tracing::error!(error = %e, "Operation failed");
return Err(e);
}
3. 数据库操作约定
// 所有数据库操作使用 sqlx::query! 或 sqlx::query_as!
// 编译时检查 SQL 语法
// 查询单个记录
let workspace = sqlx::query_as!(
Workspace,
r#"SELECT * FROM workspaces WHERE id = $1"#,
id
)
.fetch_optional(pool)
.await?;
// 查询多个记录
let workspaces = sqlx::query_as!(
Workspace,
r#"SELECT * FROM workspaces WHERE archived = FALSE"#,
)
.fetch_all(pool)
.await?;
// 插入记录
let workspace = sqlx::query_as!(
Workspace,
r#"INSERT INTO workspaces (id, branch, ...) VALUES ($1, $2, ...) RETURNING *"#,
id,
branch,
...
)
.fetch_one(pool)
.await?;
4. 前端数据获取约定
// 使用 ElectricSQL 进行实时数据
const workspaces = useQuery(
workspacesShape,
{ projectId },
{ orderBy: { updated_at: 'desc' } }
);
// 使用 TanStack Query 进行一次性请求
const { data } = useQuery({
queryKey: ['workspace', workspaceId],
queryFn: () => fetch(`/api/workspaces/${workspaceId}`),
});
// 使用 Mutation 进行写操作
const mutation = useMutation({
mutationFn: (data) => fetch('/api/workspaces', {
method: 'POST',
body: JSON.stringify(data),
}),
});
值得注意的 TODO / 技术债务
已知限制
-
SQLite vs PostgreSQL 差异
- 本地使用 SQLite,远程使用 PostgreSQL
- 某些 SQL 语法不兼容,需要注意
- [待确认] 是否有自动化测试覆盖两种数据库
-
ElectricSQL 仅用于远程版本
- 本地版本没有实时同步
- 前端需要处理两种数据源
- 可能增加维护成本
-
Executor 配置的持久化
- 当前配置存储在用户本地文件
- 多设备同步需要手动导出导入
- [待确认] 是否有云同步计划
-
测试覆盖率
- 核心逻辑有单元测试
- 集成测试覆盖不足
- 前端测试较少
-
文档完整性
- 用户文档较完整
- API 文档需要补充
- 架构设计文档需要更新
改进方向
-
性能优化
- 数据库查询优化(添加更多索引)
- 前端 bundle 大小优化
- ElectricSQL 同步性能
-
用户体验
- 添加更多键盘快捷键
- 改进错误提示和恢复
- 添加引导教程
-
可维护性
- 增加集成测试
- 完善 API 文档
- 添加架构决策记录(ADR)
-
功能扩展
- 支持更多 AI Agent
- 添加团队协作功能
- 集成更多 Git 托管平台
附录:快速参考
常用命令速查
# 开发
pnpm run dev # 启动开发环境
pnpm run backend:dev:watch # 只启动后端(热重载)
pnpm run local-web:dev # 只启动前端
# 构建
pnpm run build:npx # 构建 npx CLI
cargo build --release # 构建 Rust 后端
# 测试
cargo test --workspace # Rust 测试
pnpm run check # 前端类型检查
pnpm run lint # 前端 Lint
# 数据库
pnpm run prepare-db # 准备 SQLite 数据库
pnpm run remote:prepare-db # 准备 PostgreSQL 数据库
# 类型生成
pnpm run generate-types # 生成 TypeScript 类型
pnpm run remote:generate-types # 生成远程类型
# 代码格式化
pnpm run format # 格式化所有代码
关键目录速查
# 后端核心
crates/server/src/main.rs # 后端入口
crates/server/src/routes/mod.rs # 路由定义
crates/db/src/models/ # 数据模型
crates/executors/src/executors/ # Agent 执行器
# 前端核心
packages/local-web/src/routes/ # 页面路由
packages/web-core/src/hooks/ # 共享 Hooks
packages/ui/src/components/ # UI 组件
# 配置
Cargo.toml # Rust workspace 配置
package.json # pnpm workspace 配置
crates/db/migrations/ # 数据库迁移
关键概念速查
| 概念 | 说明 | 关键文件 |
|---|---|---|
| Workspace | 独立的工作区(Git 分支) | crates/db/src/models/workspace.rs |
| Session | 工作区中的会话(Agent 实例) | crates/db/src/models/session.rs |
| ExecutionProcess | 具体的执行进程 | crates/db/src/models/execution_process.rs |
| Executor | AI Agent 执行器抽象 | crates/executors/src/executors/mod.rs |
| MCP | Model Context Protocol | crates/executors/src/mcp_config.rs |
| Shape | ElectricSQL 数据订阅 | crates/remote/src/shapes.rs |
| txid | Postgres 事务 ID(用于同步) | crates/remote/src/response.rs |
最后更新:2026-02-25
维护者:Vibe Kanban 团队
贡献指南:请在 PR 前通过 GitHub Discussions 或 Discord 讨论想法和变更