# Skill：把重复流程变成一条命令

> 每次发版都要粘贴同一段检查清单？这一课讲清 SKILL.md 的结构、存放位置、前置信息字段和按需加载原理，分清 Skill、CLAUDE.md 与斜杠命令各管什么，并手把手写一个生成中文发版说明的 Skill。

- 网址：https://coding.chengyu.dev/learn/claude-code/skills/
- 学习路径：Claude Code 精通
- 预计用时：22 分钟；核实日期：2026-10-25
- 官方来源：[Extend Claude with skills（Claude Code 官方文档）](https://code.claude.com/docs/en/skills)，[Extend Claude Code（Claude Code 官方文档）](https://code.claude.com/docs/en/features-overview)，[Commands（Claude Code 官方文档）](https://code.claude.com/docs/en/commands)，[Best practices for Claude Code（Claude Code 官方文档）](https://code.claude.com/docs/en/best-practices)

## 学习目标

- 能说出 SKILL.md 的结构，以及个人级、项目级 Skill 放在哪里
- 能解释 Skill 按需加载的原理，并用前置信息控制谁能调用它
- 能判断一条内容该放进 CLAUDE.md、路径规则、Skill 还是 hook
- 能独立写出一个带参数和动态上下文的 Skill 并调试触发

小周每次发版前都要在 Claude Code 里粘贴同一段话：「看一下上个 tag 以来的提交，按新功能、修复分类，用中文写成面向用户的更新说明，追加到 CHANGELOG.md 顶部，别把 CI 配置的改动写进去……」这段话已经粘了十几次，每次还会漏掉一两条要求。

把它写进 CLAUDE.md？可这段内容一个月只用两次，却要在每个会话里占着上下文。这正是 Skill 要解决的问题：**把反复使用的流程或资料打包起来，需要时用一条 `/命令` 调出来，不需要时几乎不占空间。**

## Skill 是什么

一个 Skill 就是一个目录，里面至少有一个 `SKILL.md` 文件。文件分两部分：开头 `---` 之间的 YAML 前置信息（frontmatter），告诉 Claude 这个 Skill 叫什么、什么时候用；后面是 Markdown 正文，写 Claude 执行时要遵循的指令。

调用方式有两种：你在消息开头输入 `/skill-name` 直接调用，或者 Claude 发现你的请求和 Skill 的描述匹配时自动加载。Claude Code 的 Skill 遵循 Agent Skills 开放标准，同一份 Skill 在其他支持该标准的工具里也能用（部分字段是 Claude Code 独有的扩展）。

> **自定义斜杠命令已经并入 Skill**
>
> 以前的自定义命令写在 `.claude/commands/deploy.md`，现在 `.claude/skills/deploy/SKILL.md` 同样生成 `/deploy`，两者效果相同，旧文件继续有效；同名时 Skill 优先。新写的建议都用 Skill 目录，因为它能放支持文件，还能控制谁来调用。

## 放在哪里

| 位置 | 路径 | 在哪些会话里加载 |
|---|---|---|
| 个人 | `~/.claude/skills/<名字>/SKILL.md` | 你本机的所有项目 |
| 项目 | `.claude/skills/<名字>/SKILL.md` | 这个仓库里的会话，提交到 git 后团队共享 |
| 嵌套 | `<子目录>/.claude/skills/<名字>/SKILL.md` | 在该子目录启动，或 Claude 处理到那里的文件时 |
| 插件 | `<插件>/skills/<名字>/SKILL.md` | 启用该插件的地方，命令形如 `/插件名:skill名` |
| 企业 | 托管设置目录下的 `.claude/skills/` | 组织部署到的机器 |

同名时，企业级优先于个人级，个人级优先于项目级；插件 Skill 带命名空间，不会冲突。命令名默认取目录名，也可以用前置信息里的 `name` 指定。

Claude Code 会监听这些目录，会话中新增或修改 `SKILL.md` 立即生效，不用重启。只有当你新建了一个会话启动时还不存在的顶层 skills 目录，才需要运行 `/reload-skills`。输入 `/skills` 可以列出当前所有 Skill，按 `t` 还能按 token 数排序。

## 前置信息：常用字段

所有字段都是可选的，但官方强烈建议写 `description`。字段名必须完全一致，写错了不会报错，只会被悄悄忽略。

| 字段 | 作用 |
|---|---|
| `name` | 显示在 `/` 菜单里的命令名，默认用目录名 |
| `description` | 做什么、什么时候用。Claude 靠它决定是否自动加载，关键用途要放在最前面 |
| `when_to_use` | 补充触发场景或示例说法，和 description 合计超过 1536 字符会被截断 |
| `argument-hint` | 输入命令时的参数提示，如 `[版本号]` |
| `disable-model-invocation` | 设为 `true` 后只能由你手动调用，Claude 不会自己触发 |
| `user-invocable` | 设为 `false` 后从 `/` 菜单隐藏，只让 Claude 在需要时加载 |
| `allowed-tools` | 调用这个 Skill 的那一轮里，列出的工具无需逐次批准 |
| `model`、`effort` | 这个 Skill 运行时临时换用的模型和推理强度 |
| `context`、`agent` | `context: fork` 让 Skill 在独立的子智能体里运行，`agent` 指定子智能体类型 |
| `paths` | 只在处理匹配这些路径的文件时才自动加载 |

正文里还能用几种替换：`$ARGUMENTS` 是调用时跟在命令后面的全部参数，`$0`、`$1` 按位置取单个参数，`${CLAUDE_SKILL_DIR}` 是 Skill 所在目录，方便引用附带的脚本。以 `` !`命令` `` 开头的写法叫动态上下文注入：Claude Code 会先在本机执行这条命令，把输出替换进正文，Claude 看到的就是真实数据而不是命令本身。

## 按需加载：为什么 Skill 几乎不占地方

这是 Skill 和 CLAUDE.md 最大的区别：

| 前置信息 | 你能调用 | Claude 能自己调用 | 进入上下文的方式 |
|---|---|---|---|
| 默认 | 能 | 能 | 描述始终在上下文里，调用时才加载全文 |
| `disable-model-invocation: true` | 能 | 不能 | 描述也不进上下文，调用时才加载全文 |
| `user-invocable: false` | 不能 | 能 | 描述始终在上下文里，调用时才加载全文 |

会话开始时，Claude 只看到每个 Skill 的名字和简短描述；正文、参考资料、脚本都留在磁盘上，用到才读。所以一份上千行的 API 规范做成 Skill，平时几乎不花成本。

调用之后，渲染好的 `SKILL.md` 正文会作为一条消息留在对话里，后续轮次不会重新读文件。上下文被压缩时，每个 Skill 只保留开头的 5000 token，所以**最重要的指令要写在 SKILL.md 前面**。有副作用的流程，比如部署、发消息、打 tag，务必加 `disable-model-invocation: true`，你不会希望 Claude 觉得「代码看起来差不多了」就自己去部署。

## Skill、CLAUDE.md、斜杠命令各管什么

| 机制 | 什么时候生效 | 适合放 |
|---|---|---|
| CLAUDE.md | 每个会话自动加载 | 每次都要遵守的事实和规则：构建命令、代码约定 |
| `.claude/rules/` 路径规则 | 每次会话，或处理匹配文件时 | 只和某类文件、某个目录相关的约定 |
| Skill | 你输入 `/名字`，或 Claude 判断相关时 | 偶尔用的参考资料、多步骤流程 |
| 内置命令 | 你输入 `/clear`、`/compact` 等 | Claude Code 自身的固定功能，不是给模型的指令 |
| Hook | 到了特定生命周期事件必定执行 | 必须每次都发生、不需要模型判断的动作 |

判断起来很简单：**每次都需要知道的**写 CLAUDE.md；**偶尔需要、或者是一套操作步骤的**做成 Skill；**必须保证执行、不能靠模型自觉的**写成 hook（见第 7 课[Hooks 自动化](/learn/claude-code/hooks/)）。官方给的信号也很实用：同一段流程第三次粘贴进聊天，或者 CLAUDE.md 里某一节已经从「事实」长成了「操作步骤」，就该把它挪成 Skill。

> 小测：你写了一个 /deploy-staging Skill，会执行构建并推送到测试服务器。你希望它只在你明确输入命令时才运行。应该怎么配置？

## 手把手：写一个中文发版说明 Skill

目标：输入 `/changelog-zh v1.4.0`，Claude 读取上一个 tag 之后的提交，按模板生成中文发版说明，追加到 `CHANGELOG.md` 顶部。

**第 1 步：建目录。** 这是项目相关的流程，放在项目里，团队都能用：

```bash
mkdir -p .claude/skills/changelog-zh
```

**第 2 步：写 SKILL.md。** 保存为 `.claude/skills/changelog-zh/SKILL.md`：

```markdown
---
name: changelog-zh
description: 根据上一个 tag 之后的 git 提交，生成面向用户的中文发版说明并写入 CHANGELOG.md。用于发版、写更新日志时。
argument-hint: "[版本号，如 v1.4.0]"
disable-model-invocation: true
allowed-tools: Bash(git log *) Bash(git describe *)
---

为版本 $ARGUMENTS 生成中文发版说明。

## 仓库现状
- 最近的 tag：!`git describe --tags --abbrev=0 || echo "（还没有 tag）"`

## 步骤
1. 运行 `git log <最近的 tag>..HEAD --no-merges --pretty=format:"%h %s"` 获取提交；没有 tag 时取最近 50 条。
2. 按 feat → 新功能、fix → 问题修复、perf → 性能优化分类，其余归入「其他改动」。
3. 去掉对用户没有影响的提交：ci、chore、仅改文档或测试的提交。
4. 每条用用户能看懂的话重写，不出现函数名，末尾保留短 hash。
5. 按 [template.md](template.md) 的格式输出，先给我看，确认后再插入 CHANGELOG.md 顶部。

## 规则
- 没有给出版本号时，先问我，不要自己编。
- 不要修改 CHANGELOG.md 里已有的历史内容。
```

**第 3 步：加一个支持文件。** 在同一目录新建 `template.md`：

```markdown
## 版本号（发布日期 YYYY-MM-DD）

### 新功能
- 一句话说明用户能做什么新事情（abc1234）

### 问题修复
- 一句话说明修好了什么现象（def5678）

### 其他改动
- 性能、依赖升级等（9a8b7c6）
```

SKILL.md 里用链接指向它，Claude 需要时才去读。官方建议 SKILL.md 控制在 500 行以内，更长的参考资料、示例都拆成支持文件。

这份 Skill 里有几个刻意的设计：

- **`disable-model-invocation: true`**：写文件、发版这类有副作用的事，只在你说要做的时候做；
- **动态注入**：`` !`git describe ...` `` 在 Claude 看到正文之前就执行，它拿到的是真实的 tag。注意注入的命令一旦以非零码退出，整个 Skill 调用会失败，所以加了 `|| echo` 兜底，覆盖仓库还没有 tag 的情况；
- **`allowed-tools`**：只在这一轮免确认 `git log` 和 `git describe`，你发下一条消息后就失效，不会长期放宽权限；
- **先展示、后写入**：生成的说明先给你审，再落盘。

**第 4 步：测试。**

```text
> /skills
# 列表里应能看到 changelog-zh，没有的话检查目录名和 SKILL.md 第一行是否是 ---
> /changelog-zh v1.4.0
  Bash git log v1.3.2..HEAD --no-merges --pretty=format:"%h %s"
⏺ 共 23 条提交，过滤掉 7 条 ci / chore 提交后，整理如下：
  ## v1.4.0（2026-10-25）
  ### 新功能
  - 订单列表支持按退款状态筛选（a1c93e2）
  ...
⏺ 确认无误的话，我把它插入 CHANGELOG.md 顶部。
```

**第 5 步：迭代。** 不满意就直接改 SKILL.md，保存后在同一会话里再调用一次就能看到效果。如果 Skill 本该被自动调用却没触发（没设 `disable-model-invocation` 的那类），先检查 description 里有没有用户自然会说的关键词；反过来触发太频繁，就把描述写得更具体。

> 小测：你的 SKILL.md 有 800 行，前 600 行是接口字段说明，最后 200 行才是「生成代码时必须遵守的五条规则」。会话很长、发生过几次自动压缩后，Claude 开始不遵守这五条规则。最合理的改法是？

## 动手：把你自己的重复流程做成 Skill

**从聊天记录里提炼一个 Skill**

```text
我想把一个经常重复的流程做成 Claude Code 的 Skill。流程是：（在这里用几句话描述，例如「每次新增后端接口：建 DTO、写 Service、注册路由、补单元测试、更新接口文档」）。

请你：
1. 先读项目里一个现有的同类实现，总结出这个流程在本项目里的真实步骤和约定；
2. 判断它更适合做成 Skill 还是写进 CLAUDE.md，说明理由；
3. 如果适合做 Skill，起草 .claude/skills/对应名字/SKILL.md：写好 description（关键用途放最前）、argument-hint；判断是否有副作用，决定要不要 disable-model-invocation；正文控制在 60 行以内，长的参考内容拆到支持文件；
4. 列出我该用哪两三句话测试它能否被正确触发。

先给我看草稿，确认后再创建文件。
```

- [ ] 按本课步骤创建 `.claude/skills/changelog-zh/`，用 `/skills` 确认它出现在列表里
- [ ] 在一个有 tag 的仓库里运行 `/changelog-zh v0.0.1-test`，检查输出是否符合模板
- [ ] 故意删掉 SKILL.md 第一行的 `---`，观察前置信息失效后的变化，再改回来
- [ ] 用上面的提示词，把你自己最常粘贴的一段流程做成 Skill
- [ ] 检查你的 CLAUDE.md，把其中只在特定任务里才用到的步骤迁移成 Skill
