计划画布 是做什么的?
计划和可视化制品的审查循环:你编写制品,人工在浏览器中审查——注释他们确切指出的元素、聊天、并给出批准计划 / 请求更改的裁决——而你则阻塞在一个 CLI 调用上,该调用将反馈作为 JSON 返回。
灵感来源于 lavish-axi;围绕 /plan 确认门重新构建为 ECC 原生方案,零依赖。
何时使用
- 你刚刚编写了一个计划制品(来自
/plan的.claude/plans/*.plan.md),需要 CONFIRM/批准决策——画布的裁决取代了手动输入“是/继续”。 - 用户应该指出要更改什么:审查设计、对比、报告或任何本地的
.md/.html制品。 - 用户请求
/plan-canvas、可视化审查,或“在浏览器中打开它”。
不要用于:差异的代码审查(/code-review)、运行 Web 应用程序或远程 URL。画布仅服务本地制品文件。
工作原理
通过 CLI 调用 ecc-plan-canvas——由 ecc-universal 包提供的二进制文件(在全局/插件安装后位于 PATH 上;插件安装也可使用 node "$CLAUDE_PLUGIN_ROOT/scripts/plan-canvas.js")。在你正在审查的项目目录中运行;它可以从任何工作目录工作。它管理一个共享的、独立的环回服务器(127.0.0.1:4517),以制品路径为键,无需跟踪会话 ID。
工作流是一个简单的 CLI + JSON 循环,因此与模型和工具无关:任何能运行 shell 命令并读取标准输出的代理都可以以相同方式驱动它(Claude Code、Codex、Cursor、Gemini、OpenCode、Copilot)。无论你的工具如何暴露技能——例如 Claude Code 中的 /plan-canvas,Codex 中的 $plan-canvas——或者直接运行 ecc-plan-canvas 命令。
# 1. 在用户的浏览器中打开制品(立即返回)
ecc-plan-canvas open .claude/plans/feature.plan.md
# 2. 阻塞直到人工响应。保持运行;如果中断则重新运行:
# 排队的反馈永远不会丢失。
ecc-plan-canvas await .claude/plans/feature.plan.md
保持监听,否则人工会对着空椅子说话
只有当 await 实际挂起在会话上时,反馈才能到达你。如果你的轮次结束时没有任何监听,消息会留在队列中,从人工的角度看,发送似乎没有任何效果。
所以将 await 作为后台任务运行(如果你的工具支持,在 Claude Code 中,使用 run_in_background: true 的 Bash 调用)。它在反馈到达时立即退出,工具将 JSON 交给你,这使循环跨轮次保持存活,而不是随着前台调用而终止。前台 await 也可以工作,但仅限于工具时间限制它之前。
存在两个后援措施,但这些都不是跳过上述做法的借口:
ecc-plan-canvas pending列出没有监听器的排队反馈。当你不确定是否错过了某些内容时检查它。stop:plan-canvas-pending钩子在画布反馈未送达时阻止你的轮次结束,并将消息交给你。如果你从该钩子读取反馈,说明你过早停止了监听。
await 在人工操作时打印 JSON:
{
"status": "feedback",
"items": [
{ "kind": "annotation", "text": "将这部分分成两个阶段",
"anchor": { "selector": "h2:nth-of-type(3)", "tag": "h2", "snippet": "阶段 2:迁移" } },
{ "kind": "verdict", "verdict": "request-changes" }
]
}
kind: "chat"—— 自由格式消息;在画布中回答,而不是终端。kind: "annotation"—— 锚定到某个元素的反馈(anchor.selector、anchor.snippet显示他们指向的内容;当他们高亮了一段文字时,anchor.textRange.text可用)。kind: "verdict"——approve表示计划已确认:停止轮询,结束会话,开始实施。request-changes表示修改制品(画布会实时重载)并保持循环继续。
3. 始终在画布中回复,然后继续监听。一条命令即可完成两项操作:
ecc-plan-canvas await <file> --reply "已按要求拆分阶段 2。请查看。"
每条人工消息都会在画布中得到回复,即使是一行字如“正在处理,现在重写风险表。”。聊天面板中的沉默与画布损坏无法区分,而这正是此循环旨在避免的故障。在那里回答,不仅仅在终端中。
在你工作时,使用活动指示器保持聊天诚实:
# 动画“代理正在思考...”气泡;在长时间工作期间刷新它
ecc-plan-canvas typing <file> --state thinking
# 在回复即将到达前切换为“代理正在输入...”
ecc-plan-canvas typing <file> --state typing
await 在将一批消息交给你的那一刻会自动设置 thinking,而 --reply 会清除它。两种状态都会自动过期,因此崩溃的代理会退化为诚实的“已排队”状态,而不是让人工永远看着圆点。如果修订需要超过一分钟,请刷新 thinking。
4. 结束当审查完成时:ecc-plan-canvas end <file>。
图表(Mermaid)
当计划的某部分涉及流程图、架构图、时序图、状态机、ER 模型或依赖关系图时,请将其编写为带围栏的 ```mermaid 代码块,而不是 ASCII 艺术或大段文字——画布会将其渲染为带主题的图表,人工可以指向它。当一张图比一段文字阅读更快时使用它;对于简单的列表或表格则跳过。
```mermaid
flowchart LR
A[市场结算] --> B{有观察者?}
B -->|是| C[将任务加入队列] --> D[扇出工作器]
```
图表以 ECC 深色主题和强调色板渲染。Mermaid 从固定的 CDN 加载到浏览器中;如果 CDN 不可用(离线),该块会退化为显示其源代码,因此审查永远不会被阻塞。将本地镜像指向 ECC_PLAN_CANVAS_MERMAID_URL 以用于气隙环境。
规则
- Markdown 制品以 ECC 的计划模板渲染(包括 Mermaid 块);
.html制品按原样渲染,并注入注释图层。关于 HTML 编写指导,请使用frontend-design-direction和artifact-design技能。 - 编辑制品文件以进行修订——画布在保存时实时重载。永远不要重新运行
open来刷新。 {"status": "ended", "endedBy": "user"}(或反馈批中的sessionEnded: true)表示用户关闭了审查:停止轮询,在聊天中提供剩余更新,不要重新打开。对该会话的普通open会被拒绝;只有当用户要求恢复时,才使用--reopen。- 兄弟资源(图片、CSS)必须位于制品旁边,并通过相对路径引用。
- 服务器仅限环回,并在 30 分钟空闲后退出(
ECC_PLAN_CANVAS_IDLE_MS);stop会显式关闭它。状态存储在~/.claude/plan-canvas/(ECC_PLAN_CANVAS_STATE_DIR)。
示例
计划批准流程 —— /plan 写入 .claude/plans/notifications.plan.md 并且必须等待确认:
ecc-plan-canvas open .claude/plans/notifications.plan.md
ecc-plan-canvas await .claude/plans/notifications.plan.md
# → {"status":"feedback","items":[{"kind":"verdict","verdict":"approve"}]}
ecc-plan-canvas end .claude/plans/notifications.plan.md
# 计划已确认——开始实施
修订循环 —— 反馈到达,你编辑文件,回复,继续监听:
# await 返回注释 → 编辑 .plan.md(画布实时重载)
ecc-plan-canvas await <file> --reply "已重做风险表。"
# → 再次阻塞直到下一个响应
反模式
- 在循环中使用
--timeout-ms轮询。它仅用于测试。改为保持普通的await运行。 - 在审查仍打开时,以没有
await监听的方式结束你的轮次。这是人工会体验为“我发送了消息但什么也没发生”的唯一故障。 - 读取反馈但只在终端中回答。人工正在看着画布。
- 在用户发起的结束之后重新打开“只是为了展示”什么东西。
- 将整个计划粘贴到聊天中并且打开画布——选择画布,并将终端摘要保持在一行。
- 从状态文件解析画布聊天——你需要的一切都通过
await到达。
设计说明和起源:docs/design/plan-canvas.md。