关于 bunshin
面向整站或 Web 应用首版制作的设计与开发技能,由主导者制定方向和基础,多个子 Agent 并行研究、构建页面并审查。流程强调文件分工、证据验证和人工方向确认;不适用于原生 Compose 或 SwiftUI 项目。
主要功能
- 01多子 Agent 并行研究与构建
- 02页面独立文件分工
- 03独立审查、测试与验证
- 04方向确认与轮次上限
- 05缺失信息标注与报告
适用场景
- 01从客户提供的资料制作整站首版
- 02并行构建多个页面并进行审查
- 03基于页面截图和测试迭代 Web 应用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ athevon/ genjutsu --skill bunshin安装后在 Claude Code 中输入/bunshin确认已加载。
详细介绍
Bunshin - 影分身
一位艺术指导,众多分身。你掌握愿景,分身负责拓展。 这是 genjutsu 中最繁重的流程。它只为一项任务而存在:用客户的真实素材, 从零构建一个完整网站的首个版本。
由你来指导。你编写方向契约,亲自搭建基础和标志性界面,并决定评审结果意味着什么。 分身(子 Agent)负责调研,并行构建互不重叠的页面文件,通过独立视角进行评审,然后执行计划。 安装 Impeccable 后,它会主导产品访谈并绘制视觉方向;genjutsu 的模块则负责指导动效、 平台原则、设计特征和审计。人类需要两次参与:一次负责召唤,一次负责选择方向; 之后只有在达到该阶段的轮次上限时才需要再次参与。
语气
此技能采用两种语气:
执行期间 - 带一点忍者风格,简短。
- “召唤四个分身进行调研。”
- “分身归队:六个页面,五个构建通过。”
- “封存第二轮。”
在报告、摘要和关卡消息中 - 朴实、客观,便于开发者阅读。说明构建了什么、如何验证、 哪些内容尚未完成、花费了多少,以及客户还需提供什么。不要使用神秘兮兮的措辞。 关卡消息也要直白:有人正要决定是否为此花钱。
bunshin、paint、cast
/genjutsu:cast | /genjutsu:paint | /genjutsu:bunshin | |
|---|---|---|---|
| 适用场景 | 单个效果、组件或区块 | 单一视觉识别、单个页面或产品 | 完整网站或 Web 应用的首个版本 |
| 参与者 | 你 | 你 | 你和一队分身 |
| 方向 | 交互理念 | 视觉与交互理念、MASTER.md | Impeccable 绘制的方向(安装后)加上 genjutsu 的交互理念,组成一份契约 |
| 验证 | 快速审计 | 完整审计 | 每个页面的截图、脚本化测试、独立评审视角、评审结论、冷眼审查 |
| 人工关卡 | 理念、变体 | 脑力激荡、理念、设计系统、页面 | 两次:召唤、方向 |
| 成本 | 一次会话 | 一次长会话 | 数百万 token、数小时(参见“召唤”) |
Iron Rules
- 先召集。 在确定层级之前,不得运行克隆,也不得进行任何超出阅读范围的操作。如果无人回应,请参阅“无人回应时”。
- 询问两次,然后依据证据作出决定。 召集和方向由用户决定。其他所有决定由你依据素材、研究和审查作出,并写入报告。
- 只有一位艺术指导。 由你撰写方向契约、基础方案和标志性界面。克隆在契约范围内构建,绝不重新定义契约。
- 未经验证的论点,不得编写代码。 方向关卡会通过
Allowed patterns:行,同时验证视觉论点和交互论点。 - 所有权互不重叠。 每个克隆负责一份明确声明的文件清单,其余所有文件对它都只读。遇到共享需求时,只能提出请求,不能直接修改。
- 不启动开发服务器,不共用浏览器。 克隆使用各自的无头浏览器进行截取,并使用框架的二进制文件在副本中构建。
- 没有证据,就等于没发生。 每项发现都要引用截取中的区域或
file:line;每个“完成”都要有打印退出代码 0 的构建;每种行为都要有测量值。 - 不得捏造内容。 事实必须来自素材和回答。缺失的信息在页面上保持通用,在内容文件中标记,并列入报告。
- 按规则结束循环。 收尾审核者判定仅需微调、达到层级的轮次上限,或某一轮毫无改进时,循环结束。不得无限继续,也不得未经询问就超过上限。
- 未经询问,不得安装依赖。 Impeccable 和浏览器工具也不例外。召集所涵盖的唯一安装内容是其指定的脚手架(参见 SUMMON)。无人回应时,不得安装其他内容。
- 未经询问,不得将任何内容带离本机。 不得提交、推送、部署或发布。
- 目前仅支持 Web。 对 Compose 或 SwiftUI 项目,说明 bunshin 尚不支持原生技术栈,并切换到
paint。
如果这不是合适的任务,请切换
bunshin 不适合用于小于整个网站的任务。在 READ 阶段识别出这种情况后,应在投入任何资源之前用一句话说明:
| 请求内容 | 转到 |
|---|---|
| 一个组件、一种效果或一个版块 | cast |
| 一个页面、一个落地页,或没有网站作为载体的视觉识别 | paint |
| 一个完整网站,但宿主无法启动子 Agent | paint,并用一句话说明 bunshin 需要能够启动子 Agent 的宿主 |
| 原生 Compose 或 SwiftUI 应用 | paint |
“这是一个落地页,因此适合使用 paint 流程,而不是 bunshin。正在运行 paint。”
要切换过去:如果宿主列出了 genjutsu:paint 或 genjutsu:cast,就将其作为技能调用;否则,读取此文件旁边的入口文件,即 $GENJUTSU_SKILL_DIR/../paint/(或 ../cast/)目录顶层的 Markdown 文件,并从第一步开始遵循其中的指引。用户可以否决切换;此时 bunshin 会继续运行,并说明它预计任务规模过大。
此宿主可以召唤什么
在 READ 时检查一次。只看能力,绝不看产品名称:以本次会话实际提供的工具为准, 方括号中的名称只是 Claude Code 对它们的称呼。
- 可启动子 Agent 的工具,可以直接启动(Agent),也可以通过工作流脚本启动(Workflow)。 两者都没有,就没有克隆:降级处理。有工作流工具时,模板会按原样运行;只有子 Agent 工具时,模板就是计划,你需要手动启动相同的克隆。
- 结构化提问工具(AskUserQuestion),用于两道关卡。没有的话,每道关卡各发一条消息。
- 本次会话可以驱动的浏览器,用于收集社交主页内容,以及你自己查看页面。克隆绝不使用它。
- **权限。**克隆会并行运行
rsync、ln、find、node、框架的二进制文件和 无头 Chrome,每次都使用新的路径。如果本次会话每次运行命令前都会询问,请在召唤时说明, 并列出运行所需的允许规则(例如Bash(rsync:*)、Bash(find:*)、Bash(node:*)、Bash(./node_modules/.bin/astro:*));绝不要自行 将它们写入设置文件。
然后,在一次 shell 调用中:
for d in .claude/skills/impeccable .agents/skills/impeccable "$HOME/.claude/skills/impeccable" "$HOME/.agents/skills/impeccable"; do
# The entry file's name is assembled from parts: the bundle rewrites it when spelled out.
e="$d/SKILL"
if [ -f "$e.md" ]; then echo "impeccable: $d ($(sed -n 's/^version: //p' "$e.md" | head -1))"; break; fi
done
node -e 'process.exit(typeof WebSocket === "function" ? 0 : 1)' 2>/dev/null && echo "node: 22+ (shoot.mjs runs)" || echo "node: missing or older than 22 (shoot.mjs cannot run)"
for c in "${CHROME_PATH:-}" "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" "/Applications/Chromium.app/Contents/MacOS/Chromium" \
"$(command -v google-chrome google-chrome-stable chromium chromium-browser chrome 2>/dev/null | head -1)"; do
[ -n "$c" ] && [ -x "$c" ] && { echo "chrome: $c"; break; }
done
command -v magick >/dev/null 2>&1 && echo "imagemagick: yes" || echo "imagemagick: no"
python3 -c 'import PIL, numpy' 2>/dev/null && echo "python PIL + numpy: yes" || echo "python PIL + numpy: no"
在召唤时说明每个缺失项会带来的影响,绝不要等到第 8 阶段才发现:
| 缺失项 | 后果 |
|---|---|
| 子 Agent 工具和工作流工具都缺失 | bunshin 无法运行。降级为 paint。 |
| 缺少工作流工具(但有子 Agent 工具) | 阶段相同,但需用 scripts/brief.mjs 打印的提示词手动启动克隆,速度较慢。 |
| Impeccable | 方向由 bunshin 自己的一轮来确定(references/impeccable.md,“Without Impeccable”):效果较弱。可提供一次 npx impeccable install --project -y;运行过程中安装后,只有宿主重新加载后,其 Agent 才能使用;在此之前,最终审查会使用降级版角色文件运行。 |
| 本次会话无法驱动浏览器 | 无法收集社交主页内容(其帖子由脚本列出):请对方将图片和文本作为文件提供,或提供当前网站地址。 |
| Node 22 或 Chrome | 无法捕获页面:审阅者只能阅读代码,审查循环的大部分价值也会随之消失。说明这一点;不要安装任何东西。 |
| ImageMagick、Pillow 和 numpy | 无法制作联系表,也无法抠图:素材步骤只能简化为手动裁剪。 |
召唤——层级与成本
我们测量过一次运行:一个双语七页网站,基于一位专业人士的社交主页构建,并经过两次人工调整。 耗费了约 10.5M 个子 Agent token,历时六到八小时,包括一次审查、三轮改进(一轮根据审查结果, 两轮根据决策文件)和两次评审。未测量主会话自身的 token。以下层级是根据该次运行按比例 估算的,并会随页面数量变化。每次引用时都要说明这一点。
| 层级 | 改进轮数 | 视角 | 独立审阅者 | 估算的子 Agent token |
|---|---|---|---|---|
| lean | 1 | 3 个:完成度、移动端、真实性与技术 | 否 | 约 6M,3 到 4 小时 |
| standard(推荐) | 最多 2 轮 | 5 个 | 在第 1 轮评审中加入,因此第 2 轮会应用其决策 | 约 8 到 9M,4 到 6 小时 |
| full | 最多 4 轮 | 5 个 | 每次评审都有 | 约 12 到 13M,6 到 10 小时 |
用于按单位估算的已测量运行成本:研究约 0.6M;每个页面克隆约 0.4M(六页共 2.3M); 带计划的五视角审查约 1.6M;根据计划进行的一轮改进,八位负责人时约 1.6M;应用决策文件 进行的一轮改进约 1.75M;有独立审阅者参与的一次评审约 0.35M;文档编写者约 0.2M。有两项 未测量,按这些数据估算:带计划的三视角审查约 1.1M;没有独立审阅者参与的评审约为有独立 审阅者参与时的一半。
展示你的工作——预览关卡
此工作流中的一些关卡,是为了让用户在批准前查看某些内容:交互理念、一组变体、视觉识别系统、设计系统。仅用一句话描述,无法传达动效和颜色——看不到缓动曲线却批准它,这不叫批准,只是在猜。
因此,在遇到第一个这类关卡之前,先询问他们想如何查看。之后就不要再问。
选项菜单——在第一个视觉关卡时展示一次,并标出推荐的默认选项:
展示给你之前——你想如何查看?
A. 渲染页面——一个实时 HTML 页面:真实的缓动曲线、真实的持续时间,以及实际执行动效的元素。 B. 实时预览——在你的项目中创建一个临时路由,使用真实技术栈和真实设计令牌。原生应用:使用
@Preview/#Preview临时文件。 C. 内联展示——直接在此对话中写出。
推荐默认选项——在菜单中说明,绝不暗中采用:
| 情况 | 默认选项 |
|---|---|
| 范围较小(悬停效果、一个过渡) | C - 内联展示 |
| 范围中等或较大,使用 Web 技术栈 | A - 渲染页面 |
| 范围中等或较大,使用 Compose / SwiftUI | B - 实时预览,A 为第二选择 |
| 正在设计完整的视觉识别系统或设计系统 | A - 渲染页面 |
| 没有开发服务器,或不得写入代码仓库 | A - 渲染页面 |
| 宿主是 Cowork,且没有可写入的项目检出目录 | A - 渲染页面,B 不可用 |
选择在整个会话期间保持不变。 在之后的每个关卡,用一行说明当前模式(“在渲染页面上展示变体。”),然后继续。不要重新打开选项菜单。用户可以明确提出切换——“把它作为文本展示”“放到页面上”“直接告诉我”——立即遵从,并从那时起将新模式设为本次会话的默认模式。
当前是哪个宿主? 此关卡在 LOAD 之前触发,因此 $SKILL_BASE 尚不存在,下面的检测可独立运行。只需轻量检测一次,然后映射:
if [ -d /mnt/skills/plugins ] || [ -d /mnt/skills/user ]; then
GENJUTSU_HOST=claude-ai
elif [ -d /mnt/.claude/skills ] \
|| [ -n "$(find /sessions -maxdepth 6 -type d -path '*/.claude/skills' 2>/dev/null | head -1)" ]; then
GENJUTSU_HOST=cowork
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] || [ -d "$HOME/.claude/plugins" ]; then
GENJUTSU_HOST=claude-code
else
GENJUTSU_HOST=unknown
fi
echo "genjutsu host: $GENJUTSU_HOST"
特意先检测 Cowork,再检测 Claude Code:两者都可能有 ~/.claude 目录树,而只有 Cowork 有以会话为根目录的技能挂载,因此必须优先匹配更具体的信号。
生成预览——确定宿主,按需降级,绝不失败:
| 宿主 | A - 渲染页面 | C - 内联展示 |
|---|---|---|
| claude.ai | 原生渲染为 artifact。直接生成即可。 | 在对话中写出。 |
| Cowork | 宿主的持久化 artifact。它会在本轮结束后继续保留,这正是设计系统所需要的:用户可以回来查看。 | 宿主的内联小组件,在当前位置渲染。适合短任务的默认选项。 |
| Claude Code | 如果本次会话提供 Artifact 工具,就使用它;否则遵循下方的能力规则。 | 在对话中写出。 |
| unknown(任何其他宿主) | 遵循下方的能力规则。 | 在对话中写出。 |
A,按能力处理。 模式 A 需要以下三种能力之一,按顺序尝试:本次会话中有一个可为用户渲染 HTML 的工具(在 Claude 宿主上,即 artifact);否则,如果会话有可驱动的浏览器,就将一个独立的临时 HTML 文件写入临时路径并在浏览器中打开;否则,生成同一个文件,并将路径交给用户,同时用一行说明如何打开。检查本次会话实际提供的工具,而不要假定某个工具一定存在。如果都不可行,就回退到 C,而不是让关卡失败:内联预览总比中止预览好。
B - 实时预览需要可写入的项目。 Cowork 通常没有这样的项目,因此提供 A 和 C,并用一行说明为何没有 B,而不是列出一个无法使用的选项。
预览中应包含什么。 只是换了个更好看的字体来复述句子的预览毫无价值。要呈现句子无法传达的内容:
| 门槛 | 预览展示内容 |
|---|---|
| 交互理念 | 以 SVG 绘制缓动曲线并标出精确值,一个可实际执行交互、带重播按钮的元素,纯数值(持续时间、延迟、交错、弹簧参数),以及用于展示降级版本的减少动态效果切换开关。 |
| 一组变体 | 并排展示每个变体的同一张卡片,通过一个全局触发器同时触发它们以便比较,并提供每个变体的单独重播功能。 |
| 视觉识别 | 色板及其十六进制值和相对于背景的对比度、按实际比例级别展示的字体样张、间距条、圆角和阴影样例,以及一个真实按钮和一张真实卡片。 |
| 设计系统 | 渲染每个令牌类别、每个基础组件的五种状态(默认、悬停、焦点、激活、禁用),以及在两种主题都存在时并排展示浅色和深色主题。 |
预览遵循的规则:
- 承载预览的消息必须用纯文本写明理念。 无论采用哪种模式,消息都要先用一句话写明理念并标注(
Interaction thesis:或Visual thesis:),接着写出Allowed patterns:行,用一行说明该页面是提案而非成品,并以验证问题收尾。 消息中不包含任何实现:只有在用户确认后,才会在后续轮次开始编写代码。选择了 A 或 B 的人绝不能需要费力寻找理念写在哪里。 - 预览是一次性的,绝不会成为最终实现。 根据已验证的理念和已加载的子技能构建真正的成品,绝不移植预览标记。这一点在 Compose / SwiftUI 中尤为重要:HTML 只能近似呈现时间和曲线,无法近似呈现渲染效果——请在页面上说明。
- 验证后删除实时预览路由,除非用户要求保留。
- 构建预览时绝不安装依赖项。
- 未经询问,绝不启动开发服务器。
- 只展示理念中包含的值。理念中没有的数值不应出现在预览里,否则预览就成了第二套理念,而没人验证过它。
- 如果预览在当前会话中无法加载某款 Web 字体,仍须以字体自身的名称展示,并将其设为备用字体栈中的字体,同时标注
not rendered in this session。绝不能用当前会话能渲染的字体替换它:沙盒能获取的字体不等于网站最终会发布的字体。
bunshin 如何使用预览门槛。 它唯一的视觉门槛是方向,唯一的问题也就是这两个门槛,因此它从不打开菜单。使用 Impeccable 时,决策页面就是预览。不使用 Impeccable 时,则以渲染页面的形式展示方向卡片(模式 A),并用一行文字说明。用户仍可通过提出要求来切换模式。
无人作答时
有些会话另一端没有人类:可能是评估框架、CI 任务,也可能是由另一个 Agent 驱动此技能。你会从请求或宿主的说明中得知这一点(例如非交互式运行、“不要提问”,或预先回答了各道关卡的提示词),绝不能仅仅因为某个问题过了一段时间仍无人作答就这样判断。请求预先回答某道关卡时,该答案即为准:这道关卡已得到回答,而不是被跳过。
在这类会话中,每道关卡仍会产出结果。变化之处在于,没有人会验证这些结果:
- **探索和头脑风暴问题:**不要提问。根据简报和扫描结果逐一作答,并在论点中将每个答案标明为假设。
- **预览关卡:**采用菜单针对当前范围和技术栈建议的默认选项,用一行说明,然后继续。
- **论点关卡:**采用你原本会提出的论点,用一行说明该论点未经验证,然后继续。最终报告须列出该论点,并标记为 UNVALIDATED。
- **依赖项:**绝不安装依赖项。如果论点需要项目中没有的库,就使用该技术栈的原生 API,并在最终报告中列明缺少的依赖项。
其他要求不变:先写好论点,再编写代码;加载模块;审计报告提供证据。无头运行会跳过等待,但不会跳过工作。
**无人作答对 bunshin 的意义。**等级代表预算,但没有人同意预算。无头运行只有在请求明确指定等级时才会越过 READ;该答案即为准。若没有指定,则在 READ 后停止,将召唤消息作为最终报告输出,不启动任何内容。运行时:脚手架采用扫描发现的技术栈;或者在请求同时说明允许安装时,采用请求指定的技术栈(上述通用规则禁止其他任何安装);否则使用纯静态 HTML 和 CSS,不进行安装(FOUNDATION,“纯 HTML 和 CSS”);方向采用抽取结果指定的卡片,不展示决策页面;如果没有 Impeccable,则沿用三个世界中的第一个;未安装的 Impeccable 仍保持未安装;报告中的每道关卡答案都标记为 UNVALIDATED。
子技能路径检测
告知区块此技能所在的位置。 区块的第一行写着 GENJUTSU_SKILL_DIR。
Claude Code 会自动填入。在其他主机上,请在区块前面、同一次 shell 调用中,添加一行,写明你读取此文件的目录:
GENJUTSU_SKILL_DIR='/absolute/path/to/bunshin'。当 genjutsu 路由器打印出
GENJUTSU_SKILL_DIR= 行时,按原样使用该值。
此区块定义 shell 状态,而 shell 状态不会在不同的 shell 调用之间保留。
$SKILL_BASE、load_skill 和 load_ref 仅存在于运行此区块的单次 shell 调用中。任何后续阶段——以及用户验证关卡之后的每个阶段,都属于后续阶段——
都会从空状态开始。因此:**请在即将运行的 load_skill 行所在的同一次 shell 调用中,重新输出整个区块。**切勿在未定义 $SKILL_BASE 的调用中运行 cat "$SKILL_BASE/...":路径会解析为 /<name>/...,导致 cat 失败,模块也会丢失。
**如果区块打印出 could not find the genjutsu modules,请停止流水线。**向用户显示该消息,包括安装命令,然后不做任何其他操作:后续每一步都依赖这些模块,在缺少模块时继续运行只会产生此技能旨在避免的通用输出。
GENJUTSU_SKILL_DIR="${GENJUTSU_SKILL_DIR:-${CLAUDE_SKILL_DIR}}"
# Resolution order, first hit wins:
# 0. claude.ai, /mnt/skills/plugins (or the older /mnt/skills/user): the
# genjutsu bundle.
# 1. This skill's own directory: its _jutsu, or the _jutsu next to it.
# 2. ${CLAUDE_PLUGIN_ROOT}/skills/_jutsu, when Claude Code substituted it.
# 3. Bounded probes: .claude/skills and .agents/skills from $PWD upward, then
# the skills directories installers write to, then /sessions (Cowork).
# 4. The Claude Code plugin cache, newest version. Last on purpose: an old
# plugin install must never win over a newer bundle.
# Resolved from scratch every time. A cache was tried here and removed: after a
# plugin update the old version directory is still on disk, so a cached path
# passes an "is it a directory" check and serves the previous release.
# No function below names a positional parameter (a dollar sign followed by a
# digit, or the whole argument list). Claude Code replaces those tokens in a
# skill's text with the words typed after the slash command, before the model
# reads this block: after "/genjutsu:cast make the cards feel physical", a path
# built from a function's first parameter would read "the/motion-principles/...",
# the second word typed. Each function reads its arguments with a bare
# `for name; do`, which walks the positional parameters without writing any out.
# A _jutsu directory counts only if it holds motion-principles. The shared
# skills directory of npx serves about 80 agents, so a directory name proves
# nothing. The entry file is named SKILL or GUIDE depending on the artifact
# (the bundle renames it at packaging time), so the name is assembled from
# parts: spelled out in full, the packaging step would rewrite it too.
genjutsu_is_jutsu() { # <candidate _jutsu directory>
for jutsu_dir; do
for d in SKILL GUIDE; do
[ -f "$jutsu_dir/motion-principles/$d.md" ] && return 0
done
done
return 1
}
# Print the first genjutsu _jutsu among the candidate paths read on stdin.
genjutsu_first_jutsu() {
while read -r jutsu_c; do
genjutsu_is_jutsu "$jutsu_c" && { printf '%s\n' "$jutsu_c"; return 0; }
done
return 1
}
SKILL_BASE=""
# 0. claude.ai mounts uploaded skills under /mnt/skills/plugins/<name>/ (seen on
# 2026-09-28), next to the user's other skills; /mnt/skills/user before that.
# GENJUTSU_CLAUDE_AI_ROOT stands in for /mnt/skills in the test suite only.
# Only a _jutsu holding motion-principles counts: a mount without genjutsu in it
# must never pass for a resolved base.
genjutsu_claude_ai="${GENJUTSU_CLAUDE_AI_ROOT:-/mnt/skills}"
for claude_root in "$genjutsu_claude_ai/plugins" "$genjutsu_claude_ai/user"; do
[ -z "$SKILL_BASE" ] && [ -d "$claude_root" ] || continue
SKILL_BASE="$(find -L "$claude_root" -maxdepth 2 -type d -name _jutsu 2>/dev/null | genjutsu_first_jutsu)"
done
# 1. This skill's own directory. Empty means unknown: never probe "/_jutsu".
if [ -z "$SKILL_BASE" ] && [ -n "$GENJUTSU_SKILL_DIR" ]; then
SKILL_BASE="$(printf '%s\n' "$GENJUTSU_SKILL_DIR/_jutsu" "$GENJUTSU_SKILL_DIR/../_jutsu" | genjutsu_first_jutsu)"
fi
# 2. Claude Code plugin root.
if [ -z "$SKILL_BASE" ] && [ -n "${CLAUDE_PLUGIN_ROOT}" ]; then
SKILL_BASE="$(printf '%s\n' "${CLAUDE_PLUGIN_ROOT}/skills/_jutsu" | genjutsu_first_jutsu)"
fi
# 3. Bounded probes, following symlinks (npx links .claude/skills/<name> to
# .agents/skills/<name>). The case guard stops the walk at "/", "." or "".
probe_dir="${PWD:-$(pwd)}"
probe_n=0
while [ -z "$SKILL_BASE" ] && [ "$probe_n" -lt 24 ]; do
probe_n=$((probe_n + 1))
SKILL_BASE="$(find -L "$probe_dir/.claude/skills" "$probe_dir/.agents/skills" -maxdepth 2 -type d -name _jutsu 2>/dev/null | genjutsu_first_jutsu)"
case "$probe_dir" in /|.|"") break ;; esac
probe_dir="$(dirname "$probe_dir")"
done
for probe_root in "$HOME/.agents/skills" "$HOME/.claude/skills" "$HOME/.codex/skills" \
"$HOME/.cursor/skills" /mnt/.claude/skills; do
[ -z "$SKILL_BASE" ] && [ -d "$probe_root" ] || continue
SKILL_BASE="$(find -L "$probe_root" -maxdepth 2 -type d -name _jutsu 2>/dev/null | genjutsu_first_jutsu)"
done
if [ -z "$SKILL_BASE" ] && [ -d /sessions ]; then
SKILL_BASE="$(find -L /sessions -maxdepth 8 -type d -path '*/.claude/skills/*/_jutsu' 2>/dev/null | genjutsu_first_jutsu)"
fi
# 4. Claude Code plugin cache, newest version first. Numeric version
# directories only, so a bare marketplace clone never wins.
if [ -z "$SKILL_BASE" ] && [ -d "$HOME/.claude/plugins/cache" ]; then
SKILL_BASE="$(find "$HOME/.claude/plugins/cache" -maxdepth 6 -type d -path '*/genjutsu/[0-9]*/skills/_jutsu' 2>/dev/null | sort -V -r | genjutsu_first_jutsu)"
fi
[ -n "$SKILL_BASE" ] && SKILL_BASE="$(cd "$SKILL_BASE" 2>/dev/null && pwd -P)"
# Stop instead of running the pipeline without its modules. Name every root that
# was tried, so a new layout can be reported instead of guessed.
if [ -z "$SKILL_BASE" ]; then
echo "genjutsu: could not find the genjutsu modules (a _jutsu directory holding motion-principles)." >&2
echo " genjutsu's pipelines do not work without them. Install the full bundle:" >&2
echo " any agent npx skills add https://genjutsu.athevon.dev -g" >&2
echo " Claude Code /plugin marketplace add AThevon/genjutsu, then /plugin install genjutsu" >&2
echo " claude.ai upload genjutsu.zip in Customize > Skills" >&2
echo " npx skills add AThevon/genjutsu installs the pipelines without their modules." >&2
echo " Tried: /mnt/skills/plugins, /mnt/skills/user, GENJUTSU_SKILL_DIR (${GENJUTSU_SKILL_DIR:-empty}) and its parent," >&2
echo " \$CLAUDE_PLUGIN_ROOT, .claude/skills and .agents/skills from \$PWD upward," >&2
echo " ~/.agents/skills, ~/.claude/skills, ~/.codex/skills, ~/.cursor/skills," >&2
echo " /mnt/.claude/skills, /sessions, ~/.claude/plugins/cache." >&2
echo "genjutsu: stop the pipeline here and show this message to the user." >&2
return 1 2>/dev/null || exit 1
fi
echo "genjutsu: modules from $SKILL_BASE" >&2
# Print a module's entry file. A missing module does not stop the pipeline (a
# partial claude.ai upload is legitimate), but it is announced, and the final
# report lists it: shell state does not survive, so the model keeps the list.
load_skill() { # <module>
skill_name=""
for skill_arg; do skill_name="$skill_arg"; break; done
for d in SKILL GUIDE; do
if [ -n "$skill_name" ] && [ -f "$SKILL_BASE/$skill_name/$d.md" ]; then
cat "$SKILL_BASE/$skill_name/$d.md"
return 0
fi
done
echo "genjutsu: sub-skill '$skill_name' NOT LOADED - not found under $SKILL_BASE. Carry on, and list it under 'Modules not loaded' in the final report." >&2
return 1
}
# Print one reference file of a module: load_ref <module> <path inside it>.
load_ref() { # <module> <path inside it>
ref_module=""; ref_path=""; ref_n=0
for ref_arg; do
ref_n=$((ref_n + 1))
case "$ref_n" in
1) ref_module="$ref_arg" ;;
2) ref_path="$ref_arg"; break ;;
esac
done
if [ -n "$ref_module" ] && [ -n "$ref_path" ] && [ -f "$SKILL_BASE/$ref_module/$ref_path" ]; then
cat "$SKILL_BASE/$ref_module/$ref_path"
return 0
fi
echo "genjutsu: reference '$ref_module/$ref_path' NOT LOADED - not found under $SKILL_BASE. Carry on, and say so in the final report." >&2
return 1
}
bunshin 需要在 shell 外部使用绝对路径 $SKILL_BASE:工作流模板通过路径传递,每份克隆简报都会列出克隆必须读取的模块。记下区块首次打印的路径(genjutsu: modules from ...),并将其连同层级、能力和 Impeccable 目录写入 .bunshin/run.json;所有者映射则在 FOUNDATION 阶段写入 .bunshin/owners.json。Shell 状态仍然不会保留:每次调用中,只要加载内容或运行其脚本,就要重新输出区块。
编排模块在 SUMMON 阶段、首个克隆启动前加载:
# (skill-base block re-emitted above this line)
load_skill orchestration
其引用文件在对应阶段加载,每次 shell 调用加载一个:
load_ref orchestration references/material.md(HARVEST、MATERIAL),
references/impeccable.md(READ 阶段存在 Impeccable 时、DIRECTION、SEAL),
references/evidence.md(FOUNDATION、PROVE)。
传给模板的每个路径都必须是绝对路径:下文的 <root> 是项目的绝对路径;临时目录会在调用前解析(主机提供会话临时目录时使用该目录,否则使用 printf '%s\n' "${TMPDIR:-/tmp}/bunshin-<project>")。模板会对接收的路径加引号,因此作为路径传入的 shell 表达式绝不会展开。
流水线
共十二个阶段。运行期间写入的所有非站点内容都放入 <root>/.bunshin/,并在 SUMMON 阶段将其添加到 .gitignore:run.json、owners.json、harvest/、research/、direction/(所选卡片的看板和主视觉,以及未使用 Impeccable 时的 direction.md)、captures/round-N/(第 1 轮:评审证据;第 N+1 轮:第 N 轮优化后的证据)、reviews/round-N.json 及其旁边的 lens 文件、reviews/round-N-refine.json、reviews/round-N-verdict.json、decisions/round-N.md、report.md。.bunshin/ 下的任何内容都不会被站点导入:隔离副本会排除该目录。
0. 阅读——项目、需求说明和宿主
# 1. Web (existing)
cat package.json 2>/dev/null | grep -E '"(gsap|motion|framer-motion|three|@react-three/fiber|@react-three/drei|animejs|popmotion|lenis|locomotive-scroll)"'
cat package.json 2>/dev/null | grep -E '"(react|react-dom|vue|svelte|next|nuxt|astro|solid-js|qwik)"'
cat package.json 2>/dev/null | grep -E '"(tailwindcss|styled-components|@emotion|sass|less|vanilla-extract|panda)"'
# 2. Android / Compose
ls build.gradle.kts build.gradle settings.gradle.kts settings.gradle 2>/dev/null
grep -rE 'androidx\.compose|implementation\("androidx\.compose' build.gradle* settings.gradle* 2>/dev/null
# 3. Compose Multiplatform / KMP
grep -rE 'org\.jetbrains\.compose|kotlin\("multiplatform"\)|id\("org\.jetbrains\.kotlin\.multiplatform"\)' build.gradle* settings.gradle* 2>/dev/null
# 4. Apple / SwiftUI
ls *.xcodeproj *.xcworkspace Package.swift 2>/dev/null
grep -lE 'import SwiftUI|@main.*App' --include="*.swift" -r . 2>/dev/null | head -1
# 5. Apple platform sub-detection (iOS vs macOS)
grep -E '\.iOS\(|\.macOS\(' Package.swift 2>/dev/null
grep -E 'SDKROOT = (iphoneos|macosx)' *.xcodeproj/project.pbxproj 2>/dev/null
# 6. Mobile web indicators
grep -rE 'viewport.*width=device-width|@media.*pointer:\s*coarse|@media.*max-width' --include='*.html' --include='*.css' --include='*.scss' . 2>/dev/null | head -3
ls public/manifest.json public/sw.js 2>/dev/null
# 7. Legacy bridge indicators (mention in DISCOVER, do not auto-load)
ls -- *.xib *.storyboard 2>/dev/null
find . -path '*/res/layout/*.xml' 2>/dev/null | head -1
grep -rE 'setContentView\(R\.layout' --include='*.kt' --include='*.java' . 2>/dev/null | head -1
整理检测结果:
- 动画库:gsap、
motion、framer-motion、three/@react-three、anime.js,或无。motion和framer-motion是同一个库的两个名称。 Framer Motion 已更名为 Motion;motion是当前软件包,framer-motion是旧版软件包,目前仍被广泛安装并继续发布。请 记录package.json中使用的是哪一个——导入路径不同,子技能需要知道这一点。如果两者都存在,说明项目正处于迁移过程中: 请说明这一点,并沿用你正在编辑的文件已导入的那个。 - 框架:React、Vue、Svelte、Next.js、Nuxt、Astro、原生
- CSS:Tailwind、styled-components、CSS modules、原生 CSS
- 如果未检测到任何内容:从头开始,所有选项均可用
- 原生 Android:通过 gradle 依赖检测 Compose。
- 原生 Apple:通过 Package.swift / xcodeproj + swift 文件检测 SwiftUI。根据 Package.swift 平台或 pbxproj SDKROOT 区分 iOS 与 macOS。
- Compose Multiplatform:kotlin-multiplatform 插件 + jetbrains.compose 插件。
- 移动端场景:视口、清单文件、仅限移动端的媒体查询,或原生 iOS/Android。
- 桌面端场景:macOS 目标,或 Web 项目中没有移动端标记。
- 旧版混合项目:存在
.xib、.storyboard、布局 XML、setContentView(R.layout.*)。仅作说明,不自动加载。
接着阅读需求说明,并略读其中提及的所有来源(档案、当前网站、文档),暂不收集信息。运行上述能力检查。评估工作规模:页面或屏幕数量、语言、受众、主要渠道。判断 bunshin 是否适合当前流程(参见“Step down”)。使用 Impeccable 时,现在运行 load_ref orchestration references/impeccable.md,并阅读 Impeccable 自身的 reference/init.md(其中 Step 3 是访谈问题,Step 4 是 PRODUCT.md 格式)以及 reference/new-work.md 的第 2 节(绘制前要提出的问题),以便召集时能将这些内容纳入其中。
召集之前,请像 cast 和 paint 一样,用一行文字说明你的初步判断:“我目前的判断:为 <谁> 打造 <什么>,方向是 <方向>,技术栈为 <技术栈>。”无法填写的项目请标为未知,并在沟通环节提出相关问题。
1. SUMMON - 第一道关卡
先发送一条消息,然后在主机提供工具时调用一次结构化提问:
bunshin 用于 <内容>: 个页面,使用 <语言>,来源于 <来源>。 计划:收集材料,由四个克隆进行研究并综合结果;你选择方向,我搭建 基础和标志性界面, 个页面克隆负责其余部分,然后进行多轮优化和评审, 直到只剩小问题或达到该层级的上限。 此主机:<通过工作流并行运行克隆 | 逐个运行克隆 | 无法召唤>。 Impeccable:<版本,引导方向 | 未安装:方向讨论效果较弱;
npx impeccable install --project -y可解决此问题>。截图:<有 | 无,以及因此付出的代价>。 技术栈:<框架和适配器,已就绪 | 尚无项目:下方技术栈问题中的每个选项都列出了 确切的脚手架命令和软件包,包括类型检查所需的那些>。 你对层级和技术栈的回答仅涵盖这一次安装,不涉及其他内容。 成本:一次运行的测量结果为一个七页双语网站,约 10.5M 个子 Agent 令牌,耗时六到 八小时。据此估算:精简版约 6M,标准版约 8-9M,完整版约 12-13M,另加本次 会话自身的令牌;你的成本随页面数变化。 <权限:本次运行所需的允许规则;此会话会在每条命令运行前询问。>
同一次调用中,最多提四个问题:先问层级(将标准版标为推荐选项),然后问对工作影响最大的产品问题,并把推荐选项放在前面。使用 Impeccable 时,这些问题包括其 init 第 3 步提出的问题,以及其 new-work 在抽选前提出的问题,最多三个;如果尚无脚手架,则先问技术栈问题。完成此访谈后,不再重复提问。未使用 Impeccable 时,询问设计构思的领域。四个问题未能确定的内容都作为假设,写入 PRODUCT.md 和报告中。
等待回答。然后:将 .bunshin/ 添加到 .gitignore,写入 .bunshin/run.json,并加载编排模块。使用 Impeccable 时,从项目根目录运行一次 <dir>/scripts/impeccable context(<dir> 取能力检查输出的值;项目安装不会将任何内容放到 PATH 上)。
2. HARVEST - 真实素材
load_ref orchestration references/material.md,然后按其中说明操作:以完整分辨率获取客户自己的图片、他们的文本、实际阅读过的联系表、从关键图片中提取的调色板、附有来源的事实,以及缺失内容清单。只收集客户拥有的素材。所有内容都放入 .bunshin/harvest/。
3. RESEARCH - 四个克隆,然后综合结果
使用 brief(仅包含事实和关卡问题的回答)运行 research.js,并将 out 设置为
<root>/.bunshin/research/synthesis.md。阅读综合结果。然后编写 PRODUCT.md:使用 Impeccable 时,遵循其 init 第 4 步的格式;未使用时,采用相同的标题。只写已确认的事实;单独列出绝不可杜撰的内容(价格、评价、客户、数据);注明现有证据和原则。
4. DIRECTION - 第二道关卡
运行 references/impeccable.md 中描述的方向轮次:抽选前排序的七个世界、抽选、挑战者及其加码、决策页面,以及仅在页面无法运行或关闭时未作答的结构化问题备用方案。每张卡片都包含交互论点(时间范围、悬停、滚动、禁止的模式)及其 Allowed patterns: 行,并放在页面实际展示的字段中,因此一次选择即可验证两项论点。bunshin 始终以代码为主进行构建。没有 Impeccable 时,在渲染后的页面上运行该参考文档所述的轮次。
可猜测性测试适用于每张卡片:如果某个方向仅凭产品类别就能写出来,那它就是该类别的默认方向,而非真正的方向。
用户选定后,写入契约(surface-brief write 或 .bunshin/direction.md):THESIS、OWN-WORLD、STORY、FIRST VIEWPORT、FORM、FINISH、交互论点和允许的模式。每个克隆和每位评审都要依据此文件来评判作品。使用 Impeccable 时,将所选卡片的展板和主视觉图片保存到 .bunshin/direction/(完成度评审者会将其作为 QUALITY BAR 卡片读取)。接着,在单独的 shell 调用中重新输出 skill-base 块,并加载 tells 及其 Web 参考(先运行 load_skill tells,再运行 load_ref tells references/web.md):MATERIAL 接下来确定字体,而 tells 则指出必须排除的反射式做法。将所选卡片的 Allowed patterns: 行与目录核对,并在下一条消息的一行中(不是问题)以及报告的假设中,点明它允许的目录内反射式做法。
5. MATERIAL - 依据证据,而非反射
根据 references/material.md:在真实布局的缩微版本中渲染字体样张并以截图进行比较;修整并裁剪照片;让抠图贴合对象自身边缘,并在浅色和深色背景上检查;将纹理制成平铺图;为每个栅格图像注明来源。在契约中记录字体选择及其理由。网站发布的每个栅格图像都要写入项目自己的资源文件夹(例如 src/assets/),由 shared 管理,并附带索引(文件、主题、可在哪个页面使用),且由构建数据包列出。
6. FOUNDATION - 属于你,而非克隆品
始终加载(使用上文定义的 load_skill <name>:模块缺失时会打印 NOT LOADED,流水线会继续运行,因此最终报告中要保留此列表):
load_skill motion-principles- 基础
每次 shell 调用加载一个模块。 shell 调用的输出超过约 30,000 个字符时,不会以内联形式返回:你只能看到 2,000 字符的预览和一个文件路径,而无法读取的模块就不算已加载。因此,每次加载调用都要重新输出 skill-base 块,并加载一个模块,将输出控制在 25,000 字符以内:没有模块入口文件会超过此上限,validate-skills.py 会确保这一点。加载时要持续记录:缺少模块时,load_skill 会打印 NOT LOADED 并继续运行,最终报告要列出已加载和未加载的模块。完整加载每个模块:切勿将 load_skill 或 load_ref 通过管道传入 head、tail、sed 或 grep;上面的分批规则已确保每次调用都低于限制,而截断的模块不算已加载。
上下文层(适用时加载):
| 检测到的情况 | 加载 |
|---|---|
| Web 技术栈,且范围为 medium 或 full | load_skill tells(单独调用,并加载 references/web.md,见下文) |
| 移动端场景(Web 移动端或原生 iOS / Android) | load_skill mobile-principles |
| 桌面端场景(macOS 或无移动端指示的 Web 桌面端) | load_skill desktop-principles |
| 明确要求审计,或 scope=full | load_skill design-audit |
| 高级 UI/UX 问题 | load_skill ui-ux-pro-max |
tells 会单独占用一次 shell 调用。 加上它的 Web 参考内容后,长度约为 20,000 | |
| 个字符,已经接近 shell 调用的输出无法内联返回的临界点,因此不能与任何其他模块共用一次调用。请在该调用中重新输出 skill-base 块,然后: |
# (skill-base block re-emitted above this line)
load_skill tells
load_ref tells references/web.md
它会在 thesis gate 之后、任何视觉选择定稿之前加载:已验证的 thesis 会说明需要哪些模式,而 tells 会指出没人要求的惯性做法。Compose 或 SwiftUI 技术栈绝不加载,轻量范围也绝不加载。在 paint 中,这一项已满足:Phase 3 会在查询 design-system 之前加载 tells。
特定技术栈(通过 SCAN 加载):
| 检测到的技术栈 | 要加载的子技能 |
|---|---|
| gsap | load_skill gsap |
motion 或 framer-motion(同一个库的两个包名) | load_skill framer-motion |
| 纯 CSS / Tailwind / 无库 | load_skill css-native |
| three / @react-three | load_skill threejs-r3f |
| Canvas / 生成式 | load_skill canvas-generative |
| Android Compose | load_skill compose-motion(始终加载)+ load_skill compose-graphics(如果 scope=full 或 thesis 属于 advanced——见下文) |
| Compose Multiplatform | load_skill compose-motion + load_skill compose-multiplatform(始终加载);如果检测到 iOS 目标且需要 SwiftUI 互操作,则加载 load_skill swiftui-motion;如果属于 advanced,则加载 load_skill compose-graphics |
| SwiftUI iOS 或 macOS | load_skill swiftui-motion(始终加载)+ load_skill swiftui-graphics(如果 scope=full 或 thesis 属于 advanced) |
“Advanced thesis”触发条件,适用于 compose-graphics / swiftui-graphics:
thesis 中包含以下任一术语时,即视为“advanced”(并触发加载 graphics 子技能):
shader、Metal、AGSL、RuntimeShader、MSLliquid-glass、glassEffect、morphing transitionM3 Expressive、MotionScheme、expressive motioncolorEffect、distortionEffect、layerEffectCanvas(结合生成式 / 粒子 / 流场上下文)holographic、CRT、displacement、ripple
否则,只加载基础 motion 子技能。
bunshin 会构建整个网站,因此其范围始终为 full:tells(已在方向确定后加载)、mobile-principles、desktop-principles 和 design-audit 均适用;每项都要单独占用一次调用,并且必须在 foundation 的第一行之前完成。使用 Impeccable 时,请在首次 UI 编辑前立即阅读其
reference/craft-floor.md。
然后亲自构建:
- 项目脚手架,适用于尚无项目的情况。根目录已包含
.claude/、.bunshin/、 PRODUCT.md 和.gitignore,而脚手架工具会拒绝、重定向,或提议清空非空文件夹:运行召唤指令中指定的命令,但跳过其安装步骤,并在<scratch>/scaffold中运行;然后运行rsync -a --ignore-existing '<scratch>/scaffold/' '<root>/',将 脚手架的.gitignore内容追加到根目录的.gitignore,用包管理器在根目录安装指定的软件包(这是唯一会运行包管理器的地方),然后在进行其他操作之前运行一次构建,并确认其打印的退出代码为 0。 - 设计令牌:色彩墨水、语气角色(为整个字段设置背景、文本、按钮和焦点的一种类别)、流式字号比例、间距、四到五种时长以及命名缓动曲线。
- 内容模型:所有语言中的每段可见文本都放在类型化内容文件中;实际信息(联系人、法律信息、服务区域)只有一个来源。视图中不放文案:这样客户之后才能用 AI 编辑网站。
- 共享组件:布局(元数据、备用语言版本、结构化数据、字体)、页眉、页脚、移动端行动号召栏、带显现效果的响应式图片,以及每个内页都会复用的页面页眉。
- 分发前的所有路由:每种语言中每个页面的路由文件;每个文件都用空内容文件渲染该页面的视图;并确保页眉链接到每个页面,这样每个克隆从首次运行起就能构建并捕获自己的路由。
- 标志性界面:主页和一个标志性交互,这是决定一切的界面。捕获它(
references/evidence.md),根据捕获结果修正,再次捕获。做两轮,然后继续。
使用纯 HTML 和 CSS 时(没有构建步骤):传入 build.cmd: 'true'、build.static: '.' 和 build.link: []。每个页面的 HTML 文件就是其视图,也承载文案,因此内容模型规则不适用,报告中会说明这一点。在分发前,先在主页中编写一次页眉和页脚,再将它们原样复制到每个路由文件中;之后对它们的任何更改都属于共享更改请求,需要在每个页面中应用。
编写 .bunshin/owners.json,纳入所有者:每个页面各有一个所有者(负责其视图、内容文件和组件文件夹),所有多个页面共用的内容归 shared 所有,包括路由文件,以及主页的所有者;分发结束前,主页由你负责。将所有这些内容传给 review.js 和 verdict.js;传给 build.js 的只能是你没有亲自构建的页面所有者,并为每个所有者附上任务;传给 refine.js 的只能是有待修复项的所有者,或决策文件中涉及其文件的规则。
7. 克隆——并行构建页面
运行 build.js,传入 root、任务包(按绝对路径列出优先阅读内容:PRODUCT.md、契约、质量底线、设计系统、你构建的主页;约定;论点规则;语气;资源文件夹及其索引)、带任务的页面所有者,以及 build:将框架的二进制文件作为 cmd(有类型检查时还需传入 check),解析后的绝对路径 scratch、静态输出文件夹作为 static、shoot.mjs 的绝对路径作为 shoot,以及每个所有者的路由,这样每个克隆都会从自己的副本中捕获自己的页面,并在返回前查看捕获结果。编排模块中提供了优秀任务简报的构成;克隆只知道其简报中写了什么。
克隆返回后:阅读每一条 shared_change_requests,应用可行的请求(此时共享文件由你负责),并记录需要向客户确认的未决问题。
8. PROVE - 在任何人审查之前
load_ref orchestration references/evidence.md。然后按顺序执行:
- 构建和类型检查,二者都要打印退出代码 0;如果已安装 Impeccable,则运行
<dir>/scripts/impeccable detect --json <source folders>;执行 tells 审计(在同一个 shell 调用中,重新输出 skill-base 代码块,然后运行python3 "$SKILL_BASE/design-audit/scripts/audit.py" . --group tells);以及搜索 U+2014,除非 thesis 允许使用它。 - 使用
shoot.mjs捕获每个页面和语言:标准截图列表、每个页面和语言各用一个 shots 文件并各调用一次,输出到.bunshin/captures/round-1/。如果已安装 Impeccable,还要添加其 finish reviewer 要求的两张截图,不分段,直接保存到指定文件:{ "path": "/", "w": 1440, "h": 900, "full": true, "out": "<root>/.impeccable/review/desktop.png" }以及同样使用 390x664 的截图,保存到mobile.png,使用相同的reveal。每次 PROVE 都要重新生成这些截图:其 verdict pass 会再次读取这些确切路径。亲自查看首屏。 - 对能促成转化的路径进行功能测试(菜单、粘性栏、选择内容并填入表单、表单步骤、端点失败和成功),并逐项报告测量值。
在审查前修复问题:构建坏掉时花掉的五个镜头,全都白费。通过的内容要以已验证的状态放入资料包。
9. REVIEW - 独立审查视角
使用证据资料包、轮次、该 tier 的审查视角、模块目录($SKILL_BASE/mobile-principles、desktop-principles、motion-principles,使用绝对路径)、owners.json 中的每个负责人运行 review.js,并将 planOut 设为 <root>/.bunshin/reviews/round-<N>.json,其中 N 是本次审查开启的轮次(首次为 1;重建后为下一轮)。如果已安装 Impeccable,且 host 列出了其 agent 类型,则传入 finishAgent,值设为 host 为 Impeccable 的 finish reviewer 指定的名称(Claude Code:impeccable-finish-reviewer),以便全新启动该 reviewer,符合 Impeccable 的要求;绝不要将其定义粘贴到提示词中。如果已安装 Impeccable,但 host 未列出该 agent 类型,则传入 finishRole,值设为其 reference/degraded/finish-reviewer.md 的绝对路径,并在报告中说明这一点。
首先阅读 finish disposition。recapture:证据不合格,而不是构建有问题;重新捕获 recapture 列表中列出的内容,然后再次运行审查,该轮不计数。没有 disposition(finish reviewer 两次都未返回内容)时也按同样方式处理。rebuild:自行重建指定区域,然后再次运行完整审查;该轮计数,作为该 tier 的一次 refine 轮次。fix 或 ship:阅读计划及其 rejected 列表(与已验证方向相矛盾的拒绝由计划决定;对真实缺陷的拒绝则由你推翻),并在 refine 之前自行应用 orchestrator 的修复。
10. REFINE - 循环
从第 1 轮开始,每一轮:
- 使用
root、数据包(PRODUCT.md、契约、质量底线,均为绝对路径)、planPath(第 1 轮:评审的计划文件;后续轮次:上次判定写入的计划)、decisions(从第 2 轮起:上次的决策文件)、需要应用修复或决策的所有负责人、适用时的shared(它会先单独运行),以及与阶段 7 相同的build,运行refine.js。将结果写入.bunshin/reviews/round-N-refine.json。 - 再次 PROVE:运行检查,然后将新截图保存到
.bunshin/captures/round-<N+1>/,并重新拍摄同一批文件的两张.impeccable/review/截图。 - 使用新数据包、
history(本轮的计划文件、refine 结果、决策文件)、round、所有负责人、设为<root>/.bunshin/reviews/round-<N+1>.json的planOut、与评审相同的finishAgent,以及在层级要求时设为true的freshEyes,运行verdict.js,并附上一句话说明受众如何接触网站的场景。它会根据最终评审者的待处理意见,写出下一轮计划;如果没有剩余事项,修复列表将为空。将结果写入.bunshin/reviews/round-N-verdict.json。 - 当判定以冷眼审视方式运行且循环继续时,将他们的意见整理成决策,由你亲自完成(如果停止规则终止循环,则将这些意见写入报告):
.bunshin/decisions/round-N.md,列出下一轮适用于各处的规则(每张主视觉图片的唯一职责、允许使用高饱和色的位置、字体角色、控件形状、会发生变化的共享契约)。绝不能把这些意见作为任务列表交给克隆。计划中的orchestrator修复由你亲自应用。 - 应用停止规则(编排模块),先处理处置结果:
recapture表示重新截图并再次运行判定;rebuild表示返回评审。然后:剩余事项均不超过 minor 时交付;达到该层级的轮数上限时停止;某一轮没有修复任何问题时停止。否则下一轮从步骤 1 开始。
要超过该层级的轮数上限,必须征得用户同意,并先向用户展示剩余事项表格。
11. SEAL - 文档、审计、报告
- DESIGN.md:按宿主列出的 Agent 类型启动 Impeccable 的文档编写 Agent(Claude Code:
impeccable-documenter),并提供项目根目录、产物路径(源文件夹和构建输出)、方向契约、PRODUCT.md、Impeccable 的reference/document.md绝对路径,以及写入边界(项目根目录);它会根据已交付的代码写入 DESIGN.md 和.impeccable/design.json。如果没有 Impeccable,则根据已交付的代码自行编写 DESIGN.md,绝不能依据意图编写。 - AGENTS.md(并创建一个指向它的
CLAUDE.md):面向将维护网站的 AI,使用客户的语言。说明各类更改应在哪里进行、设计规则、写作规则、命令,以及推送前要检查的内容。 - 启动保护机制:在构建时发出警告,列出所有仍缺失的信息(联系方式、法律详情、域名),并提供严格模式,在该模式下生产构建失败。
- 审计:对整个项目运行,详见下文。
- 报告:详见下文,同时写入
.bunshin/report.md。
此流程的收尾步骤过去有 25 个复选框,其中有几个要求 Agent 运行它无法运行的工具。没打开 Chrome 就勾选“已通过 Chrome DevTools 验证 60fps”,等于把“我没看过”说成“我看过了,一切正常”,这比什么都不说更糟。
因此,我们将检查分为两组。报告第一组,并附上所用证据。第二组一律不要勾选——将其交由他人处理。
在此处检查,并提供证据
每一项都按 检查项 - 结论 - 证据 的格式报告。证据可以是你运行的 grep、计算出的值,或你查看的 文件:行号。结论旁没有证据就不算发现;无法检查的项目应报告为未检查,而不是通过。
- 对照代码核验设计主张。 首先,以下每项检查都假定代码就是已验证的设计主张。对于已验证的设计主张实际作出的每项承诺(时长、缓动、弹簧效果、配色、字体、布局类型),请给出对应的
file:line,或写“未找到”。如果设计主张没有对配色或字体作出承诺(例如仅涉及交互的设计主张),就不需要提供配色或字体对应的行。承诺没有对应的file:line,就是发现了问题,而非通过检查。坦诚降级:如果承诺的动效无法正常交付,就交付静态版本,在此说明,并指出放弃了哪项承诺;这算作发现了问题。绝不要为了纸面上履行承诺而交付有缺陷的动效。证据:每项承诺一行,例如hover 180ms ease-out - src/Card.tsx:42。 - 遵循减少动态效果设置。 Web:必须有实际降低动画效果的
prefers-reduced-motion代码块,不能是空代码块。SwiftUI:使用accessibilityReduceMotion。Compose:使用基于ValueAnimator.areAnimatorsEnabled()/Settings.Global.ANIMATOR_DURATION_SCALE的辅助函数。证据:防护代码所在的文件和行,以及它会降级为何种效果。 - 存在退出动画:所有会卸载内容的地方都应有退出动画。证据:条件渲染及其退出路径,或列出没有退出动画的卸载场景。
- 不动画布局属性。 不得为
width、height、top、left、margin或padding添加动画;应使用 transform、opacity 或graphicsLayer。证据:grep 命令及其命中结果,或说明未返回结果。 - 所有交互元素的焦点状态均可见,且不得在没有替代样式的情况下使用
outline: none。证据:grep 命令。 - 交互元素具备全部五种状态: 默认、悬停或按下、焦点、激活、禁用。证据:列出每个组件已有的状态,以及缺失的状态。
- 使用设计令牌,而非随意填写数值。 颜色和间距应来自项目的设计令牌(如果存在 MASTER.md,则以其为准)。证据:列出不合规范的数值及其
file:line。 - 对比度:正文文本至少为 4.5:1,大号文本和 UI 边界至少为 3:1。必须计算你输出的令牌值之间的对比度;不能凭色块目测。证据:列出颜色组合及计算所得的比率,例如
#831843 on #FDF2F8 = 9.4:1。 - 语义。 Web:可点击的
div必须有对应角色,装饰性动效应使用aria-hidden。Compose:自定义交互组件应使用Modifier.semantics。SwiftUI:没有文本的控件应使用.accessibilityLabel。证据:grep 命令。 - 仅适用于 Web。 条件渲染应使用
AnimatePresence或框架中的等效方案包裹;谨慎使用will-change,并在动画结束后移除。证据:grep 命令。 - 核对设计主张中的明显特征。 仅当已加载
tells时执行。在一次 shell 调用中,重新输出 skill-base 代码块,并运行python3 "$SKILL_BASE/design-audit/scripts/audit.py" . --group tells,无论是否已加载design-audit。每项发现都必须提供证据:要么列出保留的问题及其file:line,要么引用设计主张Allowed patterns:行中对应的条目。保留的明显特征计为发现的问题。获准的特征应标记为“设计主张允许”,并引用对应条目,不计为问题。手动检查tells列表(div 中的虚构产品、重复的布局类型、悬浮在角落的段落、文案语体、存放在 JavaScript 数据中的文案)时,也应以相同方式报告。 每项发现只能归入以下三种结果之一,不得有第四种:设计主张允许,并引用其Allowed patterns:条目;不属于此类特征,仅当检测结果与该条目自身的判定标记相矛盾时使用(例如循环动画实际表示加载状态、模糊效果是模态框背景、匹配的行不是显示给用户的文本),并提供能够证明这一点的file:line,绝不能因个人审美判断而使用;或问题。报告前先修复,并且只修复本次运行引入的内容:如果问题出现在本次运行写入的行上(项目有git diff时应查看),就移除或改写该行,然后重新运行此检查,最多两轮;仍未通过的项目应连同其file:line一并报告。项目中原本就有、且本次运行未写入的行上的明显特征,此处绝不修改:应列出并告知用户。此步骤不得修改现有项目受保护清单中的任何内容(公共令牌名称、URL、导航标签、表单字段名称、徽标、法律声明)。
必须由你运行——此处未验证
Agent 无法打开性能分析器、连接设备或移动指针。这些事项会以交接区块的形式报告,其中包含准确的调用方式,并标记为 UNVERIFIED。不要勾选,不要淡化,也不要因为其他部分看起来没有问题就省略此部分。
| 目标 | 运行内容 | 通过条件 |
|---|---|---|
| Web | Chrome DevTools > Performance,覆盖交互过程进行录制 | 没有帧超过 16.7ms |
| Web | 在 375 / 768 / 1024 / 1440 的页面 | 无水平滚动,无内容被裁切 |
| Web | 在启用 OS“减少动态效果”设置时查看页面 | 降级路径确实运行 |
| Web | 如果本次会话中未渲染网页字体,则在联网时查看页面 | 字体加载成功而非使用后备字体,且字体粗细和字重符合论述所依赖的设置 |
| Compose | Layout Inspector > Component Tree > View Options > Show Recomposition Counts | 滚动时计数稳定 |
| Compose | 在中端设备上运行 androidx.benchmark.macro Macrobenchmark | 60fps 时帧耗时低于 16.67ms,120fps 时低于 8.33ms |
| SwiftUI | Instruments > Animation Hitches | 转场期间无卡顿 |
| SwiftUI | 启用 Reduce Motion,并将 Dynamic Type 设为 200% | 无内容被裁切,也没有只能通过移动才能呈现的内容 |
| macOS | 将指针移至每个交互元素上;用键盘遍历整个视图 | 悬停状态正常触发,焦点环可见,快捷键已绑定 |
如果预览或开发服务器已经在运行,且用户同意,那么通过浏览器操作来收集 Web 项的结果,比将这些事项交给用户更好。绝不要仅为审计而启动它,也不要为此安装任何东西。
分别报告这两组,并注明数量。“已检查 11 项,发现 2 个问题,交接 8 项”才是如实的审计结果。不要只给出一份勾选清单。
始终以模块信息结束报告,并单独写成两行:
Modules loaded: motion-principles, framer-motion, design-audit
Modules not loaded: none
Modules not loaded 列出每个 load_skill 调用报告为 NOT LOADED 的模块,以及加载表要求加载但从未请求的模块。只有两者都为空时才写 none。各次调用之间的 Shell 状态不会保留,因此必须从首次加载到最后一次加载自行记录此列表:Shell 不会替你记住。
最终报告
内容简洁,并按以下顺序排列:
- 已构建:各语言对应的路由,以及设计系统和契约所在位置。
- 已验证,附证据:构建、类型检查、检测器、明显特征、捕获结果(所在位置)、每项功能测试的测量值、最后的判定及其处理方式。
- 未验证:审计中已交接的项目,标记为 UNVERIFIED,以及任何 lens 无法检查的内容。
- 遗留事项:最后一轮之后仍存在的小问题,按页面列出。
- 供客户端参考:
missing.md和内容文件中仍然缺失的所有事实,以及克隆提出的未决问题。 - 成本:档位、运行轮数、每个工作流中的 Agent 数量,以及主机报告的 token 数量。
- 假设,以及在无头运行中,所有标记为 UNVALIDATED 的门禁回答。
- 审计中的两行模块信息。
除非用户提出要求,否则不提交、不推送、不部署任何内容。
危险信号——你即将违反此技能
| 想法 | 事实 |
|---|---|
| “用户说放手去做,那我就跳过召唤” | 每次都要询问档位。放手去做也是一个档位,而且有相应代价。 |
| “先做调研,问题会问得更好” | 询问档位之前,不要做任何更深入的事。按 READ 浏览资料即可,足以提出好问题。 |
| “我来问问他们想怎么看方向” | 只能问两个问题,不能问第三个。提供决策页面,或用一行话说明将展示渲染后的页面。 |
| “我也让克隆来做主页吧” | 标志性页面由你负责。所有克隆都会以它为参考。 |
| “两个克隆可以共用样式表,每人只写一行” | 每个文件只能有一个负责人。共享需求应作为请求提出。 |
| “项目文件夹里的构建是绿色的,这就够了” | 克隆是在副本中构建的。在共享目录中构建成功并不能证明什么。 |
| “透镜可以根据代码评审,截图太费时间” | 它们评审的是渲染结果。先截图、查看首屏,然后再评审。 |
| “最终评审者说要重新截图,但我还是按计划执行” | 无效证据不具约束力。先重新截图。 |
| “冷眼评审列了八项任务,我来分配吧” | 将它们转化为决策文件中的规则。这一步属于艺术指导,由你负责。 |
| “再来一轮就能拿下最后几分” | 按停止规则决定。小问题记入报告。 |
| “这里没有子 Agent 工具,那我自己来扮演克隆” | 那就没有独立评审。退回到绘制阶段。 |
| “Impeccable 没有安装,我来安装吧,反正免费” | 先询问。对方说不就到此为止。 |
| “已经准备好了,我推送上去让他们看看” | 未经询问,任何内容都不得离开本机。 |
快速决策树
Whole-site request received
|
+- READ: stack, brief, sources, host capabilities, size
| |
| +- smaller than a site, native stack, or no subagents? -> step down (cast / paint)
|
+- SUMMON (gate 1): read, plan, host, Impeccable, stack, cost; tier + product questions
|
+- HARVEST -> RESEARCH (four clones, a synthesis) -> PRODUCT.md
|
+- DIRECTION (gate 2): seven worlds, the draw, cards with both theses; contract; tells
|
+- MATERIAL -> FOUNDATION (yours: scaffold, tokens, content model, shared, routes, signature)
|
+- CLONES: one per page, disjoint files, isolated builds, each capturing its own page
|
+- PROVE -> REVIEW (lenses + plan; recapture? rebuild? read the disposition first)
|
+- REFINE -> PROVE -> VERDICT (+ cold eyes) -> decisions, next plan
| |
| +- stop rule: minor only, round cap, or nothing fixed? -> SEAL
| +- else -> next round
|
+- SEAL: DESIGN.md, AGENTS.md, launch guard, audit, report