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是自动触发的唯一依据。写「做什么 + 什么时候用」,别只写「做什么」。
技巧二(进阶):四个 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 限定激活范围,避免技能在无关文件上乱触发 |
两个容易踩的点:
description与when_to_use合并后,在技能清单里截断于 1536 字符——最关键的用例必须写在最前面,写在后面等于没写。- 技能一旦加载,正文会跨轮次驻留在上下文中。所以官方明确要求正文写「做什么」,不要写「为什么这样做」的长篇叙述——每一行都是重复计费的 token。用你写 CLAUDE.md 时的同一套精简标准来写它。
技巧三(进阶):渐进式披露 —— 大参考资料别塞进 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(但不覆盖它的别名)。
两个实用细节:
- 热加载:改
SKILL.md不用重启,当前会话内就生效。但如果你新建了一个之前不存在的顶层 skills 目录,需要重启让它开始监听。 - Monorepo:
apps/web/.claude/skills/里的技能,会在 Claude 首次读写该子目录下的文件时自动可用,命令名带目录前缀/apps/web:deploy。
⚠️ 一个已知问题:社区反馈
allowed-tools在部分场景下只被解析、未被强制执行,不要把它当作安全边界来依赖——硬约束仍然应该用 hooks(第 4 课)和权限 deny 规则(第 9 课)。 📎 Issue #37683
一句话总结
重复三次以上的流程 → 写成 skill;长参考资料 → 拆成附件按需加载;读文件的脏活 → 加 context: fork 丢给子上下文;要全团队用 → 提交进 .claude/skills/。
主页