教程定位:这是一份以 kanban(github.com/cline/kanban,Apache-2.0,v0.1.70) 为蓝本的从零构建教程。你将扮演一个能胜任产品经理的开发者:从 PRD 出发,把 kanban 拆成可执行的开发计划——每个功能模块按开发顺序罗列,讲清楚目的、实现形式、技术栈、功能定义取舍、优劣分析、如何互相配合。不放过大大小小的技术细节。 核心叙事:跟着「用户点下卡片上的播放按钮」这个动作,从零走到尾——它如何创建独立 worktree、如何启动一个 Agent、Agent 的每条消息如何通过 hooks 反向注入回卡片、如何 review diff、如何 commit/开 PR、如何通过依赖链自动启动下一张卡。这条链就是整个项目的骨架。 阅读建议:先读第 1 章(PRD)和第 2 章(总览与里程碑),然后按顺序推进。第 20 章(完整工作流)是全书的高潮,建议在读完模块 1-9 后精读。
目录
第一部分 · 立项与地基
第二部分 · 看板核心(按序构建)
第三部分 · 终端与多 Agent 执行
第四部分 · Cline 深度集成
第五部分 · 前端与体验
第六部分 · 安全、收尾与完整工作流
- 模块 18:安全四层(passcode / Host / CORS / token)
- 模块 19:CLI 与子命令(task / hooks / update)
- 模块 20:桌面端 Electron
- 「用户点下播放按钮之后」:完整工作流详解
- 测试策略与工程化(dogfood / CI/CD)
第 1 章 PRD:我们要做什么、不做什么
1.1 一句话产品定义
kanban 是一个跑在本地的多 Agent 看板:每张卡片 = 一个独立 git worktree + 一个独立终端会话,把多个编码 Agent(Claude Code / Codex / Cline / Droid / Kiro)并行组织起来干活,用看板四列管流程、卡片链接做依赖链、hooks 反向注入实时状态,支持 diff 审查、评论回传、自动 commit / 自动开 PR。(README 原话:A replacement for your IDE better suited for running many agents in parallel and reviewing diffs)
1.2 用户与场景
- 核心用户:重度使用编码 Agent 的开发者/团队——并行跑 10~100 个任务时,IDE 已经装不下它们。
- 场景 A(并行):一个大需求拆成 20 张卡,同时跑,每张卡独立 worktree,零 merge conflict。
- 场景 B(流水线):卡 A 完成 → 自动 commit → 卡 B(链接等待者)自动启动 → 再 commit → 卡 C……全自主链式执行。
- 场景 C(审查):Agent 干完活进 review 列,人看 diff、行内评论、回传,满意后一键 Commit / Open PR。
- 场景 D(监控):一眼扫几百张卡,每张卡实时显示 Agent 当前在说什么、在用什么工具。
1.3 功能需求(FR)与优先级
| 编号 | 功能 | 优先级 |
|---|---|---|
| FR-1 | 本地 server + 浏览器看板(四列:backlog/in_progress/review/trash) | P0 |
| FR-2 | 每卡独立 git worktree + gitignored 路径 symlink 共享 | P0 |
| FR-3 | 每卡独立终端(node-pty + 浏览器 xterm) | P0 |
| FR-4 | 多 Agent 检测与适配(claude/codex/cline/droid/kiro) | P0 |
| FR-5 | hooks 反向注入实时状态(工具调用/消息显示在卡片上) | P0 |
| FR-6 | 卡片链接 → 依赖链自动启动 | P1 |
| FR-7 | diff 审查 + 行内评论回传 | P1 |
| FR-8 | Commit / Open PR(注入 prompt 让 Agent 自己 git) | P1 |
| FR-9 | auto-commit / auto-PR 全自主模式 | P1 |
| FR-10 | Cline native SDK 深度集成(Provider/OAuth/MCP) | P1 |
| FR-11 | 桌面端 Electron | P2 |
| FR-12 | 多项目管理 + git 历史可视化 + 快捷脚本按钮 | P2 |
1.4 非功能需求(NFR)
- 本地优先:一切跑在本地进程,浏览器只是控制面,数据不离开机器(telemetry 可关)。
- 零配置启动:
npx kanban直接可用,自动检测已装 Agent,无需账号。 - 并发安全:多实例、多标签页、多 viewer 共享同一 PTY 不串。
- 崩溃恢复:server 重启后卡片状态、会话、worktree 都能恢复。
- 安全:默认只绑 127.0.0.1;远程模式 passcode;防 DNS rebinding 和跨源读取。
- 性能:几百张卡的实时状态推送不卡(150ms 批合并、背压控制)。
1.5 明确不做什么(非目标)
- ❌ 不自己实现 Agent 主循环——Agent 是执行器,kanban 是编排层。
- ❌ 不管理 Agent 的 provider 密钥(PTY 路径)——那是各 Agent 自己的事;只有 Cline SDK 路径需要。
- ❌ 不自动 merge 代码——git 冲突交给 Agent 处理(它懂上下文)。
- ❌ 不做多人协作/云同步——本地单用户工具。
1.6 PRD 验收口径
npx kanban在任何 git 仓库根目录启动,浏览器自动打开看板。- 建卡 → 播放 → 卡片进入 in_progress,worktree 出现在
~/.cline/worktrees/。 - Agent 的消息/工具调用实时出现在卡片上(hooks 注入)。
- 卡完成 → review 列;点 Commit → worktree 变分支提交;trash → worktree 清理、依赖链自动启动下一卡。
- server 崩溃重启后,所有卡片状态和会话可恢复。
第 2 章 总览:架构三理念、里程碑、团队
2.1 目标架构(对齐 kanban v0.1.70)
浏览器(控制面,React SPA)
│ tRPC httpBatchLink / WS 状态流 / WS 终端 io+control
本地 runtime(唯一数据源)
├─ server 层:runtime-server / middleware / assets / runtime-state-hub
├─ API 层:tRPC 4 router(runtime/workspace/projects/hooks)
├─ 核心层:api-contract(zod) / workspace-state / task-board-mutations
├─ 执行层:terminal(PTY+双WS) / cline-sdk(ClineCore) / workspace(git)
└─ 持久化:~/.cline/kanban/(JSON)+ ~/.cline/worktrees/(git)
三个核心理念(来自 docs/architecture.md):
1. 浏览器只是控制面:渲染、发命令、收实时更新,不存状态。
2. 本地 runtime 是唯一数据源:projects/worktrees/sessions/git/流式状态全在本地进程。
3. 两条 Agent 执行路径:绝大多数 Agent = PTY 子进程;Cline = native SDK 会话(进程内)。
2.2 里程碑(约 6 个月,3 人团队)
| 阶段 | 内容 | 对齐版本 |
|---|---|---|
| M1(1 个月) | 脚手架 + 数据模型 + worktree + 基本看板(卡片增删/四列) | ~0.1.4 |
| M2(1 个月) | PTY 终端 + 会话管理器 + Claude Code 适配 + xterm | ~0.1.5 |
| M3(1 个月) | hooks 反向注入 + Codex/Droid/Kiro 适配 + 依赖链 | ~0.1.9 |
| M4(1 个月) | diff 审查 + 评论回传 + Commit/PR + auto-review | ~0.1.12 |
| M5(1 个月) | Cline SDK 集成 + Provider/MCP + 上下文压缩 | ~0.1.30 |
| M6(1 个月) | 桌面端 Electron + 多项目 + git 历史可视化 + 打磨发布 | ~0.1.70 |
2.3 团队分工(3 人)
- 后端工程师 A:server、git、终端、多 Agent 适配、hooks。
- 前端工程师 B:看板 UI、终端 UI、git 界面、状态同步。
- 全栈工程师 C:Cline SDK 集成、tRPC API、安全、桌面端、发布。
2.4 开发纪律(三条红线,来自 AGENTS.md)
- 无
any、禁 inline import(biome 强制)。 - 优先直接 PATH 检测而非交互 shell 启动 Agent——
which会 spawn shell,conda/nvm 环境会卡死 runtime。 - GritQL 静态检查:禁 console.*、禁 process.exit(除 CLI 入口)、禁 process.env 解构。
第 3 章 模块 0:项目脚手架与工程纪律
目的
一个 npm 包同时产出 CLI(bin: kanban)、库入口(main)、web-ui 静态资源、桌面端。工具链统一。
实现形式(对齐 kanban 的 package.json)
- 单包(非 monorepo)+ 子目录
web-ui/(独立 npm 包)+packages/desktop/(独立私有包)。根包type: module。 - 脚本:
build(clean → web:build → esbuild → 拷贝 web-ui 产物 → sentry sourcemap);dev(tsx watch);typecheck(tsc --noEmit);test(vitest);check(biome + typecheck + test)。 - 依赖:commander(CLI)、node-pty(PTY)、ws(WebSocket)、tRPC(server/client)、zod(契约)、proper-lockfile(文件锁)、tree-kill(进程树)、@xterm/headless(服务端终端镜像)、@clinebot/core(Cline 引擎)、@sentry/node(遥测)。
技术栈明细
TypeScript 5.9 + esbuild(打包)+ tsx(dev)+ Biome(lint/format)+ vitest(测试)+ husky(pre-commit:biome → typecheck → test:fast)+ GritQL(结构性检查)。
功能定义取舍
- 用 esbuild 而非 tsc 打包:快,且只需 external 一个
node-pty。 npm run dogfood是开发利器:先 build 再用生产 CLI 跑自己,强制"吃的自己做的饭"。
优劣分析
- ✅ 单包简单、发布只推一个 npm 包;❌ web-ui 和 desktop 各自依赖要手动
npm install(install:all脚本)。 - ✅ tsx watch 秒级热重载;❌ esbuild 不做类型检查(typecheck 单独跑)。
如何互相配合
根包 build 产物 dist/ 是 CLI 与 web-ui 的交付形态;desktop 的 stage-cli.mjs 把 dist/ 复制进 Electron 壳;dogfood.mjs 依赖完整 build。
验收
npm run build && npx kanban起服务;npm run check全绿;npm run dogfood能跑起来。
第 4 章 模块 1:数据模型与 API 契约(zod 全契约)
目的
前后端 + CLI 三方共享同一份类型契约——一个 z 类型定义全 API,杜绝"前端说东后端说西"。
实现形式(对齐 src/core/api-contract.ts,1277 行)
- 所有请求/响应 schema 成对命名
runtimeXxxRequestSchema / ResponseSchema+ 导出推断类型。 - 核心类型:
RuntimeBoardCard(id/title/prompt/startInPlanMode/autoReviewMode/images/agentId/clineSettings/baseRef/createdAt/updatedAt)、RuntimeTaskSessionSummary(state/mode/reviewReason/latestHookActivity)、看板 4 列、dependencies(fromTaskId→toTaskId)。 - 会话状态机:
idle/running/awaiting_review/failed/interrupted,reviewReason: attention|exit|error|interrupted|hook。 - WS 推送消息是 discriminatedUnion(11 种:snapshot/workspace_state_updated/task_sessions_updated/task_ready_for_review/task_chat_message…)。
src/core/api-validation.ts(605 行)二次校验:trim、必填、去重、路径沙箱。
技术栈明细
zod v4 + TypeScript 类型推断。web-ui 通过 alias @runtime-trpc import 同一份契约。
功能定义取舍
- 卡片
title由 prompt 自动推导(截 80 字)而非手填——减少用户负担;task-id用 5 位短 id 便于口头交流。 - "done" 兼容映射到 trash——早期叫 done,后来统一为 trash(回收站语义:可恢复)。
优劣分析
- ✅ 契约即文档,改类型全链路编译期报错;❌ 契约文件会膨胀(1277 行),需要纪律维护。
- ✅ discriminatedUnion 让 WS 消息类型安全;❌ 加一种消息要动契约+后端+前端三处。
如何互相配合
模块 2(状态持久化)序列化的是这里的类型;模块 5(tRPC)的 procedure 输入输出直接引用这里的 schema;模块 6(状态流)的推送消息就是这里的 discriminatedUnion。
验收
- 前端 import 契约类型通过 typecheck;改一个字段,三端(server/CLI/web)同时编译报错。
第 5 章 模块 2:看板状态机与乐观并发持久化
目的
看板状态(卡片/列/依赖/会话摘要)安全持久化,多标签页并发编辑不丢数据。
实现形式(对齐 src/state/workspace-state.ts 745 行 + src/core/task-board-mutations.ts 657 行 + src/fs/locked-file-system.ts 165 行)
- 存储布局:
~/.cline/kanban/workspaces/<id>/{board.json, sessions.json, meta.json}。 - 纯函数式 mutation:
task-board-mutations.ts的 add/move/update/trash/delete/link 每次返回新 board,不改原对象——与 React 不可变更新同构。 - revision 乐观并发:
saveWorkspaceState({board, sessions, expectedRevision}),meta.json 自增 revision,不匹配抛WorkspaceStateConflictError并携带currentRevision。 - 原子写 + 文件锁:
locked-file-system.ts用 proper-lockfile,withLocks按 lockfile 路径排序加锁(防死锁),writeTextFileAtomictmp+rename。
技术栈明细
TypeScript + proper-lockfile + node:fs。无数据库——JSON 文件足够,且人类可读可修。
功能定义取舍
- 不用 SQLite/leveldb:看板状态是低频小数据,JSON + 原子写足够;会话消息体量大走 SDK 自己的存储(见模块 12)。
- 锁粒度:整个 workspace state 一个锁,简单可靠;代价是高频操作排队。
优劣分析
- ✅ 文件即真相,崩溃不丢(tmp+rename);✅ 乐观并发比悲观锁更适合前端交互(不阻塞拖拽);❌ 文件锁在 NFS/某些网络盘上不可靠;❌ 没有事务(多文件不一致时靠恢复逻辑兜底)。
如何互相配合
模块 1 定义类型 → 模块 2 持久化;模块 5 的 workspace.saveState procedure 暴露给前端(带 expectedRevision);前端 120ms 防抖调用(模块 14)。
验收
- 两个标签页同时拖卡片,后保存的收到 CONFLICT,前端自动同步重试;
kill -9server 后重启,看板状态完好。
第 6 章 模块 3:git 层——worktree、symlink、checkpoint、git 界面数据
目的
用 git 的机制做资源隔离:每卡一个 worktree,Agent 并行零冲突;symlink 共享依赖省安装成本;checkpoint 记录每轮改动。
实现形式(对齐 src/workspace/ 全部)
- worktree 生命周期(
task-worktree.ts,695 行): git worktree add --detach <baseCommit>,路径~/.cline/worktrees/<taskId>/<workspaceLabel>。- detached HEAD 不建分支——分支在 Commit/Open PR 时才动态创建。
- worktree 已存在即权威,不因 base 前进重建;创建过程在
.git/kanban-task-worktree-setup.lock锁内。 - trash 时
captureTaskPatch存 diff 到~/.cline/kanban/trashed-task-patches/,再git worktree remove --force;resume 时git apply恢复。 - symlink 共享依赖(
syncIgnoredPathsIntoWorktree,L358):git ls-files --others --ignored --exclude-per-directory=.gitignore --directory列 gitignored 路径 → 原位symlink(主仓库→worktree)→.git/info/exclude写入 kanban-managed block → 失败静默跳过 →getUniquePaths只保留最浅根。 - checkpoint(
turn-checkpoints.ts,92 行):每消息轮后用临时 GIT_INDEX_FILE(/tmp)创建 commit(作者 kanban-checkpoint),update-ref refs/kanban/checkpoints/<base64(taskId)>/turn/<N>——不污染分支,纯 ref。 - git 命令封装(
git-utils.ts):execFile("git", ["-c","core.quotepath=false", ...]),10MB maxBuffer;非 0 退出码不抛异常,返回{ok,stdout,stderr,exitCode}。 - git 界面数据(
git-history.ts,478 行):git log --topo-order --date-order+\x1f/\x1e分隔 +for-each-ref+rev-list --not算 relation。 - 同步动作(
git-sync.ts,385 行):fetch/pull --ff-only(有本地改动拒绝)/push/checkout/discard。 - Turbopack 例外(
task-worktree-turbopack.ts):Turbopack 跟随 symlink 会坏缓存 → 检测到则 node_modules 不 symlink。 - 路径沙箱(
path-sandbox.ts):isPathWithinRoot防越界。
技术栈明细
git CLI(不引 node-git 库——直接用 execFile 更可控)+ node:fs symlink + proper-lockfile。
功能定义取舍
- detached worktree 而非一卡一分支:分支语义(基于哪个 base、何时 push)留到 commit/PR 时决定,避免提前造一堆分支。
- kanban 不自动 commit/merge:auto-commit 靠注入 prompt 让 Agent 自己 git(模块 17)——git 冲突处理交给懂上下文的 Agent。
- checkpoint 用临时 index + commit-tree 而非直接 commit:不污染工作区、不触发 hooks、不碰分支。
优劣分析
- ✅ worktree 是 git 原生机制,隔离彻底、可审计;✅ symlink 省掉每卡一份 node_modules(几 GB);❌ Agent 改 gitignored 文件会串(README 明说别这么用);❌ Turbopack 是特例,得维护检测逻辑。
如何互相配合
模块 2 持久化看板状态(worktree 是卡片的"影子");模块 7 的 PTY 进程 cwd 指向 worktree;模块 10 的 workspace-trust 只对 kanban 创建的 worktree 自动信任;模块 14 的 diff 面板调 getWorkspaceChanges。
验收
- 并行开 3 张卡,3 个 worktree 各自 git log 独立;node_modules 是 symlink 且不提交;trash 卡后
~/.cline/kanban/trashed-task-patches/有 patch,resume 后文件恢复。
第 7 章 模块 4:HTTP 服务器与静态资源
目的
一个本地 HTTP(S) server:服务 React 静态资源 + 挂 tRPC + 挂 WS + 挂 MCP OAuth 回调。
实现形式(对齐 src/server/runtime-server.ts 519 行 + middleware.ts 167 行 + assets.ts)
- 原生
node:http/node:https,不用 express(依赖少、控制细)。 - 请求链路:middleware(Host 白名单 + CORS 门)→ passcode gate(仅远程模式)→ MCP OAuth callback → tRPC handler(
createHTTPHandler,basePath/api/trpc)→ 静态资源(多路径探测 web-ui 构建产物、防路径穿越、SPA fallback index.html)。 - WS 在
server.on("upgrade")按 pathname 分流:/api/runtime/ws(状态流)、/api/terminal/io、/api/terminal/control。 - 支持
--https --cert --key(自签证书远程访问)。
技术栈明细
node:http + ws + @trpc/server/adapters/standalone。
功能定义取舍
- 用 standalone adapter 而非 express:零框架依赖;HTTP handler 与 WS upgrade 共用同一 server 实例。
- SPA fallback:任何非 API 路径都回 index.html(React 无路由,靠 URL query 记状态)。
优劣分析
- ✅ 原生 http 性能好、无框架包袱;❌ 所有中间件手写(Host/CORS/passcode),要自己保证正确。
- ✅ 静态资源与 API 同端口,一个 URL 搞定;❌ 开发时 Vite dev server 是另一个端口(dev-full.mjs 协调)。
如何互相配合
模块 5(tRPC)挂进来;模块 6(状态流)和模块 7(终端 WS)在 upgrade 处分流;模块 18(安全)的 passcode 在这里做门。
验收
curl http://127.0.0.1:<port>/返回 index.html;/api/trpc/...走 tRPC;/api/runtime/wsupgrade 成功。
第 8 章 模块 5:tRPC API 层(4 个 router + 依赖注入)
目的
前后端类型安全的远程调用层,同时服务 web-ui、CLI、hooks 子进程三个客户端。
实现形式(对齐 src/trpc/app-router.ts 731 行 + 4 个 API 文件)
runtimeAppRouter暴露 4 个 router:runtime(约 40 procedure,会话/Cline/MCP/shell/更新)、workspace(14,git/changes/worktree/state/文件搜索)、projects(5,多项目管理)、hooks(1,事件摄入)。- 薄路由 + 依赖注入:每个 procedure 只是 schema 配对后委托给 context 里的 service。
workspaceProcedure中间件强制工作区作用域(从x-kanban-workspace-id头解析 workspaceId/workspacePath)。errorFormatter透出 CONFLICT 的currentRevision。- 传输纯 HTTP(
httpBatchLink);三个客户端:web-ui(按 workspaceId 缓存 client)、CLI task.ts/hooks.ts(自定义 fetch + 内部 Bearer token)。
技术栈明细
@trpc/server v11 + @trpc/client + zod。无 tRPC ws adapter——WS 只用于状态流和终端。
功能定义取舍
- runtime procedure 是"协调器"而非"执行器":
startTaskSession内部按 agentId 分派 SDK/PTY 两条路径。 - 看板 CRUD 不进 tRPC workspace 路由——CLI 直接对 JSON 文件做
mutateWorkspaceState(乐观并发),改完调workspace.notifyStateUpdated广播。一条写路径(文件),读走 tRPC。
优劣分析
- ✅ 类型安全贯穿三端;✅ procedure 薄、逻辑在 service 层可单测;❌ 40+ procedure 的文件要小心组织(按域拆分)。
- ✅ CLI 与 web 同一套 API 语义;❌ hooks 子进程要手动管理 token 认证。
如何互相配合
模块 1 契约定义输入输出;模块 2 状态层被 workspace.api 调用;模块 10 的 hooks 子进程是 hooks.api 的客户端;模块 14 前端 trpc-client 直连。
验收
- web-ui 调用
runtime.startTaskSession能启动会话;CLIkanban task start与 web 操作同一张卡效果一致。
第 9 章 模块 6:实时状态流(WS snapshot + 增量 fanout)
目的
几百张卡片的实时状态(Agent 消息/工具调用/看板变化)毫秒级推到浏览器。
实现形式(对齐 src/server/runtime-state-hub.ts 604 行)
- 单一 WS 端点
/api/runtime/ws?workspaceId=。 - 首次连接发全量
snapshot;之后增量推送,消息类型 11 种(discriminatedUnion): workspace_state_updated/task_sessions_updated(150ms 批合并——高频事件合并成一条)task_chat_message/task_chat_clearedtask_ready_for_review/workspace_metadata_updated/mcp_auth_updated/cline_session_context_updated- fanout:所有订阅者广播;按 workspaceId 隔离。
技术栈明细
ws + 内存 hub(Map
功能定义取舍
- 用 WS 而非 SSE:双向(浏览器要发 ack/控制消息),且与终端 WS 同栈。
- 批合并 150ms:把"一个工具的多个事件"压成一次推送,降客户端渲染压力。
优劣分析
- ✅ 增量优于轮询(省 CPU、实时);✅ snapshot 让新标签页秒开;❌ 状态都在内存,server 重启靠模块 2 的文件恢复 + 前端 refetch 校准。
如何互相配合
模块 5 的 service 在状态变化时调 hub 广播;模块 14 的 use-runtime-state-stream 是它的前端镜像;模块 10 的 hooks.ingest 迁移状态后也触发广播。
验收
- 开两个标签页,一张卡拖动,另一标签页实时同步;agent 发消息,卡片活动区毫秒级更新。
第 10 章 模块 7:PTY 终端与双 WebSocket
目的
每张卡一个真实终端(Agent 的 TUI 跑在里面),浏览器里的 xterm 与它实时双向同步。
实现形式(对齐 src/terminal/pty-session.ts 161 行 + ws-server.ts 591 行 + terminal-protocol-filter.ts + terminal-state-mirror.ts)
- node-pty:
PtySession.spawn()真实 PTY;stop()杀进程组(process.kill(-pid, SIGTERM))。 - 双 WS(
server.on("upgrade")按 pathname 分流): /api/terminal/io:原始字节流——服务端ws.send(chunk)直接喂 xterm;浏览器消息即键盘字节透传writeInput。/api/terminal/control:JSON——{type:"state",summary}/{type:"exit",code}/{type:"restore",snapshot,cols,rows};浏览器发resize/stop/output_ack/restore_complete。- 多标签页共享:同任务多个 viewer 共享一个 PTY,
TerminalStateMirror(@xterm/headless + SerializeAddon)给新 viewer 离线快照。 - 背压(仿 VS Code):跟踪每 viewer 的
bufferedAmount+未 ack 字节(高水位 100KB),任一落后 →pauseOutput暂停共享 PTY,全跟上 →resumeOutput。 - 协议过滤:逐字节解析 ESC/OSC/CSI;拦截
OSC 10;?/11;?代答颜色;丢弃 Droid DA 查询(CSI c);半截序列pendingChunk暂存。 - workspace-trust 自动确认:检测"trust this folder"提示自动回车(只对 kanban 创建的 worktree)。
技术栈明细
node-pty(原生模块,desktop 里要 patch)+ ws + @xterm/headless + @xterm/addon-serialize。
功能定义取舍
- 服务端镜像 xterm:headless xterm 跑在服务端做滚动缓冲,浏览器只需渲染——这就是"持久终端"的根基(切换卡片不丢内容)。
- 协议过滤在服务端做而非浏览器:浏览器可能还没连上,TUI 的查询会被挂住。
优劣分析
- ✅ 字节级透传 = 任何 TUI 都能跑(不只是 Agent,还能跑 vim);✅ 背压保护 Agent 进程不被慢浏览器拖垮;❌ node-pty 是原生模块,跨平台构建/Electron 打包都要处理(patch-node-pty.mjs 修 asar 双后缀 bug)。
如何互相配合
模块 8(会话管理器)持有 PtySession;模块 9(Agent 适配)决定 spawn 什么命令;模块 15(前端持久终端)是 io/control 的浏览器端;模块 6 的 hub 与终端 WS 各自独立。
验收
- 浏览器 xterm 能跑
vim;两个标签页共享同一 PTY 输出一致;一个标签页断线重连后 restore 出完整滚动缓冲。
第 11 章 模块 8:会话管理器与状态机
目的
并发管理所有任务的会话:启动/停止/输入/崩溃恢复/重启持久化。
实现形式(对齐 src/terminal/session-manager.ts 1040 行 + session-state-machine.ts 78 行 + agent-registry.ts 128 行)
TerminalSessionManager:Map<taskId, SessionEntry>,每 entry 持 summary + active PtySession + listeners + restartRequest。- 崩溃自动重启:
shouldAutoRestart(5 秒窗口内最多 3 次)→scheduleAutoRestart用保存的 restartRequest 重跑。 - 状态机(纯函数
reduceSessionTransition):idle/running/awaiting_review/failed/interrupted;事件驱动迁移(hook.to_review、process.exit 等)。 hydrateFromRecord:重启后从磁盘恢复 summary。markInterruptedAndStopAll:优雅停全部(配合 shutdown-coordinator 集成测试)。agent-registry.ts:resolveAgentCommand(从 catalog 拼命令)、detectInstalledCommands、buildRuntimeConfigResponse(给设置 UI)。
技术栈明细
TypeScript + node-pty + tree-kill(server/process-termination.ts 双保险进程树清理)。
功能定义取舍
- 会话与卡片分离:卡片是流程实体,会话是执行实体,一个卡片生命周期内可多次 start/restart。
- 自动重启限频(5s/3 次)防死循环崩溃烧 CPU。
优劣分析
- ✅ 崩溃自愈是长跑 Agent 的刚需;✅ 状态机纯函数好测试;❌ 复杂状态迁移(reviewReason 细分)心智负担大,迁移规则散落多处。
如何互相配合
模块 7 提供 PtySession 原语;模块 10 的 hooks 事件是状态机输入;模块 5 的 runtime.startTaskSession 是入口;模块 6 广播 summary 变化。
验收
- 手动 kill Agent 进程,5 秒内自动重启且进度保留;连续崩溃 3 次后停止重启并标 failed。
第 12 章 模块 9:多 Agent 适配层(7 种 CLI Agent)
目的
检测用户装了什么 CLI Agent,为每种 Agent 生成正确的启动参数和 hook 配置——引擎中立。
实现形式(对齐 src/terminal/agent-session-adapters.ts 1451 行 + command-discovery.ts 78 行 + agent-catalog.ts 95 行)
- 命令发现:不用
which(spawn shell 有 conda/nvm 副作用),直接accessSync(X_OK)扫 PATH;Windows 走 PATHEXT。 - agent-catalog:7 个 Agent 的 autonomous 启动参数:claude
--permission-mode auto、codex--auto-approve-all、droid autonomyMode 等。README 依赖这些实验特性。 - 7 个适配器(
prepareAgentLaunch按 agentId 分发): - claude:
--settings注入 settings.json(7 种 hook 全指向kanban hooks notify) - codex:
-c features.hooks=true+ 5 个 config override + sha256 trust hash 预信任 +CODEX_TUI_RECORD_SESSION=1轮询 rollout jsonl - gemini:
GEMINI_CLI_SYSTEM_SETTINGS_PATH - opencode:动态生成 JS plugin,payload base64 后
$shell 调 kanban CLI - droid:settings.json(autonomyMode: spec/auto-high/normal)
- kiro:
~/.kiro/agents/kanban.json(tools:["*"] + hooks) - cline:
--hooks-dir下生成 bash/PowerShell 脚本
技术栈明细
TypeScript + 各 Agent 的官方 CLI 参数/hook 机制。
功能定义取舍
- 默认 cline(恒 installed:true,SDK 深度集成);其他 Agent 检测到才可选。
- opencode/gemini 在 v0.1.70 被注释掉(启用 5 种)——适配器是增量演进的,先上主力再补齐。
优劣分析
- ✅ 引擎中立是差异化卖点(Claude Code/Codex/Cline 一个看板全跑);❌ 每个 Agent 的 hook 机制不同,适配器是持续维护成本(Codex 甚至要包 wrapper 轮询日志);❌ 依赖 bypass 权限实验特性,README 自标 Research Preview。
如何互相配合
模块 10(hooks)是适配器的"出口";模块 7(PTY)跑适配器生成的命令;模块 2 的卡片 agentId 字段决定选哪个适配器;设置 UI 调 buildRuntimeConfigResponse。
验收
- 只装 claude 的机器上自动选 claude;claude/codex/cline 三台机器都能起任务且状态实时上卡。
第 13 章 模块 10:hooks 反向注入
目的
Agent 每说一句话、每次工具调用,都实时反映到卡片上——不用轮询终端输出(各 TUI 格式不可靠)。
实现形式(对齐 src/commands/hooks.ts 820 行 + hook-events/*)
核心一句话:Agent 在干活时,主动调用 kanban 自己的 CLI 来汇报状态。
Agent 事件 → hook 命令(kanban hooks notify --event ...)
→ 子进程从 stdin 读 JSON payload(或 --metadata-base64)
→ 解析 toolName/activityText/finalMessage
→ tRPC hooks.ingest.mutate 回传 runtime server
→ 状态机迁移 + 广播 workspace_state_updated / task_ready_for_review
ingest同步阻塞(等待 ack);notify后台分离进程(best-effort 不阻塞 Agent)。hook-events/按 Agent 解析各自 payload:codex(TUI session log + rollout jsonl 200ms 轮询 + fingerprint 去重)、kiro/droid(normalize 函数)。hook-runtime-context.ts:KANBAN_HOOK_TASK_ID/WS_ID环境变量传 task 归属。
技术栈明细
TypeScript + 子进程 stdin + tRPC mutate。hook 配置是各 Agent 官方机制。
功能定义取舍
- 状态归属用环境变量而非参数:hook 子进程从 Agent 继承 env,天然带 taskId。
notify分离进程:Agent 不能被 hook 阻塞(claude 的 hook 是同步的,慢会卡住 Agent)。
优劣分析
- ✅ 语义干净("这个工具跑完了"是事实,不是从乱码输出里猜);✅ 适配 7 种 Agent;❌ Codex 的 hooks 不可靠,被迫轮询 rollout jsonl(每 200ms,成本高);❌ 每个 Agent 一套 payload 格式,解析器是维护大头。
如何互相配合
模块 9 生成 hook 配置 → 模块 10 接收解析 → hooks.api(模块 5)摄入 → 状态机(模块 8)迁移 → hub(模块 6)广播 → 卡片 UI(模块 14)渲染。
验收
- Agent 开始跑工具,卡片实时显示 "Using read_files" / "Completed run_commands";Agent 请求权限时卡片变 awaiting_review。
第 14 章 模块 11:Cline SDK 集成(ClineCore 多会话宿主)
目的
把 Cline 引擎(@clinebot/core)深度嵌入:同进程、会话级集成、Provider/OAuth/MCP 全能力。
实现形式(对齐 src/cline-sdk/sdk-runtime-boundary.ts 126 行 + cline-session-runtime.ts 580 行)
- 唯一 import 边界:只在这里 import
@clinebot/core,重导出ClineCore.create({backendMode:"auto"})。 - 关键认知:npm 发布版
@clinebot/core@0.0.38暴露的是有状态多会话宿主——sessionsMap(start/send/stop/abort/get/list/readMessages/subscribe)、send()增量续聊、SessionHistoryRecord落盘。这与 Cline main 分支的无状态 AgentRuntime 不同(发布版 API 仍在快速变动,代码里留着 0.0.36 兼容分支)。 - 任务 ↔ 会话映射:
ClineSessionRuntime(自己定义的接口)+InMemoryClineSessionRuntime实现;sessionIdByTaskId/taskIdBySessionId双向 Map;createSessionId(taskId)带前缀,重启后从host.list()找回。 send()带delivery: "queue"|"steer"(排队 vs 打断当前回合)。cline-runtime-setup.ts:组装userInstructionService(skills/rules/workflows watch)、resolvePrompt(slash 命令)、requestToolApproval(默认全部 approved:true)。
技术栈明细
@clinebot/core ^0.0.38 + TypeScript。运行时边界刻意收窄到 126 行——换引擎版本只动这一个文件。
功能定义取舍
- 用 ClineCore 而非自己实现 SessionRuntime:引擎中立但 Cline 优先——Cline 是本家,深度集成是默认路径。
requestToolApproval默认全通过:看板模式(autonomous)就是要 Agent 自己干活;人工把关在 review 列(流程级)而非工具级。
优劣分析
- ✅ 白拿 Cline 全能力(Provider 目录/OAuth/MCP/账户);✅ 边界文件隔离版本差异;❌ SDK 版本演进风险(0.0.36/0.0.38 兼容分支说明 API 不稳);❌ 与 main 分支 API 不一致,看官方源码时容易困惑。
如何互相配合
模块 5 的 runtime procedure 调 ClineTaskSessionService;模块 12(事件翻译)消费 subscribe 事件;模块 13(Provider/MCP)是 SDK 的配置层;模块 2 的卡片 clineSettings 字段透传。
验收
- Cline 会话启动/续聊/打断全可用;切换 @clinebot/core 版本只改 boundary 文件。
第 15 章 模块 12:事件翻译与消息仓库
目的
把 SDK 推来的事件流翻译成卡片能显示的 summary + 消息;消息读回不重复存储。
实现形式(对齐 src/cline-sdk/cline-event-adapter.ts 816 行 + cline-message-repository.ts 364 行 + cline-session-state.ts 440 行 + cline-context-overflow-compaction.ts 125 行)
- 事件翻译(
applyClineSessionEvent):5 类事件(agent_event/chunk/hook/status/ended)→ summary patch + 消息 mutation:
| SDK 事件 | 卡片效果 |
|---|---|
| assistant-text-delta / content_start / content_end | assistant 消息 |
| tool-started / tool-finished | tool 消息(Tool: xxx / Input: ...) |
| run-finished / done | awaiting_review(hook/error/interrupted) |
| error / run-failed | awaiting_review(error) |
| ask_followup_question / plan_mode_respond | awaiting_review(attention) |
- 消息仓库:SDK 侧消息在
~/.cline会话存储(host.readMessages),kanban 只存内存视图,页面刷新后hydrateTaskMessages灌水。 - 上下文溢出压缩:send/start 报错 → 32 条正则判超长 → stop 旧会话 → 取后一半消息(截到 user 消息)→ 首条 user 消息预览嵌入 Cline 压缩说明 → restart 重跑。(临时方案,等 SDK 提供可插拔 compaction)
- 工具调用显示(
cline-tool-call-display.ts):read_files(a.ts:1-20, b.ts)摘要;kanban task create转人话 "Creating task"。
技术栈明细
TypeScript + SDK 事件类型。无额外依赖。
功能定义取舍
- kanban 不重复持久化 Cline 会话——信任引擎存储;消息视图纯内存 + hydrate。
- 压缩用"截断 + 说明"而非 LLM 摘要:快、零成本,副作用是丢上下文(看板场景可接受,用户可见)。
优劣分析
- ✅ 事件翻译规则表清晰可测(cline-event-adapter.test.ts);✅ 消息不重复存储;❌ 压缩是"丢记忆"式,长任务反复超长会退化;❌ 事件类型多,漏分支=静默丢消息。
如何互相配合
模块 11 的 subscribe 喂给 adapter;模块 6 把 chat 消息广播;模块 14 的 chat 面板渲染 ClineTaskMessage;模块 5 的 getTaskChatMessages 读仓库。
验收
- Agent 流式输出逐字上屏(chunk 合并);工具调用显示摘要;上下文超长自动压缩重启且卡片状态连续。
第 16 章 模块 13:Provider 与 MCP 服务
目的
Cline SDK 路径的模型配置与外部工具注入——只服务 Cline(PTY 路径的 Agent 用自己的配置)。
实现形式(对齐 src/cline-sdk/cline-provider-service.ts 1294 行 + cline-mcp-settings-service.ts 216 行 + cline-mcp-runtime-service.ts 879 行)
- Provider:
Llms.getAllProviders()目录 + 模型列表(本地/远程 catalog + LiteLLM 探测);managed OAuth 三类(cline/oca/openai-codex,token 加workos:前缀)+ device auth + 手动 apiKey + 环境变量;ClineAccountService查 profile/balance/org;resolveLaunchConfig()输出{providerId, modelId, apiKey, baseUrl, reasoningEffort}。 - MCP 配置:读写 Cline 标准文件
~/.cline/data/settings/cline_mcp_settings.json——与 Cline 扩展共享同一份配置。 - MCP 工具注入:每次 startTaskSession 时
createToolBundle():settings →InMemoryMcpManager→createMcpTools→localRuntime.extraTools注入 +disableMcpSettingsTools:true去重。 - MCP OAuth:自实现
OAuthClientProvider(持久化到cline_mcp_oauth_settings.json),回调走 kanban 端点/kanban-mcp/mcp-oauth-callback。
技术栈明细
@clinebot/core(Llms/ProviderSettingsManager)+ @modelcontextprotocol/sdk + zod。
功能定义取舍
- MCP 配置与 Cline 扩展共享文件:用户在 kanban 配的 MCP,在 Cline 扩展里也能用(生态互通)。
- OAuth 回调走 kanban 自己的端点:MCP 远程 server 授权不依赖浏览器。
优劣分析
- ✅ 复用 Cline 生态的 179 个 Provider ID;✅ 工具注入在会话级(每会话独立 tool bundle);❌ 1294 行的 provider 门面复杂度高;❌ MCP 工具与 Agent 内置工具同名时要小心去重。
如何互相配合
模块 5 的 runtime procedure(provider 增删改/OAuth/MCP 设置)→ 模块 11 的 start 参数;模块 12 的工具调用显示识别 MCP 工具名;桌面端(模块 20)的 OAuth relay 转发深链回调。
验收
- 浏览器里 OAuth 登录 Anthropic,账户余额显示;加一个 MCP server,卡片 Agent 能用它的工具。
第 17 章 模块 14:前端看板 UI(React + 乐观并发三件套)
目的
看板界面:拖拽卡片、实时状态、详情面板(chat + diff + 终端)、git 视图、项目导航。
实现形式(对齐 web-ui/src/App.tsx 1199 行 + state/board-state.ts + runtime/ 三件套 + components/ 89 文件)
- 布局:
ProjectNavigationPanel(左,可拖拽/折叠)+ TopBar + 主区。主区三形态:Home(KanbanBoard 或 GitHistoryView + 底部 home 终端)、CardDetailView(absolute inset-0覆盖,URL query 记 task id)、Fallback。 - 看板状态机(
board-state.ts约 2.1 万行):normalizeBoardData(schema 校验/清洗)、applyDragResult、trashTaskAndGetReadyLinkedTaskIds(级联依赖);drag-rules.ts(review 列不可手动放入、trash 只能进不能出)。 - 数据同步三件套:
use-runtime-state-stream.ts:WS 连接,useReducer store,指数退避重连(500ms→5s),snapshot 全量 + 增量合并。use-workspace-sync.ts:stream → normalizeBoardData → setBoard,revision 判重,页面可见时 refetch 校准。use-workspace-persistence.ts:120ms 防抖 save + expectedRevision 乐观锁;CONFLICT 时用 currentRevision 更新本地 + refetch 覆盖。- 卡片实时状态:
getCardSessionActivity()解析 summary 的 latestHookActivity → "Thinking..." / 工具标签 / "Waiting for review" / 最终消息,状态点颜色实时刷新。 - 组件库:@radix-ui 原语 + Tailwind v4 + lucide + @hello-pangea/dnd + react-virtuoso + sonner + motion(非 shadcn)。
技术栈明细
React 19 + Vite + Tailwind v4 + Radix + tRPC client + ws。
功能定义取舍
- 无 React Router:状态驱动视图,URL query 只记 task id——SPA 单页复杂度低。
- 乐观更新:拖拽即时生效,失败回滚——交互爽快优先,一致性由 revision 兜底。
优劣分析
- ✅ 乐观并发三件套是教科书级的本地优先数据同步;✅ 组件分层清晰(ui/detail-panels/git-history/shared/dependencies);❌ App.tsx 1199 行单组件偏大;❌ 状态全靠内存 + WS,离线无缓存。
如何互相配合
模块 6 的 WS 是它的输入;模块 5 的 tRPC 是它的写通道;模块 2 的 revision 是它的并发锚点;模块 15(终端)嵌在详情面板底部。
验收
- 拖卡即时移动且两标签页一致;Agent 消息实时上卡;断线自动重连且不丢状态。
第 18 章 模块 15:前端持久终端(xterm 双 WS)
目的
浏览器里一个"不会因为切卡片而丢内容"的终端。
实现形式(对齐 web-ui/src/terminal/persistent-terminal-manager.ts 约 2.1 万行 + use-persistent-terminal-session.ts)
- xterm@6 封装 + 双 WS:
/api/terminal/io(二进制 stdout/stderr + 上行 stdin)、/api/terminal/control(JSON:restore/state/exit/error/resize/output_ack/stop)。 clientId(randomUUID)标识标签页:后端按 taskId 共享 PTY、按 clientId 隔离 socket 状态。- "持久"实现:DOM 节点"停车"到隐藏的
#kb-persistent-terminal-parking-root——切卡片不销毁,restore 时把服务端滚动快照写回。 - 插件:Fit、WebGL(上下文丢失降级)、Clipboard、WebLinks、Unicode11;ResizeObserver 50ms 防抖上报。
terminal-prompt-heuristics.ts:从输出文本启发式判断 shell 提示符(供 waitForLikelyPrompt)。
技术栈明细
@xterm/xterm + @xterm/addon-fit 等 + ws。
功能定义取舍
- 快照恢复走 control WS 而非重新拉全量 io:控制面与数据面分离,restore 一次完成。
- 终端控制器注册表(
terminal-controller-registry):程序化输入(git 动作、快捷键)优先走本地 WS 的 input/paste,未连接才回退 tRPC。
优劣分析
- ✅ 切卡片零丢内容,体验接近 IDE 内置终端;✅ 双 WS 分离让协议清晰;❌ 停车根节点是隐藏 DOM,内存/焦点管理要小心。
如何互相配合
模块 7(服务端镜像 + 双 WS)是它的后端;模块 14 的 CardDetailView 嵌入 AgentTerminalPanel;模块 19 的提交/PR 走 paste 模式。
验收
- 切卡片再切回,终端内容原样;vim 正常渲染;WebGL 上下文丢失后自动降级 canvas。
第 19 章 模块 16:git 界面、评论与提交/PR
目的
不离开浏览器完成:看 commit 图、切分支、review diff、行内评论、Commit / Open PR。
实现形式(对齐 web-ui/src/git-actions/build-task-git-action-prompt.ts + components/git-history/ 6 组件 + use-git-actions.ts)
- git 历史:
GitHistoryView+use-git-history-data(分页 150、working-copy/commit 双视图、diff 逐文件懒加载)+git-refs-panel(分支选择)。 - Commit/PR:
handleCommitTask→ 组 prompt(模板四层回退:用户模板 → 用户 default → 服务端 default → 兜底句;唯一变量{{base_ref}})→sendTaskSessionInput(paste 模式写入终端)→ Agent 自己执行 git。模板语义:在 worktree 里 cherry-pick 到 base_ref 提交 / 开 PR。 - fetch/pull/push/checkout/discard 走 tRPC
workspace.runGitSyncAction。
技术栈明细
React + tRPC + xterm paste。
功能定义取舍
- 让 Agent 执行 git 而非 kanban 自动跑:merge conflict 处理需要上下文理解,Agent 天生会;kanban 只发指令。
- 模板四层回退:默认模板可用性优先,用户可定制(config 里的 commitPromptTemplate/openPrPromptTemplate)。
优劣分析
- ✅ 冲突处理"外包"给 Agent,代码量小且鲁棒;❌ Agent 执行 git 有不确定性(可能失败重试),需要 review 环节兜底;❌ 行内评论回传(点行留 comment 发回 agent)实现成本高(comment 要注入会话)。
如何互相配合
模块 6(git 数据)供 git-history 组件;模块 5(workspace.runGitSyncAction)供同步动作;模块 15(终端 paste)执行 prompt;模块 2 的 autoReviewMode 决定自动程度。
验收
- 卡片详情看 diff、点行评论回传 Agent;点 Commit,Agent 在 worktree 里完成提交并回复结果。
第 20 章 模块 17:home agent 与看板注入指令
目的
侧边栏聊天里那个"帮我把活儿拆成任务"的 Agent——它不写代码,只操盘看板。
实现形式(对齐 src/prompts/append-system-prompt.ts 316 行)
注入的系统指令核心内容:
- 角色:"Kanban board management helper",明确禁止做编码/改文件——实现请求一律转为建任务。
- 命令前缀解析:按安装方式生成 npx -y kanban / pnpm dlx kanban / yarn dlx kanban / bun x kanban 或 node/tsx 直调。
- 完整 CLI 参考:task list/create/update/done/delete/link/unlink/start 参数与语义。
- 链接与自动审查流水线:review 完成 → 自动启动链接的 backlog 任务;auto-commit/auto-open-pr 组合实现全自主链式执行。
- 工作区识别:cwd 在 .cline/worktrees/ 时强制 --project-path(从 worktree 回指主工作区)。
技术栈明细
纯 prompt 工程 + CLI 设计。无新依赖。
功能定义取舍
- home agent 是普通 Agent 会话 + 注入指令,不做特殊运行时——复用了模块 8/11 的会话设施。
- "禁止编码"写进 prompt 而非强制:软约束,靠模型遵守(成本低,偶尔失效可接受)。
优劣分析
- ✅ 一行指令让 Agent 具备操盘能力,产品价值极大("帮我把这个需求拆成 10 个并行任务");✅ CLI 是 Agent 与看板之间的稳定协议;❌ 软约束可能失效(Agent 手痒去改代码);❌ 指令要跟着 CLI 演进维护。
如何互相配合
模块 22(CLI task 子命令)是它调用的对象;模块 2(依赖链)是它链接卡的机制;home 会话显示在侧边栏(模块 14)。
验收
- 对 home agent 说"把这个需求拆成任务并链接起来",它正确建卡、链卡、启动第一张。
第 21 章 模块 18:安全四层(passcode / Host / CORS / token)
目的
本地服务不暴露给外人——威胁模型:远程绑定模式下,本地 runtime 有任意代码执行能力(git/shell/文件系统)。
实现形式(对齐 src/security/passcode-manager.ts 247 行 + middleware.ts 167 行 + runtime-server.ts)
四层纵深防御:
1. 默认只绑 127.0.0.1——其他机器不可达。
2. Host 白名单——防 DNS rebinding(恶意域名解析到 127.0.0.1 时,Host 头不对直接拒)。
3. CORS 精确 Origin 白名单——阻止本机其他网页跨源读取(恶意网页无法用浏览器偷 API)。
4. 远程模式 passcode:--host 绑定非 loopback 时启用;8 位随机码(randomBytes 拒绝采样、纯内存)、timingSafeEqual 比较、每 IP 5 次失败锁 30s、验证后发 32 字节 token(HttpOnly + SameSite=Strict Cookie,24h)。
另有 KANBAN_INTERNAL_AUTH_TOKEN bearer token:CLI 子进程(hooks ingest / task)认证用,不经过浏览器流程。静态资源放行(让 React 的 PasscodeGate 先渲染),API 硬拦截。
技术栈明细
node:crypto + cookie 头 + ws upgrade 校验。
功能定义取舍
- passcode 只防"未经授权的浏览器访问者";信任本机用户(loopback 无 passcode)。
- Host/CORS 双门是"无密码时也能防大部分攻击"的基石。
优劣分析
- ✅ 纵深防御分层清晰、每层独立可测;✅ 8 位随机码 + 限流对暴力破解足够;❌ 远程模式体验差(每次 24h 要输一次码);❌ 静态资源放行意味着 React 源码可达(无秘密,可接受)。
如何互相配合
模块 4(server)挂门;模块 5(tRPC)在门后;模块 7(终端 WS upgrade)同样校验;模块 22(CLI)带内部 token。
验收
- 默认 loopback 无码可用;
--host 0.0.0.0后无码访问被拒、输码后 24h 免登;错误 5 次锁 30s。
第 22 章 模块 19:CLI 与子命令(task / hooks / update)
目的
CLI 是 Agent 与看板之间的协议面:人可以用,home agent 也可以用。
实现形式(对齐 src/cli.ts 755 行 + src/commands/task.ts 1347 行 + src/commands/hooks.ts 820 行 + src/update/update.ts 787 行)
- commander:主命令 kanban(启动 server + 开浏览器、端口复用探测已有实例);子命令
task(list/create/update/trash/delete/link/unlink/start,全 JSON 输出)、hooks(ingest/notify/gemini-hook/codex-hook/codex-wrapper)、update。 - task 核心模式:看板变更不走 tRPC,本进程内
mutateWorkspaceState(乐观并发)直接改 JSON 文件,改完调workspace.notifyStateUpdated广播;只有 worktree/会话/查询走 runtime。trash/delete 会停会话、删 worktree、自动启动就绪的链接任务。 - update:npm registry 查最新版,识别 npm/pnpm/yarn/bun/npx,startup 或 shutdown 时机自动更新。
- 懒加载:子命令走轻量路径,不拉起 server 栈。
技术栈明细
commander + zod + 自定义 fetch(附内部 token)。
功能定义取舍
- task 直接写文件而非走 API:CLI 与 server 是同一进程的两个入口,直接共享状态层更简单;server 只负责广播。
- 全 JSON 输出:Agent 可解析(headless 友好)。
优劣分析
- ✅ 写路径唯一(文件 + 乐观并发),CLI/web 行为一致;✅ JSON 输出天然可脚本化;❌ CLI 与 tRPC 双入口要维护一致语义(man page 同步);❌ 自动更新可能打断用户(在 shutdown 时机做)。
如何互相配合
模块 10(hooks 子进程)调用 hooks 子命令;模块 17(home agent 指令)调用 task 子命令;模块 20(桌面端)spawn 主命令。
验收
kanban task create "fix bug"建卡并上卡;kanban task link A B建依赖;kanban task start A启动会话。
第 23 章 模块 20:桌面端 Electron
目的
一个"装了就能用"的桌面应用:常驻进程、原生菜单、自带 Node 运行时、崩溃自愈、OAuth 深链。
实现形式(对齐 packages/desktop/src 2,327 行 TS)
纯进程包装器,不 import 根包任何代码:
- spawn("kanban", ["--no-open","--port",3484,...]) → HTTP 健康探测(<title>Kanban</title>)→ BrowserWindow loadURL。
- runtime-orchestrator.ts(604 行):connect(探测已有 runtime,无则 startOwnRuntime)/restart/shutdown/dispose;双探针(attached 500ms 检测崩溃、recovery 2s 自动重连);powerSaveBlocker 防 App Nap;generation 计数器 + terminated 锁防竞态。
- runtime-child.ts:spawn + 30s 就绪轮询 + treeKill 优雅关闭 + 4GB V8 堆上限。
- oauth-relay.ts(46 行):kanban://oauth/callback 深链 → main 转发 http://127.0.0.1:3484/kanban-mcp/mcp-oauth-callback,重试 3 次。
- runtime-child-env.ts:GUI 进程 PATH 补全 Homebrew/nvm——双击启动没有 shell 环境。
- shim 用 ELECTRON_RUN_AS_NODE=1 复用 Electron 二进制当 Node 用(用户不用装 Node)。
- 打包:root build → stage-cli.mjs 复制 dist → electron-builder extraResources + asarUnpack(只解包 cli/node-pty/*.node)→ patch-node-pty.mjs 修 asar 双后缀 bug。
技术栈明细
Electron + electron-builder + node-pty。
功能定义取舍
- 零根包 import、零自定义 IPC、运行时依赖仅 node-pty(5-way split 的硬约束):桌面端只是壳,所有逻辑在 CLI 里。
- 健康探测用页面 title 而非端口:轻量且验证了 web-ui 真的渲染了。
优劣分析
- ✅ 壳极薄,CLI 演进桌面端自动跟上;✅ OAuth 深链是纯 web 做不到的硬能力;❌ 双运行时(Electron + Node)体积大;❌ node-pty 原生模块在 Electron 里要 patch(构建坑)。
如何互相配合
模块 4(server)是被 spawn 的对象;模块 13(MCP OAuth)的深链回调由 relay 转发;模块 19(CLI)是 child process 本体。
验收
- 双击桌面应用启动看板;
kanban://深链 OAuth 登录成功;杀掉 runtime 进程 2s 内自动拉起。
第 24 章 「用户点下播放按钮之后」:完整工作流详解
这是全书的高潮。跟着一张卡片从创建到进入依赖链的完整旅程:
┌─ 1. 建卡 ─────────────────────────────────────────────┐
│ 用户在看板点 "New task" → 填 prompt → 选 agent/列 │
│ → 前端乐观更新卡片 → 120ms 防抖 saveWorkspaceState │
│ (expectedRevision 乐观锁)→ 写入 board.json │
│ → 若 autoReview 开,同时建 turn checkpoint ref │
└──────────────────────────┬───────────────────────────┘
▼
┌─ 2. 播放 ─────────────────────────────────────────────┐
│ 点 ▶ → tRPC runtime.startTaskSession │
│ ├─ a) 确保 worktree(若不存在): │
│ │ git worktree add --detach <baseCommit> │
│ │ → symlink gitignored 路径(node_modules 等) │
│ │ → .git/info/exclude 标记 kanban-managed block │
│ │ → 全程在 .git/kanban-task-worktree-setup.lock 锁 │
│ ├─ b) 选执行路径:agentId == cline? │
│ │ ├─ Cline SDK:ClineCore.start(进程内会话) │
│ │ │ → Provider/OAuth 解析 → MCP 工具注入 │
│ │ └─ 其他 Agent:PTY spawn(node-pty 子进程) │
│ │ → 生成 hook 配置(--settings / -c hooks) │
│ │ → workspace-trust 自动确认(若弹信任提示) │
│ └─ 卡片 → in_progress,会话 → running │
└──────────────────────────┬───────────────────────────┘
▼
┌─ 3. 执行与实时状态(循环)───────────────────────────────┐
│ Agent 干活 → 每个事件触发: │
│ ├─ hook 路径:hook 命令 → kanban hooks notify │
│ │ → stdin JSON → tRPC hooks.ingest │
│ │ → 状态机迁移 + 捕获 turn checkpoint │
│ │ → runtime-state-hub 广播 task_sessions_updated │
│ └─ SDK 路径:subscribe 事件 → cline-event-adapter │
│ → summary patch + 消息 mutation → 同上广播 │
│ 浏览器:WS 收到 → useReducer → 卡片活动区更新 │
│ ("Using read_files" / "Thinking..." / 最终消息) │
│ 终端:PTY 字节 → 协议过滤 → 服务端 xterm 镜像 │
│ → io WS → 浏览器 xterm(背压保护) │
│ 若 Agent 请求权限/提问 → awaiting_review(attention) │
│ 若上下文超长 → 截断压缩 → restart 会话重跑 │
└──────────────────────────┬───────────────────────────┘
▼
┌─ 4. 完成与审查 ────────────────────────────────────────┐
│ Agent 干完 → run-finished/hook → awaiting_review │
│ → 卡片进 review 列(latestHookActivity=finalMessage) │
│ → 通知(readyForReviewNotificationsEnabled) │
│ 用户点卡片 → 详情面板:chat + diff(baseRef 对比)+ 终端 │
│ → 点行评论 → 回传 Agent → 继续干 │
│ 满意 → 点 Commit / Open PR: │
│ → 组 prompt({{base_ref}} 模板)→ paste 进终端 │
│ → Agent 自己 cherry-pick/commit/开 PR → 回复结果 │
│ auto-commit 开 → Agent 干完自动 commit → 直接进 trash │
└──────────────────────────┬───────────────────────────┘
▼
┌─ 5. 收尾与依赖链 ──────────────────────────────────────┐
│ 拖卡进 trash(或 auto 完成)→ │
│ ├─ 停会话 → 捕获 diff patch → git worktree remove │
│ ├─ trashTaskAndGetReadyLinkedTaskIds: │
│ │ 找等待这张卡的 backlog 依赖 → 自动 start(级联) │
│ └─ 全自主模式:卡A commit → trash → 卡B auto-start │
│ → commit → trash → 卡C …… 整条链无人值守 │
└────────────────────────────────────────────────────────┘
关键答疑
Q1:为什么 worktree 用 detached HEAD 而不建分支? 分支的语义(基于哪个 base、要不要 push、PR 从哪发)在"提交/开 PR"时才确定;提前建分支等于替用户做决定。detached worktree 只是隔离执行空间,提交时模板会 cherry-pick 到 base_ref 再建分支。
Q2:hooks 反向注入和轮询终端输出有什么区别? 轮询是"从乱码里猜状态"(各 TUI 格式不同且会变);hooks 是 Agent 官方事件出口——"这个工具跑完了"是事实。但 Codex 的 hooks 不可靠,所以它反而要走 wrapper + 轮询 rollout jsonl(每 200ms),说明没有银弹,每种 Agent 都要单独伺候。
Q3:为什么 Cline 走 SDK 而其他 Agent 走 PTY?
SDK(@clinebot/core)给最深集成:Provider 目录、OAuth、MCP 工具注入、账户余额——但只服务 Cline。其他 Agent 只能当黑盒子进程跑,靠 hooks 捞事件。两条路径的代价是 sendTaskSessionInput 得先试 Cline 再试终端。
Q4:一个卡片 = 一个会话吗?重启后会话怎么恢复?
是。Cline 路径:sessionId 带 taskId 前缀,重启后从 host.list() 按前缀找回,消息从 SDK 持久化存储 hydrate。PTY 路径:session-manager 的 hydrateFromRecord 从磁盘恢复 summary,PTY 进程本身不可恢复(重启即重跑,用 restartRequest)。
Q5:多标签页/多 viewer 共享一个终端不会乱吗? 不会。后端按 taskId 共享 PTY,按 clientId 隔离 socket 状态;每个 viewer 通过 control WS 的 restore 拿自己的快照;背压机制保证慢 viewer 不拖垮共享 PTY。
Q6:revision 乐观并发到底防什么? 防"两个标签页同时编辑看板互相覆盖"。每次保存带 expectedRevision,服务端比对,不匹配抛 CONFLICT + currentRevision,前端更新本地再 refetch 覆盖——最后写者的数据不丢,中间状态以服务端为准。
第 25 章 测试策略与工程化(dogfood / CI/CD)
测试矩阵(对齐 kanban 的 test/ 目录)
| 层 | 内容 | 策略 |
|---|---|---|
| unit(web-ui/tests + src 内 test) | 状态机、契约、拖拽规则、prompt 模板 | 纯函数优先,零 mock |
| runtime(test/runtime/ 23 个) | api-validation、hooks 解析、task-worktree、cline-sdk、终端 | 临时 HOME + git sandbox |
| integration(test/integration/ 6 个) | task-worktree、workspace-state、shutdown-coordinator、runtime-state-stream、cli-compat | describe.sequential + 真实 git 操作 |
关键集成测试: - task-worktree:验证 git worktree 创建/symlink 镜像/清理。 - workspace-state:验证持久化与 CONFLICT。 - shutdown-coordinator:SIGTERM 后 in_progress/review 卡片不丢失。
dogfood(对齐 scripts/dogfood.mjs 470 行)
- 先 build 再用生产 CLI 跑自己——吃的自己做的饭。
--skip-shutdown-cleanup:多实例时由锁文件选举的"清理 owner"执行清理,防重复。- 剥离 repo 的
node_modules/.bin(避免遮蔽全局 Agent CLI);10s SIGKILL 兜底。 - 仓库自带 .claude/.cline/.codex/.factory 配置:同一个仓库同时被 4 个 Agent 当真实场景用。
CI/CD(对齐 .github/)
ci.yml:push/PR → main 触发。test.yml:3 平台矩阵(ubuntu20/22 + macos22)build + lint + typecheck + 测试。publish.yml:手动 workflow_dispatch 指定 tag → 校验 vX.Y.Z 与 package.json 一致 → prepublishOnly → OIDC trusted publishing npm publish → 自动提取 changelog 建 GitHub Release → 发 Slack。- 发布 SOP(RELEASE_WORKFLOW.md + .clinerules/workflows/release.md):维护者改 CHANGELOG/版本/tag →
gh workflow run publish.yml。
工程纪律清单
- 每提交过 biome + typecheck + test:fast(husky)。
- GritQL 禁 console.* / process.exit(除 CLI 入口)/ process.env 解构。
- 无 any、禁 inline import。
- 优先 PATH 检测而非交互 shell 启动 Agent。
- 新 Agent 支持 = 适配器 + hooks 解析 + 集成测试三件套。
验收
- 全平台
npm run check绿;npm run dogfood能自己跑自己;发布流程一次点击出 npm 包 + GitHub Release。