---
name: run-skill-generator
description: 通过创建带驱动程序脚本的每个项目运行技能，教 /run 和 /verify 如何构建和启动你的项目。
---

你的工作是生成一个位于 `<unit>/.claude/skills/run-<unit-name>/` 的**技能**，使未来的代理能够从一台干净的机器上构建、启动和**驱动**此项目。

该技能有两个部分组成，放在一起：

```
<unit>/.claude/skills/run-<unit-name>/
  SKILL.md      ← 面向代理的指令——简短。指向驱动程序。
  driver.mjs    ← （或 driver.py、smoke.sh 等——或没有：Web 应用使用现成的
                   chromium-cli，SKILL.md 中的 heredoc 就是脚本）
```

这几乎总是意味着**编写代码**，而不仅仅是散文。如果应用有任何交互表面（GUI、TUI、长运行服务器、REPL），未来的代理需要一种编程方式去操控它。单独的 markdown 文件无法点击按钮——但有时按钮点击器已经存在：对于 Web 应用是 `chromium-cli`，对于服务器是 `curl`。你现在构建（或编写脚本）那个框架，与技能一起提交，`SKILL.md` 记录如何使用它。

## 完成定义

当**所有**以下都为真时，你才算完成：

1. **你在此容器中启动了应用并与之交互**——不是其测试套件，是实际运行的应用。对于任何有 GUI 的东西，意味着你磁盘上有你拍摄的截图文件。
2. **交互框架已提交**在技能旁边。驱动程序脚本、REPL 包装器、冒烟测试，或 `SKILL.md` 中的 `chromium-cli` heredoc 内联——无论你用什么在步骤 1 中驱动应用。（已升级到 `scripts/`/`e2e/`？——好的，指向它。使用现成 `chromium-cli` 的 Web 应用？——内联脚本就是框架；不需要单独的文件。）
3. **`SKILL.md` 将框架记录为**主要代理路径——未来代理首先阅读的章节是"运行此驱动程序 / 将这些命令管道到 `chromium-cli`"，而不是"运行 `npm start` 并打开一个窗口"。
4. **`SKILL.md` 中的每个代码块都是你运行并生效的命令。** 当前会话。当前容器。不是来自 README，不是推断出来的。

如果你即将编写技能却没有做到 (1)，**停止。** 你即将改述现有文档。那份文档已经存在——它叫 README，你来这里的全部原因就是它不够用。

## 交付物是代码和文档

典型输出是一个技能目录，包含以下两者：

```
<unit>/.claude/skills/run-<unit>/
  SKILL.md         ← 简短。指向驱动程序。具有前置元数据，
                     使 Claude 在有人要求"运行 <unit>"或"截图 <unit>"时自动加载它。
  driver.mjs       ← （或 driver.py、smoke.sh 等——或没有：Web 应用
                     使用现成的 chromium-cli，SKILL.md 中的 heredoc 就是脚本）
```

驱动程序**默认放在技能目录内**。它们是一对——技能的指令和实现它们的代码。放在这里的驱动程序可以比生产代码稍微乱一点；它是代理工具，不是产品表面。

**升级：** 如果驱动程序成长为项目自身测试套件想要重用的东西——共享的启动辅助函数、真正的 e2e 框架——将其移到 `scripts/` 或 `e2e/` 并更新 `SKILL.md` 引用新路径。技能保留；驱动程序找到一个更好的归宿。

确切的形态取决于项目，但原则是一致的：**驱动程序就是交付物。** `SKILL.md` 是它的手册页。对于 Web 应用，驱动程序已存在——`chromium-cli`（[examples/playwright.md](examples/playwright.md)）——技能就是运行它的脚本。对于桌面应用（[examples/electron.md](examples/electron.md)），驱动程序是 tmux 下的自定义 REPL，暴露 `launch`/`ss`/`click`/`eval`。对于服务器，驱动程序是 `curl`。无论采取什么形式，没有能触及运行中应用的东西，技能就是一个没人能触碰的描述窗口。

## 技能放在哪里

技能位于 `<unit>/.claude/skills/run-<unit-name>/`，其中 `<unit>` 是**一个可部署事物**的目录——一个应用、一个服务、一个库。

