# Story Setup

> 网文写作工具集基础设施部署。为 Claude Code / OpenCode / Codex / ZCode / TRAE Code / WorkBuddy / CodeBuddy Code / OpenClaw / Reasonix 提供内置适配；Web AI / 通用 Agent 可走 skills + AGENTS.md 文件模式。触发方式：/story-setup、$story-setup、「准备写书」「帮我搭一下环境」「配置写作项目」。

- Skill: `qin1473692580-ux/story-setup` (Agent Skill, multi-file: 235 files)
- Install (CLI): `npx skillmds@latest add qin1473692580-ux/story-setup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/qin1473692580-ux/story-setup/raw
- Safety review: WARNING
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: qin1473692580-ux (https://skillmd.com/u/qin1473692580-ux)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/qin1473692580-ux/story-setup

---

# story-setup：网文写作工具集基础设施部署

你是写作基础设施部署器。将网文写作工具集部署到用户项目目录：已适配的 CLI 走专用 hooks/agents/config；NarraFork、Web AI、自定义 Agent 等环境走通用文件模式。

**执行铁律：不覆盖用户已有配置，合并而非替换。**

**交互工具兼容门**：本 Skill 中的 `AskUserQuestion` 表示“必须让用户做选择”，不表示必须存在同名工具。当前运行时未暴露该工具时（包括部分 TRAE Code、WorkBuddy / CodeBuddy Code、Web AI 会话），主 Agent 必须在当前主对话中直接提出同一个简短问题并等待回答；不得因工具缺失跳过确认、伪造已选结果或让子 Agent 代替提问。

---

## Phase 1：检测项目状态

**先自检参考目录**：以正在执行的本 `SKILL.md` 所在目录为准，列出与它同级的 `references/` 下的子目录，核对下面 10 个名字是否都在**且都非空**——`agent-references`、`templates`、`opencode`、`codex`、`zcode`、`trae`、`workbuddy`、`openclaw`、`reasonix`、`generic`；同级 `scripts/merge-claude-settings.py`、`scripts/merge-codex-hooks.py`、`scripts/merge-trae-hooks.py`、`scripts/trae-core-ownership.py`、`scripts/merge-workbuddy-settings.py` 与 `scripts/copy-path-safety.py` 也必须存在（Claude/Codex/TRAE/WorkBuddy hooks 合并、shared-core 归属验证与递归复制安全预检依赖它们）。有缺即 skill 包没装全，**立即停止，不写任何部署文件**，报告里区分「缺目录」和「目录为空」，并给修复指令：「story-setup 参考资料包不完整，缺 {目录名}。按你的安装方式重装 oh-story-claudecode（命令行装的重跑 `npx skills add https://github.com/qin1473692580-ux/oh-story-claudecode/releases/latest/download/oh-story-release.zip -y -g`，marketplace / Plugin Management 装的在面板里重装），再执行 /story-setup。」

> 判据是「有没有 `SKILL.md`」：只看正在执行的 `SKILL.md` 同级的 `references/`。项目内 `.claude/skills/story-setup/`、`.codex/skills/story-setup/` 和 OpenCode 的 `skills/story-setup/` 只有 `references/agent-references/`、不含 `SKILL.md`，不会是执行目录，也不要拿它们核对。ZCode / TRAE Code / WorkBuddy / CodeBuddy Code / OpenClaw / Reasonix / generic 的项目副本是整份 skill 拷贝、自带 `SKILL.md`，10 个子目录本就齐全，照常核对即可。

1. 检查当前目录是否已部署过（存在 `.story-deployed`）
   - `agents_version` 缺失、非整数或小于 `39` → 标记为待更新，继续执行当前部署
   - `agents_version: 39` → 使用 AskUserQuestion 确认是否重新部署；提示里写明重新部署只用**当前本地 skill 包**刷新项目文件，要拿 skill 本身的新版本得先用固定 GitHub Release 资产重跑 `npx skills add https://github.com/qin1473692580-ux/oh-story-claudecode/releases/latest/download/oh-story-release.zip -y -g`，再回来重跑
   - `agents_version` 大于 `39` → 当前 story-setup 比项目部署旧；停止以避免降级覆盖，提示先更新 oh-story-claudecode，不写任何部署文件
   - 同时读 `target_cli` 字段。**已部署项目以 sentinel 里的值为准**：非空时（逗号分隔的多端组合原样保留）跳过下面第 5-14 步的环境探测与选择，直接按这些端重新部署。只有字段缺失或为空，才回落到探测。用户明确要求增删目标端时，用 AskUserQuestion 在现有值基础上改，改完的值写回 sentinel。
2. 检查是否有书名目录（包含 `追踪/` 子目录的目录，或用户自定义结构）
   - 有 → 识别为长篇项目，显示当前项目信息
   - 无 → 识别为新项目或短篇项目
3. 检查 `.claude/settings.local.json` 是否存在
   - 存在 → 读取现有配置，后续合并
   - 不存在 → 后续创建新文件
4. 检查 `.active-book` 文件是否存在
   - 存在 → 显示当前活跃书目
   - 不存在 → 跳过
5. 检查 `opencode.json` 或 `.opencode/` 是否存在
   - 存在 → 识别为 opencode 项目，`target_cli = opencode`
   - 不存在 → 跳过
6. 检查 `.codex/`、`.codex/config.toml`、`.codex/agents/`、`.codex/hooks.json`、`AGENTS.md` 中的 Codex 段
   - 存在 → 识别为 Codex 项目，`target_cli = codex`
   - 不存在 → 跳过
7. 检查 `.zcode/`、`.zcode/config.json`、`zcode.json`、`.zcode/skills/`、`.zcode/commands/`、`AGENTS.md` 中的 ZCode 段
   - 存在 → 识别为 ZCode 项目，`target_cli = zcode`
   - 不存在 → 跳过
8. 检查 `.trae/`、`.trae/hooks.json`、`.trae/skills/`、`.trae/agents/`、`.trae/commands/`、`.trae/rules/`，或 `AGENTS.md` 中的 TRAE 段（标题行含 `网文写作工具集（TRAE Code）`）
   - 存在 → 识别为 TRAE Code 项目，`target_cli = trae`
   - 不存在 → 跳过
9. 检查 `.codebuddy/`、`.codebuddy/settings.json`、`.codebuddy/skills/`、`.codebuddy/agents/`、`.codebuddy/commands/`、`.codebuddy/rules/`，或 `.codebuddy/CODEBUDDY.md` / 根 `CODEBUDDY.md` / 根 `AGENTS.md` 中的 oh-story WorkBuddy 管理段
   - 存在 → 识别为 WorkBuddy / CodeBuddy Code 项目，`target_cli = workbuddy`
   - 不存在 → 跳过
10. 检查 `openclaw.json`、`.openclaw/`，或 `AGENTS.md` 中的 OpenClaw 段（标题行含 `网文写作工具集（OpenClaw）`）
   - 存在 → 识别为 OpenClaw 项目，`target_cli = openclaw`
   - 不存在 → 跳过
11. 检查 `.reasonix/`、`reasonix-plugin.json`、`REASONIX.md`，或 `AGENTS.md` 中的 Reasonix 段（标题行含 `网文写作工具集（Reasonix）`）
   - 存在 → 识别为 Reasonix 项目，`target_cli = reasonix`
   - 不存在 → 跳过
12. 检查 `AGENTS.md` 中的通用段（标题行含 `网文写作工具集（通用 Agent / Web AI）`）
   - 存在 → 识别为通用 Web AI 项目，`target_cli = generic`
   - 不存在 → 跳过

   > 第 8-12 步只认各端**互斥**的标记。`skills/*/SKILL.md` 的 `metadata.openclaw` 不作 OpenClaw 信号：canonical 中文主包 Skill 都带这个字段，而 OpenClaw / Reasonix / generic 三条 skills-only 路径部署出的 `skills/` 长得一样，用它判定会把后两者一律误认成 OpenClaw。`.agents/skills/` 同理由 Codex 与 Reasonix 共用，也不单独作准。TRAE 只认 `.trae/` 或 TRAE 专用标题行；WorkBuddy 只认 `.codebuddy/` 或其 CODEBUDDY 管理段；skills-only 三端真正的分辨点是各自 `AGENTS.md` 模板的标题行。

