Mingyu's Library主页
2026年8月26日

第 19 课:Skills —— 把你的工作流封装成可复用技能

2026-08-26 · 主题:skills(SKILL.md、渐进式披露、forked 执行、分发)

前置课参考:第 1 课 CLAUDE.md 项目记忆、第 3 课 Subagents、第 5 课 自定义斜杠命令、第 17 课 插件与 Marketplace。

本课与第 5 课的关系:官方已把「自定义命令」合并进 skills——.claude/commands/deploy.md 和 .claude/skills/deploy/SKILL.md 都会生成 /deploy,行为一致;老的 commands/ 文件继续能用。Skills 多出来的是:一个可以放附件的目录、控制「谁能调用」的 frontmatter、以及让 Claude 自己判断何时加载。(官方文档)


什么时候该写 skill

官方给了一个很好用的判据:

当你反复把同一段指令、清单或多步流程粘贴进对话,或者 CLAUDE.md 里某一节已经从「事实」长成了「流程」时,就该把它变成 skill。

关键差别在成本:CLAUDE.md 的内容每个会话都常驻上下文,而 skill 的正文只在被用到时才加载。所以长参考资料放 skill 里,不用的时候几乎零成本。


技巧一(入门):写出你的第一个 skill —— 带「动态上下文注入」

技能就是一个目录 + 一个 SKILL.md。目录名就是你敲的命令名。

mkdir -p ~/.claude/skills/summarize-changes

~/.claude/skills/summarize-changes/SKILL.md:

---
description: 总结未提交的改动并标出风险点。当用户问「我改了什么」、想要 commit message、或让我 review diff 时使用。
---

## 当前改动

!`git diff HEAD`

## 指令

用两三条 bullet 总结上面的改动,然后列出你注意到的风险:
缺失的错误处理、硬编码的值、需要同步更新的测试。
如果 diff 为空,直接说没有未提交的改动。

这里的关键是 !`git diff HEAD` 这一行——这是「动态上下文注入」:Claude Code 会先跑这条命令,把输出替换进正文,然后才把内容交给 Claude。也就是说 Claude 看到的是「已经带着真实 diff 的指令」,而不是「你去跑个 git diff 吧」。这一步省掉一次工具往返,也杜绝了 Claude 凭打开的文件瞎猜。

两种触发方式,都试一下:

我改了什么?            ← 靠 description 自动匹配,Claude 主动加载
/summarize-changes     ← 你手动调用

description 是自动触发的唯一依据。写「做什么 + 什么时候用」,别只写「做什么」。

📎 官方 quickstart


技巧二(进阶):四个 frontmatter 开关,决定「谁在什么时候调用」

默认所有人都能调、Claude 也能自动调。真实项目里你往往要精确控制:

---
name: deploy
description: 把应用部署到生产环境
disable-model-invocation: true    # ① 只有我能敲 /deploy,Claude 永远不会自作主张
argument-hint: "[env]"            # ② 补全时提示参数
allowed-tools: Bash(npm test *) Bash(npm run build *)   # ③ 本轮免确认的工具
paths: "infra/**, deploy/**"      # ④ 只有在动这些文件时才自动激活
---

部署流程:
1. 跑测试套件
2. 构建
3. 推到 $0 环境

四个开关分别解决四类问题:

字段 解决的问题
disable-model-invocation: true 危险操作(部署、发版、删数据)只能人工触发
user-invocable: false 反过来:这是给 Claude 看的背景知识,不该出现在 / 菜单里
allowed-tools 这一轮内免权限确认;你发下一条消息时授权自动失效
disallowed-tools 从工具池里摘掉某些工具,比如后台循环任务不该有 AskUserQuestion
paths glob 限定激活范围,避免技能在无关文件上乱触发

两个容易踩的点:

  1. description 与 when_to_use 合并后,在技能清单里截断于 1536 字符——最关键的用例必须写在最前面,写在后面等于没写。
  2. 技能一旦加载,正文会跨轮次驻留在上下文中。所以官方明确要求正文写「做什么」,不要写「为什么这样做」的长篇叙述——每一行都是重复计费的 token。用你写 CLAUDE.md 时的同一套精简标准来写它。

