我过去一直被一个问题困扰:明明每天都在用 Claude Code 写代码、改配置、整理项目,但每次让它做点稍微专业的事,它就开始“自由发挥”——写出来的代码风格不像我的,处理仓库的流程跟我想的完全不一样,甚至在某些固定场景下反复犯同样的错。
后来我才想明白,问题不在 Claude 本身,而是我从来没有正式教过它。Claude Code 的所有能力,都建立在它对你的项目、你的偏好、你的流程的“了解”上面。而这种了解,不是靠你在对话里多说几句“记得用 pnpm”“记得先跑测试”就能沉淀下来的。你需要一种更系统、更结构化的方式,把某类任务的完整处理流程封装成一个 Claude 能反复调用的技能模块——这就是Claude Code Skills干的事。
这篇文章,我会从零开始,完整拆解如何从“手写一堆自然语言指令”进化到“把技能工具化”,覆盖第一个技能怎么建、SKILL.md 怎么写才能被 Claude 有效解析、怎么调试、怎么配合 hooks 和指令系统做自动化,以及怎么维护一个越来越庞大的技能库。全程基于我实跑过的项目经验,不是抄文档,是踩过坑之后留下来的东西。适合刚接触 Claude Code 不久、想系统管理 AI 行为模式的人,也适合已经在用但觉得“哪里不对劲”的人。
1. 进场之前:先搞清楚 Claude Code Skills 到底是哪个层面的东西
很多人第一次接触 Skills,天然会把它想复杂——以为是一种插件框架,或者某种特殊脚本格式。其实不是。Claude Code Skills 非常简单,简单到会让你觉得“就这?”,但它恰恰是在“Claude Code 的工作机制”这个层面上补上了一块关键拼图。
1.1 定位:介于“系统指令”和“临时对话”之间的第三个层级
先捋清楚 Claude Code 现在怎么控制 AI 行为,从上到下大概是这么几层:
- 第一层,系统提示词:这是 Claude 模型自带的、不可改的底层行为准则。
- 第二层,用户级/项目级指令:也就是
CLAUDE.md、~/.claude/CLAUDE.md这类文件,相当于给 AI 写“员工手册”,告诉它你这个项目的规矩、偏好、技术栈、架构约定。 - 第三层,对话上下文:你每次在交互式输入框里打进的话,包括
/clear之后保留的所有临时指令。 - 第四层,Skills(技能):它不是一个独立的叠加层,而是像“外部工具箱”一样,让 Claude 在某些特定时刻主动去查阅一套专门的操作手册。
这个定位很重要。Skills 不像CLAUDE.md那样每次对话都全局加载(会吃大量上下文),也不是对话上下文那样一次性、临时性的存在。它是按需加载的:Claude 收到用户请求后,会先判断“这个任务跟哪个技能相关”,如果命中了,就去技能目录里读取对应的SKILL.md和资源文件,再结合当前对话上下文执行。
我有个很直观的比喻:CLAUDE.md是公司规章制度,贴在墙上,所有人每时每刻都得看;对话上下文是老板当下的一句口头交代;而 Skill 是岗位SOP——平时收在柜子里,等你接单干这个活的时候,再把对应 SOP 抽出来照着走。三者配合,AI 的行为才既有全局一致性,又有专项深度。
1.2 技能命中机制:Claude 是怎么决定“该用哪个技能”的
搞清楚 Skills 的运作逻辑,才不会在使用时犯方向性错误。Claude Code 在处理每次请求时,大致会经历下面这个判断链:
- 解析用户请求的意图,提取任务类型关键词(例如“发布新版本”“生成 API 文档”“写单元测试”“跑性能分析”)。
- 检索当前项目技能仓库中所有技能的名称和描述(技能仓库就是
.claude/skills/目录)。 - 根据技能描述与任务意图的匹配度,决定是否加载这个技能,以及加载哪些技能。
- 加载后,Claude 会阅读
SKILL.md里的完整指令,把其中规定的工作流作为本次执行该任务的纲领。 - 过程中遇到需要脚本辅助的步骤,Claude 会调用技能目录下的脚本文件来获取数据或执行操作。
这里面最关键的设计是:技能名称和描述写得越好,命中率越高。如果你把一个技能命名为skill-docs-generate.md,描述写“用于生成文档”,Claude 在八九成情况下都能在合适的场景想到它;但如果你把描述写得过于笼统,比如“处理各种任务”,那基本等于没写,Claude 很难在关键时刻想起来调用它。
我在实际使用中最舒服的状态是:让技能描述里带上这个技能最适合处理的任务动作词 + 输出结果类型。比如“根据现有代码结构和 JSDoc 注释,生成并更新 TypeScript 项目的 API 参考文档,输出 Markdown 格式”,这么长一串肉眼看觉得啰嗦,但对 Claude 来说这是非常强的触发信号。
1.3 Skills、Commands、Hooks 和 Agents 之间的关系
如果你用过一阵子 Claude Code,一定听说过/commands(斜杠命令)、hooks(钩子脚本)、还有 Agents(子代理)。很多人会混淆这些概念,我顺手把它们放一张表说清楚,后面所有内容都以这张表为基础:
| 机制 | 本质 | 触发时机 | 典型用途 |
|---|---|---|---|
| Skills | 按需加载的操作手册 + 辅助资源 | 任务意图匹配时 | 教 AI 执行某种完整任务流程(发版、文档、测试、审查) |
| Commands | 自定义斜杠指令 | 用户主动输入/xxx | 快速给 AI 指定一种任务模式(比如/review开始代码审查) |
| Hooks | 事件触发的脚本钩子 | 特定生命周期事件发生时 | 自动执行校验、格式化、防呆操作(提交前跑测试) |
| Agents | 专门的子代理角色 | 主任务需要子任务委派时 | 让 AI 分角色协作(比如架构师、代码评审员、测试员) |
| CLAUDE.md | 全局行为规范 | 每次对话加载 | 声明项目偏好、技术栈、禁止事项 |
这张表我建议反复看。实际项目里,它们经常是协同工作的:Skill 定义了“怎么发一个版本”的完整流程,Command 提供“我现在要发版”的快捷入口,Hooks 在发版前自动跑测试并拦截失败,Agent 则负责在发版流程中把变更日志整理环节单独交给一个“日志撰写代理”处理。
理解了这张表,再看接下来所有实战内容,你会很清楚每一环插在哪里。
2. 从手写指令到第一个技能文件:一次完整的演进实验
理论聊够了,直接上手。这一节我会用我最近在某个模拟项目中实际做过的“自动生成变更日志(CHANGELOG)”功能作为案例,完整展示一个技能从“对话里手写”到“落成技能文件”的全过程。
2.1 手写阶段:你每天都在用的临时指令
最早我没用 Skills 的时候,每次要求 Claude 生成变更日志,我在对话里都会输入一大段指令,内容大致是:
请根据 git 仓库最近的提交记录,整理出一份 CHANGELOG.md。 要求: 1. 使用 Keep a Changelog 格式; 2. 按照 Added / Changed / Fixed / Removed 分类; 3. 只统计最近一个版本(从上一个 tag 到现在的提交); 4. 跳过 chore、docs 类型的提交; 5. 每个条目写明影响模块,并给出一句话描述。这段指令本身不复杂,但它有几个致命问题:
- 每次都要打一长串,费时间。
- 不同人(或者同一个人的不同心情下)写的指令可能略有差异,比如这次忘了说“跳过 docs”,下次就漏了。
- Claude 每次都是“即读即执行”,缺少稳定沉淀。今天你对它说了要按 Keep a Changelog 格式,明天新开一个对话它又忘了。
- 如果这个流程有 8 个步骤,对话里塞满指令,会严重挤占上下文窗口,留给真正工作内容的 token 就少了。
你可能觉得“写个变更日志这么简单的事,没必要弄个技能吧?”对,这个例子我只是拿来说明演进路径,它足够简单,容易看清楚机制。实际我后来用技能跑通的流程比这复杂得多——比如“发布 npm 包”这个技能包含 10 多个步骤、3 个脚本调用。
2.2 第一次封装:把指令抄进 SKILL.md
手写了一段时间后,我决定把这套指令固化成技能。在项目根目录下创建.claude/skills/update-changelog/SKILL.md,内容如下:
--- name: update-changelog description: 根据 git 提交记录自动生成或更新 CHANGELOG.md,使用 Keep a Changelog 格式,按 Added/Changed/Fixed/Removed 分类,仅统计最近一个版本(从上一个 tag 到 HEAD)的提交,跳过 chore 和 docs 类型。 --- # 更新变更日志 从 git 历史中提取提交信息,生成标准格式的 CHANGELOG.md。 ## 执行步骤 1. 运行 `git describe --tags --abbrev=0` 获取上一个版本 tag。 2. 运行 `git log <上一步的tag>..HEAD --oneline` 获取提交列表。 3. 过滤提交信息,移除以 `chore:`、`docs:` 开头的行。 4. 将剩余提交按以下规则分类: - `feat:` 或 `feature:` -> Added - `fix:` -> Fixed - `refactor:` 或 `perf:` -> Changed - `revert:` -> Removed - 无法分类的合并到 Changed 5. 按 Keep a Changelog 格式写入 CHANGELOG.md,版本号取当前 package.json 中的 version。 6. 如果在文件头部已有相同版本号的条目,则更新该条目而不是重复新增。 ## 注意事项 - 保持现有 CHANGELOG.md 的排版风格,不要擅自改变标题层级。 - 如果仓库还没有任何 tag,则把整个提交历史视为当前版本。 - 生成后不要运行 git commit,只负责文件内容更新。创建完这个文件,我就把对话里的那段长指令彻底删了。之后再让 Claude 更新变更日志,它自己就会在技能库里检索到匹配项,加载这个 SKILL.md,然后照着里面的步骤走。
第一次演进完成。你可能会发现,这本质上就是“把提示词存成文件”——对,这就是 Skills 的雏形。但千万别小看这一步,光是“提示词被固化”这一个行为,带来的改变就非常可观:一致性好、上下文省了、可维护了、新成员接手项目也能继承这套标准。
2.3 第一次优化:补上“判断逻辑”和“异常分支”
跑了几次之后,我发现第一个版本的 SKILL.md 有个明显的短板:它只有“主路径”,没有“分支路径”。比如当用户要求的是“更新整个项目的全部变更日志”而不是“最近一个版本”,或者仓库的提交信息风格不统一(有的带feat(module):有的不带括号),Claude 就容易处理得很僵硬。
于是我在 SKILL.md 里增加了一个“前置判断”部分:
## 前置判断 在开始之前,先向用户确认以下信息(如果对话上下文中已明确则跳过): - 本次要更新的是全部历史还是仅最近一个版本?默认只更新最近一个版本。 - 提交信息中如果包含 `(module)` 形式的作用域标记,是否需要保留模块名称?默认保留。 - 分类规则与用户预期不一致时,以用户口头要求为准。加上这段之后,Claude 在遇到不完全匹配的情况时不会闷头乱做,而是会先问清楚再动手。这个改动极其重要——技能文件不是僵死的规则,而是带有判断逻辑的执行框架。
这一步做完了,一个最基础的“从手写到工具化”的演进就算闭环了。但如果你只想做到这个程度,那 Skills 的威力你连一半都没发挥出来。下面两节才是真正拉开差距的地方。
3. 复杂技能的工具化:脚本、模板、外部资源一个都不能少
当技能要处理的任务开始变复杂,SKILL.md 里写满自然语言指令就会显得力不从心。这时候需要引入第二个层面的东西:把技能目录变成一个“迷你工具箱”。
3.1 技能目录的完整形态:SKILL.md + scripts + templates + assets
一个成熟的技能应该是这样的目录结构:
.claude/skills/publish-release/ ├── SKILL.md # 核心指令文件 ├── scripts/ │ ├── get-release-diff.sh # 获取发布差异文档 │ ├── check-tests.sh # 检查测试状态 │ └── bump-version.js # 计算并更新版本号 ├── templates/ │ └── CHANGELOG-template.md # 变更日志标准模板 └── assets/ └── release-checklist.md # 发布检查清单我明确说一下每个部分的作用:
- SKILL.md:是整个技能的大脑。它负责描述“什么时候用这个技能”“执行这个技能的整体流程是什么”“在某个节点调用哪个脚本”以及“有哪些潜在陷阱”。
- scripts/:存放可执行的脚本,用来完成那些 Claude 靠“读文件和写文字”解决不了的事,比如解析 git 数据结构、执行版本号计算、调用外部 API。
- templates/:存放输出模板。Claude 可以直接基于模板填充内容,保证输出格式高度可控。
- assets/:放一些辅助文档,比如检查清单、示例文件,供 Claude 在执行过程中查阅参考。
为什么需要这些东西?因为大语言模型本质上是个“文字推理引擎”,它不是操作系统,不能直接可靠地执行各种计算和读取操作。你当然可以靠让它敲命令去拿结果,但多了之后你就发现:
- 每敲一次命令都消耗 token;
- 命令输出的内容都是原始文本,塞进上下文又是一大坨;
- 不同环境的输出格式有差异,Claude 的解析偶尔会出错。
把关键逻辑封装成脚本后,Claude 只需要“运行脚本 -> 读取精简后的输出”,输出的信息可以高度定制——只保留 AI 真正需要的那几行数据。这相当于给了 AI 一个“专用传感器”,而不是让它自己去看仪表盘上所有乱七八糟的表针。
3.2 实操示例:升级“发新版本”技能
用“publish-release”这个技能举个例子。假设我的项目用 npm 发包,流程是:检查测试 -> 计算新版本号 -> 更新 CHANGELOG -> 更新 package.json -> 提交 tag -> push。这个流程如果在 SKILL.md 里全部用自然语言写,大概是这样的:
## 执行步骤 1. 先运行测试命令 pnpm test,确认所有测试通过。 2. 根据 commit 信息判断版本号是 major、minor 还是 patch。 3. 用 npm version 更新 package.json。 4. 生成或更新 CHANGELOG.md。 5. git commit 并打 tag。看起来也还行,但实操中你会发现第 2 步的判断标准模糊,第 4 步的格式一致性不稳定。我把它工具化之后长这样:
## 执行步骤 1. 运行 `scripts/check-tests.sh`,如果退出码非 0,立即停止并向用户报告测试失败。 2. 运行 `scripts/bump-version.js dry-run`,获取建议的版本号更新类型。脚本会根据 git 提交信息中的 feat/fix/breaking 关键字自动判断 major/minor/patch。 3. 将计算结果展示给用户确认,确认后运行 `scripts/bump-version.js execute`。 4. 运行 `scripts/render-changelog.js`,基于 templates/CHANGELOG-template.md 生成最新变更日志。 5. 运行 `scripts/finish-release.sh` 提交所有变更、打 tag、推送到远端。这里的好处一眼就能看出来:判断交给脚本,AI 只做确认和编排。对“版本号该升几位”这种需要精确逻辑的事,脚本永远不会判断错;AI 的判断压力小了,行为也就更可预测了。
脚本我一般用什么语言写?没有绝对标准,基本原则是“哪种语言在你的环境里运行成本最低就用哪种”。在 Node 项目里我用 JavaScript,在 Python 项目里我用 Python,需要 shell 操作时直接用 Bash。Claude Code 对脚本语言没有限制,它只要能运行node xxx.js或python xxx.py就行。
3.3 数据文件:让技能跨越“单次对话”的临时性
如果你某个技能需要持久保存一些状态数据,比如“上一轮发布到哪个版本了”“哪些模块已经做过技术债评估”,你可以让脚本把这些数据写进技能目录下的state.json文件。Claude 在执行技能时如果发现state.json存在,会读取它作为起始状态;执行完后再让脚本更新它。
这相当于给技能装了“记忆”。我举一个真实的例子:
我在某个模拟项目里维护了一个“代码健康巡检”技能。每次巡检完,脚本会把检查出的问题按严重程度写入state.json。下次再巡检时,Claude 会先去读这个文件,然后说:“根据上次的记录,这里有三个未关闭的中等级别问题,我们先从它们开始核对。”——这体验根本不是临时对话能达到的。
4. 写 SKILL.md 的硬核方法论:让 Claude 每一次都懂你
技能文件是给 AI 读的,不是给人读的。很多开发者习惯了写给人看的文档,习惯性地把 SKILL.md 写成了维基百科风格——大段背景介绍、历史沿革、设计哲学。这完全走错了方向。我给 SKILL.md 定了几条铁律,每条都是从失败案例里提炼的。
4.1 铁律一:YAML frontmatter 是技能的生命线
SKILL.md 开头的 YAML 部分不是装饰,它直接决定了技能能不能被准确命中。我踩过一次大坑:把 description 写得太简略,结果 Claude 在该用技能的时候完全没触发,而是直接“临场发挥”处理任务。
正确写法应该是:
--- name: update-changelog description: 当用户需要根据 git 提交记录生成、更新或整理 CHANGELOG.md(变更日志/版本发布记录)时,使用此技能。适用于发版前整理变更、查看某个版本改了什么、重新生成规范格式的变更日志等场景。输出 Keep a Changelog 格式的 Markdown 文件。 ---你仔细对比一下这条 description 和我最早那条的区别:早期写的是“用于生成文档”,现在写的是“当用户需要……时,使用此技能。适用于……场景。输出……”——用“行为触发条件 + 适用场景 + 输出物”三段式结构来描述技能。实际测试下来,这种描述被命中的概率远高于抽象描述。
注意一个细节:Claude Code 在匹配技能时,通常不只是匹配 name,还会把 description 里的关键词参与语义匹配。所以描述里自然地嵌入“变更日志”“版本发布记录”“CHANGELOG”这类场景词,命中率会大幅提升。
4.2 铁律二:步骤要像“给外包开发派活”,不要像“写散文”
SKILL.md 的主体是执行步骤。这是 Claude 在任务过程中逐条遵循的“操作纲领”。写的时候要时刻提醒自己:读这份文件的 AI 虽然聪明,但它对你的项目和团队一无所知。所以你写的每一步,都要让一个“有常识但无背景”的执行者能看懂。
我总结的效果比较好的步骤结构是:
- 动作先行:以动词开头(获取、运行、解析、写入、校验)。
- 给出精确参数:说清命令、路径、文件名,不要只说“找到上一个版本标签”。
- 明确完成标准:这一步做到什么程度算完成。
- 指明下一步依赖:这一步产生的产物会如何被下一步使用。
拿一个发布技能举例,不推荐这么写:
先看下最近改了什么东西,然后根据这些内容判断版本号,别忘了更新文档,最后推上去。推荐这么写:
1. 运行 `node scripts/get-diff-summary.js`,它会输出一个 JSON:{ commits: [...], types: { feat: 3, fix: 2 } }。 2. 读取 JSON 中的 types.feat 和 types.fix 数量。如果 feat 数量大于 0 且包含 breaking change(提交信息带 `BREAKING CHANGE:` 前缀),建议 minor 或 major 升级;否则 patch。 3. 将建议版本号写入 `state.json`,然后进入版本确认环节,向用户展示拟更新的版本号和依据。区别很明显:后者每一步都“可执行”,Claude 不需要自己脑补任何环节。
4.3 铁律三:把“你不想让 AI 做的事”写出来
只写“该做什么”不够,“不该做什么”同等重要。因为大模型在执行中有时会“过度发挥”——做的比你要的多,或者自作主张做了你没授权的操作。
我在所有 SKILL.md 里都会有单独的“禁止事项”或“边界”小节。比如:
## 禁止事项(边界) - 禁止在未获取用户明确确认的情况下执行 git push 操作。 - 禁止修改 package.json 中的 dependencies 字段,除非任务明确要求。 - 禁止删除任何历史 CHANGELOG 条目。 - 禁止将文档自动转换成公司外部链接或引用未经确认的外部资源。 - 生成的文件必须经过用户 review 后才能作为最终产物。这些边界条款在关键时刻能帮你避免灾难。我见过有人技能忘记写“禁止自动安装依赖”,结果 Claude 在跑某个脚本时发现缺依赖,直接执行了npm install,装了十几个包进去,虽然没搞坏东西,但足够让人血压升高。
4.4 铁律四:技能文件要渐进式加载,别做成 300 行的巨无霸
一个常见误区是“把能塞的都塞进一个 SKILL.md”,最后搞出几百行。这会带来两个问题:
- 语义匹配阶段就会因为内容杂糅、核心信息不突出而降低命中率。
- Claude 加载技能后,几百行内容会挤占大量上下文窗口。
正确的拆分姿势是“主文件管流程,子文件管细节”。SKILL.md 只保留最核心的步骤和判断逻辑,需要细查的内容放到assets/下的文档里,并在步骤中明确告知 Claude“详细规则见 assets/xxx.md”。Claude 在大多数情况下只需要主文件就够了,真到了需要细节的时候再去引用子文件,这样上下文消耗最小。
我自己给 SKILL.md 定的参考行数是80 到 150 行。超过 200 行我就会开始拆分。当然这不是硬标准,有些技能天然复杂,但你至少要有拆分意识。
5. 三个实战案例拆解:从“会写”到“写得好”的进阶素材
方法论讲完了,下面用三个不同领域的实战案例,带你看看 SKILL.md 在真实任务里是怎么设计的。这三个案例的复杂度是递增的,你可以对照自己的项目情况做移植。
5.1 案例一:规范 commit message 的技能(适合任何项目)
Commit message 规范这件事,靠人自觉基本不可能长期坚持,靠 CLAUDE.md 全局命令又容易麻木。我给它做成了一个技能,效果意外地好。
核心 SKILL.md 内容梗概:
--- name: standardize-commit description: 当用户需要提交代码、编写 commit message、整理提交信息时使用此技能。适用于 git commit 前生成规范提交信息,或批量整理 rebase 时的提交信息。 --- # 标准化提交信息 ## 执行步骤 1. 运行 `git diff --cached --name-only` 获取暂存文件列表。 2. 运行 `git diff --cached` 获取具体改动内容。 3. 根据文件路径和改动内容,按 Conventional Commits 规范生成提交信息: - 新增功能: `feat(影响模块): 一句话描述` - 修复 bug: `fix(影响模块): 一句话描述` - 重构: `refactor(影响模块): 一句话描述` - 文档/注释: `docs(影响模块): 一句话描述` - 其他杂项: `chore(影响模块): 一句话描述` 4. 提交信息描述部分不超过 15 个中文字符,能表达清楚改动意图即可。 5. 展示待执行的 commit 命令让用户确认,不要自动执行。 ## 禁止事项 - 禁止使用 `git commit -m "update"` 这类无意义信息。 - 禁止自动运行 git commit 命令,必须用户手动执行。 - 如果暂存区为空,提示用户先 git add。这个技能看起来轻量,但它解决的是一个高频、容易偷懒的操作。写成技能后,每次 Claude 帮我提交代码前都会自动走这套流程,提交信息质量直线上升。
5.2 案例二:生成并维护 API 文档的技能(适合前后端项目)
这个技能是我目前最满意的作品之一。它的来源是我发现大多数项目里的 API 文档要么写完就烂尾,要么靠人肉同步,最终全部过时。
这个技能的设计逻辑:
--- name: update-api-docs description: 当用户需要生成、更新或审查 API 文档时使用此技能。适用于根据代码变更同步接口文档、为新接口自动生成文档、检查现有文档与代码不一致等问题。 --- # API 文档生成与维护 ## 执行步骤 1. 运行 `scripts/scan-api-routes.js`,扫描 routes/ 目录下所有接口定义,输出 JSON 格式的接口清单,包含 method、path、handler 引用位置。 2. 读取 scann 结果与现有 `docs/api.md` 做对比,列出缺失接口、多余接口、以及参数变化的接口。 3. 对每个新增或变更的接口,从 handler 源码中提取: - 请求参数的数据结构(JSDoc @param 或 TS 类型) - 响应数据的结构 - 错误码定义 4. 更新 docs/api.md,追加新接口文档,按以下模板渲染: - 接口路径与 method - 请求参数表(参数名、类型、必填、说明) - 响应示例 - 错误码表 5. 所有变更完成后输出一个变更摘要,供用户 review。 ## 资源引用 - 详细文档模板见 `templates/api-doc-template.md` - 接口扫描脚本见 `scripts/scan-api-routes.js` - 现有文档检查清单见 `assets/api-review-checklist.md` ## 禁止事项 - 不要替用户发布到外部文档平台。 - 不要修改前端调用代码,只更新文档文件。这个技能设计得比较经典,原因在于它把“机械扫描”和“语义生成”分得非常清楚:扫描交给脚本,生成和对比交给 Claude。文档维护这种活,一旦变成“每次发版后跑一下”,就再也不会出现文档烂尾的问题了。
5.3 案例三:每周自动归档信息的“信息管家”技能(适合个人知识库)
最后一个案例跳出程序员语境,讲一个我用来管理个人知识库的技能。我维护着一个以 Markdown 为底层的笔记本,每周会有大量随手记录的内容。这些内容杂乱无章,很难后期整理。
这个技能做的事情是:给我一个整理指令,Claude 读取我的 inbox 目录下新增的随手笔记,然后:
- 为每篇笔记判断主题(工作、学习、生活、灵感)。
- 提取核心观点,用“一句话总结”写进对应的主题索引文件。
- 把完整笔记移动到归档目录中该主题的子目录下。
- 在周报文件里追加本周新增的知识条目。
这个技能的特殊之处在于它跟“数据状态”强相关——每次执行都需要知道“上一次处理到哪一篇笔记了”。我用state.json记录已处理文件的 hash 值,每次只处理新增文件,避免重复归档。
技能看似简单,但它把“知识管理的方法论”固化成了 AI 能跟随的流程。这不只是“让 AI 做一次整理”,而是“让 AI 每次都用同一种高质量方式整理”。
6. 让工具化更进一步:Skills 与 Commands、Hooks 的配合艺术
单有 Skill 文件还不够,真正让工具化发挥全力的,是让技能进入 Claude Code 的完整自动化链路里。这一节讲讲我实际项目里常用的三种配合方式。
6.1 给技能配上 Command 入口:显式调用的快感
技能是让 Claude“自己想起来用”,但有时候你需要“强制触发”。用斜杠命令就能实现这种强制。
在.claude/commands/下创建一个命令文件release.md,内容形如:
--- description: 执行一次标准版本发布流程 --- 请加载 publish-release 技能,并严格按照技能中定义的步骤执行完整发布流程。如果遇到任何需要用户确认的环节,暂停并向我说明。这样我就有了两个入口:让 Claude 自动判断何时需要发版(技能命中),或者我主动输入/release(命令触发)。两者指向同一个技能文件,行为完全一致。
实际用下来,手动触发入口解决了一个大痛点:AI 的自动判断偶尔会“迟钝”——明明该用技能了它却没想起来。有了命令入口,我一句话就能把它拽回正轨。
6.2 挂上 Hooks:全自动校验闸门
Hooks 可以监控 Claude Code 生命周期里的一些事件,比如PreToolUse(使用工具前)、PostToolUse(使用工具后)、Stop(一次生成结束)。当某个技能改完文件之后,我想自动校验一下产物是否合规,就可以在 hooks 里挂一个检测脚本。
在.claude/settings.json里配置一个 hook:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "node scripts/check-generated-files.js" } ] } ] } }这段配置的意思是:每当 Claude 完成一次文件编辑或写入,自动运行一次检查脚本。在脚本里可以做三件事:检查生成的CHANGELOG.md格式字符串是否符合预期模式、确认没有错误的自动安装依赖、扫描是否生成体积异常大的文件。脚本检查失败时返回非零退出码,Claude Code 会中断当前操作,避免带病产物溜过防线。
这个组合拳用起来非常舒服:技能负责“教会 AI 如何做”,hooks 负责“每一步都做对了没”。两者一配合,AI 的产出质量甚至比我亲自动手还稳。
6.3 技能内嵌 Agent 委派:把任务分给“专业小组”
当技能任务里的某个环节需要极高的专业专注度时,可以把它委派给一个专用 Agent。在 SKILL.md 里可以写:
## 执行步骤 3. 当需要生成 API 参考文档时,创建一个 `security-reviewer` 类型的子代理,专门审查改动中涉及鉴权与权限的部分,生成安全审查意见,写入 assets/security-notes.md。Claude Code 会启动一个 Agent,这个子代理只专注你分配给它的那部分工作,可以反复调用工具、阅读文件,而不受主对话上下文的干扰。等它完成后,主流程再拿它的产出继续。这种方式特别适合发版、文档生成、代码审查等需要“多角色协作”的复杂技能。
7. 技能库的管理之道:命名、版本、实验与团队共享
当技能从一两个增长到二三十个,就会迎来第三个阶段的挑战:技能库本身怎么治理。我自己经历过一次“技能库混乱期”,那段时间技能数量虽多,但基本处于“写了一个再也没用过”的僵尸状态。后来我花了一个下午梳理,总结出一套管理方法,现在技能库非常清爽。
7.1 命名规范与目录隔离
技能命名必须遵循“动词 + 名词”结构,一眼能看出用途:
update-changelog(更新变更日志) publish-release(发布版本) review-security(审查安全性) generate-api-docs(生成 API 文档) archieve-inbox-notes(归档收集箱笔记)另外,技能库要做按作用域分层:
- 项目级技能放在项目的
.claude/skills/下,只对这个项目生效。 - 个人级技能放在
~/.claude/skills/下,对当前用户的所有项目生效。 - 团队级技能放在共享仓库中,通过 CI 同步到所有成员的机器上。
这样做的好处是:项目特有流程(比如某公司的自定义部署步骤)不会污染其他项目;而通用的个人工作流(比如提交信息规范)可以在不同项目间复用,不必复制粘贴。
7.2 技能也要有生命周期:写 changelog、做版本标记
技能文件本身也是代码,我只维护一条底线规则:每次修改 SKILL.md 后,都要在技能目录下更新;重大修改后在 changelog 里记一行。比如:
# Skill Changelog ## [2025-04-06] - 更新 update-changelog 技能:增加“前置判断”步骤,处理提交信息含作用域标记的情况。 - 修复 publish-release 技能:bump-version.js 不再支持 prerelease 版本号。很多人觉得给技能写 changelog 是小题大做,但当你技能的调用者不止自己(还有团队成员、甚至未来的自己)时,这些记录可以省下大量考古时间。
7.3 如何安全地实验一个技能:影子模式与回归测试
新技能上线我一般不会直接替换旧流程,而是先让它“影子运行”几天。具体做法是:让旧流程和技能产生的结果同时生成,人工对比几次,确认差异不影响预期后再切换正式路径。
对于脚本类技能,我会准备最小化测试用例。比如bump-version.js就有一个专门的测试命令,输入几组 mock 提交信息,断言输出的版本号类型正确。任何改动技能目录里的脚本前,都要先跑一遍测试命令,这样才能保证“工具化”之后的可靠性真正可信。
7.4 团队共享的坑:不是所有人都在同一个 Claude Code 版本里
最后说个团队实战里一定会遇到的坑:不同成员的 Claude Code 版本不一致,导致技能解析行为略有差异。老版本的 Claude Code 可能不支持某个 YAML 字段,或者 skills 目录扫描逻辑不同。
我的应对策略:
- 在技能仓库里放一个
REQUIREMENTS.md,写明最低支持的 Claude Code 版本、Node/Python 脚本运行环境。 - 涉及全局技能更新时,用脚本一键同步,而不是让成员手动拷贝。
- 成员本地跑不通技能时,第一反应是检查环境依赖,而不是直接怀疑技能逻辑。
8. 从工具箱到工作流:技能库驱动的高效日常
最后,把视角拉升一点,聊聊技能库怎么把一整天的 AI 协作串起来。我现在的项目工作流大致是这样运转的:
早上开工时,我会先运行一个“daily startup”技能,它读取前一天的state.json和项目进度文件,生成一份今天的待办优先清单。开始写代码时,“commit message 规范”技能帮我生成提交信息;写完一段功能后,“生成单测”技能自动补测试;代码告一段落后,“代码审查”技能做一轮自检;要发版了,“发布流程”技能接管,从测试到打 tag 全流程推进。
这些技能单个拆开都只解决一个小问题,但组装起来,它们构成了一条完整的“AI 工作流水线”。这里的核心体会是:不要试图用一个巨大的 Skill 管理所有事情,而是把流程拆成颗粒度合适的小技能,再让它们在流程上衔接起来。每个技能内部足够简单可靠,衔接处又通过统一的state.json和数据文件传递信息。
我最初也幻想过能做出来一个“全能总控技能”,把整个项目生命周期全部塞进去。后来发现这种做法既难写、又难调试,Claude 加载它时上下文压力也大。拆分之后一切豁然开朗——这大概就是“工具化”相比“手写指令”最大的理念差异:你教的不是一次性的大任务,而是一个个可复用、可组合、可演进的小能力。
为了让这套体系更长久地运转,我给自己定了几条维护纪律:
- 每个技能都要有明确 Owner(如果团队使用),个人项目则明确为“当前唯一维护人”。
- 每月花半天时间检查一遍技能调用次数,把长期无人调用的技能归档或删除。
- 出现“同类技能重复写”的苗头时,立刻合并,不要等它积累成垃圾。
- 技能升级后随时用真实任务回测,别让技能版本和实际需求脱节。
9. 常见问题排查与工具调试指南
写了这么多,最后一个部分最贴近实战:当技能不好用时,你怎么定位问题。我几乎每个技能都踩过“莫名其妙不生效”的坑,也总结出了一套行之有效的排查路径。
9.1 问题一:Claude 压根不触发技能
这是最高频的问题。排查思路依次是:
- 确认技能目录路径正确(
.claude/skills/<skill-name>/SKILL.md)。 - 检查 SKILL.md 的 YAML frontmatter 是否被正确解析(开头和结尾的
---不能丢)。 - 检查 description 是否写清楚了触发场景,不要写“处理各种任务”。
- 打开调试模式(
/debug)看 Claude 是否提到技能检索的过程。
调试模式是我强烈推荐的排查入口,它会把 Claude 内部判断链路的摘要信息展示出来,你能清楚地看到它有没有在技能目录里“翻阅”过你的技能文件、为什么跳过。这是让我少走无数弯路的功能。
9.2 问题二:技能加载了,但 Claude 不按步骤走
这种情况通常是 SKILL.md 里的步骤写得不够“可执行”。排查建议:
- 把自己当成“一个对外部项目一无所知的人”,重新读一遍每个步骤,看哪些地方会让你停住。
- 检查步骤中是否缺少命令、文件名、完成标准。
- 用
/skills命令确认当前加载的是不是你最新保存的版本(Claude Code 通常会自动读取最新文件,但偶尔缓存会作怪)。 - 把文件路径写绝对点,或者在一个步骤里明确写上从项目根目录出发的相对路径。
9.3 问题三:脚本报错,技能中断
脚本层面的问题相对容易定位:
- 手动在终端里跑一遍脚本,确认能独立工作。
- 检查脚本是否有依赖环境变量,缺少时给默认值。
- 脚本输出尽量精简为结构化文本(比如限定 JSON 单行输出),避免大段冗杂文本干扰 Claude 解析。
- 异常退出时给出明确的错误码和 stderr 信息,帮助 Claude 理解失败原因。
9.4 一套用于技能调试的通用规则
无论哪种问题,我都会遵循一条铁律:不要盲改文件然后反复试,要先确认“Claude 到底干了什么”。用调试模式、日志文件、脚本输出去还原它的决策过程,再针对性修改。这个思路跟调试代码完全一样——你不可能靠猜解决 bug,你得先看到现场。
技能调试并不是一次性工作,它更像是对 AI 行为持续校准的过程。每多跑一个真实任务,你就会发现新的边界情况,然后回到 SKILL.md 里补充一条注意事项。技能库就是这样一步步从粗糙走向精细的。
最后说一句个人体会。从“手写指令”到“工具化技能”这段路,最大的变化并不是 AI 变聪明了,而是我的工作方式从“每次都重新教”变成了“教一次,用很久”。技能库逐渐长成一套独属于我的 AI 协作方法论——它比任何文档都贴近我真实的工作流,也比我自己的记忆更可靠。
如果你现在还在对话里反复输入同一套长篇指令,那我的建议很简单:挑一个你每天都在做、且步骤相对固定的任务,花半个小时把它写成第一个 SKILL.md。不用一步到位,先写主流程,跑几天,哪里不对改哪里。等你体会过一次“AI 主动找到正确技能并照章办事”的时刻,你就回不去了。