<!-- title: 从零构建提交信息生成Skill -->

## 概述

本教程带你从零构建一个**提交信息生成 Skill（commit-message generator）**——它会读取你暂存的代码变更，自动产出符合 Conventional Commits 规范的提交信息草稿。借此案例，你将完整掌握 Claude Code Skill 的核心机制：`SKILL.md` 的 YAML frontmatter、渐进式披露（progressive disclosure）、脚本封装与触发调试。适合已能基本使用 Claude Code、想封装第一条自定义能力的中级读者。

## 前置条件

- 已安装 Claude Code（`claude --version` 可正常输出）
- 已安装 Git 且本地有一个可提交的代码仓库
- 了解 Conventional Commits 规范的基本概念（`feat: / fix: / docs:` 等）
- 一台 macOS / Linux / WSL 终端（Windows 原生 CMD 部分脚本语法需调整）

---

## 第一步：搞清楚 Skill 到底是什么

很多人把 Skill、Slash 命令、MCP、CLAUDE.md 混为一谈。先花一分钟厘清边界，后面才不会选错工具：

| 能力载体 | 本质 | 加载时机 | 适合场景 |
|---------|------|---------|---------|
| **CLAUDE.md** | 静态上下文/规则 | 每次会话全量注入 | 团队规范、编码风格、常驻约束 |
| **Slash 命令** | 一段提示词的快捷方式 | 用户主动输入 `/xxx` | 固定流程的提示词复用 |
| **MCP 服务器** | 外部工具/数据源协议 | 显式连接后可调用 | 接入数据库、API、第三方系统 |
| **Skill** | 文件系统能力包 | Claude 按语义**自动判断**是否加载 | 可复用的、有步骤的专业工作流 |

关键差异：**CLAUDE.md 是"总在场"的背景，而 Skill 是"按需登场"的专家**。Skill 的精髓在于 *progressive disclosure*——平时只把一段 `description` 摆在货架上，Claude 判断"现在需要这位专家"时，才把整本"操作手册"加载进上下文。这正是它比把所有规则塞进 CLAUDE.md 更省 Token、更精准的原因。

> 一句话记忆：CLAUDE.md 是公司的《员工手册》，Skill 是你随叫随到的「外包专家」，Slash 命令是你按下的「快捷按钮」，MCP 是接进系统的「外接设备」。

## 第二步：为什么拿"提交信息生成"练手

选择第一个实战 Skill 有三个标准：**高频、自包含、可验证**。提交信息生成完美命中：

- **高频**：每个开发者每天写无数次 commit，痛点真实
- **自包含**：只需 `git diff`，不依赖任何外部 API，离线可跑
- **可验证**：产出物（commit message）有明确规范（Conventional Commits），对错一眼可判

更重要的是，它能完整覆盖一个 Skill 的所有要素：需要脚本提取 diff、需要参考资料记录 type 列表、需要正文指导 Claude 如何组织语言、需要 frontmatter 让 Claude 在合适时机自动触发——麻雀虽小，五脏俱全。

## 第三步：创建项目骨架

Skill 可以放在两个位置：

- **用户级**：`~/.claude/skills/<skill-name>/SKILL.md`——所有项目可用
- **项目级**：`.claude/skills/<skill-name>/SKILL.md`——仅当前仓库可用

我们做的是一个通用工具，放用户级最合适。先建目录：

```bash
mkdir -p ~/.claude/skills/commit-message/scripts
mkdir -p ~/.claude/skills/commit-message/references
```

最终目录结构如下：

```
~/.claude/skills/commit-message/
├── SKILL.md                    # 入口文件（必需）
├── scripts/
│   └── staged-diff.sh          # 提取暂存区差异的脚本
└── references/
    └── conventional-commits.md # 规范速查（按需加载）
```

> **常见问题**：目录名必须和 frontmatter 里的 `name` 字段一致，且只能是**小写字母、数字、连字符**（kebab-case），最长 64 字符。写成 `commit_message` 或 `CommitMessage` 都会导致加载失败。

## 第四步：编写 SKILL.md 的 frontmatter

`SKILL.md` 顶部是一段 YAML frontmatter，用 `---` 包裹。其中只有两个字段是必填且决定生死的：`name` 和 `description`。

