本教程面向已装好 Claude Code、想从「会用别人写的 Skill」升级到「自己造 Skill」的开发者。你将掌握 2026 版 Skill 的完整骨架——三个存放目录的区别、SKILL.md 的 15 个 frontmatter 字段、进度披露(progressive disclosure)的拆分原则,并最终亲手造出一个 regex-forge 技能:输入一句自然语言,输出一条已被脚本验证过、可直接复制的正则表达式。适合做过一轮 Claude Code 入门、能看懂 Markdown 与少量 JavaScript 的读者。
/ 命令。node(执行验证脚本用,v18+ 即可);没有也行,可用 Python/Shell 改写,思路一致。.claude/skills/。\d+、.、* 这一级别足够),不需要精通。一个 Skill 在磁盘上就是一个文件夹,里面至少有一个带 YAML frontmatter 的 SKILL.md。Claude Code 启动时扫描这些文件,把它们的 name 和 description 注册进自己的「技能目录」。当对话内容命中某条描述,或你手动敲 /技能名,对应的指令才会被加载进上下文执行。
存放位置有三档,优先级从高到低:
| 目录 | 作用域 | 是否进 git | 典型用途 |
|---|---|---|---|
~/.claude/skills/ |
用户全局,所有项目可用 | — | 个人跨项目复用的小工具 |
<项目>/.claude/skills/ |
项目级,团队共享 | 是(建议) | 团队规范、项目专属流程 |
<项目>/.claude/skills.local/ |
项目级,仅自己 | 否(gitignore) | 个人临时实验 |
命名规则:技能名只能小写字母、数字、连字符,最长 64 字符,目录内唯一。我们这次造的叫 regex-forge,放在项目级 .claude/skills/regex-forge/。
操作说明:
mkdir -p .claude/skills/regex-forge/scripts
mkdir -p .claude/skills/regex-forge/references
预期结果:得到如下骨架——
.claude/skills/regex-forge/
├── SKILL.md # 入口,唯一会被默认读进上下文的文件
├── scripts/ # 进度披露:可执行脚本,按需调用
└── references/ # 进度披露:参考资料,按需加载
常见问题:
skills.local 去覆盖团队版本——它优先级最低。name 字段缺省时回退到目录名,但官方明确建议始终显式写 name,否则重命名目录会悄悄改变 / 命令。frontmatter 是 SKILL.md 顶部被 --- 包住的 YAML 块。2026 版规范里它有多达 15 个字段,但对绝大多数 Skill 来说,description 决定「能不能被触发」,allowed-tools 决定「跑得顺不顺」,其余按需。
description——整个 Skill 最该花心思的一行它驱动自动触发:模型是靠读这段话来判断「现在该不该把这个技能拽进来」。官方给的有效公式是:
[动词短语:它做什么]。Use when [触发短语]。
对比:
# ❌ 模糊,模型不知何时用
description: 正则助手
# ✅ 具体,触发词密集
description: Build and test a regular expression from a natural-language description of what should match or be rejected. Use when the user asks for a regex, a pattern, to match/validate/extract emails, phone numbers, hashtags, or any text pattern.
注意:description 加上可选的 when_to_use,在技能目录里被截断到 1536 字符。宁可具体不可啰嗦。
allowed-tools——给脚本开绿色通道我们的技能要跑 node 做验证。如果不声明,每次执行都会弹权限确认,打断节奏。声明后,技能激活期间这些工具免确认:
allowed-tools:
- Bash(node:*)
- Read
铁律:尽量收窄。 写 Bash(node:*) 而不是 Bash,写 Read(./src/**) 而不是 Read。松散的权限等于在每个加载该技能的项目里预授权,是安全隐患。而且全局 deny 永远优先,allowed-tools 只能「加」,不能「绕」。
---
name: regex-forge
description: Build and test a regular expression from a natural-language description of what should match or be rejected. Use when the user asks for a regex, pattern, or validation rule (match emails, validate phone numbers, extract hashtags).
argument-hint: <what-to-match> [--tests="mustMatch;mustNotMatch"]
allowed-tools:
- Bash(node:*)
- Read
---
字段小结:
name:技能标识,也是 /regex-forge 的命令名。argument-hint:纯 UX,用户敲 /regex-forge 时补全菜单里显示的提示,<必填> 与 [可选] 约定同 man 手册。description:自动触发的核心,第三人称,含触发词。allowed-tools:技能激活期间免确认的工具白名单。常见问题:
disable-model-invocation: true? 凡是有副作用(部署、提交、删分支、发邮件、开 PR)的技能,默认加,防止模型自作主张触发。regex-forge 是只读+本地验证,无副作用,故不加,让模型在合适时机自动调用。disable-model-invocation),只有 when_to_use 用下划线。写错一个字符整段失效。body 是 frontmatter 之后的正文。原则是可执行、可校验——把模糊的「帮我写正则」拆成模型能照做的清单,并强制走验证脚本。
# Regex Forge
把一句自然语言的匹配需求,变成一条**已被测试通过**的正则。
## 步骤
1. 读懂 `$ARGUMENTS` 或对话,明确:什么必须匹配?什么必须**不**匹配?
2. 写出**一条** JavaScript 风格正则。优先选满足需求的最窄模式。仅当需求隐含整串校验时才加 `^`/`$` 锚点或 `\b`。
3. **返回前必须先验证**,运行内置测试器:
```bash
node scripts/regex-tester.js "<PATTERN>" "<必须匹配,samples>" "<必须不匹配,samples>"
```
脚本输出 `PASS` 才算数,否则逐条列出失败样本。未得 `PASS` 前不要向用户展示正则。失败就改模式重跑。
4. 通过后,输出:正则代码块 + 每段(锚点/分组/量词)的一句话解释 + 测试证据。
5. 涉及进阶构造(后行断言、Unicode、反向引用)时,查阅 `references/regex-cookbook.md` 并注明引用的小节。
预期结果:模型拿到这段 body 后,行为从「直接甩一个可能错的正则」变成「生成→自测→必要时改→再给」。这正是 Skill 相比纯 prompt 的价值:把流程固化下来。
常见问题:
references/,别把 SKILL.md 写成论文——它默认全量进上下文。SKILL.md 默认加载,但 scripts/、references/ 不会——它们是按需才被读进来。这就是「进度披露」:入口保持精简,重资源用到才取,省 token、保聚焦。
新建 scripts/regex-tester.js(注意把 g 标志剥掉,避免 test() 的有状态陷阱):
#!/usr/bin/env node
// 校验一条正则:必须匹配的全匹配,必须不匹配的全不匹配。
// 用法: node regex-tester.js "<pattern>" "<must,match>" "<must,not,match>"
const [, , patternStr, matchStr = "", nomatchStr = ""] = process.argv;
if (!patternStr) { console.error("FAIL: no pattern"); process.exit(2); }
// 支持 /pattern/flags 形式,统一拆出 source,并去掉 g 以保证 test() 无状态
let source = patternStr, flags = "";
const m = patternStr.match(/^\/(.+)\/([gimsuy]*)$/);
if (m) { source = m[1]; flags = m[2].replace(/g/g, ""); }
let re;
try { re = new RegExp(source, flags); }
catch (e) { console.error("FAIL: invalid regex -> " + e.message); process.exit(2); }
const must = matchStr ? matchStr.split(",") : [];
const mustNot = nomatchStr? nomatchStr.split(","): [];
const fails = [];
for (const s of must) if (!re.test(s)) fails.push(`should MATCH: "${s}"`);
for (const s of mustNot) if ( re.test(s)) fails.push(`should NOT match: "${s}"`);
if (!fails.length) {
console.log(`PASS — /${re.source}/${flags} | ${must.length} positive, ${mustNot.length} negative`);
process.exit(0);
}
console.log("FAIL:");
fails.forEach(f => console.log(" - " + f));
process.exit(1);
预期结果——在项目根目录手测一次:
node .claude/skills/regex-forge/scripts/regex-tester.js \
'^\d{4}-\d{2}-\d{2}$' \
'2026-07-09,1999-12-31' \
'26-07-09,2026/07/09'
# PASS — /^\d{4}-\d{2}-\d{2}$/ | 2 positive, 2 negative
脚本返回 PASS,正则可信;改个故意错的模式则会列出哪条样本挂了。
常见问题:
node 不会。把可机械判定的部分交给脚本,把需要判断的部分(怎么设计模式)留给模型,这是高质量 Skill 的通用分工。g 标志为什么剥掉? RegExp.test() 在带 g 时会记住 lastIndex,连续调用会跳着匹配,是经典的「同一个串第一次 true 第二次 false」坑。测试场景不需要全局,剥掉最稳。进阶构造不必塞进 SKILL.md,放进 references/regex-cookbook.md,body 第 5 步提示模型「需要时去查」即可:
# Regex Cookbook(按需查阅,仅在用到进阶构造时加载)
- 后行断言:匹配前面是 `$` 的 `foo` → `(?<=\$)foo`
- 命名分组:`(?<year>\d{4})-(?<month>\d{2})` → `match.groups.year`
- Unicode 属性:任意文字 `\p{L}+`(需 `u` 标志)
- 反向引用:连续重复字符 `(\w)\1`
- 锚点取舍:`^...$` 用于表单整串校验;`\b` 用于从长文本里定位词
这样 SKILL.md 始终轻量,复杂知识按需出现,上下文不爆。
保存好三份文件后,新开一个 Claude Code 会话(老会话不会热重载技能目录)。两种触发方式:
/regex-forge 匹配中国大陆手机号,补全菜单应出现你写的 argument-hint。description 里的触发词,应自行激活该技能。验证「是否被加载」的窍门:在对话里问「你现在激活了哪些技能」,或观察它有没有去调用 node .../regex-tester.js——调用了,就说明 body 生效了。
常见问题:
/ 看不到这个技能? 八成是 frontmatter 解析失败(YAML 缩进错、字段名拼错、--- 漏了第二根)。frontmatter 全文大小写敏感,逐字核对。description 没写好触发词。把「Use when …」那段写具体,列出用户真实会说的话术("写个正则""匹配 xxx""校验")。paths: ["**/*.log"] 这类 glob,按文件类型门控自动激活(注意 **/*.log 与 *.log 不是一回事,前者跨目录)。手动 / 调用不受 paths 限制。完整跑通这个 Skill。用户输入:
/regex-forge 校验公司邮箱:用户名是字母数字下划线,域名是 xxx.com,要整串匹配,并给我两组测试
技能被激活后,模型按 body 执行:
名@xxx.com、整串、用户名限 [A-Za-z0-9_]。^[A-Za-z0-9_]+@xxx\.com$node scripts/regex-tester.js '^[A-Za-z0-9_]+@xxx\.com$' '[email protected],[email protected]' '[email protected],[email protected],空格 @xxx.com'
# PASS — /^[A-Za-z0-9_]+@xxx\.com$/ | 2 positive, 3 negative
✅ 正则:/^[A-Za-z0-9_]+@xxx\.com$/
逐段解释:
- ^ ... $ 整串校验,前后不允许多余字符
- [A-Za-z0-9_]+ 用户名:字母/数字/下划线,至少 1 位
- @xxx\.com 字面域名(. 被转义,只匹配点号)
测试证据(脚本 PASS):通过 2 条正例、3 条反例。
整个链路里,模型没把未经测试的正则交给你——脚本兜住了正确性。这就是把 Skill 做成「自带质检」的好处。
flowchart TD
A["用户:帮我写个匹配邮箱的正则"] --> B{"description 命中触发词?"}
B -- 否 --> Z["走常规对话"]
B -- 是 --> C["加载 SKILL.md 进上下文(默认全量)"]
C --> D["解析 $ARGUMENTS / 参数"]
D --> E["按 body 步骤生成候选正则"]
E --> F["Bash: node scripts/regex-tester.js"]
F --> G{"脚本输出 PASS?"}
G -- 否 --> E
G -- 是 --> H["返回正则 + 解释 + 测试证据"]
C -. 用到进阶构造时 .-> R["按需加载 references/regex-cookbook.md"]
style F fill:#e8f5e9
style G fill:#fff8e1
model: haiku + effort: low,便宜又快;若改造成需要权衡架构的技能,再 model: opus。context: fork + agent: Explore,让它跑在隔离子代理里,不污染主对话、还省主上下文。skill-creator 自动脚手架:不想手敲骨架,可直接调用官方的 skill-creator 技能,描述需求即可生成目录与初版 SKILL.md,再把 frontmatter 按本教程精修。SKILL.md 要薄、脚本与参考资料为什么要按需。PASS。PASS/FAIL);③allowed-tools 收窄合理,无过度授权。paths 门控或 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 等场景全覆盖。
