本教程以从零打造一个「智能提交(smart-commit)」技能为线索,带你完整走通 Claude Code 技能的开发全流程:解剖结构、目录约定、frontmatter 字段、动态上下文注入、参数替换、支持文件(渐进披露)、可执行脚本、权限预授权、触发调试,以及用 skill-creator 做量化评估与迭代。学完后你不仅能照葫芦画瓢做出这个提交技能,还能迁移到任意自定义工作流。适合已经装通 Claude Code、想把自己的重复操作沉淀成可复用技能的开发者。
${CLAUDE_PROJECT_DIR} 等新特性;/run、/verify 至少需 v2.1.145)git,并有一个可以随意改动的 git 仓库用于练习Claude Code 的技能(Skill)遵循开放的 Agent Skills 标准。它的物理形态非常朴素——一个目录,里面至少有一个 SKILL.md:
my-skill/
├── SKILL.md # 入口文件(必需):frontmatter + 指令
├── references/ # 可选:详细参考文档,按需加载
├── examples/ # 可选:示例输出,告诉模型期望格式
└── scripts/ # 可选:可执行脚本,由模型调用而非读入
SKILL.md 由两部分组成:
--- 之间):声明元数据,告诉 Claude「这是什么、什么时候用、能用哪些工具」。一个关键认知:正文不是常驻上下文。技能的 description 会常驻(让 Claude 知道有这个能力),但正文只有在被触发后才进入上下文,且整个会话期间持续存在。所以正文要写得精炼——官方建议控制在 500 行以内,详细参考资料拆到支持文件里。
| 级别 | 路径 | 生效范围 |
|---|---|---|
| 企业级 | 受管设置(managed settings) | 组织内所有用户 |
| 个人级 | ~/.claude/skills/<name>/SKILL.md |
你的所有项目 |
| 项目级 | .claude/skills/<name>/SKILL.md |
仅当前项目 |
| 插件级 | <plugin>/skills/<name>/SKILL.md |
启用该插件处 |
同名时的覆盖优先级:企业 > 个人 > 项目。项目级技能甚至能覆盖内置的 /code-review。本教程用个人级目录,这样你在任何项目里都能用 /smart-commit。
小贴士:Claude Code 会实时监听技能目录的变化——新增、编辑、删除技能在当前会话内无需重启即可生效(前提是会话启动时该 skills 目录已存在)。
动手前先想清楚三件事,避免做出一个「能触发但不好用」的技能:
git diff --staged),按 Conventional Commits 规范生成提交信息,必要时直接提交。disable-model-invocation: true,只能由你手动 /smart-commit 触发。!command`:把实时 diff 注入提示
-$ARGUMENTS参数:支持--dry-run只生成不提交
- 支持文件:把提交类型目录放进references/(渐进披露)
- 可执行脚本:scripts/validate-scope.sh校验 scope
-allowed-tools:预授权 git 命令,免反复确认
-disable-model-invocation`:控制触发主体设计表如下:
| 设计点 | 取值 | 理由 |
|---|---|---|
| 触发方式 | 仅手动 /smart-commit |
提交有副作用 |
| 权限 | 预授权 git diff/status/log/commit |
减少打断 |
| 参数 | $ARGUMENTS,支持 --dry-run |
安全演练 |
| 上下文注入 | git diff --staged、git status |
让模型看到真实改动 |
| 支持文件 | references/commit-conventions.md |
大段规范不污染主文件 |
| 脚本 | scripts/validate-scope.sh |
确定性校验 scope |
mkdir -p ~/.claude/skills/smart-commit/{references,scripts}
注意:目录名就是命令名。
.claude/skills/smart-commit/SKILL.md会生成/smart-commit。frontmatter 里的name字段只是显示名,不改变你输入的命令(插件根 SKILL.md 是唯一例外)。
创建 ~/.claude/skills/smart-commit/SKILL.md,先写一个最小骨架验证它能被识别:
---
name: smart-commit
description: Analyzes staged git changes and drafts a Conventional Commit message. Use when the user wants to commit, asks for a commit message, or says "提交".
---
# Smart Commit
骨架占位:稍后填充。
预期结果:在任意 git 项目里启动 Claude Code,输入 /sm 应当能在自动补全里看到 /smart-commit。输入「What skills are available?」应能在列表里看到它。如果看不到,多半是 YAML 写错了——可以用 claude --debug 启动查看解析错误(YAML 损坏时,技能正文仍会以空元数据加载,所以 /smart-commit 也许能跑,但自动触发会失效)。
frontmatter 所有字段都可选,但 description 强烈推荐。下面是我们这个技能的完整 frontmatter,每一行都很关键:
---
name: smart-commit
description: Analyzes staged git changes and drafts a Conventional Commit message. Use when the user wants to commit, asks for a commit message, or says "提交/commit". Refuses to commit secrets.
disable-model-invocation: true
allowed-tools: Bash(git diff *), Bash(git status *), Bash(git log *), Bash(git commit *)
argument-hint: [--dry-run]
---
字段详解:
| 字段 | 作用 | 本例取值 |
|---|---|---|
name |
技能列表中的显示名 | smart-commit |
description |
Claude 据此判断何时用;前 1536 字符会进入技能清单 | 详细描述触发场景 |
disable-model-invocation |
true 则 Claude 不会自动触发,仅手动 |
true(提交有副作用) |
allowed-tools |
技能激活期间免确认使用的工具 | 预授权四类 git 命令 |
argument-hint |
自动补全时显示的参数提示 | [--dry-run] |
description 写法要点:把最核心的使用场景放在最前面。因为技能清单里每条描述+when_to_use 合计在 1536 字符处截断,靠后的关键词可能被砍掉。先说「做什么」,再补「何时用」。
其他常用字段(本例暂未用,但开发复杂技能时必备):
user-invocable: false——只让 Claude 触发、不暴露给 / 菜单(适合「背景知识」型技能)disallowed-tools——技能激活期间移除某些工具(注意:你在下一条消息发送后该限制即清除)context: fork + agent: Explore——在隔离子代理里跑,主对话上下文不会被污染model / effort——技能激活期间临时切换模型或推理力度paths: ["src/**/*.ts"]——仅当你在匹配的文件上工作时才自动加载这是技能的核心。关键是让 Claude 看到真实的暂存改动,而不是靠猜。Claude Code 的 !command`` 语法会在把正文送给 Claude 之前先执行命令,用输出替换占位符:
---
name: smart-commit
description: Analyzes staged git changes and drafts a Conventional Commit message. Use when the user wants to commit, asks for a commit message, or says "提交/commit". Refuses to commit secrets.
disable-model-invocation: true
allowed-tools: Bash(git diff *), Bash(git status *), Bash(git log *), Bash(git commit *)
argument-hint: [--dry-run]
---
# Smart Commit
为当前已暂存的改动生成 Conventional Commit 信息。
## 已暂存的改动(实时注入)
!`git diff --staged`
!`git status --short`
## 最近 5 条提交(对齐项目既有风格)
!`git log -5 --pretty=format:%s`
## 指令
1. 阅读上方的暂存 diff。若 diff 为空,告知用户「没有已暂存的改动」并停止。
2. 依据 references/commit-conventions.md 判定提交类型(feat/fix/docs/refactor/test/chore/perf/style/build/ci)。
3. 从改动路径推断一个简洁的 scope(如 `auth`、`api`)。
4. 写标题行:`<type>(<scope>): <subject>`,subject ≤ 50 字符、祈使语气、首词小写、句尾不加句号。
5. 改动非平凡时,补 body 说明「为什么」改。
6. 若 `$ARGUMENTS` 含 `--dry-run`:只打印拟用的提交信息,**不要提交**,到此结束。
7. 否则执行:`git commit -m "<title>" -m "<body>"`。
## 安全护栏
- 若 diff 中出现疑似密钥/令牌(如 AKIA、-----BEGIN、长 hex/token 串),**拒绝提交并警告用户**。
- subject 必须小写首词、无句尾句号。
- scope 用 scripts/validate-scope.sh 校验;若脚本判定不合法,则不带 scope。
几个易错点:
! 必须在行首或空白之后。像 KEY=!cmd`` 这种紧贴字符的形式不会被识别,会原样留下。```! 围栏代码块,而不是行内形式。ultrathink:在正文任意位置写这个词,会请求更深度的推理——适合本例这种需要判断的场景。把 Conventional Commits 的完整类型目录放进 references/commit-conventions.md,主文件只放一句指引。这样大段规范平时不进上下文,需要时才被加载:
~/.claude/skills/smart-commit/references/commit-conventions.md:
# Conventional Commits 类型目录
| type | 含义 | 示例 |
|:--|:--|:--|
| feat | 新功能 | feat(auth): 支持扫码登录 |
| fix | Bug 修复 | fix(api): 修复分页越界 |
| docs | 文档 | docs(readme): 更新安装步骤 |
| refactor | 重构(不改行为) | refactor(core): 拆分调度器 |
| perf | 性能优化 | perf(db): 给查询加索引 |
| test | 测试 | test(auth): 补登录边界用例 |
| build | 构建系统/依赖 | build(npm): 升级到 vite 5 |
| ci | CI 配置 | ci(github): 增加缓存步骤 |
| chore | 杂项 | chore: 整理示例数据 |
| style | 格式 | style: 统一缩进 |
## Breaking Change
在 footer 写 `BREAKING CHANGE: <说明>`,或在 type 后加 `!`,如 `feat(api)!: 重命名 /users 为 /accounts`。
在 SKILL.md 里引用它(前面正文已经引用了 references/commit-conventions.md),Claude 才知道这个文件的存在和加载时机。这是把「几百行规范」挡在常驻上下文之外的标准手法——和把 CLAUDE.md 里膨胀成操作流程的内容拆成技能是同一种思路。
让模型判断 scope 容易飘忽,不如写个脚本兜底。脚本是被执行的,不会读进上下文。
~/.claude/skills/smart-commit/scripts/validate-scope.sh:
#!/usr/bin/env bash
# 用法: validate-scope.sh <scope> <allowed-scopes-file>
# 退出码 0=合法,1=非法
set -euo pipefail
scope="${1:-}"
allow_file="${2:-}"
# 没给 scope 视为合法(允许无 scope 提交)
[ -z "$scope" ] && exit 0
# 没有白名单文件视为合法
[ -z "$allow_file" ] && exit 0
[ -f "$allow_file" ] || exit 0
if grep -Fxq -- "$scope" "$allow_file"; then
exit 0
else
echo "scope '$scope' 不在允许列表中" >&2
exit 1
fi
赋予执行权限:
chmod +x ~/.claude/skills/smart-commit/scripts/validate-scope.sh
在 SKILL.md 里用 ${CLAUDE_SKILL_DIR} 引用脚本路径——这个变量无论技能装在个人级、项目级还是插件级都能正确解析(需 v2.1.196+ 的 ${CLAUDE_PROJECT_DIR} 适用于引用项目内文件):
## scope 校验
运行 `bash ${CLAUDE_SKILL_DIR}/scripts/validate-scope.sh "<scope>" ./allowed-scopes.txt`,
退出码 0 才采用该 scope,否则不带 scope。
可选:在项目根放一个 allowed-scopes.txt(每行一个 scope),技能就会校验 scope 是否合规——这是把团队约定变成机器可执行规则的典型做法。
--dry-run 的分支前面 frontmatter 已经声明 argument-hint: [--dry-run],正文用 $ARGUMENTS 接收全部参数。你也可以用 $0、$1 取按位置的参数,或在 frontmatter 声明 arguments: [scope] 后用 $scope。
完整变量速查:
| 变量 | 含义 |
|---|---|
$ARGUMENTS |
调用时传入的全部参数 |
$N / $ARGUMENTS[N] |
第 N 个参数(0 基) |
${CLAUDE_SKILL_DIR} |
技能 SKILL.md 所在目录 |
${CLAUDE_PROJECT_DIR} |
项目根目录 |
${CLAUDE_SESSION_ID} |
当前会话 ID(可用于日志) |
${CLAUDE_EFFORT} |
当前推理力度 low/medium/high/xhigh/max |
带空格的参数要加引号:/smart-commit --dry-run 中 $ARGUMENTS 展开为 --dry-run。
进入一个练习仓库,做几处改动并暂存:
git add -A
/smart-commit --dry-run
预期:Claude 读到注入的 diff,按规范生成提交信息并打印,不提交。确认无误后:
/smart-commit
预期:生成信息并执行 git commit。由于 allowed-tools 预授权了 git commit,过程中不会再弹权限确认。
因为设了 disable-model-invocation: true,它的描述不进入上下文,Claude 不会主动用它。你可以这样确认:直接说「帮我把改动提交」,Claude 通常会走自己的流程而不会触发本技能——这正是我们想要的(提交必须由你显式发起)。
/sm 补全里没有它:检查目录是否在 ~/.claude/skills/ 下、目录名是否就是 smart-commit。claude --debug 启动,能看到 frontmatter 解析错误和注入命令的实际输出。! 在行首或空白后;确认没有在 settings 里设 disableSkillShellExecution: true。references/commit-conventions.md 没被加载——检查正文里是否引用了它。「能触发」不等于「好用」。要验证,得做基线对比:收集几条真实提示,分别在「启用技能」和「禁用技能」的新会话里跑,比较结果。
skill-creator 插件把这个循环自动化了。安装:
/plugin install skill-creator@claude-plugins-official
若提示找不到插件,先刷新市场:
/plugin marketplace update claude-plugins-official
/reload-plugins
然后让 Claude 自己评估你的技能:
evaluate my smart-commit skill with skill-creator
它会:
evals/evals.jsongrading.jsonbenchmark.json,对比「有技能 vs 无技能」实战建议:给 smart-commit 准备这几类用例——纯功能新增(期望 feat)、纯 bug 修复(期望 fix)、含疑似密钥(期望拒绝)、--dry-run(期望不提交)。跑一轮 benchmark,看命中率有没有因为加了 references 文件而提升。
| 场景 | 做法 |
|---|---|
| 团队共享 | 把 .claude/skills/ 提交进版本库 |
| 跨项目复用 | 放 ~/.claude/skills/,或打包成插件 |
| 组织级下发 | 通过 managed settings 分发 |
打包成插件还能顺带带上 agents、hooks、MCP 服务器。如果担心别人项目里的技能给自己开了过宽的工具权限——记住:项目级技能的 allowed-tools 在你接受 workspace 信任对话框后才生效,检视陌生仓库的技能再决定是否信任。
到这一步,你的技能目录长这样:
~/.claude/skills/smart-commit/
├── SKILL.md # 入口:frontmatter + 指令 + 注入
├── references/
│ └── commit-conventions.md # 类型目录(渐进披露)
└── scripts/
└── validate-scope.sh # scope 校验脚本
完整 SKILL.md(把前面碎片拼起来):
---
name: smart-commit
description: Analyzes staged git changes and drafts a Conventional Commit message. Use when the user wants to commit, asks for a commit message, or says "提交/commit". Refuses to commit secrets.
disable-model-invocation: true
allowed-tools: Bash(git diff *), Bash(git status *), Bash(git log *), Bash(git commit *)
argument-hint: [--dry-run]
---
# Smart Commit
为已暂存改动生成 Conventional Commit 信息。
## 已暂存的改动(实时注入)
!`git diff --staged`
!`git status --short`
## 最近提交风格
!`git log -5 --pretty=format:%s`
## 指令
1. 读上方 diff。空则回复「没有已暂存的改动」并停止。
2. 依 references/commit-conventions.md 定 type。
3. 从路径推 scope,用 `bash ${CLAUDE_SKILL_DIR}/scripts/validate-scope.sh "<scope>" ./allowed-scopes.txt` 校验。
4. 标题:`<type>(<scope>): <subject>`,subject ≤ 50 字符、祈使、小写首词、无句尾句号。
5. 非平凡改动补 body 说「为什么」。
6. 若 `$ARGUMENTS` 含 `--dry-run`:只打印,不提交。
7. 否则 `git commit -m "<title>" -m "<body>"`。
## 安全护栏
- 疑似密钥/令牌(AKIA、-----BEGIN、长 token 串)→ 拒绝提交并警告。
- scope 校验不过则不带 scope。
试用流程:
cd ~/some-practice-repo
echo "// new feature" >> app.js
git add app.js
claude
在会话里:
/smart-commit --dry-run # 先预览
/smart-commit # 确认后真提交
git log -1 # 验证结果
下图展示从开发到触发的完整链路:
flowchart TD
A[识别重复工作流] --> B[规划: 触发主体/权限/参数]
B --> C[创建 ~/.claude/skills/smart-commit/]
C --> D[写 SKILL.md frontmatter]
D --> E[写正文 + 动态注入 !`git diff`]
E --> F[拆 references/ 支持文件]
F --> G[加 scripts/ 可执行脚本]
G --> H[手动 /smart-commit 测试]
H --> I{行为正确?}
I -- 否 --> J[claude --debug 排查]
J --> E
I -- 是 --> K[skill-creator 跑 evals]
K --> L{基线达标?}
L -- 否 --> M[调 description/正文]
M --> K
L -- 是 --> N[提交到仓库 / 打包插件分发]
技能内容的生命周期(理解为什么正文要精炼):
flowchart LR
S1[description 常驻上下文] --> S2{被触发?}
S2 -- 用户 /smart-commit 或 Claude 自动 --> S3[注入 !`cmd` 输出]
S3 --> S4[整段正文进入上下文]
S4 --> S5[整个会话持续存在]
S5 --> S6{上下文填满?}
S6 -- 压缩 --> S7[重挂最近一次调用<br/>前 5000 token, 共享 25000 预算]
S6 -- 否 --> S5
Q:/smart-commit 不出现在补全里?
检查目录路径(~/.claude/skills/smart-commit/SKILL.md)、目录名拼写。若会话启动时该 skills 目录不存在,新建后需重启 Claude Code 才能监听。
Q:Claude 该自动用时没触发?
本技能故意设了 disable-model-invocation: true,所以这是预期行为。若你做的是自动触发型技能却不触发:把用户会说的关键词写进 description 头部;确认 description 没被 1536 字符截断(用 /doctor 看);用 /context 的 Skills 行看实际进上下文的大小。
Q:技能触发太频繁?
让 description 更具体,或干脆加 disable-model-invocation: true 改为纯手动。
Q:项目里技能很多,描述被截断?
技能清单预算是上下文窗口的 1%。可在 settings 里调 skillListingBudgetFraction(如 0.02)或环境变量 SLASH_COMMAND_TOOL_CHAR_BUDGET;低优先级的技能在 skillOverrides 里设为 "name-only" 省预算。
Q:担心技能里的脚本被滥用?
用户/项目/插件级技能的 !cmd` 注入可用 settings 的disableSkillShellExecution: true` 关闭(托管设置里设可防用户绕过)。内置和受管技能不受影响。
Q:想在隔离环境里跑,别污染主对话?
frontmatter 加 context: fork(配 agent: Explore 走只读探索)。注意:fork 模式下子代理看不到你的对话历史,适合「有明确任务」的技能,不适合「风格约定」类参考型技能。
paths 字段让它只在 *.py 文件上自动触发并切换为 feat 优先;挑战题——用 context: fork 把评估提交质量的逻辑拆成一个只读子代理,主技能只负责写与提交。[!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 等场景全覆盖。