```yaml
---
name: commit-message
description: 生成符合 Conventional Commits 规范的 Git 提交信息。当用户准备提交代码、
  询问"该怎么写 commit message"、"帮我写提交信息"、暂存了变更想总结改动，
  或者在提交前想确保信息规范时使用。读取暂存区的 git diff，产出可直接复制的
  提交信息草稿，包含 type、scope、subject 与可选的 body。
---
```

这里有一个反直觉但至关重要的设计：**`description` 的写法直接决定 Skill 能否被触发**。Claude 不会看到你的正文，它只看这段描述来决定"要不要请这位专家出场"。所以好的 `description` 必须同时回答两个问题：

1. **这个 Skill 干什么**（What）
2. **什么情况下该用它**（When）

写 `description` 的三条经验法则：

- **列出触发短语**：把用户可能说的话写进去——"帮我写提交信息"、"该怎么 commit"、"总结一下这次改动"
- **描述能力边界**：明确它能做什么、不能做什么，避免误触发
- **控制在 1024 字符内**：超长会被截断，关键信息放前面

> **常见问题**：把 `description` 写成 `"一个关于提交的工具"` 这种废话——Claude 永远不知道何时该调用它。务必具体到"什么场景 + 产出什么"。

## 第五步：编写正文指令（渐进式披露的灵魂）

frontmatter 之下就是 Markdown 正文。**正文要精简**——官方建议控制在 500 行以内。原则是：让 Claude 一眼读懂工作流，细节丢给脚本和参考资料。

```markdown
# 提交信息生成

## 何时使用
用户准备 `git commit`、询问如何写提交信息、或希望总结暂存的改动时。

## 工作流程
1. 运行 `scripts/staged-diff.sh` 获取当前暂存区的变更摘要
2. 如果暂存区为空，提示用户先 `git add`，不要自行 add
3. 分析变更，判断本次提交的主导 type（参考 `references/conventional-commits.md`）
4. 产出提交信息，格式：`<type>(<scope>): <subject>`
5. 把信息展示给用户，**只生成草稿，绝不自动执行 commit**

## 输出规范
- subject 用祈使句、现在时，首字母小写，结尾不加句号，不超过 50 字符
- 当改动跨多个不相关模块时，建议拆分为多条提交信息而非一条大杂烩
- body 只在有非显而易见的背景时才写，每行不超过 72 字符

## 重要约束
- **只读 git diff，只生成建议**：任何 `git commit`、`git push` 都必须由用户确认后手动执行
- 不要为了凑 type 而强行归类；实在无法判断就用 `chore`
```

注意最后那条"重要约束"——这与"不要自动 commit"的工程纪律完全一致。Skill 的正文里要明确写死这类护栏，避免 Claude 自作主张。

> **常见问题**：把所有 Conventional Commits 的细则全塞进正文——会让 SKILL.md 臃肿、每次加载都烧 Token。正确做法是把完整规范放进 `references/`，正文里只留"按需查阅"的指引。这就是渐进式披露。

## 第六步：封装脚本（bundled vs separated）

Skill 里的脚本分两种风格，要按复杂度选择：

- **内联脚本（bundled）**：直接在 SKILL.md 里用反引号或 ` ```bash ` 代码块写命令。适合 1~3 行的简单逻辑。
- **分离脚本（separated）**：独立成文件放在 `scripts/`，正文里用路径引用。适合多步骤、可复用、需要单独测试的逻辑。

提取暂存区差异是典型的"分离脚本"场景——它有健壮性处理（空暂存区、二进制文件），值得独立成文件。新建 `scripts/staged-diff.sh`：

```bash
#!/usr/bin/env bash
# 提取暂存区的变更摘要，供提交信息生成使用
set -euo pipefail

# 确认处于 git 仓库
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  echo "ERROR: 当前目录不是 git 仓库" >&2
  exit 1
fi

# 获取暂存区变更的文件名清单（带状态码）
staged_files=$(git diff --cached --name-status)

if [ -z "$staged_files" ]; then
  echo "NOTICE: 暂存区为空，请先 git add 需要提交的文件。"
  exit 0
