Skip to content

第 16 章 Skills 与自定义命令:把重复工作流固化为能力包

Skills 是 Claude Code 把重复工作流固化成可复用能力包的机制。一个 SKILL.md 文件,带步骤、检查点、退出标准,按需加载——这是 Process not prose,不是又一份参考文档。本章给出从写第一个 skill 到规避 namespace 陷阱的完整路径。

16.1 结论:Skills 是 Process not prose

当你第三次把同一段"修 Bug 前先复现、加回归测试、最小修复、跑 lint"的指令粘进对话时,就该把它写成 skill 了。Skill 的本质不是"存一段 prompt",而是把一条带步骤、检查点、退出标准的工作流编码成 Claude 可以一致执行的能力包。

对比三种固化方式:

text
临时 Prompt   :只存在于当前会话,下一次还得重写
CLAUDE.md     :每轮加载,适合稳定规则(技术栈/命名/红线),不适合流程
Skill         :按需加载,适合可重复流程,带 frontmatter 控制触发
Hook          :确定性执行,适合必须强制的检查(第 14 章)

一条经验法则:CLAUDE.md 里一旦某段从"事实"长成了"流程",就该迁出去做成 skill。CLAUDE.md 说"用 pnpm,测试跑 vitest"是事实;说"修 Bug 要先复现再加测试再最小修复"是流程,流程属于 skill。

Simon Willison 在 Skills 发布后评价"可能比 MCP 更重要"——这话指的不是功能强弱,而是触达面:MCP 接的是外部系统,Skills 接的是工程师每天都在重复的工作流,后者改变行为的密度更高。

反过来,什么时候不该做成 skill:一次性任务(这次迁移完就不再做)、纯事实陈述("项目用 pnpm")、需要每轮强制加载的稳定规则(放 CLAUDE.md)、必须确定性执行检查(放 Hook)。skill 的定位是"反复出现、有明确步骤、按需触发"的工作流,超出这个边界的强行做成 skill 反而增加维护负担。

16.2 Skills 是什么:发布、合并、渐进披露

16.2.1 发布背景与与自定义命令的关系

Claude Code 的 Skills 于 2025-10-16 正式发布,遵循 Agent Skills 开放标准(跨 Claude Code / Cursor / Codex / Copilot / Gemini CLI 等通用)。Claude Code 在标准之上扩展了调用控制、子代理执行、动态上下文注入三项能力。

关键变化:自定义命令已合并进 Skills.claude/commands/deploy.md.claude/skills/deploy/SKILL.md 都会产生 /deploy,行为一致。官方原话:"Custom commands have been merged into skills." 旧的 .claude/commands/ 文件继续工作,但 Skills 多了支持文件目录、frontmatter 调用控制、按需自动加载三项能力。新写建议直接用 Skills。

16.2.2 渐进披露:为什么省 token

这是 Skills 设计的核心。会话开始时,Claude 只加载每个 skill 的 name + description(且 description 合计有预算,默认约上下文窗口的 1%),用于判断何时该用;真正调用时才把完整 SKILL.md 正文注入对话。

text
会话启动  :加载 24 个 skill 的 name+description  ≈ 2K tokens
用户说"修 Bug":匹配到 /fix-bug,加载其全文        ≈ 1K tokens
其余 23 个 :正文永不进入上下文

对比把所有流程写进 CLAUDE.md:24 个 skill 每个 200 行,就是 5 万 token 起步,每轮都加载——这是为什么 CLAUDE.md 只适合放稳定规则。

auto-compaction 后 skill 的行为也要知道:压缩时 Claude Code 会重新挂载每个被调用过的 skill 的最近一次内容,每个保留前 5000 token,合计预算 25000 token,从最近调用的开始填。溢出时最老的 skill 会被整丢。所以一个会话里调了七八个 skill 后,早期的可能已经不在上下文——如果发现某个 skill"不再影响行为",重新 /skill-name 调一次即可恢复全文。

官方还给了预算细节:每个 skill 的 description + when_to_use 合计在列表中被截断到 1536 字符;预算溢出时,最少使用的 skill 的 description 先被丢掉。用 /doctor 可以看到哪些 skill 的描述被截短;用 /context 的 Skills 行可以看到列表实际占用。

16.2.3 四个存放位置与优先级

位置路径作用范围
Enterprisemanaged settings 配置组织全员
Personal~/.claude/skills/<name>/SKILL.md你的所有项目
Project.claude/skills/<name>/SKILL.md当前项目
Plugin<plugin>/skills/<name>/SKILL.md启用插件处

同名优先级:enterprise > personal > project,任一级别都可覆盖同名 bundled skill(例如在项目 .claude/skills/code-review/ 里放一个,就会替掉官方 /code-review)。Plugin skills 走 插件名:技能名 namespace,不参与覆盖链,与其他级别物理隔离。

还有两个发现规则要知道:

  • 父目录链:从工作目录向上到仓库根,每一级 .claude/skills/ 都加载。在子目录启动也能用到根目录的 skill。
  • 嵌套按需:编辑 packages/frontend/ 下文件时,packages/frontend/.claude/skills/ 的 skill 会被发现。同名时嵌套的用 apps/web:deploy 这样的目录限定名显式调用。

另外有 live change detection:编辑已存在 skill 目录下的 SKILL.md,当前会话即时生效,不用重启。但新建一个会话启动时不存在的顶层 skills 目录,需要重启 Claude Code 才能被发现。这是新手常踩的坑——建了目录却调不到,其实是会话启动时没注册监听。

16.3 SKILL.md 结构:frontmatter + 六段式正文

借鉴 Addy Osmani 的 agent-skills 包(24 个 skill,Google 工程纪律范式),一份高质量 SKILL.md 应该是六段式——不是随意罗列说明,而是带步骤、检查点、反合理化表、验证收尾的流程。六段的逻辑是:Overview 给一句话定位,When to Use 给触发条件,Process 给执行路径,Rationalizations 治跳步,Red Flags 划红线,Verification 强制证据收尾。缺 Overview,Claude 不知道何时用;缺 Process,它不知道怎么做;缺 Rationalizations,它会找借口跳步;缺 Verification,它会"看起来对"就停。六段缺一,闭环就漏。

markdown
---
description: <一句话用途+触发时机,关键词放前面>
argument-hint: <参数提示>
disable-model-invocation: true
allowed-tools: [工具列表]
disallowed-tools: [禁用工具列表]
---

## Overview
这个 skill 做什么,一句话。

## When to Use
触发场景与用户会说的自然语言。

## Process
1. 步骤一(产出:XXX)
2. 步骤二(检查点:YYY)
3. ...
退出标准:ZZZ

## Common Rationalizations
| 借口 | 反驳 |
|---|---|
| "测试太慢跳过" | 回归测试是退出标准,没通过就不算修完 |

## Red Flags
- 删除测试让其通过
- 添加 @ts-ignore
- 空 catch 吞异常

## Verification
- 目标测试 exit 0
- lint / typecheck exit 0
- diff 审查无无关变更

这个结构为什么有效:Process 给路径,Rationalizations 治跳步,Red Flags 划红线,Verification 强制证据收尾。缺任何一段,Claude 都会沿最短路径走——"看起来对"就停。

正文长度约束:SKILL.md 保持在 500 行以内,详细参考材料放到同目录的 reference.md 等支持文件,在 SKILL.md 里引用。一旦加载,正文在整个会话都留在上下文,每行都是反复消耗的 token。

16.4 frontmatter 字段详解与动态上下文注入

16.4.1 字段速查(2026-07-08 核对官方文档)

字段必需作用
name显示名,默认目录名(插件根 SKILL.md 例外,由 name 定命令名)
description推荐用途+触发时机,Claude 据此自动匹配;首 1536 字符有效
when_to_use附加触发上下文,追加到 description
argument-hint自动补全参数提示,如 [issue-number]
arguments命名位置参数,空格分隔或 YAML 列表,对应 $name
disable-model-invocationtrue 禁止 Claude 自动加载,只能 /name 手动触发
user-invocablefalse/ 菜单隐藏,只给 Claude 用
allowed-tools激活时免确认工具(不限制其他工具可用)
disallowed-tools激活时移除的工具,下条消息后恢复
model激活时切换模型,下一轮恢复
effort激活时推理档(low/medium/high/xhigh/max)
contextfork 在分叉子代理中运行
agentcontext: fork 时指定子代理类型(Explore/Plan/general-purpose/自定义)
hooks技能生命周期钩子
pathsglob 模式,只在工作匹配文件时自动激活
shell!command 用的 shell,bash(默认)或 powershell

两个调用控制字段的组合效果(官方表格):

frontmatter你能调用Claude 能调用加载时机
(默认)description 常驻,正文调用时加载
disable-model-invocation: truedescription 不进上下文,你调用才加载
user-invocable: falsedescription 常驻,正文调用时加载

经验:/deploy /commit /fix-bug 这类有副作用的,加 disable-model-invocation: true;legacy 系统背景知识这类不该当命令敲的,加 user-invocable: false

一个常见误用:把 disable-model-invocation: true 当成权限开关。它只控制"是否自动加载",手动 /name 照样能跑。要从根上禁止某个 skill 被调用,用 settings.jsonpermissions.denySkill(name) 规则,或在 skillOverrides 里设为 "off"

16.4.2 字符串替换变量

变量含义
$ARGUMENTS调用时传入的全部参数
$ARGUMENTS[N] / $N第 N 个参数(0 基)
$namearguments 声明的命名参数
${CLAUDE_SESSION_ID}当前会话 ID
${CLAUDE_EFFORT}当前推理档
${CLAUDE_SKILL_DIR}skill 的 SKILL.md 所在目录
${CLAUDE_PROJECT_DIR}项目根(v2.1.196+)

$ARGUMENTS 不在正文中出现时,Claude Code 会把参数以 ARGUMENTS: <value> 追加到正文末尾。多词参数用引号:/my-skill "hello world" second$0=hello world$1=second

16.4.3 动态上下文注入:!command`` 语法

这是 Skills 杀手锏之一。在 SKILL.md 正文里写:

当前未提交变更:
!`git diff HEAD`

Claude Code 在把 SKILL.md 发给 Claude 之前,会先执行 git diff HEAD,把输出替换掉那一行。Claude 收到的是已经带着真实 diff 的 prompt,不是命令本身。

执行顺序:

text
1. 你输入 /summarize-changes
2. Claude Code 执行 SKILL.md 里的 !`git diff HEAD`
3. 输出替换占位符,生成最终 prompt
4. Claude 看到的是"当前未提交变更: <真实 diff>"

要点:

  • 这是预处理,不是 Claude 执行的。Claude 只看到结果。
  • 内联形式 !`cmd` 只在 ! 处于行首或紧跟空白时识别;KEY=!cmd`` 这种会当字面量。
  • 多行命令用 ```! 代码块。
  • 输出以纯文本插入,不会被二次扫描——命令不能 emit 出另一个 ! 占位符。
  • 在 settings 里设 "disableSkillShellExecution": true 可全局禁用(Managed 场景有用,bundled/managed skill 不受影响)。

经典用法:PR 摘要 skill 里注入 gh pr diff、环境检查 skill 里注入 node --version、变更审查 skill 里注入 git diff HEAD

16.5 实战:写一个 /fix-bug skill

完整示例(改编自 CLAUDE_CODE_GUIDE examples/skills/fix-bug):

markdown
---
description: Reproduce and fix a code bug with a regression test, minimal changes, and a complete verification loop.
argument-hint: <failure-symptom-or-test>
disable-model-invocation: true
allowed-tools:
  - Read
  - Grep
  - Glob
  - Bash(git status)
  - Bash(git status *)
  - Bash(git diff)
  - Bash(git diff *)
  - Bash(npm test)
  - Bash(npm test *)
  - Bash(npm run lint)
  - Bash(npm run lint *)
  - Bash(npm run typecheck)
  - Bash(npm run typecheck *)
  - Bash(pytest)
  - Bash(pytest *)
disallowed-tools:
  - Bash(git push *)
  - Bash(git reset *)
  - Bash(git rebase *)
  - Bash(npm publish *)
---

Fix this bug: $ARGUMENTS

## Process
1. Read project instructions and inspect `git status` without changing user work.
2. Reproduce the failure with the smallest relevant command.
3. Trace the failing path and state the evidence-backed root cause.
4. Add or tighten a regression test that fails for the root cause.
5. Make the smallest production change that fixes the root cause.
6. Re-run the targeted test. Read new failures and continue until it passes.
7. Run the relevant lint, typecheck, and broader test commands.
8. Review the final diff for unrelated changes, weakened assertions, swallowed errors.

退出标准:目标测试 exit 0,lint/typecheck exit 0,diff 无无关变更。

## Common Rationalizations
| 借口 | 反驳 |
|---|---|
| "测试只是 flaky,跳过" | 没复现就不算修,flaky 也要先稳定复现 |
| "改动太小不用加测试" | 回归测试是退出标准,没有例外 |
| "顺手把依赖升了" | 未经授权的升级算无关变更 |

## Red Flags
- delete or weaken a test merely to make it pass
- add `@ts-ignore`, blanket lint disables, or empty catches
- upgrade dependencies unless the bug requires it and the user authorized it
- modify database schema, credentials, CI/CD, or production configuration
- stage, commit, push, publish, or deploy

## Verification
Report: changed files, commands with exit status, what the regression test proves, remaining risks.

Stop and ask only when the fix requires a product decision, destructive action, schema migration, credential change, or external side effect.

安装与调用:

bash
mkdir -p .claude/skills/fix-bug
# 把上面的内容存为 .claude/skills/fix-bug/SKILL.md

进入项目目录启动 claude,调用:

text
/fix-bug 用户切换账号后仍看到上一个账号的缓存数据

首次使用前先确认 skill 被发现:

text
/skills

预期在列表中看到 fix-bug 及其 description。如果看不到,检查目录是否是会话启动后新建的——新建的顶层 skills 目录需要重启 Claude Code 才能被监听(见 16.2.3)。已存在的目录下编辑 SKILL.md 则即时生效。

预期输出(示意):

text
Reproduced: npm test -- session-refresh fails at "should clear cache on account switch"
Root cause: RefreshToken.rotate() does not invalidate in-memory cache
Regression test added: apps/api/test/session-refresh.test.ts (fails before fix)
Fix: apps/api/src/services/session-service.ts (1 line, calls cache.invalidate)
Targeted test: PASS
Lint: PASS  Typecheck: PASS
Changed files: 2
Remaining risk: mobile client behavior not covered by this repo

验证:

text
/skills          # 列表中能看到 fix-bug
/fix-bug <已知失败测试>
git diff         # 确认只改了最小范围
git status       # 确认没有自动 commit

失败边界:

  • 没有失败测试可复现时,skill 不会盲改——它会停在"请提供可复现的失败"。
  • 不会自动 git add git commit git push——disallowed-tools 与 Process 双重拦截。
  • 不会改数据库 schema / 凭据 / CI——Red Flags 明确禁止,需人工授权。

allowed-tools 不是权限策略,它只是"激活时免确认"。要真正禁止某工具,用 disallowed-tools,并在项目 settings.jsonpermissions.deny 里加全局禁条(见 16.10)。

16.6 .claude/commands/*.md 兼容写法与 $ARGUMENTS

旧式自定义命令仍然工作,语法更简单但能力更少(没有支持文件目录):

markdown
<!-- .claude/commands/standup.md -->
---
description: 生成今日站会汇报
argument-hint: <昨日完成的 ticket>
disable-model-invocation: true
---

根据以下信息生成站会汇报:
- 昨日完成: $ARGUMENTS
- 今日计划: !`git log --since="1 day ago" --pretty=format:"%s" | head -5`
- 风险阻塞: 无

调用:

text
/standup 完成用户登录态刷新逻辑

同名时 skill 优先于 command。.claude/commands/deploy.md.claude/skills/deploy/SKILL.md 同存时,skill 赢。新写建议直接用 skills——多出的支持文件目录、调用控制、按需自动加载都是实打实的能力。

迁移建议:老的 .claude/commands/*.md 不用急着搬。先按需新建 skill,只在需要支持文件或自动加载时才把对应 command 迁成 skill。迁的时候整目录搬(.claude/commands/foo.md.claude/skills/foo/SKILL.md),避免同名的 command 与 skill 并存造成歧义。

参数传递细节:

  • $ARGUMENTS 展开为 /skill-name 后面的全部文本。
  • /fix-issue 123$ARGUMENTS = 123
  • skill 正文没写 $ARGUMENTS 时,Claude Code 把参数以 ARGUMENTS: <value> 追加到末尾,Claude 仍能看到。
  • 多 skill 叠加(v2.1.199+):/code-review /fix-issue 123 两个都加载,123 作为 $ARGUMENTS 传给每一个。

16.7 Bundled skills 速览与插件市场

16.7.1 Bundled skills(官方随包附带)

装好即有,本质是 prompt 驱动的 skill(不是固定逻辑命令)。可用 disableBundledSkills 设置全局关闭。核心几个:

命令用途
/code-review审查当前 diff,支持 --fix --comment 与 effort 档
/security-review待提交变更安全审查
/review审查 GitHub PR
/commit生成 Conventional Commit 并提交
/pr创建 PR
/debug调试辅助
/loop定时循环运行 prompt/skill
/deep-research扇出搜索 + 对抗验证 + 引用报告
/claude-apiClaude API 参考(模型/定价/参数)
/run启动并驱动项目 app(v2.1.145+)
/verify端到端验证变更(v2.1.145+)
/run-skill-generator教 /run /verify 如何启动你的项目
/simplify简化代码(质量向,不查 Bug)
/fewer-permission-prompts扫描历史生成权限白名单
/update-config配置 settings.json
/dataviz数据可视化
/init生成 CLAUDE.md(既是命令也是 skill)

/run /verify /run-skill-generator 三件套解决"改完跑测试不算数,要跑真 app"的问题:/run-skill-generator 录下从干净环境启动 app 的配方(安装命令、env、启动脚本),存成 .claude/skills/run-<name>/,之后 /run /verify 按配方走,不再重新猜。

bundled skill 与 true built-in command 的区别要说清:built-in command(如 /help /compact /plan /model)执行的是固定逻辑,直接在 CLI 里跑;bundled skill(如 /code-review /debug /loop)是 prompt 驱动的,给 Claude 一段详细指令,让它用工具编排工作。两者都通过 /名字 调用,但内部机制不同。bundled skill 可以被 project/personal 同名 skill 覆盖,built-in command 的覆盖行为官方未保证。

16.7.2 插件市场

text
/plugin                              # 打开插件管理
/plugin marketplace add anthropics/claude-plugins-official
/plugin install skill-creator@claude-plugins-official
/reload-plugins                      # 当前会话生效

skill-creator 插件可以帮你评估自定义 skill:生成 should-trigger / should-not-trigger 测试用例,跑 A/B 基准,出 HTML 报告。流程:/plugin install skill-creator@claude-plugins-officialevaluate my summarize-changes skill with skill-creator

16.8 namespace 冲突机制与裸拷陷阱

这是全书命令真实性纪律(第 06 章)在 Skills 领域的延续。

16.8.1 插件 namespace 机制

插件 skill 强制走 插件名:技能名。例如装了 addyosmani/agent-skills 插件,它带的 /plan /review /build 实际触发名是:

text
/agent-skills:plan
/agent-skills:review
/agent-skills:build
/agent-skills:test
/agent-skills:spec
/agent-skills:ship
/agent-skills:code-simplify
/agent-skills:webperf

与内置物理隔离,不覆盖、不冲突。这是插件安装的安全保证。

16.8.2 裸拷陷阱(红线)

常见错误操作:看到插件的 .claude/commands/*.md 觉得有用,直接拷到 ~/.claude/commands/:

bash
# 危险操作,别这么做
cp -R agent-skills/.claude/commands/* ~/.claude/commands/

后果:

  1. 丢 namespace 保护:原本 /agent-skills:review 变成裸 /review,与同名 bundled skill 或你已有的 skill 撞名。
  2. 覆盖行为官方未保证:对 /plan 这种 true built-in(进入 Plan Mode 的固定逻辑命令),project/personal skill 的覆盖行为官方文档未明确保证一致;对 /review /code-review 这种 bundled skill,虽然官方说 project skill 可以覆盖,但你拿到的是"插件命令文件盖掉官方 skill",行为不可预测。
  3. 盖掉已有自定义:你之前写的 /review 被静默替换,团队其他人不知道。

正确做法:

text
/plugin marketplace add addyosmani/agent-skills
/plugin install agent-skills
# 用 /agent-skills:review 调用,带 namespace

16.8.3 命令真实性再确认

你敲的真相
/plantrue built-in,进 Plan Mode,不是 agent-skills 的规划 skill
/agent-skills:plan插件 skill,带 namespace,与上面完全不同
/reviewbundled skill,审 GitHub PR
/agent-skills:review插件 skill,五轴审 diff,与上面语义不同
/code-reviewbundled skill,审当前 diff
/bug/feedback 别名,向 Anthropic 报问题,不修 Bug

命令名本身是安全接口的一部分。用 /fix-bug 不用 /bug,用 /agent-skills:plan 不裸敲 /plan

16.9 四个设计哲学(抄 agent-skills)

Addy Osmani 的 agent-skills 包(本地 /Users/mba/claude/agent-skills)提供 24 个 SKILL.md 范本,沉淀了 Google 工程文化(Hyrum's Law / Beyonce Rule / 测试金字塔 / Chesterton's Fence)。其四个设计哲学值得每份自定义 skill 抄:

哲学一:Process not prose

skill 正文是带步骤、检查点、退出标准的工作流,不是参考文档。对比:

text
# 差(prose)
审查代码时注意错误处理、硬编码值、测试覆盖。

# 好(process)
1. 读取 diff,列出变更文件
2. 对每个文件检查:错误处理 / 硬编码 / 测试覆盖(检查点)
3. 退出标准:每条发现附文件:行号 + 修复建议

参考文档放 reference.md 支持文件,SKILL.md 只放流程。判断标准:如果正文里大段是"是什么/为什么"的叙述,没有编号步骤和检查点,它就是 prose,不是 process——Claude 读完会"知道"但不"照做"。

哲学二:Anti-rationalization 表

AI Agent 会沿最短路径跳步,且擅长给自己找合理借口。每份 skill 附"常见借口 + 反驳"表治这个病:

markdown
## Common Rationalizations
| 借口 | 反驳 |
|---|---|
| "这个测试只是 flaky" | flaky 也要先稳定复现,没复现不算修 |
| "改动太小不用加测试" | 回归测试是退出标准,无例外 |
| "顺手升级依赖" | 未经授权的升级算无关变更 |
| "这个 catch 空着没事" | 空 catch 吞异常是 Red Flag |

这张表比多写三段"请认真对待测试"有效得多——它直接堵住 Claude 最常生成的借口。实测发现,Claude 在跳步前会先输出一段"虽然……但是……"的自我合理化,如果 skill 里没有对应反驳,它会顺着这段话跳过去;有了反驳表,它会自己引用反驳继续按流程走。

哲学三:Verification non-negotiable

以证据要求收尾,"看起来对"不够。skill 必须列出机器可检查的退出标准:

markdown
## Verification
- 目标测试 exit 0(贴命令)
- lint exit 0
- typecheck exit 0
- diff 审查:无无关变更、无弱化断言、无空 catch
- 报告:changed files / commands with exit status / 回归测试证明了什么 / 剩余风险

没通过验证就不算完成。这条纪律与第 06 章的可执行闭环一脉相承——"完成"必须是机器可检查的状态。Beyonce Rule(If you like it then you shoulda put a test on it)在这里落地:没测试覆盖的行为,skill 不算验证通过。

哲学四:Progressive disclosure

SKILL.md 是入口,详细参考按需加载。结构:

text
my-skill/
├── SKILL.md           # 入口,流程为主,< 500 行
├── reference.md       # 详细 API/约定,Claude 需要时才读
├── examples/
│   └── sample.md      # 期望输出格式示例
└── scripts/
    └── validate.sh    # Claude 执行的脚本

在 SKILL.md 里显式引用:

markdown
## Additional resources
- 完整 API 细节见 reference.md
- 用法示例见 examples.md

这样 SKILL.md 正文短,加载成本低,需要细节时 Claude 自己去读支持文件。判断要不要拆:如果某段参考材料超过 50 行,且不是每次调用都用到,就移到支持文件。SKILL.md 里只留一句"完整 X 见 reference.md"的引用。

16.10 失败边界

16.10.1 skill 过大违背渐进披露

SKILL.md 超过 500 行,就把详细参考拆到支持文件。加载后的正文在整个会话都留在上下文,每行都是反复消耗的 token。一个 2000 行的 skill,等于每轮对话都背着 2000 行的债。

症状:/context 里 Skills 行占用过大;auto-compaction 后 skill 被截断(官方只保留每个 skill 前 5000 token,合计 25000 token 预算,溢出从最老的开始丢)。

修复:把参考材料移到 reference.md,SKILL.md 只留 Process + Verification。

16.10.2 覆盖内置命令

别为了迎合旧教程或个人习惯,在 project .claude/skills/ 里放同名 skill 覆盖 /plan /review /commit 这类。后果:

  • 团队成员无法预测 /plan 到底是"进 Plan Mode"还是"跑规划流程"
  • 不同机器(personal 与 project 优先级)行为不一致
  • 升级 Claude Code 后覆盖行为可能变化

要用插件的同类能力,走 namespace:/agent-skills:plan。要自定义,换名字:/spec /plan-feature /review-diff

16.10.3 把敏感信息写进 skill

skill 进版本控制就等于公开。禁止:

  • 在 SKILL.md 里写 API key / token / 密码
  • allowed-tools 里授权 Bash(curl https://internal-api with secret)
  • !command`` 里执行会打印凭据的命令

凭据走环境变量(项目 .env,且 .gitignore 覆盖),skill 只引用变量名:

markdown
环境检查:
!`echo "DATABASE_URL is set: ${DATABASE_URL:+yes}"`

(正确,只输出 yes/no,不打印值)

16.10.4 把 allowed-tools 当权限策略

allowed-tools: [Read, Grep, Glob] 只是"激活时免确认",不限制其他工具。要真禁止,用 disallowed-tools: [Edit, Write],并在 settings.json 加全局 deny:

json
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(secrets/**)",
      "Bash(git push *)",
      "Bash(git reset --hard *)",
      "Bash(git rebase *)",
      "Bash(rm -rf *)",
      "Bash(npm publish *)"
    ]
  }
}

skill 描述"应该怎么做",权限规则决定"最多能做什么"。两者不能互相替代。

16.10.6 skill 触发太频繁或太稀疏

skill 该触发时不触发,或不该触发时乱触发,是常见问题。两种情况排查方向不同:

不触发:description 里缺关键词。用户说"帮我看看这次改了啥"但 skill description 只写了"Summarize uncommitted changes",没有"看看/改了/变更"这类中文触发词。修复:在 description 与 when_to_use 里补上用户会说的自然语言,把关键用例放前面(首 1536 字符有效)。另外检查 frontmatter YAML 是否格式错误——格式错时 Claude Code 会以空 metadata 加载,/name 能跑但自动匹配失效。用 claude --debug 启动能看到解析错误。

乱触发:description 太宽泛。一个 description 写"帮助理解代码"会匹配几乎所有对话。修复:加 disable-model-invocation: true 改为手动触发,或把 description 收窄到具体场景(如"仅在用户要求生成 Conventional Commit 时触发")。还可以用 paths 字段限定只在编辑特定文件时激活。

/doctor 检查 skill 描述是否被预算截断——列表里 description 被丢光的 skill 等于对 Claude 不可见。

16.10.5 验证闭环:skill 上线前的烟雾测试

在测试仓库:

bash
git clone <test-repo> /tmp/skill-smoke && cd /tmp/skill-smoke
mkdir -p .claude/skills && cp -R <your-skill> .claude/skills/
claude --permission-mode default

在会话里:

text
/skills
<触发 skill 的自然语言>
<直接 /skill-name>

验证点:

  • /skills 列表能看到 name + description
  • 自然语言能触发(或 disable-model-invocation: true 时不触发)
  • 直接调用能跑完整 Process
  • 退出标准里的命令都被执行(看 exit status)
  • 没有自动 commit / push / publish
  • 失败时读 stderr 并自我修正,不是只跑一次

验证 skill 不是"看它触发了就完事"——要测两件事:该触发时是否触发,触发后输出是否符合预期。用 skill-creator 插件跑 A/B 基准(16.7.2)是最系统的办法。


下一章:Statusline、Output Style 与主题