Claude Code **原生发现**嵌套的 `.claude/skills/` 目录中的技能：在 `<unit>` 内任何位置的代理都会看到 `/run-<unit-name>` 作为可用技能，当请求匹配其描述时自动加载（例如"运行桌面应用"、"截图账单"）。

- **单项目仓库：** 仓库根目录的 `.claude/skills/run-<repo-name>/`。
- **有许多应用的大型仓库：** 每个应用一个，同区——`apps/billing/.claude/skills/run-billing/`、`apps/desktop/.claude/skills/run-desktop/`。
- **有多个二进制的应用：** 在应用的根目录仍然是**一个**技能，每个二进制一个章节。它们共享设置。从最接近的单二进制示例开始，为每个二进制添加一个 `## Run: <name>` 章节。

如果你不确定单元边界在哪里，**询问用户。**

将目录名转为 slug：小写，空格用连字符代替，没有斜杠（`run-billing-api`，不是 `run-billing/api`）。目录名和前置元数据的 `name:` 应匹配——那是斜杠命令。

## 流程

### 0. 查找任何关于运行此应用的现有技能

列出项目的技能及其描述（与 `/run` 使用的探测相同——用户会以不同名称命名，所以按描述匹配，而不是名称）：

```bash
d=$PWD; while :; do
  grep -Hm1 '^description:' "$d"/.claude/skills/*/SKILL.md 2>/dev/null
  [ -e "$d/.git" ] || [ "$d" = / ] && break
  d=$(dirname "$d")
done
```

如果有一个关于启动/驱动此应用的——无论它叫什么——**精炼，不要重写**：验证其声明，修复错误，补充缺失，保留有效的。如果有驱动程序，重新运行它。保留其现有名称。

（同时检查旧的 `.claude/run.md`——此工具的早期版本生成了那个。如果找到了，迁移它：正文变成技能的 `SKILL.md` 内容，任何引用的脚本移入技能目录，删除旧文件。）

如果不存在，决定在哪里创建（见上文）并继续。

### 1. 发现——并将每个声明视为可证伪的

弄清楚你为谁创作：

- 此处有清单（`package.json`、`go.mod`、`pyproject.toml`…）且它是一个自包含的东西 → 这就是单元。
- 看起来像一个超大仓库的根（`apps/`、`packages/`、`services/`）→ **询问是哪一个。** 列出候选，让他们选择，`cd` 进去。
- 真的模糊 → 询问。

调查常见的地方：`README.md`、`package.json` 脚本、`Dockerfile`、`Makefile`、`.github/workflows/`、`CONTRIBUTING.md`。CI 配置通常比 README 更准确。

**现有文档中的每一个声明都是一个假设。** 尤其是负面的：

| 当文档说… | 你做什么 |
|---|---|
| "需要 macOS/Windows" | 仍然在 Linux 上启动它。应用很少拒绝启动——它们在缺失的 `.so` 上崩溃，`apt-get` 可修复。你的主机钥匙串/通知的原生模块可能空操作；核心通常能运行。 |
| "需要 GPU" | 尝试软件渲染。Electron/Chrome 使用 `--disable-gpu` 回退。 |
| "需要付费账户 / 功能标志" | 门控是你可以阅读的代码。找到它（环境变量？构建定义？SSR 嵌入的 JSON？）并为你的本地运行打补丁。记录补丁。 |
| "运行 `npm start`" | 那是人类路径（生成一个窗口，永远等待）。找到或构建*编程*路径——`electron-forge start` 来构建然后通过 Playwright 启动，或等效物。 |

README 中由 macOS 开发者写的"不支持 Linux"意思是"我从未试过。"你即将尝试。**如果你在这里放弃，你写的技能就是加了额外步骤的 README。**

### 2. 执行——并构建你需要的框架

你在一个无头 Linux 容器中。应用会跟你打架。那场架就是技能的内容。

在你进行的过程中维护一个 `NOTES.md`。每个错误 → 每个修复 → 每个最终生效的命令。这个便签会成为故障排除章节。

**逐步建立真实交互：**