fi

echo "===== 暂存文件清单 ====="
echo "$staged_files"
echo ""
echo "===== 变更统计 ====="
git diff --cached --stat
echo ""
echo "===== 实际差异（截断超长内容）====="
git diff --cached | head -200
```

赋可执行权限（**这一步常被遗漏**，会导致 Skill 调用时脚本报权限错误）：

```bash
chmod +x ~/.claude/skills/commit-message/scripts/staged-diff.sh
```

单独测试脚本，确认能跑：

```bash
cd ~/your-project
git add some-file.py
bash ~/.claude/skills/commit-message/scripts/staged-diff.sh
```

**预期结果**：依次输出文件清单、变更统计、实际差异内容三段。若暂存区为空，则输出提示并正常退出。

## 第七步：准备按需加载的参考资料

`references/conventional-commits.md` 是 Skill 的"工具书"，平时不加载，只有 Claude 需要判定 type 时才翻开。内容保持速查风格：

```markdown
# Conventional Commits 速查

## type 取值
- feat:     新功能（用户可感知）
- fix:      Bug 修复
- docs:     仅文档变更
- style:    代码格式（不影响逻辑：空格、分号等）
- refactor: 既非新增功能也非修 Bug 的重构
- perf:     提升性能的改动
- test:     增加/修改测试
- build:    构建系统或外部依赖（webpack、npm 等）
- ci:       CI 配置文件与脚本
- chore:    杂项（不修改 src 或测试的其他改动）
- revert:   回退某次提交

## 格式
<type>(<scope>): <subject>
<空行>
<body>
<空行>
<footer>

## 示例
feat(auth): add OAuth2 login flow
fix(api): handle null pointer in user lookup
docs: update README installation steps
```

这套"正文 → 脚本 → 参考资料"的三层结构，就是渐进式披露的标准落地形态。Claude 每次只加载它当前真正需要的层。

## 第八步：安装与验证

Skill 无需显式安装——放到正确目录即生效。验证它是否被识别：

```bash
claude  # 启动交互式会话
```

进入会话后，输入一句自然语言触发：

> 我刚改了登录模块，帮我看看暂存了什么、该怎么写提交信息

**预期结果**：Claude 识别到"提交信息"意图，自动加载 `commit-message` Skill，运行脚本、给出类似下面的草稿：

```
feat(auth): add remember-me checkbox to login form

- persist session token when checkbox is checked
- clear token on explicit logout
```

如果没触发，可以用 `/skills` 命令查看已加载的 Skill 列表，确认 `commit-message` 是否在册、description 是否被正确解析。

## 第九步：description 触发调试（最高频的坑）

Skill "装了却不触发"是新手最常踩的坑，根因 90% 在 `description`。排查清单：

1. **YAML 缩进/语法错误**：frontmatter 解析失败时，整个 Skill 会被静默忽略。用 `cat -A SKILL.md | head -5` 检查是否有隐藏的 Tab 字符——YAML 只认空格。
2. **触发词太抽象**：把 `"git 工具"` 改成带场景的 `"生成 Conventional Commits 提交信息，当用户准备 commit……"`。
3. **与其他 Skill 抢触发**：多个 Skill 描述重叠时，Claude 可能选错。确保每个 Skill 的"何时使用"互斥。
4. **误触发（过宽）**：用户只想看 diff 却被拉起提交信息——在 description 里收紧边界，比如"仅当用户明确要写提交信息时"。
5. **目录名与 name 不一致**：`git rev-parse` 式的低级错误，但确实常发生。

一个调试小技巧：临时把 description 改得极简极宽（如 `"处理任何与 git 提交相关的事"`），先确认能触发，再逐步收窄到精准范围。

## 第十步：迭代、复用与分享

Skill 跑通后，三条进阶路径：

- **共享给团队**：把 `.claude/skills/commit-message/` 整个目录提交到仓库，队友 pull 后即生效；或封装成插件通过 marketplace 分发
- **加护栏**：结合 hooks 在 `PreToolUse` 拦截真实的 `git commit`，强制要求提交信息先过 Skill 校验
- **加评估**：用一批历史 commit 做 Evals 盲测，量化生成的 type 准确率，持续优化 description 与正文

## 完整实战案例：端到端走一遍

假设你在 `~/demo` 仓库刚改完一个 API 的空指针处理：

```bash
cd ~/demo
# 修复 bug
sed -i 's/return user.name/return user?.name ?? "anonymous"/' api/user.py
git add api/user.py
```

启动 Claude Code，说一句"帮我把这次改动整理成提交信息"。你将看到完整的自动流程：

1. Claude 判断意图 → 加载 `commit-message` Skill
2. 运行 `scripts/staged-diff.sh` → 拿到 diff
3. 按需查阅 `references/conventional-commits.md` → 判定为 `fix`
4. 产出草稿并展示，等待你确认

最终你会拿到一条可直接复用的信息：

```
fix(api): guard against null user in name lookup
```

整个过程没有外部 API、没有密钥、完全离线，且 Claude 始终只扮演"建议者"——`git commit` 这一步始终留给你手动执行。

## Skill 加载与执行流程

```mermaid
flowchart TD
    A[用户在会话中说话] --> B{Claude 扫描所有 Skill 的 description}
    B -->|语义匹配| C[自动加载 commit-message 的 SKILL.md 正文]
    B -->|不匹配| Z[不加载任何 Skill]
    C --> D[按正文工作流执行]
    D --> E[运行 scripts/staged-diff.sh 提取 diff]
    E --> F{需要判定 type?}
    F -->|是| G[按需加载 references/conventional-commits.md]
    F -->|否| H[直接生成]
    G --> H
    H --> I[产出提交信息草稿]
    I --> J[展示给用户,等待确认]
    J --> K[用户手动执行 git commit]