📎 Frontmatter 完整字段表


技巧三(进阶):渐进式披露 —— 大参考资料别塞进 SKILL.md

一个技能可以是一整个目录。SKILL.md 只做导航,细节按需加载:

~/.claude/skills/api-review/
├── SKILL.md          # 必需:概览 + 导航
├── reference.md      # 完整 API 规范,需要时才读
├── examples.md       # 示例集合,需要时才读
└── scripts/
    └── lint.sh       # 脚本:被执行,不进上下文

SKILL.md 里这样引:

## 附加资源

- 完整 API 细节见 [reference.md](reference.md)
- 用法示例见 [examples.md](examples.md)

配合 ${CLAUDE_SKILL_DIR} 让脚本免确认执行——这是官方给的一个很漂亮的配对模式:

---
name: render-chart
description: 从 CSV 渲染图表
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)
---

运行 `${CLAUDE_SKILL_DIR}/scripts/render.sh <csv-file>` 渲染图表。

${CLAUDE_SKILL_DIR} 在正文和 allowed-tools 的 Bash 规则里都会被替换成同一个绝对路径,于是权限规则精确匹配到技能正文让 Claude 跑的那条命令,脚本执行时不会弹确认框——而且不管当前工作目录在哪都成立。

常用替换变量速查:

变量 含义
$ARGUMENTS / $0 / $1 全部参数 / 第 1 个 / 第 2 个(0-based)
${CLAUDE_SKILL_DIR} 本技能 SKILL.md 所在目录
${CLAUDE_PROJECT_DIR} 项目根目录(需 v2.1.196+)
${CLAUDE_SESSION_ID} 当前会话 ID,适合写日志文件名
${CLAUDE_EFFORT} 当前 effort 等级,可用来让技能自适应

多词参数记得加引号:/my-skill "hello world" second → $0 = hello world,$1 = second。


技巧四(高阶):context: fork —— 让脏活在子上下文里干完,主会话只收结论

这是 skills 最被低估的一个字段。加上 context: fork,技能会在独立的 forked 子智能体上下文里跑,主会话不会被它读的 30 个文件污染:

---
name: audit-deps
description: 审计依赖树里的已知漏洞与过期包,只回报结论
context: fork          # 在 forked 子上下文里跑
background: true       # 默认值:后台跑,主会话继续干活(v2.1.218+ 可设 false 等结果)
agent: general-purpose # 可选:指定子智能体类型
model: sonnet          # 可选:脏活用便宜模型,只对本轮生效
effort: medium         # 可选:覆盖会话 effort
disallowed-tools: AskUserQuestion   # 后台任务不该弹问题给用户
---

1. 读 package.json 与 lockfile
2. 逐个检查已知 CVE 与主版本落后情况
3. **只输出**一份不超过 15 行的结论表:包名 / 当前版本 / 建议版本 / 风险等级

这把第 3 课(subagent 分工)和第 15 课(成本控制)串起来了:脏上下文留在子会话,结论回主会话,便宜模型干重复劳动。三件事一个 frontmatter 搞定,不用每次手写「你去开个 subagent 然后……」。

分发到团队(承接第 17 课):

放哪 生效范围
~/.claude/skills/<name>/SKILL.md 你的所有项目
.claude/skills/<name>/SKILL.md 仅本项目(提交进仓库 = 全团队生效)
<plugin>/skills/<name>/SKILL.md 插件启用处,命令名带 插件名: 前缀,不会冲突

优先级:企业级 > 个人级 > 项目级;同名时本地技能会覆盖同名 bundled skill(但不覆盖它的别名)。

两个实用细节:

⚠️ 一个已知问题:社区反馈 allowed-tools 在部分场景下只被解析、未被强制执行,不要把它当作安全边界来依赖——硬约束仍然应该用 hooks(第 4 课)和权限 deny 规则(第 9 课)。 📎 Issue #37683


一句话总结

重复三次以上的流程 → 写成 skill;长参考资料 → 拆成附件按需加载;读文件的脏活 → 加 context: fork 丢给子上下文;要全团队用 → 提交进 .claude/skills/。


参考来源