13. 如 `.claude/` 或 `CLAUDE.md`、OpenCode、Codex、ZCode、TRAE Code、WorkBuddy / CodeBuddy Code、OpenClaw、Reasonix、generic 标记同时存在 → 使用 AskUserQuestion 让用户选择目标环境（选项：Claude Code / OpenCode / Codex / ZCode / TRAE Code / WorkBuddy / CodeBuddy Code / OpenClaw / Reasonix / 通用 Web AI 或其他 Agent / 任意组合）
14. 如九类标记都不存在（全新项目）→ 使用 AskUserQuestion 让用户选择目标环境
   - 用户选择 opencode → `target_cli = opencode`，部署时创建 `opencode.json` 和 `.opencode/`
   - 用户选择 claude-code → 按现有逻辑处理
   - 用户选择 codex → `target_cli = codex`，部署时创建 `.codex/`
   - 用户选择 zcode → `target_cli = zcode`，部署时创建 `.zcode/`、合并根 `AGENTS.md`，不创建项目 custom agents
   - 用户选择 TRAE Code → `target_cli = trae`，部署时创建 `.trae/`并合并根 `AGENTS.md`、`.trae/hooks.json`，部署原生 Skills / Subagents / Commands / Rules / Hooks
   - 用户选择 WorkBuddy / CodeBuddy Code → `target_cli = workbuddy`，部署时创建 `.codebuddy/`，按「WorkBuddy memory 合并策略」选择唯一 memory 文件并合并 `.codebuddy/settings.json`，部署原生 Skills / Agents / Commands / Rules / Hooks
   - 用户选择 openclaw → `target_cli = openclaw`，部署时复制 OpenClaw 兼容 skills 到项目 `skills/`
   - 用户选择 reasonix → `target_cli = reasonix`，部署时复制 skills 到项目 `skills/`、写入 Reasonix 版 `AGENTS.md`，不创建项目 custom agents/hooks
   - 用户选择通用 Web AI / 其他 Agent → `target_cli = generic`，部署通用 `AGENTS.md` 与项目本地 `skills/`；不写平台专属 hooks/agents
   - 用户选择多端 → `target_cli = claude-code,opencode,codex,zcode,trae,workbuddy,openclaw,reasonix,generic` 的子集（仅包含用户选择的端）

## Phase 2：部署基础设施

使用 AskUserQuestion 确认部署位置后，依次执行。

整个 Phase 2 幂等：目录复制、文件写入和下表各合并算法重复执行结果一致。因环境原因（工具不可用、权限被拒、网络失败）中途失败时，直接从头重跑本 Phase，不需要先清理半成品；`create only if absent` 的用户状态文件（见下表 Owner class）不会被二次覆盖。

### Step 1：部署清单（机械可检查）

**递归复制安全预检（先于任何目录 replace/copy）**：先把 Source path 解析为“正在执行的 story-setup skill 包”内的绝对路径，把 Target path 解析为用户项目内绝对路径，再用可用的 `python3/python/py` 运行 `scripts/copy-path-safety.py <source> <target>`。读取它的 JSON 结果：

- `status=safe`：路径互不包含，才允许继续该项复制。
- `status=same`：源、目标经 realpath/samefile 指向同一对象；把该项记录为幂等 no-op，**不得再递归复制**。
- `status=unsafe`：目标位于源内，或源位于目标内；立即停止整个 Phase 2，不执行删除/覆盖，报告 `reason` 与两条 realpath。
- `status=error`：源缺失或路径解析失败；按部署包不完整处理，立即停止。

不得用字符串前缀、未展开的相对路径或“看起来不是同一个目录”代替此预检；symlink/别名必须按 realpath 判定。文件级 replace 可继续按原有原子写入规则；所有递归目录复制都必须逐项过这一关。

**TRAE 归属与备份门（`target_cli` 含 `trae` 时）**：在改动任何已存在的 `.trae/` 文件前，先将本次会改动的原文件按相对路径备份到 `.trae/.oh-story-backups/<UTC时间戳>/`；目录备份本身也要先跑 `copy-path-safety.py`。只允许替换带 `oh-story-managed` 标记的 canonical 中文主包 skill / command / agent / rule / AGENTS 管理块、头注释明确为 oh-story TRAE adapter 的 `story_trae_hook.js`，以及满足下列任一归属证据的 `story_hook_core.js`：含通用标记 `oh-story-managed: shared-hook-core`；用 `scripts/trae-core-ownership.py` 校验后 sha256 命中唯一权威 `references/trae/legacy-managed-sha256.json`；或与已验证的同项目 Claude/OpenCode/ZCode oh-story shared core 字节一致。Skill 目录的归属证据是 `SKILL.md` 的 `metadata.openclaw.source` 指向本仓库，或精确标记 `<!-- oh-story-managed: skill/{name} -->`。同名但无归属证据的用户文件必须保留并报告冲突，不得覆盖。`.trae/hooks.json` 始终用 helper 按稳定 command 身份合并，绝不整文件替换。新创建的产物必须写入对应管理标记，以保证下次可安全升级。

**TRAE 与 Claude Hook 去重门**：TRAE 会兼容读取项目 `.claude/settings*.json`。Claude settings 命令保持跨平台可执行的直接 `bash ...` 形式，不内嵌 POSIX `if`；所有 Claude shell 入口在 `source lib/common.sh` 后由共用库检测 `TRAE_PROJECT_DIR` 并成功、静默退出。在 Claude 中照常执行，在 TRAE 进程中由 `.trae/hooks.json` 独占执行。部署后用两端合成输入验证同一事件只有一套 oh-story 输出，不能把“双份提示看起来一样”当作可接受。

**WorkBuddy 归属与 Hook 互斥门（`target_cli` 含 `workbuddy` 时）**：只替换带 `oh-story-managed` 标记的 canonical 中文主包 skill / command / agent / rule / CODEBUDDY（或 AGENTS）管理块，以及带适配器头注释或通用 shared-core 标记的 hook 文件；Skill 目录使用与 TRAE 相同的仓库 metadata / `<!-- oh-story-managed: skill/{name} -->` 双证据。同名用户文件保留并报告冲突。`.codebuddy/settings.json` 只通过 `merge-workbuddy-settings.py` 合并。若当前 skill 来自已启用的 oh-story CodeBuddy plugin（实际执行路径位于插件安装根，或当前会话 registry / `codebuddy plugin list` 明确显示 `oh-story` 已启用），plugin hooks 会自动加载，此时必须用空模板移除项目内旧的 oh-story-managed hook 注册，不能再合并 `project-hooks.json`；只有 project-local skills 模式才写项目 hooks，防止同一事件双触发。仓库里存在 `.codebuddy-plugin/plugin.json` 或插件曾经安装过都不算“当前已启用”的证据。

**WorkBuddy 命名空间门**：plugin 模式只由 `.codebuddy-plugin/plugin.json` 暴露 Skills、Agents 与 Hooks；不得同时在 manifest 暴露与 Skills 同名的 Commands，否则 `/oh-story:story-*` 会发生组件名竞争。plugin 模式调用 `/oh-story:story`、`/oh-story:story-long-write` 等命名空间化 Skill；运行 `story-setup` 后的项目模式才由 `.codebuddy/commands/` 暴露 `/story`、`/story-long-write` 等裸命令。两种模式必须在安装报告中分开写，不能把裸命令说成 plugin 命令。

