Claude Code 用了几周以后,你会发现一件事:能力上限不是模型,是你能不能把高频任务沉淀成可复用的”技能”。每次都把”先看 git status、再用 codegraph 找入口、最后跑测试再提交”这一套从头讲一遍,你和模型都累。Claude Skills 就是为了这件事存在的——把工程习惯写成可挂载的工作流文件,Claude Code 启动时按需加载。

很多教程只讲 Skills 是什么、给一些模板。但真正卡住开发者的不是概念,是写完 SKILL.md 之后它没被加载、没被识别、没被触发的那一段。本文按一个最小但完整的例子走一遍:从写一个 commit-readiness 技能,到注册到 ~/.claude/skills/,再到验证 Claude Code 真的会调用它。

Skill 是什么、不是什么

Skill 在 Claude Code 里的本质是:一份带 frontmatter 的 Markdown 文件 + 可选附件,被 Claude 在合适时机当作”系统级提示”加载进上下文。它不是插件,不是 npm 包,不是要 import 的模块。

它和你已经熟的两个东西区分一下:

东西触发方持久化用途
CLAUDE.md自动加载到每个会话一直在全局规则、不能漏的红线
Skill用户用 /skill-name 显式调用,或 Claude 判断匹配后用 Skill 工具调用按需加载可复用的工作流 / 流程文档
Sub-agent主 agent 用 Agent 工具显式 dispatch单次任务隔离大任务并行 / 隔离上下文

一句话:CLAUDE.md 是”必读”,Skill 是”按需读”,Sub-agent 是”派工”。这个区分你想清楚了,剩下的写法就是格式问题。

一个最小可复现的 Skill:commit-readiness

我们写一个非常实用的 Skill:每次准备 git commit 之前,让 Claude 先按一份 checklist 检查代码状态。需求很简单:

  • 跑一遍 git statusgit diff --stat,确认改动范围
  • 检查是否有 console.log / print / TODO 类调试残留
  • 确认改动文件的测试是否通过
  • 给出一个建议的 commit message

这个流程值不值得做成 Skill?标准是:你是否会在多个项目里反复让 Claude 做同样的事。是 → 写 Skill;不是 → 直接对话。

第一步:建目录

技能文件按”一个目录一个 skill”组织:

1
2
3
~/.claude/skills/
└── commit-readiness/
└── SKILL.md

文件夹名就是 skill 的调用名(/commit-readiness)。如果还需要附带模板、参考文档、示例脚本,就放在同一目录下,SKILL.md 里用相对路径引用。

第二步:写 SKILL.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
---
name: commit-readiness
description: Use when the user is about to commit code — runs a pre-commit checklist (status, diff, debug residue, tests) and proposes a commit message. Trigger on "commit"/"提交"/"准备提交".
allowed-tools:
- Bash(git:*)
- Bash(npm test*)
- Bash(pytest*)
- Read
- Grep
---

# commit-readiness

## 用途
在用户说"准备提交"、"commit"、"看下能不能提交"时,对当前 working tree 做一遍发车前检查,输出一个简短的报告 + 建议的 commit message。

## 步骤

1. **看变化范围** —— 跑 `git status -s` 和 `git diff --stat`,把改动文件按目录归类。
2. **找调试残留** —— 用 Grep 搜 `console.log|debugger|TODO\(claude\)|XXX:` 等模式,限制只在改动的文件范围内搜。
3. **找出对应测试** —— 改动文件如果在 `src/`,找 `tests/` 或 `__tests__/` 下同名文件;改了 `*.py`,找对应的 `test_*.py`。
4. **跑测试** —— 如果项目根有 `package.json` 且有 `test` script,跑 `npm test`;如果有 `pytest.ini` / `pyproject.toml`,跑 `pytest <相关测试>`。失败就停在这步,把失败 case 列出来。
5. **写 commit message** —— 参考 Conventional Commits(feat/fix/refactor/docs/test/chore),第一行 ≤ 72 字符,正文不超 3 行。

## 输出格式