- **安装 + 构建。** 当缺少某物时，记录确切的 `apt-get` / `npm install` 修复了它。
- **启动应用。** 不是测试套件——应用本身。桌面 GUI（Electron、原生）需要 `xvfb-run` 和一些 `lib*` 包；由 `chromium-cli` 驱动的 Web 应用在无头环境下运行，两者都不需要。启动超时和神秘的崩溃在这个阶段是正常的。阅读堆栈跟踪，安装缺失的东西，再试。
- **构建一个框架来驱动它。** 你需要一个运行中应用的句柄，让你能够编程地发送输入并观察输出。形态取决于项目（见下表）。

  **覆盖 PR 实际触及的层。** 对于 UI 变更，一个操控 CLI 用户表面的 tmux 驱动程序是正确的句柄——对于一个触及单个内部函数的 PR 则是错误的。对于后者，代理想要的是 `NODE_ENV=test bun run script.ts`（或等效物）：导入函数，调用它，观察。如果此处的 PR 大多触及内部，直接调用路径是驱动程序的主要入口点，tmux 启动是次要的。查看最近合并的 PR：它们触及什么层？覆盖那个。

  对于 **Web** 应用，`chromium-cli` 就是驱动程序——你编写脚本，不写它（见 [examples/playwright.md](examples/playwright.md)）。对于**桌面** GUI（Electron），编写一个 REPL 驱动程序（stdin 命令 → click/type/screenshot），在 tmux 内运行，并使用 `send-keys` / `capture-pane`。你将在该驱动程序上迭代——它从最小化开始（`launch`、`ss`、`quit`），随着你需要触达应用有趣部分而增长所需的任何命令。
- **做一次端到端的真实用户流程。** 点击按钮。填写表单。在 DOM 中查看结果。截图。**实际查看截图。** 如果是空白或显示错误页面，你没有完成。
- **然后运行测试。** 单元测试是健全性检查，不是主要事件。
- **干净停止。**

**障碍是内容。** 你会遇到奇怪的问题——坐标系不对齐、此 Electron 版本上 API 返回空、功能门控隐藏了你需要测试的东西。每个这些都在陷阱章节中获得一个项目符号，并且（通常）在驱动程序中获得一个辅助函数。黄金标准是一个充满没人能猜到的内容的陷阱章节。

**驱动程序脚本与技能一起提交。** 它不是脚手架。它是未来代理（和人类）将驱动此应用的方式。它默认放在技能目录内（对于使用 `chromium-cli` 的 Web 应用，这意味着内联在 `SKILL.md` 中——heredoc 就是脚本）。如果它超过了此规模——如果项目的真实测试套件想要从中导入——将其移到 `scripts/` 或 `e2e/` 并更新 `SKILL.md` 指向那里。

### 3. 编写 SKILL.md

简短。指向驱动程序。使用 [template.md](template.md) 作为起始结构——它具有前置元数据格式。

**前置元数据很重要。** `name:` 成为斜杠命令（`/run-billing`）。`description:` 是 Claude 扫描以决定是否自动加载此技能的内容——将**代理实际会输入的动词**放进去："运行"、"启动"、"构建"、"测试"、"截图"。通用描述（"账单的有用实用工具"）不会匹配。

正文结构：

1. 一段介绍：此应用是什么，它如何被驱动——桌面应用使用 `<driver-path>` 在 xvfb/tmux 下，Web 应用使用 `chromium-cli`，服务器使用 `curl`。
2. **前置条件**——你运行的确切 `apt-get install` 行。
3. **构建**——确切的命令，按顺序。包括你必须应用的所有补丁（功能门控、配置覆盖），附上确切的 `sed` 或编辑。
4. **运行（代理路径）**——首先。如何启动驱动程序，它接受什么命令，截图落在哪里。如果是 REPL，展示 tmux 包装。这是下一代理将实际使用的章节。
5. **运行（人类路径）**——其次，如果不同。`npm start` → 窗口打开 → Ctrl-C。简短。注明在无头环境中无用。
6. **陷阱**——战斗伤疤。看起来应该能工作但不能的东西，以及替代方案。如果此章节是通用的，说明你斗争得不够。
7. **故障排除**——症状 → 修复。只有你实际遇到的错误。

保持其**已验证**（你运行过）、**规范性的**（一条路径，不是选项）、**诚实**（不稳定？慢？说明）。