| Source path | Target path | Owner class | Merge mode | Validation check |
|-------------|-------------|-------------|------------|------------------|
| `skills/story-setup/references/templates/CLAUDE.md.tmpl` | `CLAUDE.md` | user+managed | marker/section merge | contains story skill routing sections |
| `skills/story-setup/references/templates/hooks/` | `.claude/hooks/` | story-setup managed | recursive replace | `session-*.sh`, `detect-story-gaps.sh`, `validate-story-commit.sh`, `guard-outline-before-prose.sh`, `check-prose-after-write.sh`, `story_hook_core.js`, `story_hook_cli.js`, `lib/common.sh`, `lib/sentinel.sh` exist；`story_hook_core.js` 与 OpenCode/ZCode 副本字节一致 |
| `skills/story-setup/references/templates/rules/*.md` | `.claude/rules/*.md` | story-setup managed | replace | every rule contains `paths` frontmatter |
| `skills/story-setup/references/templates/agents/*.md` | `.claude/agents/*.md` | story-setup managed | replace | 8 agent files exist |
| `skills/story-setup/references/agent-references/*.md` | `.claude/skills/story-setup/references/agent-references/*.md` | story-setup managed | replace | every `story-setup/references/agent-references/*.md` reference resolves |
| `skills/story-setup/references/templates/settings-hooks.json` | `.claude/settings.local.json` | user+managed | replace managed registrations by stable hook identity | hook JSON valid；旧 matcher 注册已迁移、当前模板命令各一份、用户 hook 保留 |
| `skills/story-setup/scripts/merge-claude-settings.py` | 部署时执行，不复制到项目 | story-setup helper | execute | 替换已知 story hook 注册、保留用户 hooks/顶层字段，历史 matcher 迁移与重复执行幂等 |
| `skills/story-setup/references/templates/质检进度.md.tmpl` | `{书名}/追踪/质检进度.md` | user state | create only if absent | never overwrite existing progress table |
| generated sentinel | `.story-deployed` | story-setup managed | replace | contains `agents_version`, `setup_skill_version`, `target_cli`, `resolver_strategy`, `references_dir` |
| `skills/story-setup/references/opencode/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains story skill routing sections | target_cli 含 opencode |
| `skills/story-setup/references/opencode/agents/` | `.opencode/agents/` | story-setup managed | replace | 8 agent files exist（replace 前按「配置 OpenCode Agent 模型」中的「保留已有模型配置」缓存现有 `model:`，避免覆盖用户已配模型） | target_cli 含 opencode |
| `skills/story-setup/references/opencode/plugin.ts` | `.opencode/plugins/story-hooks.ts` | story-setup managed | replace | TypeScript plugin file exists | target_cli 含 opencode |
| `skills/story-setup/references/opencode/story_hook_core.js` | `.opencode/plugins/lib/story_hook_core.js` | story-setup managed | replace | Node syntax valid；与 ZCode 副本字节一致；被 story-hooks.ts import | target_cli 含 opencode |
| `skills/story-setup/references/opencode/commands/` | `.opencode/commands/` | story-setup managed | replace | command 数量与当前 OpenCode 模板清单一致 | target_cli 含 opencode |
| `skills/story-setup/references/opencode/opencode.json.patch` | merge into `opencode.json` | user+managed | merge by plugin/permission key | plugin entry registered | target_cli 含 opencode |
| `skills/story-setup/references/agent-references/` | `skills/story-setup/references/agent-references/` | story-setup managed | replace | every reference resolves | target_cli 含 opencode |
| `skills/story-setup/references/opencode/pre-commit.sh` | `.git/hooks/pre-commit` | user+managed | append or create | file exists and is executable；含 marker 块则替换块内容，不含则检测 exit 0 位置智能插入 | target_cli 含 opencode |
| `skills/story-setup/references/codex/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains Codex story skill routing sections | target_cli 含 codex |
| `skills/story-setup/references/codex/agents/` | `.codex/agents/` | story-setup managed | replace | 8 TOML agent files parse and contain `name`/`description`/`developer_instructions` | target_cli 含 codex |
| `skills/story-setup/references/codex/hooks/hooks.json` | `.codex/hooks.json` | user+managed | replace managed registrations by stable hook identity | hook JSON valid; all stale direct/launcher registrations removed, current 6 registrations present exactly once | target_cli 含 codex |
| `skills/story-setup/references/codex/hooks/{story_codex_hook.py,run-story-hook.sh,run-story-hook.cmd}` | `.codex/hooks/` 同名文件 | story-setup managed | replace | Python/shell/cmd launcher 文件齐全 | target_cli 含 codex |
| `skills/story-setup/scripts/merge-codex-hooks.py` | 部署时执行，不复制到项目 | story-setup helper | execute | 替换已知管理注册、保留用户 hooks 与未知顶层字段，结果幂等 | target_cli 含 codex |
| `skills/story-setup/scripts/copy-path-safety.py` | 每次递归目录复制前执行，不复制到项目 | story-setup helper | execute | same=no-op；祖先/后代嵌套=阻断；仅 safe 可复制 |
| `skills/story-setup/references/agent-references/` | `.codex/skills/story-setup/references/agent-references/` | story-setup managed | replace | every reference resolves | target_cli 含 codex |
| `skills/story-setup/references/zcode/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains ZCode `$story-*` routing and solo fallback | target_cli 含 zcode |
| canonical repository `skills/{browser-cdp,story*}/` | `.zcode/skills/{browser-cdp,story*}/` | story-setup managed for 18 known skill names | replace known skill dirs only | `SKILL.md` 名字集与中文主包 18 Skill 清单一致并满足 ZCode frontmatter 限制 | target_cli 含 zcode |
| `skills/story-setup/references/zcode/commands/` | `.zcode/commands/` | story-setup managed for 18 known command names | replace known command files only | commands have valid names/frontmatter and cover the canonical skill list | target_cli 含 zcode |
| `skills/story-setup/references/zcode/hooks/story_zcode_hook.js` | `.zcode/hooks/story_zcode_hook.js` | story-setup managed | replace | Node syntax valid; hook contract tests pass | target_cli 含 zcode |
| `skills/story-setup/references/zcode/hooks/story_hook_core.js` | `.zcode/hooks/story_hook_core.js` | story-setup managed | replace | Node syntax valid; hook contract tests pass | target_cli 含 zcode |
| `skills/story-setup/references/zcode/config.json.patch` | merge into `.zcode/config.json` | user+managed | merge by event+matcher+process args | JSON valid; 按「ZCode 部署算法」第 4 步 hooks 互斥分支校验——未装 oh-story 插件时 `hooks.enabled=true`、only supported events；已装插件时校验 `.zcode/config.json` 不含（或已移除）这批 oh-story hooks 注册 | target_cli 含 zcode |
| `skills/story-setup/references/trae/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains TRAE Code story routing and fallback contract | target_cli 含 trae |
| canonical repository `skills/{browser-cdp,story*}/` | `.trae/skills/{skill-name}/` | story-setup managed for the fixed 18 source names | backup + replace managed dirs only | 目标 `SKILL.md` 数量/名字与中文主包 18 Skill 清单一致；源目标同一则 no-op | target_cli 含 trae |
| `skills/story-setup/references/trae/commands/` | `.trae/commands/{skill-name}.md` | story-setup managed by `oh-story-managed` marker | backup + replace 18 managed files only | 每个 canonical skill 恰有一个同名 command，frontmatter 含 `name` / `description` | target_cli 含 trae |
| `skills/story-setup/references/trae/agents/` | `.trae/agents/` | story-setup managed by `oh-story-managed` marker | backup + replace 精确通用名册（8 张） | 名字集精确为 `chapter-extractor`, `character-designer`, `consistency-checker`, `narrative-writer`, `revision-governor`, `story-architect`, `story-explorer`, `story-researcher`；TRAE frontmatter 可解析 | target_cli 含 trae |
| repository `skills/story-data-analyze/agents/trae/` | `.trae/agents/` | story-setup managed by `oh-story-managed` marker | backup + replace 精确数据名册（5 张） | 名字集精确为 `story-data-fetcher`, `story-data-method-validator`, `story-data-metrics-analyst`, `story-data-supervisor`, `story-data-text-improvement-planner`；与通用角色无重名 | target_cli 含 trae |
| `skills/story-setup/references/trae/rules/` | `.trae/rules/` | story-setup managed by `oh-story-managed` marker | backup + replace managed files only | rules frontmatter 含 `alwaysApply` / `globs` | target_cli 含 trae |
| `skills/story-setup/references/trae/hooks/{story_trae_hook.js,story_hook_core.js}` | `.trae/hooks/` 同名文件 | runner by adapter header; core by fixed name + packaged hash | backup + replace managed files only | Node syntax valid；shared core 与其他 adapter 副本一致 | target_cli 含 trae |
| `skills/story-setup/references/trae/hooks/hooks.json` | `.trae/hooks.json` | user+managed | replace managed registrations by stable hook command identity | `version: 1`；仅含 TRAE 支持事件；用户 hook/顶层字段保留 | target_cli 含 trae |
| `skills/story-setup/scripts/merge-trae-hooks.py` | 部署时执行，不复制到项目 | story-setup helper | execute | 迁移旧 matcher/command、保留用户配置，重复执行字节幂等 | target_cli 含 trae |
| `skills/story-setup/scripts/trae-core-ownership.py` + `references/trae/legacy-managed-sha256.json` | 部署时执行，不复制到项目 | story-setup ownership helper/registry | classify before replace | marker / legacy SHA 判 managed；unknown/error 保留并报告 | target_cli 含 trae |
| `skills/story-setup/references/workbuddy/CODEBUDDY.md.tmpl` | 既有 `CODEBUDDY.md` / `.codebuddy/CODEBUDDY.md`，或无两者时的 `AGENTS.md` / `.codebuddy/CODEBUDDY.md` | user+managed | 按「WorkBuddy memory 合并策略」只合并 marker block | 无遮蔽已有 AGENTS；条件导入占位已解析；管理块唯一 | target_cli 含 workbuddy |
| canonical repository `skills/{browser-cdp,story*}/` | `.codebuddy/skills/{skill-name}/` | story-setup managed for the fixed 18 source names | replace managed dirs only | `SKILL.md` 名字集与中文主包 18 Skill 清单一致；源目标同一则 no-op | target_cli 含 workbuddy |
| `skills/story-setup/references/workbuddy/commands/` | `.codebuddy/commands/{skill-name}.md` | story-setup managed by `oh-story-managed` marker | replace 18 managed files only | commands 与 canonical skill 一一对应，frontmatter 仅用 CodeBuddy 支持字段 | target_cli 含 workbuddy |
| `skills/story-setup/references/workbuddy/agents/` | `.codebuddy/agents/` | story-setup managed by marker | replace 精确通用名册（8 张） | 名字集精确为 `chapter-extractor`, `character-designer`, `consistency-checker`, `narrative-writer`, `revision-governor`, `story-architect`, `story-explorer`, `story-researcher`；WorkBuddy frontmatter 合法，项目 Agent 名不带 plugin namespace | target_cli 含 workbuddy |
| repository `skills/story-data-analyze/agents/workbuddy/` | `.codebuddy/agents/` | story-setup managed by marker | replace 精确数据物理名册（2 张） | 名字集精确为 `story-data-fetcher`, `story-data-readonly-runner`；4 张逻辑角色卡留在 Skill references | target_cli 含 workbuddy |
| `skills/story-setup/references/workbuddy/rules/` | `.codebuddy/rules/` | story-setup managed by marker | replace managed files only | `alwaysApply` 可解析；用户 rules 保留 | target_cli 含 workbuddy |
| `skills/story-setup/references/workbuddy/hooks/{story_workbuddy_hook.js,story_hook_core.js}` | `.codebuddy/hooks/` 同名文件 | story-setup managed | replace managed files only | Node syntax valid；shared core byte-parity | target_cli 含 workbuddy 且 project-local hook 模式 |
| `skills/story-setup/references/workbuddy/hooks/project-hooks.json` | merge into `.codebuddy/settings.json` | user+managed | merge or remove by plugin mutex | 只有 project-local 模式各注册一次；plugin 模式为零；用户设置与 hooks 保留 | target_cli 含 workbuddy |
| `skills/story-setup/scripts/merge-workbuddy-settings.py` | 部署时执行，不复制到项目 | story-setup helper | execute | 稳定 runner 身份去旧、合并/移除幂等 | target_cli 含 workbuddy |
| `skills/story-setup/references/openclaw/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains OpenClaw story skill routing sections | target_cli 含 openclaw |
| `skills/story-setup/references/generic/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains generic story skill routing sections | target_cli 含 generic |
| `skills/story-setup/references/reasonix/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains Reasonix story skill routing sections and solo/direct fallback | target_cli 含 reasonix |
| canonical repository `skills/{browser-cdp,story*}/` | `skills/{browser-cdp,story*}/` | story-setup managed for 18 known skill names | replace known skill dirs only | `SKILL.md` 名字集与中文主包 18 Skill 清单一致；OpenClaw 分支校验兼容 frontmatter | target_cli 含 openclaw 或 generic 或 reasonix |
| `skills/story-setup/references/agent-references/` | `skills/story-setup/references/agent-references/` | story-setup managed | replace via full skill copy | every reference resolves | target_cli 含 openclaw 或 generic 或 reasonix |

### opencode.json 合并算法

部署 `opencode.json.patch` 时按以下规则合并：

1. 读取现有 `opencode.json`（如存在），解析 JSON
2. 合并 `plugin` 数组：将 `./.opencode/plugins/story-hooks.ts` 加入数组，去重
3. 保留用户已有的其他配置字段（`permission`、`model`、`provider` 等），不覆盖
4. 写入合并后的 `opencode.json`

### Step 2：部署 CLAUDE.md

- 读取 `skills/story-setup/references/templates/CLAUDE.md.tmpl`
- 替换占位符（见下方「模板占位符」段）
- 写入项目根目录 `CLAUDE.md`（如已存在，按「CLAUDE.md 合并策略」处理）

### Step 3：部署 Hooks

- **递归复制完整目录树**：将 `skills/story-setup/references/templates/hooks/` 复制到用户项目 `.claude/hooks/`
- 必须保留子目录 `lib/`，其中：
  - `lib/common.sh` 提供 `project_root`、`discover_active_book`、`discover_all_books`
  - `lib/sentinel.sh` 提供 `.story-deployed` 字段读取
- 只需对 `.claude/hooks/*.sh` 设置执行权限（`chmod +x`）；`lib/*.sh` 由 hook `source`，不要求可执行位

### Step 4：部署 Rules

- 读取 `skills/story-setup/references/templates/rules/` 下所有 `.md` 文件
- 复制到用户项目的 `.claude/rules/` 目录

### Step 5：部署 Agents

- 读取 `skills/story-setup/references/templates/agents/` 下所有 `.md` 文件
- 复制到用户项目的 `.claude/agents/` 目录
- Agent 文件属于 story-setup 管理文件，可安全覆盖；版本升级时按 `UPGRADING.md` 的版本检测结果重新部署
- **`target_cli` 含 opencode 时，覆盖 `.opencode/agents/` 之前先执行下面「配置 OpenCode Agent 模型」的 Step 1 缓存现有 `model:`**。那一步写在本节后面，但必须先跑——照顺序读到哪做到哪会先覆盖再缓存，用户已配的模型就没了。
- **部署后必须新开会话**：agent 只在会话启动时注册；原因与必须输出的报告文案见「验证安装」中的「输出安装报告」。

#### Agent 兼容性处理

- Agent frontmatter 以 Claude Code 为主；OpenCode 的 `.opencode/agents/*.md`、Codex 的 `.codex/agents/*.toml`、TRAE 的 `.trae/agents/*.md` 和 WorkBuddy 的 `.codebuddy/agents/*.md` 均使用对应 adapter 下的预生成产物，不得直接复制别端 frontmatter。TRAE 通用角色的唯一来源是 `references/trae/agents/`，数据分析角色的唯一来源是 `skills/story-data-analyze/agents/trae/`；WorkBuddy 对应来源为 `references/workbuddy/agents/` 与 `skills/story-data-analyze/agents/workbuddy/`。不把 Claude 模型别名或某一端的专有字段机械带入另一端。OpenCode/Codex/WorkBuddy 预生成产物由仓库维护脚本生成；这些脚本不随 story-setup 下发，部署时只复制已提交产物。
- **ZCode 3.3.4 不部署项目 agents**：其自定义子智能体只支持用户级 `~/.zcode/agents/`，plugin manifest 中的 `agents` 当前不执行。不要创建 `.zcode/agents/` 或修改用户 home；相关 Skill 必须直接 solo/direct 并报告 fallback。
- **OpenClaw Phase 1 不部署 agents**：OpenClaw 只部署 skills，agent 协作相关 skill 必须按既有 fallback 规则降级 solo/direct，不要把 Claude/OpenCode agent frontmatter 直接复制成 OpenClaw agent。
- 部署到项目后，agent 内引用的参考资料必须走 `story-setup/references/agent-references/*.md` 这一本 skill 内复制路径；不要跨 skill 引用其他 skill 的 references。各 adapter 只使用当前规范前缀：Claude Code 为 `.claude/skills/`，OpenCode / OpenClaw / Reasonix / generic 为 `skills/`，Codex 为 `.codex/skills/`，ZCode 为 `.zcode/skills/`，TRAE Code 为 `.trae/skills/`，WorkBuddy / CodeBuddy Code 为 `.codebuddy/skills/`；插件模式可用由平台内联的 `${CODEBUDDY_PLUGIN_ROOT}/skills/`。不在运行时遍历历史备选路径。

#### 部署 Agent References

- 将 `skills/story-setup/references/agent-references/` 下所有 `.md` 复制到项目内 `.claude/skills/story-setup/references/agent-references/`
- 校验：凡 agent 或 reference 中出现 `story-setup/references/agent-references/<file>.md`，源包与目标包都必须存在 `<file>.md`

#### 部署 Codex Agents（target_cli 含 codex 时）

- 读取 `skills/story-setup/references/codex/agents/` 下所有 `.toml` 文件，复制到用户项目 `.codex/agents/`
- Agent 文件属于 story-setup 管理文件，可安全覆盖；`references/codex/agents/` 里的 TOML 由仓库根的 `scripts/generate-codex-agents.py` 从 Claude agent 模板确定性生成后提交入库，部署只做复制
- 校验每个 TOML 都能解析，且包含 Codex 必需字段：`name`、`description`、`developer_instructions`
- 只读职责 agent（`chapter-extractor`、`consistency-checker`、`revision-governor`、`story-explorer`）必须保留 `sandbox_mode = "read-only"`
- **部署后必须 trust + 新开 Codex 会话**（报告文案与 fallback 规则见「验证 Codex 部署」）；若运行时返回 `unknown agent_type`，调用方必须降级 solo/direct 并报告 fallback。
- 将 `skills/story-setup/references/agent-references/` 同步复制到 `.codex/skills/story-setup/references/agent-references/`，作为 Codex agent 的项目内参考资料主路径

#### 部署 TRAE Agents（target_cli 含 trae 时）

- 先校验 `references/trae/agents/` 的名字集精确为 `chapter-extractor`, `character-designer`, `consistency-checker`, `narrative-writer`, `revision-governor`, `story-architect`, `story-explorer`, `story-researcher`（8 张通用卡），再校验 canonical `story-data-analyze/agents/trae/` 的名字集精确为 `story-data-fetcher`, `story-data-method-validator`, `story-data-metrics-analyst`, `story-data-supervisor`, `story-data-text-improvement-planner`（5 张数据卡）。两组必须无重名，合并后名册精确为 13 张；任一必需目录缺失、为空、名字集不等、重名或 frontmatter 不可解析时停止 TRAE 分支，不进行半套部署。
- 依次写入 `.trae/agents/`，只新建或替换带 `<!-- oh-story-managed: agent/<agent-name> -->` 标记的同名文件；无标记同名文件保留并报告。不删除用户其他 agents。
- TRAE frontmatter 只用其原生字段：`name`、`description`、逗号字符串形式的 `tools` / `disallowedTools` 以及可选的 TRAE 内置 `model`；禁止自动把 Claude 模型别名写入。
- 部署后提示新开 TRAE 会话并确认 Subagents Beta 功能可用。若当前 TRAE 版本/会话不支持原生 Agent，必须按各 skill 契约降级 `solo/direct`，报告 `Fallback: TRAE subagent unavailable -> solo/direct`；不得声称 full/lean 多 Agent 已生效。

#### 部署 WorkBuddy Agents（target_cli 含 workbuddy 时）

- 先校验 `references/workbuddy/agents/` 的名字集精确为 `chapter-extractor`, `character-designer`, `consistency-checker`, `narrative-writer`, `revision-governor`, `story-architect`, `story-explorer`, `story-researcher`（8 张通用卡）；再校验 `skills/story-data-analyze/agents/workbuddy/` 的物理名字集精确为 `story-data-fetcher`, `story-data-readonly-runner`（2 张数据卡），且四张只读逻辑角色卡完整留在该 Skill 的 WorkBuddy role-card references。全部物理名必须无重复，合并后中文主包物理名册精确为 10 张。
- CodeBuddy 当前会在 `agentic` registry 查询阶段把 **内置 + 项目** Subagent 总数硬截为 20。本节列出的 WorkBuddy 精确 10 张物理卡低于平台上限；部署器不向该名册注入项目扩展卡，不靠排序或截断隐藏容量问题。
- 每个 Markdown 必须有合法 YAML frontmatter，`name` 与文件名一致，`description` 非空，`tools` / `disallowedTools` 为逗号字符串且只使用当前 CodeBuddy 工具名。plugin agent 禁止 `hooks`、`mcpServers`、`permissionMode`；项目 agent 也不依赖这些字段。
- 只新建或替换带 `<!-- oh-story-managed: agent/<name> -->` 标记的 `.codebuddy/agents/<name>.md`；无标记同名文件保留并报告冲突。项目定义优先级高于 plugin/user agent，项目模式统一用裸名称和 `Agent(subagent_type: "<name>")`。
- pooled runner 的逻辑角色卡留在所属 Skill 内，不能复制进 `.codebuddy/agents/`。主协调器必须按该 Skill 的运行时映射选择 runner，并在 prompt 中传入 `logical_role`、角色卡绝对路径、项目根、输入隔离合同和任务正文；runner 必须完整读取且核对角色卡后执行。
- plugin-only 模式的 UI 名为 `oh-story:<name>`；只有当前 registry 确实暴露该名称时才用它。未运行 story-setup 时不得凭磁盘 manifest 猜测 registry 已加载；调用失败立即按 Skill 合同降级。完成项目部署后统一使用 `.codebuddy/agents/` 的裸名称，避免命名空间分歧。
- 部署后新开 WorkBuddy / CodeBuddy 会话，用 `/agents` 或当前 registry 确认本节列出的精确 10 张物理卡全部可见；再分别冒烟一个通用 Agent 和一个 pooled runner 逻辑角色。子 Agent 不得递归 spawn。

#### 配置 OpenCode Agent 模型

> 仅当 `target_cli` 含 `opencode` 时执行。OpenCode 子代理不指定模型时继承主模型，导致低成本 Agent 也消耗主模型额度。此步骤自动检测用户模型并写入 `model:` 字段。

##### Step 1：保留已有模型配置（必须在 `.opencode/agents/` 的 replace 之前执行）

OpenCode agents 部署是 `replace`，会覆盖上次写入的 `model:`。所以在执行该 replace **之前**先扫描现有 `.opencode/agents/*.md`，缓存每个 agent 的 `model:`（agent 名 → 模型 ID）。后续检测失败/超时、或用户跳过某一级时，用缓存值回填，避免把用户上次配好的低成本模型抹成主模型。若 replace 已先发生、缓存为空，则按全新部署处理，并在安装报告中提示"未能保留上次模型配置"。

##### Step 2：获取模型列表

优先执行 `opencode models --verbose`，它输出含 cost（input/output/cache 单价）、context、capabilities 的 metadata；不可用或解析失败时回退到 `opencode models` 纯文本（每行 `provider/model`）。两者都用 60000ms（60 秒）超时，因为首次运行需加载 models.dev 缓存。

- 成功 → 进入「模型分级」
- 超时 → 重试一次（缓存可能未预热）；仍然超时则按「保留已有模型配置」缓存回填已有 `model:`、跳过自动配置，在安装报告中输出手动配置指南
- 失败（命令不存在、输出为空等）→ 同上：回填「保留已有模型配置」缓存、跳过自动配置、输出手动配置指南

##### Step 3：模型分级

**优先按成本分级（有 `--verbose` 时）**：按每模型实际 cost 从低到高分档——低端取最便宜/免费档、中端取中价档、高端取最贵或上下文/能力最强档。免费模型按真实 cost=0 归低端，**不按名字里的营销词**（如 `nemotron-3-ultra-free` 名含 `ultra` 但 cost=0，应归低端）。无 cost 数据的模型也据此进入候选，不被丢弃。

**回退按关键词分级（无 `--verbose` 或无 cost 时）**：按模型 ID 中最后一个 `/` 之后的模型名按 `-`、`.`、`_` 分割为段，逐段精确匹配关键词（不区分大小写）。例如 `minimax-m3` 拆为 `[minimax, m3]`，不匹配 `mini` 也不匹配 `max`；`claude-haiku-4.5` 拆为 `[claude, haiku, 4, 5]`，匹配 `haiku`。关键词分级是启发式，安装报告中标注 `分级依据：关键词（heuristic）`。

| 等级 | 匹配关键词 | 对应 Agent |
|------|-----------|-----------|
| 低端 | `haiku`, `flash`, `mini`, `nano`, `lite` | chapter-extractor, consistency-checker, story-explorer |
| 中端 | `sonnet`, `plus` | story-researcher, narrative-writer, character-designer, revision-governor |
| 高端 | `opus`, `pro`, `ultra`, `max` | story-architect |

- 一个模型可能匹配多个等级的关键词，取最高等级
- 关键词回退下未匹配任何关键词的模型仍列入候选附加建议（按成本分级则一律纳入），并在安装报告列出，提示"可通过自定义输入使用"
- 同一等级内，如果包含多个模型供应商，优先列出知名供应商（anthropic、openai、google、deepseek）的模型

##### Step 4：逐级交互选择

按 低端 → 中端 → 高端 顺序，每级用 AskUserQuestion 让用户选择。

**低端选项结构：**

```
问题："为低成本 Agent（chapter-extractor, consistency-checker, story-explorer）选择模型："
选项：
  - provider/model-id
  - provider/model-id
  - 自定义输入（手动输入完整模型 ID，ID 拼写错误要到运行时才会暴露）
  - 跳过，使用主模型（成本可能较高）
```

**中端选项结构：**

```
问题："为写作质量关键 Agent（narrative-writer, character-designer, story-researcher, revision-governor）选择模型："
选项：
  - provider/model-id
  - provider/model-id
  - 自定义输入（请勿使用低端模型，会影响正文质量；ID 拼写错误要到运行时才会暴露）
  - 跳过，使用主模型（主模型质量通常足够）
```

**高端选项结构：**

```
问题："为总指挥 Agent（story-architect）选择模型："
选项：
  - provider/model-id
  - provider/model-id
  - 自定义输入（手动输入完整模型 ID，ID 拼写错误要到运行时才会暴露）
  - 跳过，使用主模型（成本可能较高）
```

规则：
- 候选最多显示 5 个，超过则截断并提示"更多模型请使用自定义输入"。**每一级无论候选数是否为 0 都用 AskUserQuestion 弹出**，选项至少含：候选模型（如有）、`自定义输入`、`保留现有模型`（「保留已有模型配置」缓存到该 agent 的 model，无则不显示此项）、`跳过，用主模型`。候选为 0 时仍弹窗，并在问题说明里给出对应警告 + 列出未分级/未入档模型供参考——不再静默跳过交互（否则用户够不到自定义输入）。
- `自定义输入`：用户输入 `provider/model-id` 完整 ID；写入前校验为单行、无控制字符、匹配 `^[A-Za-z0-9._-]+/[A-Za-z0-9._:+-]+$`，不符则提示重输或改选跳过。
- `保留现有模型`：写回「保留已有模型配置」缓存的该 agent model（重新部署时保住用户上次配置），不算"跳过"。
- `跳过，用主模型`：显式清除——不写该 agent 的 `model:`，agent 继承主模型。想保留上次配置请选 `保留现有模型`。
- 各级候选为 0 时在问题说明里给出提示：
  - 低端："未检测到低成本模型，这 3 个 agent 将使用主模型，成本可能较高"
  - 中端："未检测到匹配的中端模型。narrative-writer、character-designer、story-researcher 将使用主模型。如主模型质量足够此配置合理；如需降本，请用自定义输入指定不低于主模型质量的中端模型，或从下方未分级模型里选。"
  - 高端："未检测到高端模型，story-architect 将使用主模型"

##### Step 5：写入 model 字段

对应用户选择的 agent 文件（`.opencode/agents/*.md`，由部署清单中 OpenCode agents 部署步骤在此步骤之前已部署），在 frontmatter 末尾、closing `---` 之前，以**零缩进的顶层字段**插入 `model:`（不要插进 `permission:` 等多行 map 的缩进块内部）。值含 YAML 特殊字符时加引号，确保不破坏 frontmatter：

```yaml
---
description: ...
mode: subagent
permission:
  read: allow
  edit: deny
steps: 12
model: provider/model-id
---
```

- 如果 agent 文件已有 `model:` 字段（重新部署场景），替换该顶层 `model:` 的值，不新增重复键
- `保留现有模型`：写回「保留已有模型配置」缓存的该 agent model
- `跳过，用主模型`：不写入 `model:` 字段
- 检测失败/超时、没走到本步骤的等级：用「保留已有模型配置」缓存回填 `model:`，避免 replace 抹掉用户上次配置

### Step 6：部署质检进度模板

- `追踪/上下文.md` 不再由本步部署。它已改为 `_tracking-state.json` 的派生视图，由写作 skill 自带的追踪事务工具在提交时渲染，story-setup 不再创建也不再覆盖它
- 读取 `skills/story-setup/references/templates/质检进度.md.tmpl`
- 仅当已识别为长篇书目且 `{书名}/追踪/` 已存在时，创建缺失的 `{书名}/追踪/质检进度.md`
- 如果目标文件已存在，不覆盖；短篇项目不得因此创建 `追踪/` 目录。这张表是 Phase 5 硬性必须项（consistency-checker、去AI味独立审查）的可机械核对记录，不能因为项目已在写而缺失

### Step 7：合并 Hooks 注册到 settings.local.json

1. 按现有跨平台规则探测 Python：`for PYBIN in python3 python py; do "$PYBIN" -c "" 2>/dev/null && break; done`；无可用解释器时停止，不手写或简化合并。
2. 调用 `"$PYBIN" "{story-setup skill目录}/scripts/merge-claude-settings.py" --existing "{项目}/.claude/settings.local.json" --template "{story-setup skill目录}/references/templates/settings-hooks.json" --output "{项目}/.claude/settings.local.json"`。
3. helper 会移除所有已知 story-setup hook 的历史注册，再追加当前模板；因此 matcher、timeout、if 能随版本升级，同时混在旧 block 中的用户 hook 与未知顶层字段原样保留。写后解析 JSON，验证模板命令各一份、用户配置仍在，再复跑 helper 比较文件字节确认幂等。

### Codex hooks.json 合并算法（target_cli 含 codex 时）

Codex 项目 hooks 部署到 `.codex/hooks.json`；运行脚本部署到 `.codex/hooks/story_codex_hook.py`、`run-story-hook.sh`、`run-story-hook.cmd`。JSON 只负责定位项目根与传递 event，解释器探测由平台 launcher 统一处理。

1. 定位当前 story-setup skill 目录，读取 `references/codex/hooks/hooks.json` 作为唯一当前模板，读取项目 `.codex/hooks.json`（不存在时视为空对象）。
2. 按现有跨平台规则探测可用 Python：`for PYBIN in python3 python py; do "$PYBIN" -c "" 2>/dev/null && break; done`；无可用解释器时停止，不手写或简化 JSON 合并。
3. 调用 `"$PYBIN" "{story-setup skill目录}/scripts/merge-codex-hooks.py" --existing "{项目}/.codex/hooks.json" --template "{story-setup skill目录}/references/codex/hooks/hooks.json" --output "{项目}/.codex/hooks.json"`。该 helper 会识别旧直调 `story_codex_hook.py`、当前 `run-story-hook.sh` 和 `run-story-hook.cmd` 三类管理身份，先移除所有已知管理注册，再追加当前模板。
4. 保留用户已有的非 story-setup hooks、matcher 块与未知顶层字段。重复执行必须幂等；禁止再按原始 `command` 字符串追加去重，否则 v17 直调命令会与 v18 launcher 双重注册。
5. 写入后解析 JSON 验证：旧直调 `story_codex_hook.py` 命令数为 0，当前模板 6 个注册各存在且仅存在一次，用户 hook 与未知顶层字段仍在。然后提示用户：项目 `.codex/` 层需要被 Codex trust，非 managed command hooks 还需要在 `/hooks` 中 review/trust 后才会运行；Windows 下走 `commandWindows`，launcher 从当前目录向上定位项目 `.codex/hooks/`，与 POSIX 路径的嵌套目录行为一致。

### 部署源解析（防项目旧副本自复制）

1. 记下当前正在执行的 `story-setup` 目录与其上一级 skills 根，称为 `bootstrap_root`。如果它位于目标项目的 `.trae/skills/`、`.codebuddy/skills/`、`.zcode/skills/`、`.codex/skills/`、`.claude/skills/` 或根 `skills/` 下，它只是“已部署快照”，不得一概当成升级权威源。
2. 已部署项目另外枚举这些可验证候选：当前已启用 plugin 的 `${CODEBUDDY_PLUGIN_ROOT}/skills/story-setup`、`~/.agents/skills/story-setup`、`~/.claude/skills/story-setup`、`~/.codex/skills/story-setup`，以及用户本次显式给出的本地安装目录。不搜索整个磁盘，不从网页或浮动仓库直接执行未验证内容。
3. 候选只有在以下条件全部成立时才有效：`SKILL.md` frontmatter 的 `name=story-setup`、`version` 为可比较的三段数字 SemVer、`metadata.openclaw.source` 指向本 oh-story 仓库，且 Phase 1 列出的 references 与 helper 自检全部通过。无效候选只报告，不读其他内容。
4. 在有效候选与 `bootstrap_root` 中按 SemVer 选最新完整包为 `canonical_root`；版本相同时优先项目外的安装包，版本较低的外部包不得反向降级项目。安装报告必须写出 `bootstrap_root`、`canonical_root`、各自版本/用途与选择原因。
5. 后续模板、helper 与 18 个中文主包 Skill 全部从 `canonical_root` 读取；项目自定义 Skill 不进入 TRAE/WorkBuddy 本轮适配部署源，已有用户 Skill 保留不动。
6. `copy-path-safety.py` 的 `status=same` 只让当前那一个 source/target 项 no-op；不得因项目内 `story-setup` 副本指向自身，就跳过从 `canonical_root` 刷新其他 Skills、Commands、Agents、Rules 和 Hooks。

### canonical 中文主包 Skill 清单（所有 skills 复制分支共用）

1. `canonical_root/skills/` 必须精确包含这 18 个可部署 Skill：`browser-cdp`、`story`、`story-cover`、`story-data-analyze`、`story-deslop`、`story-explore`、`story-import`、`story-long-analyze`、`story-long-scan`、`story-long-write`、`story-publish`、`story-release-package`、`story-research`、`story-review`、`story-setup`、`story-short-analyze`、`story-short-scan`、`story-short-write`。
2. 按上述固定名字集读取每个 `SKILL.md` frontmatter 的 `name` / `description`；任一缺失、目录名与 `name` 不一致或重名时停止部署。不因项目根出现额外 `story*` 目录而扩张主包。
3. 对每个源 Skill 目录与对应目标目录逐项执行 `copy-path-safety.py`；`same` 记录为 no-op，`unsafe/error` 停止，仅 `safe` 可复制。不对整个用户 skills 根目录做删除或整体替换。

### TRAE / WorkBuddy 固定 Agent 名册

1. 两端共用通用名册精确为 8 张：`chapter-extractor`, `character-designer`, `consistency-checker`, `narrative-writer`, `revision-governor`, `story-architect`, `story-explorer`, `story-researcher`。
2. TRAE 数据名册精确为 5 张：`story-data-fetcher`, `story-data-method-validator`, `story-data-metrics-analyst`, `story-data-supervisor`, `story-data-text-improvement-planner`。TRAE 物理名册必须精确等于前述 8 张通用卡与这 5 张数据卡的并集，共 13 张。
3. WorkBuddy 数据物理名册精确为 2 张：`story-data-fetcher`, `story-data-readonly-runner`。WorkBuddy 物理名册必须精确等于前述 8 张通用卡与这 2 张数据卡的并集，共 10 张。四个只读逻辑角色卡随数据分析 Skill 自身部署，由其运行时映射定位，不作物理 Agent 注册。
4. 两端都只备份/替换上述精确 marker 卡，保留用户 Agent；上次受管但本次不在对应精确名字集的卡走「管理资产收敛」备份后删除。

### ZCode 部署算法（target_cli 含 zcode 时）

ZCode 首版部署 Skills、Commands、AGENTS.md 和支持事件内的 Hooks；不部署 `.zcode/agents` 或 `.zcode/rules`。

1. 按「canonical 中文主包 Skill 清单」复制到 `.zcode/skills/{skill-name}/`；仅替换这 18 个已知目录，保留用户其他 Skills。
2. 复制 `references/zcode/commands/*.md` 到 `.zcode/commands/`；仅替换与 canonical skill 同名的管理命令，保留用户其他 Commands。若主包某个 skill 缺预生成模板，视为包不完整并停止，不临时为项目扩展生成 command。
3. 复制 `references/zcode/hooks/story_zcode_hook.js` 和 `references/zcode/hooks/story_hook_core.js` 到 `.zcode/hooks/`。
4. 读取 `references/zcode/config.json.patch` 和现有 `.zcode/config.json`（如只有根 `zcode.json`，仍创建 `.zcode/config.json` 承载 oh-story 项目 Hooks，不改写根文件）：
   - 保留用户所有未知字段、MCP、plugins、skills/commands disable overrides；
   - **hooks 互斥（避免双触发）**：若本项目经已安装的 oh-story 插件运行（marketplace 安装，仓库根 `.zcode-plugin/plugin.json` 的 `hooks.json` 已全局注册 SessionStart/PreToolUse/PostToolUse），则**跳过**下面把 `config.json.patch` 的 `hooks` 块合并进 `.zcode/config.json`——插件 manifest 已注册这批 hooks，再合并会让同一事件跑两遍（PreToolUse 拦两次、PostToolUse 注入两次）。只有未装插件（直接克隆 / 手动导入 references）时才合并 hooks。不确定时以「ZCode 是否已通过本插件注册这套 hooks」为准；skills/commands/hook 文件/AGENTS 与 config 的非 hook 字段两条路径都照常部署。
   - 合并 hooks（仅未装插件时）：设置 `hooks.enabled: true`；用户已有更大的 `timeoutMs` 时保留，否则取模板值；对 `hooks.events` 的 SessionStart、PreToolUse、PostToolUse 按 `event + matcher + process command + args` 去重追加；不复制 ZCode 不支持的 PreCompact、PostCompact、SessionEnd、SubagentStop、Notification。
5. 将 `references/zcode/AGENTS.md.tmpl` 按「AGENTS.md 合并策略」写入根 `AGENTS.md`。
6. `.story-deployed` 的 `target_cli` 写入 `zcode` 或多端组合，`references_dir` 写 `.zcode/skills/story-setup/references/agent-references`。
7. 安装报告明确说明：ZCode 3.3.4 的项目/plugin custom agents 不执行，所有专业角色走 solo/direct；系统需要可用的 `node` 命令运行项目 Hook。

Plugin 安装不经过本算法：仓库根 `.zcode-plugin/plugin.json` 直接暴露同一组 Skills/Commands/Hooks。Plugin Skills 优先级低于 workspace `.zcode/skills`；两者同时存在时项目快照优先，升级项目快照需重新运行 `$story-setup`。**Hooks 只能注册一份**：插件 manifest 与 workspace `.zcode/config.json` 注册的是同一批事件，装了插件就不要再把 `config.json.patch` 的 hooks 合并进 `.zcode/config.json`（见上算法第 4 步的 hooks 互斥），否则 PreToolUse/PostToolUse 会双触发；插件在场时以插件 manifest 为 hooks 唯一注册源。

### TRAE hooks.json 合并算法（target_cli 含 trae 时）

1. 读取 `references/trae/hooks/hooks.json` 作为唯一当前模板，读取项目 `.trae/hooks.json`（不存在时视为空对象）。TRAE 合法 schema 是顶层 `{ "version": 1, "hooks": {...} }`，事件值为 matcher group 数组，group 内是 `matcher` + `hooks`，每个 hook 仅使用 `{ "type": "command", "command": "<单一 shell command 字符串>", "timeout": <秒> }`。
2. 按跨平台规则探测 `python3/python/py`；无可用解释器时停止 TRAE 分支，不手写或整体覆盖 JSON。
3. 调用 `"$PYBIN" "{story-setup skill目录}/scripts/merge-trae-hooks.py" --existing "{项目}/.trae/hooks.json" --template "{story-setup skill目录}/references/trae/hooks/hooks.json" --output "{项目}/.trae/hooks.json"`。helper 只把 command 中包含 `.trae/hooks/story_trae_hook.js` 的注册识别为 oh-story 管理项：先从历史 event/matcher 块移除它们，再追加当前模板。同一 matcher 块中的用户 hook、其他事件、未知顶层字段全部保留。
4. 写后校验 `version == 1`；事件名只能来自 TRAE 支持集 `SessionStart`、`UserPromptSubmit`、`PreToolUse`、`PostToolUse`、`Stop`、`Notification`，当前模板不得出现 `SessionEnd`、`PreCompact`、`PostCompact`、`SubagentStop`；hook 内不得出现 ZCode 的 `process/args/timeoutMs` 或 Claude 的 `if`。正文工具 matcher 覆盖 TRAE 原生 `RunCommand|Write|Edit`，commit advisory 覆盖 `RunCommand`。
5. 统计模板中每个 oh-story command 身份在结果中恰好一份，用户 hook/顶层字段仍在；再次运行 helper，必须与上次输出字节一致。

### TRAE Code 原生部署算法（target_cli 含 trae 时）

TRAE 分支部署原生 Skills / Subagents / Commands / Rules / Hooks，不走 generic skills-only 降级模式。

1. 按「canonical 中文主包 Skill 清单」确认固定 18 个 Skill。在任何写入前，按「TRAE 归属与备份门」为本次将改动的已存管理资产建立带 UTC 时间戳的备份；遇到无 `oh-story-managed` 标记的同名文件则保留、记录冲突并停止该资产写入。
2. 逐项过 `copy-path-safety.py` 后，把 18 个 canonical Skill 复制到 `.trae/skills/{skill-name}/`。目标缺失则创建；已是 oh-story skill（`SKILL.md` 的 `metadata.openclaw.source` 指向本仓库，或含精确标记 `<!-- oh-story-managed: skill/{skill-name} -->`）才可备份后替换。现有 sentinel 不能代替单个 Skill 的归属证据；无证据则保留并报告。`status=same` 的 Skill 只验证，不复制进自身。
3. 对 18 个 canonical 名字逐一部署 `references/trae/commands/{name}.md`；任一模板缺失即按部署包不完整停止，不为项目额外 Skill 生成 fallback command。只替换带相同管理标记的文件。
4. 按「部署 TRAE Agents」与「TRAE / WorkBuddy 固定 Agent 名册」把通用 8 名与 TRAE 数据 5 名的精确并集（13 张）部署到 `.trae/agents/`。仅 Agent 内置 Agent 才能调用子 Agent；skill 流程遇到子 Agent 不可用必须走已定义的 `solo/direct` fallback。
5. 将 `references/trae/rules/*.md` 写入 `.trae/rules/`，只替换带 `<!-- oh-story-managed: rule/... -->` 标记的文件；TRAE rule frontmatter 使用 `alwaysApply: false` 和 `globs`，不复制 Claude `paths` frontmatter。将 `references/trae/AGENTS.md.tmpl` 按「AGENTS.md 合并策略」写入根 `AGENTS.md`，只替换 `<!-- BEGIN oh-story-managed: trae -->` 与 `<!-- END oh-story-managed: trae -->` 之间的块，保留用户其他段落。
6. 将 `references/trae/hooks/story_trae_hook.js` 与 `story_hook_core.js` 写入 `.trae/hooks/`；runner 只替换头注释证明为 oh-story TRAE adapter 的同名文件。既有 shared core 必须先运行 `trae-core-ownership.py --candidate <文件> --registry references/trae/legacy-managed-sha256.json`，或证明与同项目受信 sibling core 字节一致；退出 3（unmanaged）/2（registry error）都保留并报告，不覆盖。替换后执行 `node --check` 与跨 adapter byte-parity 校验，再按「TRAE hooks.json 合并算法」合并 `.trae/hooks.json`；禁止用模板整体覆盖用户配置。
   Windows 的原生 `RunCommand` 由 PowerShell 提供，runner 必须同时覆盖已测试的静态 `Set-Content`、`Add-Content`、`Out-File`、`Copy-Item`、`Move-Item`、`New-Item` 写盘形式；动态表达式、splatting、`.NET` API 和未识别外部程序仍由 Skill 自检与写后门兜底，不得声称全部 PowerShell 写入都能在 PreToolUse 硬拦。
7. 校验 `.trae/skills/` 与 `.trae/commands/` 的受管名字集都精确等于 canonical 18，受管 Ag

…(truncated)
