从零构建 · 第八套 · 多 Agent 编排看板

从 PRD 到
多 Agent 看板

跟着「用户点下卡片上的播放按钮」这个动作,从零走到尾:创建独立 worktree、启动 Agent、hooks 反向注入实时状态、review diff、commit/开 PR、依赖链自动启动下一张卡。20 个功能模块按开发顺序罗列,每个模块讲清楚目的、实现形式、技术栈、取舍、优劣、如何互相配合。

20
功能模块
25
总章数
7
种 Agent 适配
完整
工作流详解

教程定位:这是一份以 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 后精读。


Part 2

第 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 崩溃重启后,所有卡片状态和会话可恢复。

Part 3

第 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)

  1. any、禁 inline import(biome 强制)。
  2. 优先直接 PATH 检测而非交互 shell 启动 Agent——which 会 spawn shell,conda/nvm 环境会卡死 runtime。
  3. GritQL 静态检查:禁 console.*、禁 process.exit(除 CLI 入口)、禁 process.env 解构。

Part 4

第 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 installinstall:all 脚本)。
  • ✅ tsx watch 秒级热重载;❌ esbuild 不做类型检查(typecheck 单独跑)。

如何互相配合

根包 build 产物 dist/ 是 CLI 与 web-ui 的交付形态;desktop 的 stage-cli.mjsdist/ 复制进 Electron 壳;dogfood.mjs 依赖完整 build。

验收

  • npm run build && npx kanban 起服务;npm run check 全绿;npm run dogfood 能跑起来。

Part 5

第 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/interruptedreviewReason: 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)同时编译报错。

Part 6

第 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}
  • 纯函数式 mutationtask-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 路径排序加锁(防死锁),writeTextFileAtomic tmp+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 -9 server 后重启,看板状态完好。

Part 7

第 6 章 模块 3:git 层——worktree、symlink、checkpoint、git 界面数据

目的

用 git 的机制做资源隔离:每卡一个 worktree,Agent 并行零冲突;symlink 共享依赖省安装成本;checkpoint 记录每轮改动。

实现形式(对齐 src/workspace/ 全部)

  1. worktree 生命周期task-worktree.ts,695 行):
  2. git worktree add --detach <baseCommit>,路径 ~/.cline/worktrees/<taskId>/<workspaceLabel>
  3. detached HEAD 不建分支——分支在 Commit/Open PR 时才动态创建。
  4. worktree 已存在即权威,不因 base 前进重建;创建过程在 .git/kanban-task-worktree-setup.lock 锁内。
  5. trash 时 captureTaskPatch 存 diff 到 ~/.cline/kanban/trashed-task-patches/,再 git worktree remove --force;resume 时 git apply 恢复。
  6. symlink 共享依赖syncIgnoredPathsIntoWorktree,L358):git ls-files --others --ignored --exclude-per-directory=.gitignore --directory 列 gitignored 路径 → 原位 symlink(主仓库→worktree).git/info/exclude 写入 kanban-managed block → 失败静默跳过 → getUniquePaths 只保留最浅根。
  7. checkpointturn-checkpoints.ts,92 行):每消息轮后用临时 GIT_INDEX_FILE(/tmp)创建 commit(作者 kanban-checkpoint),update-ref refs/kanban/checkpoints/<base64(taskId)>/turn/<N>——不污染分支,纯 ref。
  8. git 命令封装git-utils.ts):execFile("git", ["-c","core.quotepath=false", ...]),10MB maxBuffer;非 0 退出码不抛异常,返回 {ok,stdout,stderr,exitCode}
  9. git 界面数据git-history.ts,478 行):git log --topo-order --date-order + \x1f/\x1e 分隔 + for-each-ref + rev-list --not 算 relation。
  10. 同步动作git-sync.ts,385 行):fetch/pull --ff-only(有本地改动拒绝)/push/checkout/discard。
  11. Turbopack 例外task-worktree-turbopack.ts):Turbopack 跟随 symlink 会坏缓存 → 检测到则 node_modules 不 symlink。
  12. 路径沙箱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 后文件恢复。

Part 8

第 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/ws upgrade 成功。

Part 9

第 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 能启动会话;CLI kanban task start 与 web 操作同一张卡效果一致。

Part 10

第 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_updated150ms 批合并——高频事件合并成一条)
  • task_chat_message / task_chat_cleared
  • task_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 发消息,卡片活动区毫秒级更新。

Part 11

第 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-ptyPtySession.spawn() 真实 PTY;stop() 杀进程组(process.kill(-pid, SIGTERM))。
  • 双 WSserver.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 出完整滚动缓冲。

Part 12

第 11 章 模块 8:会话管理器与状态机

目的

并发管理所有任务的会话:启动/停止/输入/崩溃恢复/重启持久化。

实现形式(对齐 src/terminal/session-manager.ts 1040 行 + session-state-machine.ts 78 行 + agent-registry.ts 128 行)

  • TerminalSessionManagerMap<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.tsresolveAgentCommand(从 catalog 拼命令)、detectInstalledCommandsbuildRuntimeConfigResponse(给设置 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。

Part 13

第 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 三台机器都能起任务且状态实时上卡。

Part 14

第 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.tsKANBAN_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。

Part 15

第 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 暴露的是有状态多会话宿主——sessions Map(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 文件。

Part 16

第 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 合并);工具调用显示摘要;上下文超长自动压缩重启且卡片状态连续。

Part 17

第 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 行)

  • ProviderLlms.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 → InMemoryMcpManagercreateMcpToolslocalRuntime.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 能用它的工具。

Part 18

第 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 校验/清洗)、applyDragResulttrashTaskAndGetReadyLinkedTaskIds(级联依赖);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 消息实时上卡;断线自动重连且不丢状态。

Part 19

第 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。

Part 20

第 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/PRhandleCommitTask → 组 prompt(模板四层回退:用户模板 → 用户 default → 服务端 default → 兜底句;唯一变量 {{base_ref}})→ sendTaskSessionInputpaste 模式写入终端)→ 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 里完成提交并回复结果。

Part 21

第 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 说"把这个需求拆成任务并链接起来",它正确建卡、链卡、启动第一张。

Part 22

第 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。

Part 23

第 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 启动会话。

Part 24

第 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 内自动拉起。

Part 25

第 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 覆盖——最后写者的数据不丢,中间状态以服务端为准。


Part 26

第 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

工程纪律清单

  1. 每提交过 biome + typecheck + test:fast(husky)。
  2. GritQL 禁 console.* / process.exit(除 CLI 入口)/ process.env 解构。
  3. 无 any、禁 inline import。
  4. 优先 PATH 检测而非交互 shell 启动 Agent。
  5. 新 Agent 支持 = 适配器 + hooks 解析 + 集成测试三件套。

验收

  • 全平台 npm run check 绿;npm run dogfood 能自己跑自己;发布流程一次点击出 npm 包 + GitHub Release。