**`SKILL.md` 中的路径是相对于 `<unit>/` 的，** 不是相对于技能目录。如果有任何歧义，在顶部说明。当驱动程序在技能内部时，其从 `<unit>` 开始的路径是 `.claude/skills/run-<unit-name>/driver.mjs`——它很长，但是明确的。

### 4. 验证

新 Shell，`cd` 进入该单元，逐行遵循技能的 `SKILL.md` 而不偏离。任何即兴创作 = 一个缺口。修复它。

## 项目类型模式

为你的驱动程序选择一个起始形态。这些示例与 `/run` 技能共享（当没有项目特定的运行技能存在时，使用相同的每个项目类型模式作为回退）——如果你正在创作一个新的，示例就是你的起始模板。

| 项目类型 | 驱动程序形态 | 示例 |
|---|---|---|
| Web 服务器 / API | 后台启动 + 基于 `curl` 的冒烟脚本 | [examples/server.md](examples/server.md) |
| CLI 工具 | 代表性参数的冒烟脚本，检查退出码 + 输出 | [examples/cli.md](examples/cli.md) |
| TUI / 交互式终端 | tmux 包装器：`send-keys` / `capture-pane` | [examples/tui.md](examples/tui.md) |
| Electron / 桌面 GUI | xvfb 下的 Playwright `_electron` REPL 驱动程序，截图，tmux 包装 | [examples/electron.md](examples/electron.md) |
| 浏览器驱动 | 开发服务器 + `chromium-cli` 脚本 | [examples/playwright.md](examples/playwright.md) |
| 库 / SDK | Import-and-call 冒烟脚本 | [examples/library.md](examples/library.md) |

对于 Web 应用，从 [examples/playwright.md](examples/playwright.md) 开始——用 `chromium-cli` 驱动它，不需要自定义驱动程序。对于桌面应用，从 [examples/electron.md](examples/electron.md) 开始——它有完整的 `_electron` REPL 驱动程序骨干、tmux 包装和你将遇到的障碍目录。

## 包含什么

- **前置条件**——操作系统包、运行时、工具。Ubuntu `apt-get` 行。确切的那种。
- **设置**——安装依赖、配置、任何补丁。
- **构建**——编译/打包。
- **运行（代理路径）**——驱动程序。命令。截图位置。
- **直接调用**——如果可调用：如何在不启动完整应用的情况下导入和运行内部代码。绕过初始化守卫的环境变量/标志。许多 PR 只需要这个。
- **运行（人类路径）**——如果有实质不同。
- **测试**——测试套件命令。
- **陷阱**——你遇到的非明显陷阱。
- **故障排除**——错误 → 修复。
- **驱动程序本身**——提交在技能目录中（或升级到 `scripts/`/`e2e/`），或对于 `chromium-cli` Web 应用内联在 `SKILL.md` 中；无论哪种方式都从 `SKILL.md` 引用。

## 省略什么

- **你没有运行的任何东西。** 如果 README 说 `yarn start:prod` 而你从未运行过，它不在技能中。句号。
- **记录在案的你不所在平台的快乐路径。** 你在一个 Linux 容器中。你不能验证的仅 macOS 章节是推测。提及它存在；不要详细阐述。
- **详尽的选项。** 一条工作的路径。
- **架构散文。** 那是其他文档。
- **通用故障排除。** "如果构建失败，检查你的 Node 版本"——无用。仅包含你实际遇到并修复的错误。

## 红旗——你即将交付错误的东西

如果出现以下情况，停下来重新考虑：

- **你没有为 GUI 应用截图。** 你没有运行它。
- **你的技能没有驱动程序/冒烟脚本**可指向，而应用是交互式的。下一代理没有驱动它的方法。（使用 `chromium-cli` 的 Web 应用？——`SKILL.md` 中的 heredoc 就是驱动程序；不需要单独的文件。）
- **你的技能读起来像 README。** 相同的结构、相同的命令、相同的注意事项。你改述了。
- **你的故障排除章节是通用的。** 真正的执行会产生特定的、奇怪的错误。通用错误 = 你没有执行。
- **你在没有尝试启动的情况下写了"不支持此平台"。** README 作者用的是 Mac。你不是。试试。
- **一切第一次就成功了。** 要么这个项目简单得微不足道，要么你运行了测试套件就叫它完成了。