```text
变化:N 个文件,X 行新增 / Y 行删除
风险点:[空 / 列出 1-3 条]
测试:M passed / K failed
建议提交信息:
<type>(<scope>): <subject>

<body>

红线

  • 不主动跑 git commitgit push,只输出建议。
  • 测试失败时不给”建议提交”,直接停在测试报告。
  • 跨多个 untracked 文件夹时,先确认是不是真的都要进这次提交。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

几个关键点解释:

**`name`** —— 必须是 kebab-case,文件夹名一致。这是 `/commit-readiness` 调用时的 ID。

**`description`** —— 这一段不是给人看的,是给 Claude 模型看的。它决定了"什么时候自动激活这个 skill"。写得好坏直接决定 skill 命中率。我的经验是:开头写 "Use when ...",中段把触发关键词列出来,最后写一句"不在 X 场景使用"避免误触发。

**`allowed-tools`** —— 限制这个 skill 能调用的工具。`Bash(git:*)` 是 glob 模式,表示"允许 git 开头的所有 bash 命令"。这一行能让用户在权限提示里更安心。如果不写,默认继承会话权限。

## 第三步:让 Claude Code 识别它

把目录扔到 `~/.claude/skills/` 之后,Skill 不是立即生效——你需要让 Claude **重新扫描技能目录**。两种触发方式:

1. 重启 Claude Code 会话(关掉重开)
2. 在会话里直接输入 `/commit-readiness` 显式调用

第二种最常用。如果输入后 Claude 提示 "skill not found",按以下顺序排查:

| 现象 | 大概率原因 |
|---|---|
| skill not found | `name:` 字段和文件夹名不一致 |
| 命令能进但不按 SKILL.md 走 | description 写得太泛,模型当场总结跑偏;或 allowed-tools 限太死 |
| 找不到 SKILL.md | 文件名不是 `SKILL.md`(大写);或者放成 `~/.claude/skill/` 单数 |
| 跑到一半要权限 | allowed-tools 没声明对应工具,每个工具都要弹窗 |

## 项目级 skill vs 全局 skill

`~/.claude/skills/` 是全局;项目级则放在仓库的 `.claude/skills/`。两者都能被加载,**项目级优先**。

什么时候用哪个?

- **全局**:和具体项目无关的工作流,比如 `commit-readiness`、`pr-review`、`extract-learnings`。我自己 9 成 skill 都在全局。
- **项目级**:依赖项目结构的,比如某个项目特有的"先跑这套 lint 再跑那套测试"流程,或者只在这个仓库才有意义的 brief 模板。

实战经验:写了一个不确定的,先放项目级试用,跑顺了再上升到全局。

## description 怎么写得让 Claude 主动用

这是 skill 体系里最反直觉的一点。Claude 不会"按文件名匹配"——它会读所有 skill 的 description,然后判断当前任务跟哪个最像。所以 description 不是给人写的文档,是给模型写的"激活条件"。

我观察过几十个开源 skill,写得好的都有共同模式:

```text
description: Use when [明确场景]. Trigger on [关键词列表]. Not for [反例].

举几个差对比:

❌ 差:description: A skill to help with commits.

❌ 差:description: This skill helps users prepare for git commits by running checks.

✅ 好:description: Use when the user is about to commit code — runs a pre-commit checklist (status, diff, debug residue, tests). Trigger on "commit"/"提交"/"准备提交"/"看下能不能提交". Not for branch management or PR creation.

差异在哪:好的版本明确了触发条件触发关键词反例边界。模型在评估”这个 skill 适不适合现在这个任务”时,就有明确依据。

用附件让 Skill 更扎实

SKILL.md 有时候不够。比如你想让 skill 在生成 commit message 时参考一份团队约定的格式说明,可以这样组织:

1
2
3
4
5
6
~/.claude/skills/commit-readiness/
├── SKILL.md
├── conventional-commits.md # 详细规则
└── examples/
├── good-message-1.md
└── good-message-2.md

然后在 SKILL.md 里:

1
2
3
4
## 写 commit message 时

参考 `conventional-commits.md` 的完整规则。
不确定 type 怎么选,看 `examples/` 里的两个样例。

注意 SKILL.md 里的路径是相对当前 skill 目录的——Claude 加载时知道这是”同一 skill 包内的资源”。

4 类常见错误

写过几个 skill 之后,我观察到几乎所有人都会踩这几坑:

1. description 太抽象
“helps with code review” / “useful for testing” 这类文案,模型完全无从判断。结果就是 skill 写了等于没写——对话里它从不主动激活。

2. 把 CLAUDE.md 该写的塞进 skill
一些团队规则(比如”所有提交都要带 ticket ID”)应该写在 CLAUDE.md,而不是 skill。skill 是按需加载的——如果是必须遵守的红线,放 skill 等于”按需遵守”,逻辑上就错了。

3. 步骤写成”建议”而不是”指令”
“You may want to check the test results” vs “After step 3, run the matching tests; if any fail, stop and report”。模型对前者会自由发挥,对后者会按部就班。skill 的价值在确定性,写法要像 SOP 不像建议书。

4. allowed-tools 漏声明
你的 skill 步骤里要 gitnpmgrep,三样都得在 allowed-tools 列出来。漏一个就在那一步弹权限框,体验断断续续。注意 Bash(git:*) 这种 glob 是常见写法,Bash(*) 太宽用户会犹豫。

验证 skill 真的在工作

写完之后,用三个简单测试验证:

测试 1:直接调用 —— /commit-readiness,看是不是按 SKILL.md 的流程一步一步跑。如果跑偏了,是 SKILL.md 步骤不够明确。

测试 2:自然语言激活 —— 改两行代码,然后说”看下能不能提交”。看 Claude 是不是主动调用 Skill 工具用了 commit-readiness。如果没有,是 description 不够明确。

测试 3:边界拒绝 —— 说”帮我创建一个新分支”。这种应该不触发 commit-readiness。如果它错触发了,description 缺反例边界。

三个测试都过了,skill 就算合格上线。

接下来你能写的几个

如果上面这套流程跑通了,下面这些是我观察到价值最高的几个 skill 方向:

  • pr-prepare —— 开 PR 前的准备:写 PR description、跑全部测试、检查 lint、扫一遍 changelog
  • dependency-review —— 升级 npm/pip 包之前,让 Claude 先看 changelog、breaking changes、migration guide
  • feature-flag-check —— 改了 feature flag 相关代码后,确认前后端、缓存、监控都对齐
  • ai-cost-check —— 给调 LLM 的代码加上成本估算和告警阈值

每一个都能用今天讲的模式直接落地。先把第一个写完跑顺,剩下的就只是抄结构。


延伸阅读: