plan-canvas
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePlan Canvas
计划画布(Plan Canvas)
Review loop for plans and visual artifacts: you write the artifact, the human
reviews it in the browser — annotating the exact element they mean, chatting,
and delivering an Approve plan / Request changes verdict — while you block
on a single CLI call that returns their feedback as JSON.
Inspired by lavish-axi; rebuilt
ECC-native around the confirmation gate, with zero dependencies.
/plan计划与可视化工件的审核循环:你编写工件,用户在浏览器中进行审核——标注他们所指的具体元素、聊天,并给出批准计划/请求修改的结论——而你只需通过一个CLI调用即可等待,该调用会以JSON格式返回他们的反馈。
灵感来自lavish-axi;围绕确认环节重构为ECC原生工具,无任何依赖。
/planWhen to Use
使用场景
- You just wrote a plan artifact (from
.claude/plans/*.plan.md) and need the CONFIRM/approve decision — the canvas verdict replaces a typed "yes/proceed"./plan - The user should point at what to change: reviewing designs, comparisons,
reports, or any local /
.mdartifact..html - The user asks for , a visual review, or "open it in the browser".
/plan-canvas
Do NOT use for: code review of diffs (), running web apps, or
remote URLs. The canvas serves local artifact files only.
/code-review- 你刚编写完一个计划工件(来自的
/plan文件),需要获取CONFIRM/批准决定——画布的结论可替代输入“yes/继续”。.claude/plans/*.plan.md - 用户需要指向要修改的内容:审核设计方案、对比内容、报告或任何本地/
.md工件。.html - 用户要求使用、进行可视化审核,或者“在浏览器中打开它”。
/plan-canvas
请勿用于:代码差异审核(使用)、运行Web应用或访问远程URL。画布仅支持本地工件文件。
/code-reviewHow It Works
工作原理
Invoke the CLI as — the bin shipped by the
package (on PATH after a global/plugin install;
also works for plugin installs). Run it from the project you are reviewing in;
it works from any working directory. It manages a detached loopback server
() shared by all sessions, keyed by artifact path — no session
ids to track.
ecc-plan-canvasecc-universalnode "$CLAUDE_PLUGIN_ROOT/scripts/plan-canvas.js"127.0.0.1:4517The workflow is a plain CLI-plus-JSON loop, so it is model- and harness-agnostic:
any agent that can run a shell command and read stdout drives it the same way
(Claude Code, Codex, Cursor, Gemini, OpenCode, Copilot). Trigger it however your
harness surfaces skills — e.g. in Claude Code, in
Codex — or just run the commands directly.
/plan-canvas$plan-canvasecc-plan-canvasbash
undefined调用CLI命令——该命令由包提供(全局/插件安装后会加入PATH;插件安装时也可使用)。在你要审核的项目目录中运行它;它可在任何工作目录下运行。它会管理一个独立的环回服务器(),供所有会话共享,按工件路径进行标识——无需跟踪会话ID。
ecc-plan-canvasecc-universalnode "$CLAUDE_PLUGIN_ROOT/scripts/plan-canvas.js"127.0.0.1:4517该工作流是一个简单的CLI加JSON循环,因此它与模型和 harness 无关:任何能运行shell命令并读取标准输出的Agent都可以以相同方式驱动它(Claude Code、Codex、Cursor、Gemini、OpenCode、Copilot)。可通过你的harness提供的技能触发它——例如在Claude Code中使用,在Codex中使用——或者直接运行命令。
/plan-canvas$plan-canvasecc-plan-canvasbash
undefined1. Open the artifact in the user's browser (returns immediately)
1. 在用户浏览器中打开工件(立即返回)
ecc-plan-canvas open .claude/plans/feature.plan.md
ecc-plan-canvas open .claude/plans/feature.plan.md
2. Block until the human responds. Leave running; re-run if interrupted:
2. 等待用户响应。保持运行;若中断可重新运行:
queued feedback is never lost.
已排队的反馈不会丢失。
ecc-plan-canvas await .claude/plans/feature.plan.md
undefinedecc-plan-canvas await .claude/plans/feature.plan.md
undefinedStay listening, or the human talks to an empty chair
持续监听,否则用户将对着空椅子交流
Feedback only reaches you while an is actually parked on the session.
If your turn ends with nothing listening, the message sits in the queue and,
from the human's side of the glass, sending appears to do nothing at all.
awaitSo run as a background task when your harness supports one (in
Claude Code, a Bash call with ). It exits the moment
feedback arrives and the harness hands you the JSON, which keeps the loop alive
across turns instead of dying with the foreground call. A foreground
works too, but only until the harness time-limits it.
awaitrun_in_background: trueawaitTwo backstops exist, and neither is an excuse to skip the above:
- lists feedback queued with no listener. Check it whenever you are unsure whether you missed something.
ecc-plan-canvas pending - The hook blocks your turn from ending while canvas feedback is undelivered, and hands you the messages. If you are reading feedback from that hook, you stopped listening too early.
stop:plan-canvas-pending
awaitjson
{
"status": "feedback",
"items": [
{ "kind": "annotation", "text": "Split this into two phases",
"anchor": { "selector": "h2:nth-of-type(3)", "tag": "h2", "snippet": "Phase 2: Migration" } },
{ "kind": "verdict", "verdict": "request-changes" }
]
}- — freeform message; answer in the canvas, not the terminal.
kind: "chat" - — feedback anchored to an element (
kind: "annotation",anchor.selectorshow what they pointed at;anchor.snippetwhen they highlighted a passage).anchor.textRange.text - —
kind: "verdict"means the plan is CONFIRMED: stop polling, end the session, and start implementing.approvemeans revise the artifact (the canvas live-reloads it) and keep the loop going.request-changes
3. Always respond in the canvas, then keep listening. One command does both:
bash
ecc-plan-canvas await <file> --reply "Split Phase 2 as requested. Take a look."Every human message gets a reply in the canvas, even a one-liner like
"On it, rewriting the risk table now." Silence in the chat panel is
indistinguishable from a broken canvas, which is exactly the failure this loop
exists to prevent. Answer there, not only in the terminal.
While you work, keep the chat honest with the activity indicator:
bash
undefined只有当实际处于会话等待状态时,反馈才能传递给你。如果你的回合结束时没有任何监听进程,消息会留在队列中,从用户的角度看,发送操作似乎没有任何反应。
await因此,当你的harness支持后台任务时,将作为后台任务运行(在Claude Code中,使用带有的Bash调用)。一旦反馈到达且harness将JSON传递给你,它就会退出,这样可以保持循环在多个回合中持续,而不会随前台调用结束而终止。前台也可以工作,但仅在harness的时间限制内有效。
awaitrun_in_background: trueawait有两个后备机制,但这不能成为跳过上述操作的借口:
- 列出所有未被监听的已排队反馈。当你不确定是否遗漏了内容时,可使用该命令查看。
ecc-plan-canvas pending - 钩子会在画布反馈未交付时阻止你的回合结束,并将消息传递给你。如果你从该钩子读取反馈,说明你停止监听的时间过早。
stop:plan-canvas-pending
当用户操作时,会输出JSON:
awaitjson
{
"status": "feedback",
"items": [
{ "kind": "annotation", "text": "Split this into two phases",
"anchor": { "selector": "h2:nth-of-type(3)", "tag": "h2", "snippet": "Phase 2: Migration" } },
{ "kind": "verdict", "verdict": "request-changes" }
]
}- ——自由格式消息;在画布中回复,而非终端。
kind: "chat" - ——锚定到元素的反馈(
kind: "annotation"、anchor.selector显示用户指向的内容;当用户高亮一段文本时,anchor.snippet会显示该文本)。anchor.textRange.text - ——
kind: "verdict"表示计划已确认:停止轮询,结束会话,开始实施。approve表示需要修订工件(画布会自动重新加载)并继续循环。request-changes
3. 始终在画布中回复,然后继续监听。一个命令即可完成这两项操作:
bash
ecc-plan-canvas await <file> --reply "Split Phase 2 as requested. Take a look."每条用户消息都要在画布中回复,即使是像“正在处理,现在重写风险表。”这样的简短回复。聊天面板中的沉默与画布故障无法区分,而这正是该循环要避免的失败情况。在画布中回复,而不仅仅是在终端中。
在你工作时,使用活动指示器保持聊天状态的真实性:
bash
undefinedanimated "agent is thinking..." bubble; refresh it during long work
动画“agent正在思考...”气泡;长时间工作时刷新状态
ecc-plan-canvas typing <file> --state thinking
ecc-plan-canvas typing <file> --state thinking
switch to "agent is typing..." just before a reply lands
在回复即将发出前切换为“agent正在输入...”
ecc-plan-canvas typing <file> --state typing
`await` sets `thinking` for you the moment it hands you a batch, and `--reply`
clears it. Both states self-expire, so a crashed agent decays to an honest
"queued" instead of leaving the human watching dots forever. Refresh `thinking`
if a revision takes more than a minute.
**4. End** when review concludes: `ecc-plan-canvas end <file>`.ecc-plan-canvas typing <file> --state typing
`await`在将一批反馈传递给你的瞬间会自动设置`thinking`状态,而`--reply`会清除该状态。这两种状态都会自动过期,因此如果Agent崩溃,状态会恢复为诚实的“已排队”,而不会让用户一直看着加载点。如果修订需要超过一分钟,请刷新`thinking`状态。
**4. 结束**:当审核完成时,运行`ecc-plan-canvas end <file>`。Diagrams (Mermaid)
图表(Mermaid)
When part of the plan is a flow, architecture, sequence, state machine, ER
model, or dependency graph, author it as a fenced block instead
of ASCII art or a wall of prose — the canvas renders it as a themed diagram the
human can point at. Reach for it when a picture reads faster than a paragraph;
skip it for simple lists or tables.
```mermaidmarkdown
```mermaid
flowchart LR
A[Market resolves] --> B{Watchers?}
B -->|yes| C[Enqueue jobs] --> D[Fan-out worker]
```Diagrams render in the ECC dark theme with the accent palette. Mermaid loads in
the browser from a pinned CDN; if that is unavailable (offline), the block
degrades to showing its source, so the review is never blocked. Point a local
mirror at for air-gapped use.
ECC_PLAN_CANVAS_MERMAID_URL当计划的一部分是流程图、架构图、序列图、状态机、ER模型或依赖图时,将其编写为带围栏的块,而非ASCII艺术或大段文字——画布会将其渲染为主题化图表,用户可以指向图表进行标注。当图片比段落更易读时使用该方式;对于简单列表或表格则无需使用。
```mermaidmarkdown
```mermaid
flowchart LR
A[Market resolves] --> B{Watchers?}
B -->|yes| C[Enqueue jobs] --> D[Fan-out worker]
```图表会以ECC深色主题和强调色板渲染。Mermaid从固定的CDN加载到浏览器中;如果CDN不可用(离线状态),该块会降级显示其源代码,因此审核不会被阻塞。对于离线使用场景,可将本地镜像指向。
ECC_PLAN_CANVAS_MERMAID_URLRules
规则
- Markdown artifacts render in ECC's plan template (including Mermaid blocks);
artifacts render as-is with the annotation layer injected. For HTML authoring guidance use the
.htmlandfrontend-design-directionskills.artifact-design - Edit the artifact file to revise — the canvas live-reloads on save. Never
re-run to refresh.
open - (or
{"status": "ended", "endedBy": "user"}on a feedback batch) means the user closed the review: stop polling, deliver remaining updates in chat, and do not reopen. A plainsessionEnded: trueon that session is refused; passopenonly when the user asks to resume.--reopen - Sibling assets (images, CSS) must sit next to the artifact and be referenced by relative path.
- The server is loopback-only and exits after 30 idle minutes
();
ECC_PLAN_CANVAS_IDLE_MSshuts it down explicitly. State lives instop(~/.claude/plan-canvas/).ECC_PLAN_CANVAS_STATE_DIR
- 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
Examples
示例
Plan approval flow — writes
and must WAIT for confirmation:
/plan.claude/plans/notifications.plan.mdbash
ecc-plan-canvas open .claude/plans/notifications.plan.md
ecc-plan-canvas await .claude/plans/notifications.plan.md计划批准流程——生成,必须等待确认:
/plan.claude/plans/notifications.plan.mdbash
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"}]}
→ {"status":"feedback","items":[{"kind":"verdict","verdict":"approve"}]}
ecc-plan-canvas end .claude/plans/notifications.plan.md
ecc-plan-canvas end .claude/plans/notifications.plan.md
plan is confirmed — begin implementation
计划已确认——开始实施
**Revision loop** — feedback arrives, you edit the file, reply, keep listening:
```bash
**修订循环**——收到反馈后,编辑文件,回复,继续监听:
```bashawait returned annotations → edit the .plan.md (canvas live-reloads)
await返回标注信息 → 编辑.plan.md文件(画布自动重新加载)
ecc-plan-canvas await <file> --reply "Reworked the risk table."
ecc-plan-canvas await <file> --reply "已重新整理风险表。"
→ blocks again until the next response
→ 再次阻塞,等待下一次响应
undefinedundefinedAnti-Patterns
反模式
- Polling with in a loop. It exists for tests. Leave the plain
--timeout-msrunning instead.await - Ending your turn with no listening while the review is still open. That is the one failure the human experiences as "I sent a message and nothing happened".
await - Reading the feedback but answering only in the terminal. The human is looking at the canvas.
- Reopening after a user-initiated end "just to show" something.
- Pasting the whole plan into chat and opening a canvas — pick the canvas and keep the terminal summary to one line.
- Parsing the canvas chat from state files — everything you need arrives via
.
await
- 在循环中使用进行轮询。该参数仅用于测试。应保持普通
--timeout-ms持续运行。await - 当审核仍在进行时,结束回合且没有监听。这是用户会遇到的唯一一种“我发送了消息但没有任何反应”的故障情况。
await - 读取反馈但仅在终端中回复。用户正在查看画布。
- 用户主动结束审核后,“只是为了展示”而重新打开。
- 将整个计划粘贴到聊天中同时打开画布——选择画布,并将终端摘要控制为一行。
- 从状态文件解析画布聊天内容——你需要的所有信息都会通过传递。
await