Claude Code 自定义 Skill 创建教程:从入门到实战
从零开始学习 Claude Code Skill 的创建、参数传递、脚本集成、子代理执行到 Plugin 打包分发的完整教程

引言
你是否在每次 Code Review 时反复粘贴相同的检查清单?是否在 CLAUDE.md 里塞了越来越长的操作流程,却发现每次对话都白白消耗几千 token?Claude Code 的 Skill 系统正是为解决这类问题而设计的。
Skill 是 Claude Code 最灵活的扩展机制。它把可复用的指令、工作流和专业知识封装成一个目录,Claude 在需要时自动加载,或者你手动通过 /skill-name 调用。和始终占用上下文的 CLAUDE.md 不同,Skill 的正文只在被触发时才加载,长参考文档几乎零成本待命。
Custom commands have been merged into skills. A file at
.claude/commands/deploy.mdand a skill at.claude/skills/deploy/SKILL.mdboth create/deployand work the same way. 来源自定义命令已合并到 Skill 中。
.claude/commands/deploy.md文件与.claude/skills/deploy/SKILL.md中的 Skill 都会创建/deploy命令,工作方式完全相同。编者注:过去
/deploy这类自定义命令可以通过.claude/commands/或.claude/skills/两种方式创建,是两套独立机制。合并后,.claude/commands/目录不再推荐使用,所有自定义命令统一通过 Skill 实现。如果你看到旧教程中提到commands目录,知道它已被 Skill 替代即可,无需纠结选哪个。
本文从创建第一个 Skill 开始,逐步深入到脚本集成、子代理执行、插件打包与分发。读完你将能独立创建、测试、分发自定义 Skill——从个人效率工具到团队共享基础设施。
第一章:认识 Skill——Claude Code 的可扩展能力模块
1.1 什么是 Skill
一个 Skill 就是一个包含 SKILL.md 文件的目录。SKILL.md 是入口,目录内还可以有可选的辅助文件。麻雀虽小,五脏俱全:
my-skill/
├── SKILL.md # 必需:入口文件(YAML 元数据 + Markdown 指令)
├── scripts/ # 可选:可执行脚本(Python/Bash/Node.js)
├── references/ # 可选:按需加载的参考文档
└── assets/ # 可选:模板、图片、字体等输出资源
Skill 在 Claude Code 生态中的定位是什么?官方文档给出了清晰的区分:
| 扩展机制 | 用途 | 加载时机 |
|---|---|---|
| CLAUDE.md | 始终在线的项目规则和背景知识 | 每次会话 |
| Skill | 按需加载的专业知识和工作流 | 触发时加载 |
| MCP | 连接外部工具和服务 | 工具调用时 |
| Hooks | 生命周期事件自动化 | 事件触发时 |
| Plugin | 打包上述所有扩展的分发单元 | 安装后激活 |
组合使用效果更佳:MCP 连接数据库,Skill 教 Claude 如何使用 Schema;CLAUDE.md 声明 API 规范,Skill 包含详细风格指南。
1.2 Skill 的存放位置
Skill 支持三级作用域:
| 级别 | 路径 | 作用范围 |
|---|---|---|
| 个人 | ~/.claude/skills/<name>/SKILL.md | 所有项目 |
| 项目 | .claude/skills/<name>/SKILL.md | 当前仓库 |
| 插件 | <plugin>/skills/<name>/SKILL.md | 插件启用时 |
Project skills load from
.claude/skills/in your starting directory and in every parent directory up to the repository root. 来源项目级 Skill 会从启动目录的
.claude/skills/加载,并向上查找所有父目录直到仓库根目录。
项目级 Skill 还支持 monorepo 嵌套发现:编辑 packages/frontend/ 下的文件时,Claude Code 也会查找 packages/frontend/.claude/skills/ 中的 Skill。
重要限制:Skill 目录不支持递归子目录发现。Claude Code 只在 ~/.claude/skills/ 或 .claude/skills/ 下扫描一层目录:
~/.claude/skills/my-skill/SKILL.md → ✅ 会被发现
~/.claude/skills/category/my-skill/SKILL.md → ❌ 不会被发现
官方文档中提到的"nested skills"指的是 monorepo 子目录下的独立 .claude/skills/(如 packages/frontend/.claude/skills/),不是单个 skills 目录内的递归子目录扫描。无论手动调用 /category/my-skill 还是自动触发,嵌套在子目录中的 SKILL.md 都不会被发现和注册——发现是触发的前提,发现失败则一切无从谈起。
手动读取的变通方案:如果你告诉 Claude “读取 ~/.claude/skills/parent/category/my-skill/SKILL.md 并按其中指令执行”,Claude 可以 Read 该文件并遵循正文内容。但 Frontmatter 中所有元数据能力全部失效——description(自动语义匹配)、allowed-tools(工具预授权)、context: fork(子代理隔离)、agent、model、paths 等均由 Claude Code 运行时在发现/注册阶段解析,手动读取文件完全绕过了这些机制。只有正文指令能被 Claude 理解和执行。
替代方案:
- 命名前缀:用
frontend--lint/SKILL.md、backend--lint/SKILL.md在扁平结构下模拟分类,调用/frontend--lint - 扁平符号链接:共享仓库保持
category/skill-name/结构,再用 symlink 打平到~/.claude/skills/ - Plugin 命名空间:嵌套 Skill 通过 Plugin 的
skills/子目录分发,利用plugin-name:skill-name实现逻辑分组
优先级规则:企业级 > 个人级 > 项目级。插件 Skill 使用 plugin-name:skill-name 命名空间,不会与其他级别冲突。
1.3 调用机制
Skill 的调用有两条路径:
- 手动调用:输入
/skill-name直接触发。适合有副作用的操作(部署、提交、发消息),你不想让 Claude 自己决定何时执行。 - 自动加载:Claude 根据
description字段匹配用户意图,自动加载相关 Skill。适合知识类 Skill(风格指南、API 文档、领域知识)。
核心原理:所有未设置 disable-model-invocation: true 的 Skill,其 description 会在会话启动时加载到上下文。Claude 将用户请求与这些 description 做语义匹配,决定是否触发。description 是 Skill 能否被自动发现的最关键因素。
第二章:第一个 Skill——5 分钟快速上手
2.1 目录结构与 SKILL.md
最小可用的 Skill 只需两步:
# 创建 Skill 目录
mkdir -p ~/.claude/skills/summarize-changes
# 创建 SKILL.md
touch ~/.claude/skills/summarize-changes/SKILL.md
目录名 summarize-changes 就是你调用时的命令名:/summarize-changes。这就是最简结构——一个目录,一个 SKILL.md。
2.2 YAML Frontmatter 必知必会
SKILL.md 由两部分组成:YAML frontmatter(元数据)和 Markdown 正文(指令)。Frontmatter 用 --- 包裹:
---
name: summarize-changes
description: 总结未提交的代码变更,标记潜在风险。当用户问"改了什么"、需要提交信息、或要求审查 diff 时使用。
---
name使用小写字母、数字和连字符,最长 64 字符,必须与目录名一致。description在 Claude.ai 上限 200 字符,Agent Skills 规范允许 1024 字符,但 Claude Code 中description+when_to_use合计截断为 1,536 字符。来源
写好 description 的要点:
- 前端加载触发词:把最常见的用户表述放在最前面(“当用户问’改了什么’、需要提交信息时使用”)
- 具体而非笼统:“总结未提交变更并标记风险” 比 “帮助处理 git” 好得多
- 区分相似 Skill:如果有多个相近 Skill,description 要写清各自边界
2.3 实战:创建一个"代码变更摘要"Skill
打开 ~/.claude/skills/summarize-changes/SKILL.md,写入完整内容:
---
description: 总结未提交的代码变更,标记潜在风险。当用户问"改了什么"、需要提交信息、或要求审查 diff 时使用。
---
## 当前变更
!`git diff HEAD`
## 指令
用 2-3 个要点总结以上变更,然后列出你发现的任何风险,例如缺少错误处理、硬编码值、需要更新的测试等。如果 diff 为空,告知用户没有未提交的变更。
这个 10 行的 Skill 展示了三个重要技巧:
- **
!command动态上下文注入**:Skill 加载时,`!`git diff HEAD先执行,命令输出替换占位符后再交给 Claude。Claude 看到的是实际的 diff 内容,而非一句"运行 git diff"的指令。 - 精简指令:正文用 3 句话说清要做什么,不需要长篇大论。
- 边界情况:明确处理了 diff 为空的情况。
Keep the body itself concise. Once a skill loads, its content stays in context across turns, so every line is a recurring token cost. 来源
保持正文精简。Skill 一旦加载,其内容会在后续对话轮次中持续占用上下文,因此每一行都是重复的 token 成本。
2.4 测试与调试
测试分两步:
第一步:手动调用验证
直接在 Claude Code 中输入 /summarize-changes。你应该看到 Skill 加载并输出变更摘要。如果没反应,检查:
- 目录名和 SKILL.md 路径是否正确
- YAML frontmatter 的
---分隔符是否正确 - 是否在正确的级别创建(个人级
~/.claude/skills/对所有项目生效)
第二步:自动触发验证
找一个有未提交变更的项目,输入"帮我看看改了些什么"。Claude 应该自动加载 summarize-changes。如果没有自动触发:
- description 是否包含了相关触发词?
- Skill 是否被禁用?检查
/skills菜单
Review Claude’s thinking to confirm it’s loading the skill. Iterate on the description if Claude isn’t using it when expected. 来源
查看 Claude 的思考过程以确认它正在加载 Skill。如果 Claude 未按预期使用 Skill,请迭代优化 description。
第三章:Frontmatter 全字段详解——精确控制 Skill 行为
Frontmatter 是 Skill 的控制中心。截至 2026 年,共 16 个可用字段。大多数开发者只用了 name 和 description,了解全部字段能让你精确控制 Skill 的触发时机、执行方式和工具权限。
3.1 调用控制
disable-model-invocation 和 user-invocable 决定了谁可以触发 Skill:
| Frontmatter | 你可以调用 | Claude 可以调用 | 上下文加载 |
|---|---|---|---|
| (默认) | 是 | 是 | description 始终在上下文,正文触发时加载 |
disable-model-invocation: true | 是 | 否 | description 不在上下文,正文手动调用时加载 |
user-invocable: false | 否 | 是 | description 始终在上下文,正文触发时加载 |
典型场景:
---
# 场景1:部署 Skill —— 只能手动触发,不允许 Claude 自动决定部署
name: deploy-staging
description: 部署到预发布环境
disable-model-invocation: true
allowed-tools: Bash(command *)
---
---
# 场景2:遗留系统上下文 —— 只能 Claude 自动加载,用户不需要手动调用
name: legacy-system-context
description: 旧系统架构背景知识
user-invocable: false
---
Use
disable-model-invocation: truefor workflows with side effects or that you want to control timing, like/commit,/deploy, or/send-slack-message. You don’t want Claude deciding to deploy because your code looks ready. 来源对于有副作用或需要控制执行时机的流程(如
/commit、/deploy、/send-slack-message),使用disable-model-invocation: true。你不会希望 Claude 因为代码看起来就绪而自行决定部署。
3.2 工具权限
allowed-tools 预授权特定工具,Skill 激活期间 Claude 使用这些工具时不再弹出权限确认:
---
name: lint-fix
description: 修复 TypeScript 文件的 lint 错误
allowed-tools: Bash(npx eslint *) Bash(npx prettier *) Read Grep
---
语法规则:
- 空格或逗号分隔,也可用 YAML 列表
Bash(command *)使用 glob 模式匹配,*匹配任意参数allowed-tools授予而非限制权限——所有工具仍然可用,只是列表中的免确认
Tool permissions in
allowed-toolsgrant access, they do not restrict. If a deny rule exists in your permission settings, it overridesallowed-tools. 来源
allowed-tools中的工具权限是授予而非限制。如果权限设置中已有 deny 规则,deny 会覆盖allowed-tools的授权。
disallowed-tools 则从可用池中移除指定工具:
---
name: background-monitor
description: 后台监控循环
disallowed-tools: AskUserQuestion
---
3.3 参数传递
Skill 支持位置参数和命名参数:
---
name: create-pr
description: 创建 Pull Request
arguments: [title, base-branch]
argument-hint: "[title] [base-branch]"
---
## 创建 PR
标题: $title
目标分支: $base-branch
使用 `gh pr create --title "$title" --base "$base-branch"`
以上 Skill 的使用方式:
/create-pr "修复登录页面 XSS 漏洞" main
当你输入这行命令后,Claude Code 在加载 Skill 内容之前,会先将变量替换为实际值:
| 变量 | 替换为 |
|---|---|
$title | 修复登录页面 XSS 漏洞 |
$base-branch | main |
所以 Claude 实际看到的内容是:
## 创建 PR
标题: 修复登录页面 XSS 漏洞
目标分支: main
使用 `gh pr create --title "修复登录页面 XSS 漏洞" --base "main"`
关键机制:变量替换发生在 Skill 内容进入 Claude 上下文之前——Claude 根本不知道有 $title 这个变量存在过,它看到的就是替换后的最终文本。
参数中包含空格时,用双引号包裹:
/create-pr "修复登录页面 XSS 漏洞" main # $0 = 修复登录页面 XSS 漏洞
/create-pr 修复 main # $0 = 修复(只取到第一个空格前)
可用变量:
| 变量 | 说明 |
|---|---|
$ARGUMENTS | 所有参数(未使用时自动追加到末尾) |
$ARGUMENTS[N] 或 $N | 第 N 个参数(0-based),如 $0、$1 |
$name | 命名参数,如 $title 对应第 0 个参数 |
${CLAUDE_SKILL_DIR} | Skill 目录的绝对路径,用于引用脚本和文件 |
3.4 路径限定
paths 限定自动激活范围——仅当当前会话涉及的文件匹配 glob 模式时,Skill 才会被自动触发:
---
name: ts-lint
description: TypeScript lint 修复
paths: "**/*.ts"
---
paths 支持 glob 模式,可指定单个模式或数组:
paths:
- "**/*.ts"
- "**/*.tsx"
paths 的行为边界(重要):
paths 的唯一作用是控制自动激活的门槛,它不限制 Skill 激活后的能力范围。这一点经常被误解,下面用三个场景说明:
场景一:文件不在 paths 范围内,Skill 会被自动触发吗?
不会。如果当前会话涉及的文件(如用户打开、引用或要求处理的文件)都不匹配 paths 模式,Skill 不会被自动激活。例如用户在处理 .py 文件时,paths: "**/*.ts" 的 Skill 不会自动加载。
场景二:文件不在 paths 范围内,但手动调用 Skill 能处理吗?
能。paths 只影响自动触发,手动输入 /ts-lint 始终可以激活 Skill。激活后,Skill 可以正常处理任何文件,包括 .py、.md 等不在 paths 范围内的文件。
场景三:Skill 激活后,只能读写 paths 范围内的文件吗?
不是。paths 不是沙箱或权限边界。Skill 激活后,Claude 可以使用所有已授权的工具(Read、Write、Bash 等)访问任意文件,不受 paths 限制。paths 只是"要不要自动唤醒这个 Skill"的判断条件,不是"能做什么"的约束。
总结:paths = 自动激活的准入条件,≠ 执行时的能力边界。
如果同时设置
disable-model-invocation: true,paths失去意义——因为 Skill 本身就不会被自动激活,准入条件自然无效。来源
3.5 模型与执行强度
model 覆盖 Skill 执行时使用的模型:
---
name: complex-refactor
description: 复杂的多文件重构
model: opus
---
可选值:
| 值 | 说明 |
|---|---|
opus | 最强推理能力,适合复杂重构、架构设计 |
sonnet | 平衡性能与成本,适合常规任务(默认) |
haiku | 最快响应,适合简单脚本、格式转换 |
effort 控制推理深度(仅部分模型支持):
---
name: security-review
description: 安全审查
model: opus
effort: high
可选值:low、medium、high。高 effort 会消耗更多 token 但推理更深入,适合安全审查、关键业务逻辑等场景。
注意:
model和effort是覆盖而非降级——即使指定model: haiku,如果全局设置要求使用 opus,最终行为取决于优先级规则。仅在你确定该 Skill 适合用特定模型时才设置。
3.6 Shell 类型
shell 指定 Skill 中命令块(!command`` 动态注入和代码块)的执行 Shell:
---
name: windows-deploy
description: Windows 部署脚本
shell: powershell
---
可选值:
| 值 | 说明 |
|---|---|
bash | Bash shell(Linux/macOS 默认) |
sh | 标准 POSIX shell |
powershell | Windows PowerShell |
cmd | Windows 命令提示符 |
未设置时,Claude Code 根据当前操作系统自动选择默认 Shell。如果你的 Skill 包含平台特定的命令(如 PowerShell 脚本),应显式指定 shell 以确保跨平台兼容性。
第四章:辅助文件——让 Skill 更强大
当 SKILL.md 超过 500 行时,就该考虑将详细内容移入辅助文件了。辅助文件的核心价值在于按需加载——大参考文档不需要每次都进上下文。
4.1 scripts/ 可执行脚本
脚本是 Skill 中最强大的能力放大器。对于需要确定性输出的操作(数据解析、统计计算、图表生成),脚本远比 token 生成可靠。
目录结构示例:
codebase-deps/
├── SKILL.md
└── scripts/
└── deps.py
SKILL.md 中使用 ${CLAUDE_SKILL_DIR} 引用脚本:
---
name: codebase-deps
description: 生成代码库的交互式依赖关系图。当用户要求"展示依赖"、"可视化导入"或"映射代码结构"时使用。
allowed-tools: Bash(python3 *)
---
1. 从项目根目录运行可视化脚本:
```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/deps.py .
- 脚本会生成
dependency-graph.html,告知用户文件位置
> Skills can bundle and run scripts in any language, giving Claude capabilities beyond what's possible in a single prompt. One powerful pattern is generating visual output: interactive HTML files that open in your browser. [来源](https://code.claude.com/docs/en/skills)
>
> Skill 可以打包并运行任意语言的脚本,为 Claude 提供超越单次 prompt 的能力。一个强大的模式是生成可视化输出:可在浏览器中打开的交互式 HTML 文件。
如果需要外部依赖,在 frontmatter 中声明:
```yaml
---
name: data-analysis
description: 分析 CSV 文件并生成可视化报告
dependencies: python>=3.8, pandas>=1.5.0, matplotlib
---
Claude can install packages from standard repositories (PyPI, npm) when loading skills. 对于 API 使用场景,所有依赖必须预装在容器中。来源
Claude 可以在加载 Skill 时从标准仓库(PyPI、npm)安装依赖包。
最佳实践:优先使用 Python 标准库,避免依赖安装步骤。如果确实需要外部包,在 SKILL.md 中让 Claude 首先检查/安装。
4.2 references/ 参考文档
references/ 中的文件是供 Claude 按需阅读的文档——数据库 Schema、API 规范、公司政策、详细工作流指南:
my-skill/
├── SKILL.md
├── references/
│ ├── api-docs.md # API 规范
│ └── schema.md # 数据库 Schema
在 SKILL.md 中引用它们:
## 数据库操作
查询前先阅读 `references/schema.md` 了解表结构。
对于分页和过滤,参考 `references/api-docs.md` 中的查询参数章节。
Avoid duplication: Information should live in either SKILL.md or references files, not both. 来源
避免重复:信息应只存放在 SKILL.md 或 references 文件中,不要两处都放。
4.3 assets/ 模板与资源
assets/ 存放不进上下文的输出资源——模板、图片、字体、样板代码:
brand-guidelines/
├── SKILL.md
└── assets/
├── logo.png
├── slides-template.pptx
└── font.ttf
Claude 可以直接使用这些文件而无需将它们加载到上下文窗口中。
4.4 动态上下文注入
!command`` 让 shell 命令在 Skill 内容进入 Claude 之前执行,输出直接内联替换占位符:
---
name: pr-review
description: 审查当前 Pull Request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---
## 当前 PR 上下文
- PR diff: !`gh pr diff`
- 变更文件: !`gh pr diff --name-only`
- PR 评论: !`gh pr view --comments`
多行命令使用三反引号形式:
```!
gh pr diff | head -200
gh pr view --json title,body
```
Dynamic context injection: The
!command`` syntax runs shell commands before the skill content reaches Claude. The command output replaces the placeholder inline. 来源动态上下文注入:
!command`` 语法会在 Skill 内容到达 Claude 之前执行 shell 命令,命令输出直接内联替换占位符。
注意:如果管理员设置了 disableSkillShellExecution: true,动态注入将被禁用,占位符会被替换为 [shell command execution disabled by policy]。
第五章:高级实战——脚本集成与子代理执行
5.1 实战:代码依赖图可视化 Skill
这个完整案例展示如何用 Python 脚本生成交互式 HTML 可视化。创建以下目录结构:
codebase-deps/
├── SKILL.md
└── scripts/
└── deps.py
SKILL.md:
---
name: codebase-deps
description: >
生成代码库的交互式依赖关系图。当用户要求"展示依赖"、
"可视化导入"或"映射代码结构"时使用。
allowed-tools: Bash(python3 *)
---
1. 从项目根目录运行可视化脚本:
```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/deps.py .
- 脚本会生成
dependency-graph.html - 告知用户文件位置,建议在浏览器中打开
**scripts/deps.py**(仅使用标准库,无需 pip install):
```python
#!/usr/bin/env python3
# Python 3.8+,无外部依赖
"""扫描目录中的导入关系并生成 HTML 依赖图。"""
import sys, re, json
from pathlib import Path
def scan_imports(root_dir):
graph = {}
for path in Path(root_dir).rglob("*.py"):
module = str(path.relative_to(root_dir))
imports = []
with open(path) as f:
for line in f:
match = re.match(r'^(?:from|import)\s+([\w.]+)', line)
if match:
imports.append(match.group(1))
graph[module] = imports
return graph
def generate_html(graph, output_path):
nodes = list(graph.keys())
edges = []
for src, targets in graph.items():
for target in targets:
for node in nodes:
if target in node:
edges.append({"source": src, "target": node})
break
html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>依赖图</title></head>
<body>
<h1>依赖关系图({len(nodes)} 个模块)</h1>
<pre>{json.dumps(graph, indent=2)}</pre>
</body></html>"""
with open(output_path, "w") as f:
f.write(html)
if __name__ == "__main__":
root = sys.argv[1] if len(sys.argv) > 1 else "."
graph = scan_imports(root)
generate_html(graph, "dependency-graph.html")
print(f"已生成 dependency-graph.html,包含 {len(graph)} 个模块")
关键设计原则:
- 仅用标准库:零安装步骤,即开即用
allowed-tools: Bash(python3 *):预授权 python3 命令,避免权限弹窗${CLAUDE_SKILL_DIR}:确保无论 Skill 安装在哪个级别,脚本路径都能正确解析
5.2 context: fork 子代理模式
当 Skill 会产生大量输出(测试报告、代码扫描)或需要长时间运行(深度研究)时,用 context: fork 在隔离的上下文中执行:
---
name: test-gaps
description: 分析测试覆盖率并识别缺口
context: fork
agent: Explore
allowed-tools: Read, Grep, Glob, Bash(npm test*, npx jest*)
---
分析当前项目的测试覆盖率:
1. 运行 `npx jest --coverage --silent`(或项目使用的测试命令)
2. 解析覆盖率报告,找到低于 80% 的文件
3. 识别未覆盖的函数和分支
4. 按风险优先级排序(auth > utils,写路径 > 读路径)
输出一个表格,包含文件名、覆盖率百分比和主要缺口。
Fork 模式的核心规则:
- 无对话历史:子代理看不到之前的对话,Skill 内容必须自包含
- 结果摘要返回:子代理完成后退回主对话,只带回结果摘要
- 子代理不能嵌套:Fork 执行的 Skill 不能再 spawn 子代理
A
context: forkskill runs in isolation. It can’t reference what you discussed earlier. Write the skill content to be fully self-contained. 来源
context: fork的 Skill 在隔离环境中运行。它无法引用之前讨论的内容。编写 Skill 内容时必须完全自包含。
Fork 子代理的用户交互限制:子代理运行在隔离上下文中,无法与用户交互。以下工具在子代理中不可用:
AskUserQuestion—— 即使在allowed-tools中声明,fork 子代理也无法调用。模型会退化为纯文本提问(用户看不到)或静默跳过- 权限确认弹窗 —— 遇到未在
allowed-tools中预授权的工具时,不会弹出确认框,直接失败
因此,fork Skill 的设计必须遵循两条硬性规则:
- 所有需要的工具必须预先列入
allowed-tools,不能依赖运行时权限确认 - 需要用户确认或输入的操作不要用 fork——去掉
context: fork回归内联模式,或者拆成两阶段:fork 完成确定性工作后返回结果,由主会话向用户确认再继续
5.3 agent 选择策略
agent 字段决定 Fork 后使用哪种子代理类型:
| Agent | 工具 | 适合 |
|---|---|---|
Explore | 只读(Read, Grep, Glob) | 代码库探索、依赖分析、无副作用的研究 |
Plan | 只读 + 规划 | 架构分析、重构方案设计 |
general-purpose | 全部工具 | 需要写文件、执行命令的复杂任务 |
| 自定义 agent | 取决于定义 | .claude/agents/ 中定义的特定用途 agent |
With
context: fork, you write the task in your skill and pick an agent type to execute it. The built-in Explore and Plan agents skip CLAUDE.md and git status to keep their context small. 来源使用
context: fork时,你在 Skill 中编写任务并选择一种代理类型来执行。内置的 Explore 和 Plan 代理会跳过 CLAUDE.md 和 git status 以保持上下文精简。
内置 agent 直接写名称即可。自定义代理需先在 .claude/agents/ 目录中创建定义文件,然后在 Skill 的 agent 字段引用其名称:
# .claude/agents/code-reviewer.md
---
name: code-reviewer
description: 专项代码审查,关注安全漏洞、性能问题和代码异味
tools: Read, Grep, Glob, Bash(git *)
model: opus
---
你是专项代码审查员。审查代码变更,按安全漏洞、逻辑错误、性能问题、代码异味四级优先级报告。
# SKILL.md 中引用
---
name: security-audit
description: 对当前分支变更做安全审查
context: fork
agent: code-reviewer
---
子代理和 Skill 是两套独立的扩展机制,子代理用 tools(白名单硬边界),Skill 用 allowed-tools(免确认放行),字段名和语义都不同。
5.4 内联 vs Fork 决策指南
需要对话上下文?
├── 是 → 内联(默认)
│ 例:代码审查、交互式重构、需要确认的部署
│
└── 否 → Fork
├── 输出量大 → Explore / general-purpose
│ 例:测试报告、代码扫描、大 diff 分析
│
├── 只读分析 → Explore
│ 例:依赖分析、代码库探索
│
└── 复杂生成 → general-purpose
例:生成项目文档、批量重构
经验法则:如果任务的输出超过 50 行,就该考虑 Fork。
第六章:高级实战——参数传递进阶
前三章介绍了参数传递的基础用法,本章从底层原理出发,展示参数在动态命令、子代理隔离和外部脚本中的高级应用。
6.1 参数替换的底层原理
3.3 节演示了参数"怎么用",这里解释"为什么这样设计"。Skill 加载流程中,参数替换发生在最早阶段:
关键推论:**Step 2(变量替换)在 Step 3(!command)之前**。这意味着 `!`command 中可以使用 $ARGUMENTS——参数可以动态决定执行哪条命令。这是参数传递最强大的用法。
6.2 参数的三种读取方式
参数在 Skill 正文中有三种读取方式,对应不同的使用场景。以如下 Skill 为例:
---
name: demo
arguments: [target, action]
---
对 /demo config.yaml update 这行调用,三种方式得到不同形态的值:
| 方式 | 写法 | 替换结果 | 适用场景 |
|---|---|---|---|
| 全量字符串 | $ARGUMENTS | config.yaml update | 直接传给 shell 命令、单参数 Skill |
| 位置索引 | $0, $1 | config.yaml, update | 参数 ≤ 2 个、不需要命名 |
| 命名参数 | $target, $action | config.yaml, update | 参数 ≥ 3 个、正文中需要语义清晰 |
选择指南:
只有一个参数?
└─ 是 → $ARGUMENTS(最简洁)
有 2 个参数?
└─ 是 → $0 / $1 或命名均可,差别不大
有 3+ 个参数?
└─ 是 → 必须命名(arguments: [...]),否则 $0 $1 $2 $3 无法阅读
参数要直接拼进 shell 命令?
└─ 是 → $ARGUMENTS 最方便(原样传入)
一个 Skill 中可以混用——正文叙述用命名参数增强可读性,命令块用 $ARGUMENTS 保持简洁。
6.3 实战一:参数驱动动态命令
场景:一个 Skill 需要根据参数决定部署到哪个环境,执行不同的检查命令。
---
name: deploy
description: 部署到指定环境和分支
arguments: [env, branch]
argument-hint: "[env] [branch]"
allowed-tools: Bash(docker *) Bash(kubectl *) Bash(git *)
disable-model-invocation: true
---
## 部署 $branch 到 $env
当前分支状态:
!`git log --oneline -5`
环境配置检查:
!`kubectl config get-contexts | grep $env`
执行步骤:
1. 确认 $env 环境可连接(见上方检查结果)
2. 构建镜像:`docker build -t app:$branch .`
3. 推送镜像:`docker push app:$branch`
4. 部署到 $env:`kubectl set image deployment/app app=app:$branch -n $env`
5. 等待就绪:`kubectl rollout status deployment/app -n $env`
调用示例:
/deploy staging hotfix-123
这里 $env 和 $branch 不仅出现在正文指令中,还嵌入了 !command`` 动态注入命令——kubectl config get-contexts | grep staging。参数替换发生在动态注入之前,因此 $env 先被替换为 staging,然后 grep staging 被执行。
6.4 实战二:参数 + Fork 子代理——大任务隔离执行
场景:代码库迁移分析,根据参数指定的源框架和目标框架,让子代理在隔离上下文中完成分析。
---
name: migration-plan
description: 为组件从一种框架迁移到另一种框架生成迁移计划
arguments: [component, from-fw, to-fw]
argument-hint: "[component] [from-fw] [to-fw]"
context: fork
agent: Explore
allowed-tools: Read Grep Glob
---
## 迁移分析任务
分析 $component 组件从 $from-fw 迁移到 $to-fw 的工作量:
1. 找到 $component 的所有源文件(搜索 `src/**/*$component*`)
2. 识别所有 $from-fw 特有的 API 调用、生命周期钩子、装饰器
3. 统计需要改写的文件数和代码行数
4. 列出 $to-fw 中对应机制的映射表
5. 按风险排序输出迁移步骤
输出一个表格:文件路径 | 改写类型 | 复杂度(低/中/高) | 备注
调用:
/migration-plan SearchBar React Vue
Fork 模式下参数仍然正常工作——变量替换发生在 Skill 内容交给子代理之前。子代理收到的就是 $component=SearchBar、$from-fw=React、$to-fw=Vue 已经替换完毕的指令。
6.5 实战三:参数 + 脚本——参数传入外部脚本
场景:生成指定时间范围的变更报告,参数传递给 Python 脚本。
目录结构:
changelog-report/
├── SKILL.md
└── scripts/
└── report.py
---
name: changelog-report
description: 生成指定版本范围的变更报告
arguments: [from-ref, to-ref]
argument-hint: "[from-ref] [to-ref]"
allowed-tools: Bash(git log *) Bash(python3 *)
---
生成 $from-ref 到 $to-ref 的变更报告:
```bash
git log $from-ref..$to-ref --oneline > /tmp/commits.txt
python3 ${CLAUDE_SKILL_DIR}/scripts/report.py /tmp/commits.txt "$from-ref → $to-ref"
脚本 report.py 接收提交记录文件和标题作为参数,输出格式化的变更报告。
调用:
```bash
/changelog-report v2.0.0 v2.1.0
此处参数做了三件事:
- 限定
git log的提交范围 - 传给 Python 脚本作为报告标题
- 出现在正文叙述中帮助 Claude 理解上下文
6.6 三种参数传递模式总结
| 模式 | 用法 | 参数流向 | 典型场景 |
|---|---|---|---|
| 指令模板 | $ARGUMENTS 替换正文中的占位文字 | Skill 正文 | fix-issue、summarize-changes |
| 命令嵌入 | $ARGS 嵌入 !command`` 中 | Shell 命令 | deploy 指定目标环境 |
| 脚本传参 | $ARGS 传给 scripts/ 中的脚本 | Python/Bash 脚本 | changelog-report、migration-plan |
三种模式可以任意组合——一个 Skill 可以同时在正文、动态注入命令和外部脚本中使用参数。
6.7 参数设计原则
- 位置参数 ≤ 3 个:超过 3 个位置参数用户很难记住顺序,考虑用命名参数或拆成多个 Skill
- 用
argument-hint降低记忆成本:[env] [branch]比空白好得多 - 声明
arguments列表提升可读性:$env比$0在正文中更清晰 - 参数永远不可信:Skill 正文中应描述参数为空或无效时的默认行为
disable-model-invocation: true+ 参数 = 安全的可复用命令:确保只有人决定何时执行,参数仅控制执行方式
6.8 参数缺失时的处理策略
前面所有示例都假设参数正常传入。实际使用中,参数缺失有两种常见场景。
场景一:自动触发时没有命令行参数
Skill 被 Claude 根据 description 语义匹配自动加载时,用户没有输入 /skill-name args,$ARGUMENTS 为空字符串。变量替换后 Claude 看到的是留白的指令。
以 deploy Skill 为例,正文中的 部署 $branch 到 $env 会变成 部署 到 。如果正文只写了执行步骤,Claude 会因信息不足而胡乱猜测。
解决方式:在正文末尾添加参数缺失引导,让 Claude 主动询问:
执行步骤:
1. 确认 $env 环境可连接
...
如果 $env 或 $branch 为空,询问用户:
- 目标环境?(staging / production / dev)
- 分支名或 commit hash?
收到信息后继续执行。
这样无论 Skill 是手动调用还是自动触发,参数缺失时 Claude 都会先收集信息再行动。
场景二:用户手动调用但省略参数
用户输入 /deploy 没有跟参数,或者只传了一个 /deploy staging。
处理方式同样是正文中写清引导逻辑,并配合 arguments 声明使缺失一目了然。当 $1(即 $branch)为空时,正文中的检查语句触发:
如果 $branch 为空,先运行 `git branch --show-current` 获取当前分支名,
然后向用户确认:"在当前分支 xxx 上部署到 $env?"
用户确认后再执行部署。
这里的关键区别:$env 有值(用户传了 staging),$branch 为空 → Claude 主动获取默认值并请求确认,而非直接失败。
核心原则:永远在 Skill 正文中写入参数缺失时的行为——“如果 $ARGUMENTS 为空,询问用户 X"或"默认使用 Y”。一句话就能避免 Claude 在参数缺失时产生不确定行为。
第七章:高级实战——多 Skill 组合
单个 Skill 能做的事有限。当工作流涉及多个独立步骤时,与其塞进一个巨型 SKILL.md,不如拆成多个 Skill 组合使用。Skill 之间通过三种机制交互:共享对话上下文、文件充当接口、子代理隔离执行。
7.1 组合的基础机制
机制一:同一会话中先后加载
Skill A 激活后其内容留在上下文中,用户继续对话时 Skill B 也可被触发。A 加载的知识和结果对 B 可见,无需重复传递。
机制二:Skill 工具互相调用
一个 Skill 的正文可以直接指示 Claude 调用另一个 Skill:
数据分析完成,报告已写入 `analysis.md`。调用 `/format-report` 将报告转为演示文稿格式。
配合 allowed-tools: Skill(xxx *) 预授权调用:
---
name: full-audit
description: 完整项目审计
allowed-tools: Skill(dependency-audit *) Skill(security-review *)
---
机制三:文件系统作为中间接口
Skill A 产出文件 → Skill B 读取。路径约定是唯一的接口契约:
# Skill A 末尾
结果已写入 `temp/audit-result.json`。
# Skill B 开头
读取 `temp/audit-result.json`。若不存在,提示用户先运行审计 Skill。
7.2 三种组合模式
模式一:管道组合(Pipeline)
A → B → C 顺序执行,每步读取上一步的文件输出。适用于:数据分析 → 格式化 → 发布。
模式二:委托组合(Delegation)
主 Skill 通过 fork 将子任务分派给其他 Skill,子任务在隔离上下文中执行,结果汇总返回。适用于:审计(并行扫描依赖 + 安全 + 性能,最后合并报告)。
模式三:分层引用(Layered Reference)
知识类 Skill(user-invocable: false)提供领域规范上下文,操作类 Skill 利用已有知识执行。适用于:API 规范 Skill + 端点生成 Skill。
7.3 注意事项
多 Skill 的上下文管理有明确预算:所有已加载 Skill 在压缩时共享 25,000 token 上限,单个 Skill 最多保留 5,000 token,最近加载的 Skill 优先保留。先加载的 Skill 在长会话中可能被挤出。
引用其他 Skill 前应验证存在性——如果目标 Skill 被删除或重命名,调用会静默失败。
多 Skill vs 单 Skill + Steps 文件:两者都做了文件拆分,区别在于触发粒度。单 Skill + Steps 文件是固定流程的内部拆解,Steps 无法独立调用;多 Skill 组合中每个 Skill 有独立的 /command 和 description,可被任意其他 Skill 引用。选择标准:步骤有独立使用场景或被其他 Skill 引用 → 多 Skill 组合;步骤只服务于一个固定流程 → 单 Skill + steps/ 文件拆分。
第八章:从 Skill 到 Plugin——打包与分发
8.1 独立 Skill vs Plugin
| 特性 | 独立 Skill(.claude/skills/) | Plugin |
|---|---|---|
| 调用名 | /hello | /my-plugin:hello |
| 作用范围 | 当前项目或个人 | 可跨项目、跨仓库 |
| 分发方式 | 手动复制或 git 提交 | Marketplace 安装 |
| 打包能力 | 仅 Skill | Skill + Agent + Hook + MCP |
| 版本管理 | 无 | marketplace.json 版本/SHA |
Use standalone configuration in
.claude/for quick iteration, then convert to a plugin when you’re ready to share. 来源在
.claude/中使用独立配置进行快速迭代,准备好分享时再转换为 Plugin。
8.2 将 Skill 升级为 Plugin
给 Skill 目录添加 .claude-plugin/plugin.json 即可升级为 Plugin:
my-tool/
├── .claude-plugin/
│ └── plugin.json
└── skills/
└── hello/
└── SKILL.md
plugin.json 最小配置:
{
"name": "my-tool",
"version": "1.0.0",
"description": "我的自定义工具集"
}
放在 ~/.claude/skills/my-tool/ 下,下次启动 Claude Code 时会自动发现为 my-tool@skills-dir Plugin。这就是 plugin init 命令的本质——在 .claude/skills/<name>/ 下生成脚手架。
Any folder under a skills directory that contains a
.claude-plugin/plugin.jsonmanifest is loaded as a plugin named@skills-diron the next session, with no marketplace and no install step. 来源Skill 目录下任何包含
.claude-plugin/plugin.json清单的文件夹,都会在下次会话中以@skills-dir为名作为 Plugin 加载,无需 Marketplace 和安装步骤。
8.3 创建 Marketplace 分发
要分享给团队或社区,需要创建 Marketplace。核心是仓库根目录下的 .claude-plugin/marketplace.json:
{
"name": "my-team-tools",
"owner": {
"name": "My Team",
"email": "team@example.com"
},
"plugins": [
{
"name": "code-review-toolkit",
"description": "代码审查辅助工具集",
"version": "1.0.0",
"source": "./code-review-toolkit",
"category": "developer-tools",
"tags": ["code-review", "quality"]
}
]
}
支持的 source 类型:
| Source | 格式 | 说明 |
|---|---|---|
| 相对路径 | "./my-plugin" | 仓库内路径,需以 ./ 开头 |
| GitHub | {"repo": "owner/repo", "ref": "main"} | 指定仓库和分支/commit |
| Git URL | {"url": "https://gitlab.com/your-org/repo", "ref": "main"} | 任意 Git 托管(示例) |
| npm | {"package": "my-plugin"} | npm 包分发 |
Host on GitHub: Create a repository, add
.claude-plugin/marketplace.json, share with/plugin marketplace add owner/repo. 来源托管到 GitHub:创建仓库,添加
.claude-plugin/marketplace.json,然后通过/plugin marketplace add owner/repo分享。
用户安装只需两条命令:
# Claude Code 中
/plugin marketplace add your-org/claude-plugins
/plugin install code-review-toolkit@my-team-tools
8.4 团队协作最佳实践
自动提示安装
在项目的 .claude/settings.json 中配置 extraKnownMarketplaces,团队成员 trust 仓库后会自动收到安装提示:
{
"extraKnownMarketplaces": {
"my-team-tools": {
"source": {
"source": "github",
"repo": "your-org/claude-plugins"
}
}
}
}
Scope 选择指南
| Scope | 写入位置 | 用途 |
|---|---|---|
user | ~/.claude/settings.json | 个人跨项目使用(默认) |
project | .claude/settings.json | 团队共享,纳入版本控制 |
local | .claude/settings.local.json | 仅本项目个人使用,gitignored |
版本策略
- 正式发布版:在
plugin.json中设置显式version,用户只在版本号变化时获得更新 - 开发迭代版:省略
version,每次 commit 即为一次更新(commit SHA 作为版本标识)
第九章:Skill 编写最佳实践与常见陷阱
9.1 写作风格
Skill 是给 AI 读的,不是给人读的。使用祈使句/不定式(verb-first instructions),而非第二人称:
# 好的写法
To generate the report, run the script with the input file.
Check the output for errors before proceeding.
# 避免的写法
You should run the script with the input file.
If you find any errors, you should fix them.
Write the entire skill using imperative/infinitive form (verb-first instructions), not second person. 来源
整个 Skill 使用祈使句/不定式(动词优先指令)编写,不要使用第二人称。
同时,陈述"做什么"而非"为什么"。正文一旦加载就在上下文里持续占用 token,每行都是成本。
9.2 渐进式信息披露
Skill 采用三层信息架构,逐层深入:
Level 1: description(始终在上下文,~100 词)
└── Level 2: SKILL.md 正文(Skill 触发时加载,<5000 词)
└── Level 3: references/ + scripts/ + assets/(按需加载,无上限)
设计原则:
- description 让 Claude 知道何时使用这个 Skill
- SKILL.md 正文告诉 Claude 如何执行
- references/ 提供深入细节
- 不要把相同的详细内容同时放在 SKILL.md 和 references/ 中
9.3 安全与隐私
公开发布 Skill 时的硬性禁令:
- 禁止绝对路径:
/home/username/、/Users/username/、OneDrive 路径 - 禁止个人信息:用户名、公司名、部门名、产品名
- 禁止安装路径:
~/.claude/skills/之类的硬编码路径 - 允许相对路径:
scripts/example.py、references/guide.md - 允许占位符:
~/workspace/project、username、your-company
CRITICAL: Skills intended for public distribution must not contain user-specific or company-specific information. 来源
重要:公开发布的 Skill 不得包含用户特定或公司特定的信息。
对于团队共享的 Skill,额外注意:
- 脚本可以访问文件系统、环境变量和网络——提交到共享仓库的脚本会在每个团队成员的机器上执行
- Code Review 你的 Skill 至少和 Review 代码一样严格
9.4 常见错误
1. allowed-tools 不生效
allowed-tools 是授予,不是限制。如果权限设置中已有 deny 规则,deny 会覆盖 allowed-tools 的 grant。解决方法:检查 settings.json 中的 permissions.deny 配置。
2. context: fork 输出为空
Fork 技能需要明确的任务指令。只写指导方针而没有具体任务,子代理会返回空结果。Skill 正文必须以"做什么"开头,而非"要知道什么"。
3. 路径引用错误
永远使用 ${CLAUDE_SKILL_DIR}/scripts/script.py 而非相对路径或 ~ 路径。Claude Code 的执行环境因启动位置而异,${CLAUDE_SKILL_DIR} 是唯一可靠的路径方案。
4. YAML 缩进问题
YAML 用空格,不是 Tab。多行 description 使用 > 或 | 折叠符时注意缩进层级:
# 正确
description: >
第一行描述,
续行与第一行对齐。
# 错误
description: >
续行多缩进了 4 格,解析结果不符预期。
5. Fork 技能引用对话上下文
Fork 执行的子代理看不到主对话历史。如果 Skill 内容写了"参考刚才讨论的方案",子代理无法理解。Fork Skill 必须完全自包含。
6. Skill 放在子目录中无法识别
Skill 目录不支持递归发现——Claude Code 只在 skills/ 下扫描一层。~/.claude/skills/category/my-skill/SKILL.md 或 .claude/skills/group/tool/SKILL.md 这种嵌套结构不会被注册,手动 /category/my-skill 会提示 “Unknown skill”。官方文档中的 “nested skills” 指的是 monorepo 各子包的独立 .claude/skills/ 目录(如 packages/frontend/.claude/skills/),不是 skills 目录内部的递归子目录。解决方法:将所有 Skill 扁平化放置,用命名前缀(如 group--skill-name)替代子目录分类。
总结
从 10 行的 summarize-changes 到带 Python 脚本的 codebase-deps,再到发布在 Marketplace 上的 Plugin——Skill 的能力随需求增长而平滑扩展:
个人效率工具
├─ 入门:SKILL.md
├─ 进阶:+ scripts
├─ 增强:+ 参数传递
├─ 高级:+ fork 子代理
└─ 完善:+ 渐进式信息披露
团队共享基础设施
├─ Plugin 化
├─ Marketplace 分发
└─ 团队 settings 自动安装
三个核心原则值得记住:
- 描述决定触发——
description是 Skill 能否被发现的关键,花时间打磨它 - 辅助文件按需加载——保持 SKILL.md 精简,详细内容移入 references/
- Fork 隔离执行——大输出任务放子代理,保护主对话上下文的干净和响应速度
Skill 的价值不在一开始写得有多复杂,而在于你愿意把重复的事情交给它。一个今天写完的 20 行 Skill,未来一年可能为你省下数百次复制粘贴。现在就开始创建你的第一个 Skill 吧。
参考
- Extend Claude with skills - Claude Code Docs
- Creating custom skills - Claude.ai Documentation
- skill-creator/SKILL.md - GitHub
- Every SKILL.md Frontmatter Field - Claude Code Guides
- Claude Skills with Embedded Scripts - Claude Code Guides
- Claude Code Skills: The Complete Guide (2026) - vanja.io
- Plugins reference - Claude Code Docs
- Create plugins - Claude Code Docs
- Create and distribute a plugin marketplace - Claude Code Docs
- Create custom subagents - Claude Code Docs
- How to Build & Install Claude Skills: Dev Guide - Verdent Guides
