mcpField
B
bunshin

bunshin

github.com/athevon设计与 UI698322 次安装3 周前更新
代码仓库

关于 bunshin

面向整站或 Web 应用首版制作的设计与开发技能,由主导者制定方向和基础,多个子 Agent 并行研究、构建页面并审查。流程强调文件分工、证据验证和人工方向确认;不适用于原生 Compose 或 SwiftUI 项目。

主要功能

  1. 01多子 Agent 并行研究与构建
  2. 02页面独立文件分工
  3. 03独立审查、测试与验证
  4. 04方向确认与轮次上限
  5. 05缺失信息标注与报告

适用场景

  1. 01从客户提供的资料制作整站首版
  2. 02并行构建多个页面并进行审查
  3. 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.mdImpeccable 绘制的方向(安装后)加上 genjutsu 的交互理念,组成一份契约
验证快速审计完整审计每个页面的截图、脚本化测试、独立评审视角、评审结论、冷眼审查
人工关卡理念、变体脑力激荡、理念、设计系统、页面两次:召唤、方向
成本一次会话一次长会话数百万 token、数小时(参见“召唤”)

Iron Rules

  1. 先召集。 在确定层级之前,不得运行克隆,也不得进行任何超出阅读范围的操作。如果无人回应,请参阅“无人回应时”。
  2. 询问两次,然后依据证据作出决定。 召集和方向由用户决定。其他所有决定由你依据素材、研究和审查作出,并写入报告。
  3. 只有一位艺术指导。 由你撰写方向契约、基础方案和标志性界面。克隆在契约范围内构建,绝不重新定义契约。
  4. 未经验证的论点,不得编写代码。 方向关卡会通过 Allowed patterns: 行,同时验证视觉论点和交互论点。
  5. 所有权互不重叠。 每个克隆负责一份明确声明的文件清单,其余所有文件对它都只读。遇到共享需求时,只能提出请求,不能直接修改。
  6. 不启动开发服务器,不共用浏览器。 克隆使用各自的无头浏览器进行截取,并使用框架的二进制文件在副本中构建。
  7. 没有证据,就等于没发生。 每项发现都要引用截取中的区域或 file:line;每个“完成”都要有打印退出代码 0 的构建;每种行为都要有测量值。
  8. 不得捏造内容。 事实必须来自素材和回答。缺失的信息在页面上保持通用,在内容文件中标记,并列入报告。
  9. 按规则结束循环。 收尾审核者判定仅需微调、达到层级的轮次上限,或某一轮毫无改进时,循环结束。不得无限继续,也不得未经询问就超过上限。
  10. 未经询问,不得安装依赖。 Impeccable 和浏览器工具也不例外。召集所涵盖的唯一安装内容是其指定的脚手架(参见 SUMMON)。无人回应时,不得安装其他内容。
  11. 未经询问,不得将任何内容带离本机。 不得提交、推送、部署或发布。
  12. 目前仅支持 Web。 对 Compose 或 SwiftUI 项目,说明 bunshin 尚不支持原生技术栈,并切换到 paint。

如果这不是合适的任务,请切换

bunshin 不适合用于小于整个网站的任务。在 READ 阶段识别出这种情况后,应在投入任何资源之前用一句话说明:

请求内容转到
一个组件、一种效果或一个版块cast
一个页面、一个落地页,或没有网站作为载体的视觉识别paint
一个完整网站,但宿主无法启动子 Agentpaint,并用一句话说明 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
lean13 个:完成度、移动端、真实性与技术否约 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 / SwiftUIB - 实时预览,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 或 fullload_skill tells(单独调用,并加载 references/web.md,见下文)
移动端场景(Web 移动端或原生 iOS / Android)load_skill mobile-principles
桌面端场景(macOS 或无移动端指示的 Web 桌面端)load_skill desktop-principles
明确要求审计,或 scope=fullload_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 加载):

检测到的技术栈要加载的子技能
gsapload_skill gsap
motion 或 framer-motion(同一个库的两个包名)load_skill framer-motion
纯 CSS / Tailwind / 无库load_skill css-native
three / @react-threeload_skill threejs-r3f
Canvas / 生成式load_skill canvas-generative
Android Composeload_skill compose-motion(始终加载)+ load_skill compose-graphics(如果 scope=full 或 thesis 属于 advanced——见下文)
Compose Multiplatformload_skill compose-motion + load_skill compose-multiplatform(始终加载);如果检测到 iOS 目标且需要 SwiftUI 互操作,则加载 load_skill swiftui-motion;如果属于 advanced,则加载 load_skill compose-graphics
SwiftUI iOS 或 macOSload_skill swiftui-motion(始终加载)+ load_skill swiftui-graphics(如果 scope=full 或 thesis 属于 advanced)