```

这张图揭示了渐进式披露的两层"按需"：**第一层**，description 不命中则整个 Skill 不加载；**第二层**，正文加载后，references 仍只在需要时才被翻开。Token 始终花在刀刃上。

## 延伸阅读

- **Conventional Commits 官方规范**：完整 type 列表、breaking change 写法、scope 约定
- **Claude Code 官方 Skills 文档**：frontmatter 全字段、allowed-tools、metadata 高级用法
- **anthropics/skills 仓库**：官方示例 Skill 集，是学习正文写法的最佳范本
- **Skill 的 Evals 评估**：如何用盲测量化 Skill 质量，进入"度量→迭代"闭环

## 训练营适用性评估

| 维度 | 评估 |
|------|------|
| 适合训练营类型 | Claude Code 入门到进阶营、AI 工程化实战营、开发者效率提升营 |
| 学员基础 | 能独立用 Git、用过 Claude Code 基本对话 |
| 教学时长 | 讲授 45 分钟 + 动手实操 60 分钟，合计约 2 课时 |
| 交付物 | 一个可运行的用户级 Skill，及一份可提交到团队的 Skill 目录 |
| 难度 | ⭐⭐⭐（中等偏低），适合作为"Skill 三件套"教学的首个落地案例 |

本案例特别适合作为训练营里"从使用者到构建者"的转折点——学员第一次亲手把一段重复劳动封装成 Claude 能自动调用的能力，这种正反馈往往成为持续探索 Agent 工程的起点。

## 参考来源

1. [Extend Claude with skills — Claude Code Docs](https://code.claude.com/docs/en/skills) — 官方 Skill 开发文档，frontmatter 字段、目录约定与渐进式披露规范的权威来源
2. [How to create custom skills — Claude Help Center](https://support.claude.com/en/articles/12512198-how-to-create-custom-skills) — 官方帮助中心，从零创建 Skill 的图文步骤指引
3. [anthropics/skills — GitHub](https://github.com/anthropics/skills) — 官方示例 Skill 仓库，正文写法与目录结构的最佳范本
4. [Claude Code Skills: How to Set Them Up and Use Them — dev.to](https://dev.to/padawanabhi/claude-code-skills-how-to-set-them-up-and-use-them-1chm) — 社区技术博客，含触发调试与踩坑实录
5. [How to Build Claude Code Skills: 5 Examples — fp8.co](https://fp8.co/articles/Claude-Code-Skills-Complete-Developer-Guide) — 含 SKILL.md 规范速查与多个可复用示例

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