“Advanced thesis”触发条件,适用于 compose-graphics / swiftui-graphics:

thesis 中包含以下任一术语时,即视为“advanced”(并触发加载 graphics 子技能):

  • shader、Metal、AGSL、RuntimeShader、MSL
  • liquid-glass、glassEffect、morphing transition
  • M3 Expressive、MotionScheme、expressive motion
  • colorEffect、distortionEffect、layerEffect
  • Canvas(结合生成式 / 粒子 / 流场上下文)
  • 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 轮开始,每一轮:

  1. 使用 root、数据包(PRODUCT.md、契约、质量底线,均为绝对路径)、planPath(第 1 轮:评审的计划文件;后续轮次:上次判定写入的计划)、decisions(从第 2 轮起:上次的决策文件)、需要应用修复或决策的所有负责人、适用时的 shared(它会先单独运行),以及与阶段 7 相同的 build,运行 refine.js。将结果写入 .bunshin/reviews/round-N-refine.json。
  2. 再次 PROVE:运行检查,然后将新截图保存到 .bunshin/captures/round-<N+1>/,并重新拍摄同一批文件的两张 .impeccable/review/ 截图。
  3. 使用新数据包、history(本轮的计划文件、refine 结果、决策文件)、round、所有负责人、设为 <root>/.bunshin/reviews/round-<N+1>.json 的 planOut、与评审相同的 finishAgent,以及在层级要求时设为 true 的 freshEyes,运行 verdict.js,并附上一句话说明受众如何接触网站的场景。它会根据最终评审者的待处理意见,写出下一轮计划;如果没有剩余事项,修复列表将为空。将结果写入 .bunshin/reviews/round-N-verdict.json。
  4. 当判定以冷眼审视方式运行且循环继续时,将他们的意见整理成决策,由你亲自完成(如果停止规则终止循环,则将这些意见写入报告):.bunshin/decisions/round-N.md,列出下一轮适用于各处的规则(每张主视觉图片的唯一职责、允许使用高饱和色的位置、字体角色、控件形状、会发生变化的共享契约)。绝不能把这些意见作为任务列表交给克隆。计划中的 orchestrator 修复由你亲自应用。
  5. 应用停止规则(编排模块),先处理处置结果: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。不要勾选,不要淡化,也不要因为其他部分看起来没有问题就省略此部分。

目标运行内容通过条件
WebChrome DevTools > Performance,覆盖交互过程进行录制没有帧超过 16.7ms
Web在 375 / 768 / 1024 / 1440 的页面无水平滚动,无内容被裁切
Web在启用 OS“减少动态效果”设置时查看页面降级路径确实运行
Web如果本次会话中未渲染网页字体,则在联网时查看页面字体加载成功而非使用后备字体,且字体粗细和字重符合论述所依赖的设置
ComposeLayout Inspector > Component Tree > View Options > Show Recomposition Counts滚动时计数稳定
Compose在中端设备上运行 androidx.benchmark.macro Macrobenchmark60fps 时帧耗时低于 16.67ms,120fps 时低于 8.33ms
SwiftUIInstruments > 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 不会替你记住。


最终报告

内容简洁,并按以下顺序排列:

  1. 已构建:各语言对应的路由,以及设计系统和契约所在位置。
  2. 已验证,附证据:构建、类型检查、检测器、明显特征、捕获结果(所在位置)、每项功能测试的测量值、最后的判定及其处理方式。
  3. 未验证:审计中已交接的项目,标记为 UNVERIFIED,以及任何 lens 无法检查的内容。
  4. 遗留事项:最后一轮之后仍存在的小问题,按页面列出。
  5. 供客户端参考:missing.md 和内容文件中仍然缺失的所有事实,以及克隆提出的未决问题。
  6. 成本:档位、运行轮数、每个工作流中的 Agent 数量,以及主机报告的 token 数量。
  7. 假设,以及在无头运行中,所有标记为 UNVALIDATED 的门禁回答。
  8. 审计中的两行模块信息。

除非用户提出要求,否则不提交、不推送、不部署任何内容。


危险信号——你即将违反此技能

想法事实
“用户说放手去做,那我就跳过召唤”每次都要询问档位。放手去做也是一个档位,而且有相应代价。
“先做调研,问题会问得更好”询问档位之前,不要做任何更深入的事。按 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

评论

支持 Markdown