Claude Code 扩展

Claude Code 扩展

使用 Skills 扩展 Claude

13 分钟阅读

用 Skills 扩展 Claude

创建、管理和分享技能,以扩展 Claude 在 Claude Code 中的能力。包括自定义命令和内置技能。

技能能扩展 Claude 的能力。创建一个包含指令的 SKILL.md 文件,Claude 就会把它加入自己的工具箱。Claude 会在合适时使用技能,你也可以用 /skill-name 直接调用某个技能。

当你发现自己反复把同样的指令、清单或多步骤流程粘贴进对话中时,或当 CLAUDE.md 中的某一节已经长成了一个流程而不再是一条事实性描述时,就应该创建一个技能。与 CLAUDE.md 的内容不同,一个技能的正文只在被使用时才会加载,因此在你真正需要之前,长篇的参考资料几乎不会产生任何成本。

关于 /help/compact 这样的内置命令,以及 /debug/code-review 这样的内置技能,请参阅命令参考

自定义命令已并入技能。 .claude/commands/deploy.md 文件与 .claude/skills/deploy/SKILL.md 技能都会创建 /deploy,且工作方式相同。你现有的 .claude/commands/ 文件仍会正常工作。技能新增了一些可选特性:一个用于存放辅助文件的目录、用于控制由你还是由 Claude 调用它们的 frontmatter,以及让 Claude 在合适时自动加载它们的能力。

Claude Code 的技能遵循 Agent Skills 这一开放标准,可跨多种 AI 工具使用。Claude Code 在该标准之上扩展了一些附加特性,例如调用权限控制子智能体执行动态上下文注入

内置技能

Claude Code 内置了一批技能,除非用 disableBundledSkills 设置禁用,否则它们在每个会话中都可用,包括 /doctor/code-review/batch/debug/loop/claude-api。与大多数直接执行固定逻辑的内置命令不同,内置技能是基于提示词的:它们为 Claude 提供详细的指令,让 Claude 用自己的工具来组织完成这项工作。调用它们的方式与调用其他任何技能一样,输入 / 加上技能名称即可。

在 Claude Code v2.1.205 及更高版本中,/doctor 安装体检是 disableBundledSkills 的唯一例外:即使该设置开启,它仍可输入使用。要隐藏它,请设置 DISABLE_DOCTOR_COMMAND 环境变量,或在 skillOverrides 中加入 "doctor": "off" 条目。在 v2.1.205 之前,/doctor 是一个内置命令,而不是内置技能。

内置技能与内置命令一起列在命令参考中,在“用途”列中标注为Skill

运行并验证你的应用

三个内置技能协同工作,用于启动你的应用,并对照正在运行的应用(而不仅仅是测试)来确认改动:

技能用途
/run启动并操作你的应用,观察某个改动是否生效
/verify构建并运行你的应用,确认某次代码改动确实达到了预期效果,而不是退而依赖测试或类型检查
/run-skill-generator教会 /run/verify 如何构建和启动你的项目

这三个技能都需要 Claude Code v2.1.145 或更高版本。

/run/verify 无需任何搭建即可使用。它们会根据你项目的类型(CLI、服务器、TUI、浏览器驱动)以及 README、package.jsonMakefile 中的内容来推断启动方式。对于需要超出标准启动流程之外的项目(数据库、环境文件、图形化会话、多步骤构建),这种推断就不那么可靠了。

/run-skill-generator 会改为记录这套配方。它会从一个干净的环境把你的应用启动起来,捕获奏效的方法(安装命令、环境变量、启动脚本),并将其提交为一个项目级技能,保存在 .claude/skills/run-<name>/。之后,/run/verify 以及仓库中的任何其他智能体都会遵循这份记录下来的配方,而不必重新摸索一遍。每个项目运行一次 /run-skill-generator,如果构建或启动流程发生变化,再重新运行一次。

快速开始

创建你的第一个技能

以下示例创建一个技能,用于总结你 git 仓库中尚未提交的更改,并标记出任何有风险的内容。它会在 Claude 读取提示词之前,先把实时的 diff 拉取到提示词中,因此回复会以你实际的工作树为依据,而不是 Claude 根据已打开文件猜测出的内容。当你询问你的更改时,Claude 会自动加载这个技能,你也可以用 /summarize-changes 直接调用它。

1

创建技能目录

在你的个人技能文件夹中为该技能创建一个目录。个人技能在你的所有项目中都可用。

mkdir -p ~/.claude/skills/summarize-changes
2

编写 SKILL.md

每个技能都需要一个 SKILL.md 文件,包含两部分:--- 标记之间的 YAML frontmatter,用于告诉 Claude 何时使用该技能;以及 markdown 内容,即该技能运行时 Claude 要遵循的指令。目录名会成为你输入的命令,description 则帮助 Claude 决定何时自动加载该技能。

将以下内容保存到 ~/.claude/skills/summarize-changes/SKILL.md

---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---

## Current changes

!`git diff HEAD`

## Instructions

Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.

!`git diff HEAD` 这一行用到了动态上下文注入:Claude Code 会运行该命令,并在 Claude 看到该技能内容之前,用其输出替换这一行,因此这些指令送达时已经内嵌了当前的 diff。

3

测试该技能

打开一个 git 项目,对任意文件做一个小的编辑,然后运行 claude 启动 Claude Code。你可以用两种方式测试这个技能。

让 Claude 自动调用它,提出一个与描述相匹配的问题:

What did I change?

或者直接调用它,输入技能名称:

/summarize-changes

无论哪种方式,Claude 都应该回复一份关于你的编辑的简短摘要,以及一份风险列表。

技能存放位置

你存放某个技能的位置决定了谁可以使用它:

位置路径适用范围
企业参阅统一管理设置你组织中的所有用户
个人~/.claude/skills/<技能名称>/SKILL.md你的所有项目
项目.claude/skills/<技能名称>/SKILL.md仅此项目
插件<插件>/skills/<技能名称>/SKILL.md插件启用所在范围

当各级别中存在同名技能时,企业级会覆盖个人级,个人级会覆盖项目级。这些级别中任意一级的技能,也会覆盖同名的内置技能。例如,你项目 .claude/skills/ 中的 code-review 技能会替换内置的 /code-review。插件技能使用 plugin-name:skill-name 命名空间,因此不会与其他级别发生冲突。如果你在 .claude/commands/ 中有文件,它们的工作方式相同,但如果某个技能与某个命令同名,技能优先。

技能还会从工作目录下方嵌套的 .claude/skills/ 目录中加载。当 Claude 读取或编辑某个子目录中的文件时,该子目录 .claude/skills/ 中的技能就会变为可用。这让单体仓库中的某个软件包可以提供自己的技能,在处理该软件包时生效,即使会话是从仓库根目录启动的。

如果一个嵌套技能与另一个技能同名,两者都会保持可用。例如,项目根目录有一个 deploy 技能,apps/web/.claude/skills/ 中还有另一个:

  • 嵌套的那个会以带目录限定的名称出现,即 apps/web:deploy
  • 它的描述会说明它适用于哪个目录。
  • Claude 会选择与它正在处理的文件相匹配的那个版本。

输入 /deploy 会运行项目根目录的技能。输入限定名称 /apps/web:deploy 可显式运行嵌套版本。

当你或 Claude 调用未限定的名称时,会加载项目根目录的技能,Claude Code 会在其内容后附加一份带目录限定名称的各版本列表,并附带一条指令,说明如果 Claude 正在处理的文件所在目录中有某个版本,也应一并调用它。因此,即使只调用了未限定的名称,嵌套技能仍会适用于其所在目录中的工作。需要 Claude Code v2.1.203 或更高版本。

企业、个人或项目位置中的一个 <skill-name> 条目,可以是指向磁盘上其他位置某个目录的符号链接。Claude Code 会跟随该符号链接,从目标目录读取 SKILL.md,如果同一个目标可以从多个位置访问到,Claude Code 只会加载这个技能一次。插件技能对符号链接的处理方式不同;请参阅用符号链接在市场内共享文件

在某个技能文件夹中添加一个 .claude-plugin/plugin.json,它就会作为名为 <name>@skills-dir插件加载,因此它可以打包智能体、钩子和 MCP 服务器。在项目的 .claude/skills/ 中,这需要先接受工作区信任对话框。

实时变更检测

Claude Code 会监视技能目录的文件变化。在 ~/.claude/skills/、项目的 .claude/skills/,或 --add-dir 目录内部的 .claude/skills/ 下添加、编辑或移除一个技能,都会在当前会话中立即生效,无需重启。创建一个会话启动时不存在的顶层技能目录,则需要重启 Claude Code,才能监视这个新目录。

实时变更检测只覆盖 SKILL.md 文本。对于同时也是插件的技能文件夹,对 hooks/.mcp.jsonagents/output-styles/ 的更改需要运行 /reload-plugins 才能生效。

从父目录和嵌套目录自动发现

项目技能会从你的启动目录以及直到仓库根目录之间的每一个父目录中的 .claude/skills/ 加载,因此即使在子目录中启动 Claude,也仍能获取到在根目录定义的技能。当你处理启动目录下方子目录中的文件时,Claude Code 还会按需从嵌套的 .claude/skills/ 目录中发现技能。例如,如果你正在编辑 packages/frontend/ 中的一个文件,Claude Code 也会在 packages/frontend/.claude/skills/ 中查找技能。这支持了单体仓库中各软件包拥有自己技能的场景。

每个技能都是一个以 SKILL.md 作为入口的目录:

my-skill/
├── SKILL.md           # Main instructions (required)
├── template.md        # Template for Claude to fill in
├── examples/
│   └── sample.md      # Example output showing expected format
└── scripts/
    └── validate.sh    # Script Claude can execute

SKILL.md 包含主要指令,是必需的。其他文件是可选的,能让你构建更强大的技能:供 Claude 填写的模板、展示预期格式的示例输出、Claude 可以执行的脚本,或详细的参考文档。在你的 SKILL.md 中引用这些文件,让 Claude 知道它们包含什么内容以及何时加载它们。详情请参阅添加辅助文件

.claude/commands/ 中的文件仍可正常工作,并支持相同的frontmatter。由于技能支持辅助文件等附加特性,推荐使用技能。

来自额外目录的技能

--add-dir 标志和 /add-dir 命令授予文件访问权限,而不是配置发现,但技能是一个例外:某个被添加目录内部的 .claude/skills/ 会被自动加载。这个例外只适用于 --add-dir/add-dirsettings.json 中的 permissions.additionalDirectories 设置只授予文件访问权限,不会加载技能。关于会话期间如何检测到编辑,请参阅实时变更检测

其他 .claude/ 配置(例如命令和输出风格)不会从额外目录中加载。关于会加载和不会加载的完整列表,以及跨项目共享配置的推荐方式,请参阅例外情况表

来自 --add-dir 目录的 CLAUDE.md 文件默认不会加载。要加载它们,请设置 CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1。请参阅从额外目录加载

配置技能

技能通过 SKILL.md 顶部的 YAML frontmatter 及其后的 markdown 内容进行配置。

技能内容的类型

技能文件可以包含任何指令,但想清楚你打算如何调用它,有助于指导你该写入什么内容:

参考类内容为 Claude 当前的工作添加知识:约定、模式、风格指南、领域知识。这类内容会以内联方式运行,因此 Claude 可以将其与你的对话上下文一起使用。

---
name: api-conventions
description: API design patterns for this codebase
---

When writing API endpoints:
- Use RESTful naming conventions
- Return consistent error formats
- Include request validation

任务类内容为 Claude 提供针对某个具体操作(例如部署、提交或代码生成)的分步指令。这些通常是你想用 /skill-name 直接调用、而不想让 Claude 自行决定何时运行的操作。加上 disable-model-invocation: true 可阻止 Claude 自动触发它。

---
name: deploy
description: Deploy the application to production
context: fork
disable-model-invocation: true
---

Deploy the application:
1. Run the test suite
2. Build the application
3. Push to the deployment target

你的 SKILL.md 可以包含任何内容,但想清楚你希望这个技能由谁调用(你自己、Claude,还是两者都可以)以及它应运行在哪里(内联还是在子智能体中),有助于指导你该写入什么内容。对于复杂的技能,你还可以添加辅助文件,让主技能保持聚焦。

保持正文本身简洁。一旦某个技能加载,其内容会在多个轮次间一直留在上下文中,因此每一行都会带来重复的 Token 成本。陈述该做什么,而不是叙述如何做或为什么做,并应用与CLAUDE.md 内容相同的简洁性标准。

frontmatter 参考

除了 markdown 内容之外,你还可以在 SKILL.md 文件顶部 --- 标记之间的 YAML frontmatter 字段中配置技能行为:

---
name: my-skill
description: What this skill does
disable-model-invocation: true
allowed-tools: Read Grep
---

Your skill instructions here...

所有字段都是可选的。只推荐设置 description,以便 Claude 知道何时使用该技能。

字段是否必需说明
name显示在技能列表中的名称。默认为目录名。关于这与你输入以调用该技能的名称有何不同,请参阅技能命令名称的来源
description推荐该技能做什么、何时使用它。Claude 用它来决定何时应用该技能。如果省略,则使用 markdown 内容的第一段。把关键使用场景放在最前面:descriptionwhen_to_use 合并后的文本,在技能列表中会被截断为 1,536 个字符,以降低上下文用量。
when_to_use关于 Claude 何时应调用该技能的额外上下文,例如触发短语或示例请求。会附加在技能列表中的 description 之后,并计入 1,536 字符的上限。
argument-hint自动补全时显示的提示,用于指示预期的参数。示例:[issue-number][filename] [format]
arguments在技能内容中用于 $name 替换的具名位置参数。接受一个以空格分隔的字符串,或一个 YAML 列表。名称按顺序映射到参数位置。
disable-model-invocation设为 true 可阻止 Claude 自动加载该技能。用于你想用 /name 手动触发的工作流。同时也会阻止该技能被预加载到子智能体中从 v2.1.196 开始,也会阻止该技能在某个定时任务以该技能作为提示词触发时运行。默认值:false
user-invocable设为 false 可从 / 菜单中隐藏。用于用户不应直接调用的后台知识。默认值:true
allowed-tools当该技能处于活动状态时,Claude 无需征得许可即可使用的工具。接受以空格或逗号分隔的字符串,或一个 YAML 列表。
disallowed-tools当该技能处于活动状态时,从 Claude 可用工具池中移除的工具。用于那些永远不应调用某些工具的自主型技能,例如后台循环不应调用 AskUserQuestion。接受以空格或逗号分隔的字符串,或一个 YAML 列表。该限制会在你发送下一条消息时清除。
model该技能处于活动状态时使用的模型。这个覆盖只在当前轮次剩余时间内生效,不会保存到设置中;会话模型会在你下一次提示词中恢复。接受与 /model 相同的值,或用 inherit 保持当前活动模型。如果该值被你组织的 availableModels 允许列表排除,则不会被使用,会话会保持当前模型。
effort该技能处于活动状态时的effort 级别。覆盖会话的 effort 级别。默认值:继承自会话。选项:lowmediumhighxhighmax;可用级别取决于模型。
context设为 fork 可在一个分叉子智能体上下文中运行。
agent当设置了 context: fork 时,使用哪种子智能体类型。
hooks限定于该技能生命周期的钩子。关于配置格式,请参阅技能和智能体中的钩子
paths限制该技能何时被激活的 glob 模式。接受一个逗号分隔的字符串,或一个 YAML 列表。设置后,Claude 只会在处理匹配这些模式的文件时自动加载该技能。使用与路径特定规则相同的格式。
shell该技能中 !`command````! 代码块使用的 shell。接受 bash(默认)或 powershell。设为 powershell 会在 Windows 上通过 PowerShell 运行内联 shell 命令。需要 CLAUDE_CODE_USE_POWERSHELL_TOOL=1

技能命令名称的来源

你用来调用某个技能的命令,取决于该技能文件所在的位置。frontmatter 中的 name 字段设置的是技能列表中显示的名称标签,除了插件根目录的 SKILL.md 之外,它不会改变你在 / 之后输入的内容。

下表展示了每种布局下命令名称的来源:

技能位置命令名称来源示例
~/.claude/skills/.claude/skills/ 下的技能目录目录名.claude/skills/deploy-staging/SKILL.md/deploy-staging
嵌套.claude/skills/ 目录,当名称与另一个技能冲突时相对工作目录的子目录路径,再加上技能目录名apps/web/.claude/skills/deploy/SKILL.md/apps/web:deploy
.claude/commands/ 下的文件不含扩展名的文件名.claude/commands/deploy.md/deploy
插件的 skills/ 子目录目录名,以插件命名空间限定my-plugin/skills/review/SKILL.md/my-plugin:review
插件根目录的 SKILL.mdfrontmatter 中的 name,以插件目录名作为后备my-plugin/SKILL.md,其中 name: review/my-plugin:review。请参阅路径行为规则

插件根目录这种情况是唯一 name 会真正决定命令名称的地方,因为这里没有技能目录可供取名。如果 frontmatter 中未设置 name,则会改用该插件的目录名。

可用的字符串替换

技能支持对技能内容中的动态值进行字符串替换:

变量说明
$ARGUMENTS调用该技能时传入的所有参数。如果内容中没有出现 $ARGUMENTS,参数会以 ARGUMENTS: <value> 的形式追加在末尾。
$ARGUMENTS[N]通过从 0 开始的索引访问某个特定参数,例如 $ARGUMENTS[0] 表示第一个参数。
$N$ARGUMENTS[N] 的简写形式,例如 $0 表示第一个参数,$1 表示第二个。
$namearguments frontmatter 列表中声明的具名参数。名称按顺序映射到位置,因此使用 arguments: [issue, branch] 时,占位符 $issue 会展开为第一个参数,$branch 展开为第二个。
${CLAUDE_SESSION_ID}当前会话 ID。适用于日志记录、创建会话专属文件,或将技能输出与会话关联。
${CLAUDE_EFFORT}当前的 effort 级别:lowmediumhighxhighmax。Ultracode 不是一个独立的级别,会报告为 xhigh。可用它让技能指令适配当前生效的 effort 设置。
${CLAUDE_SKILL_DIR}包含该技能 SKILL.md 文件的目录。对于插件技能,这是该技能在插件内部的子目录,而不是插件根目录。在 bash 注入命令中使用它来引用该技能自带的脚本或文件,而不受当前工作目录的影响。
${CLAUDE_PROJECT_DIR}项目根目录。这与钩子和 MCP 服务器收到的 CLAUDE_PROJECT_DIR 是同一路径。可用它引用项目本地的脚本或文件,例如 ${CLAUDE_PROJECT_DIR}/.claude/hooks/helper.sh,与该技能安装在何处无关。

${CLAUDE_PROJECT_DIR} 替换需要 Claude Code v2.1.196 或更高版本。它同时适用于技能正文和 allowed-tools frontmatter,因此像 Bash(${CLAUDE_PROJECT_DIR}/scripts/lint.sh *) 这样的权限规则,会解析为与技能正文中使用的相同路径。

带索引的参数使用类似 shell 的引号规则,因此要将多词值作为单个参数传入,请用引号将其括起来。例如,/my-skill "hello world" second 会让 $0 展开为 hello world$1 展开为 second$ARGUMENTS 占位符始终展开为你输入的完整参数字符串。

要在数字、ARGUMENTS 或某个已声明的参数名之前包含一个字面的 $(例如正文中的 $1.00),请用反斜杠转义它:\$1.00。任何其他 $ 之前的反斜杠会保持不变。只有紧挨在该符号之前的单个反斜杠才会转义它。像 \\$1 这样的双反斜杠会让两个反斜杠都保留原样,$1 仍会展开为参数值。

使用替换的示例:

---
name: session-logger
description: Log activity for this session
---

Log the following to logs/${CLAUDE_SESSION_ID}.log:

$ARGUMENTS

添加辅助文件

技能可以在其目录中包含多个文件。这能让 SKILL.md 专注于要点,同时让 Claude 只在需要时访问详细的参考资料。大型参考文档、API 规范或示例集合,不需要每次该技能运行时都加载进上下文。

my-skill/
├── SKILL.md (required - overview and navigation)
├── reference.md (detailed API docs - loaded when needed)
├── examples.md (usage examples - loaded when needed)
└── scripts/
    └── helper.py (utility script - executed, not loaded)

SKILL.md 中引用辅助文件,让 Claude 知道每个文件包含什么内容以及何时加载它:

## Additional resources

- For complete API details, see [reference.md](reference.md)
- For usage examples, see [examples.md](examples.md)
SKILL.md 保持在 500 行以内。把详细的参考资料移到单独的文件中。

控制由谁调用某个技能

默认情况下,你和 Claude 都可以调用任何技能。你可以输入 /skill-name 直接调用它,Claude 也可以在与你的对话相关时自动加载它。两个 frontmatter 字段可以限制这一点:

  • disable-model-invocation: true:只有你可以调用该技能。用于那些有副作用、或你想自己控制触发时机的工作流,例如 /commit/deploy/send-slack-message。你不希望 Claude 因为觉得代码看起来准备好了就自行决定部署。

  • user-invocable: false:只有 Claude 可以调用该技能。用于那些作为命令并不具备可执行意义的后台知识。一个 legacy-system-context 技能解释某个老系统的工作方式。Claude 在相关时应该知道这些,但 /legacy-system-context 对用户来说并不是一个有意义的操作。

以下示例创建了一个只有你能触发的部署技能。disable-model-invocation: true 字段阻止 Claude 自动运行它:

---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
---

Deploy $ARGUMENTS to production:

1. Run the test suite
2. Build the application
3. Push to the deployment target
4. Verify the deployment succeeded

以下是这两个字段对调用方式和上下文加载的影响:

Frontmatter你可以调用Claude 可以调用何时加载进上下文
(默认)description 始终在上下文中,被调用时加载完整技能
disable-model-invocation: truedescription 不在上下文中,你调用时加载完整技能
user-invocable: falsedescription 始终在上下文中,被调用时加载完整技能

在常规会话中,技能描述会加载到上下文中,让 Claude 知道有哪些技能可用,但完整的技能内容只有在被调用时才会加载。预加载了技能的子智能体的工作方式不同:完整的技能内容会在启动时注入。

技能内容的生命周期

当你或 Claude 调用某个技能时,渲染后的 SKILL.md 内容会作为一条消息进入对话,并在会话剩余时间内保留在那里。Claude Code 不会在之后的轮次中重新读取该技能文件,因此应把需要在整个任务过程中生效的指导,写成常设指令,而不是一次性步骤。

当 Claude 重新调用一个渲染内容与上下文中已有副本完全相同的技能时,Claude Code 会加上一条简短的说明,指出该技能已经加载,而不是再插入一份内容副本。当渲染内容有所不同——因为参数变了,或某个动态上下文命令产生了新的输出——Claude Code 会再次附加完整内容。在 v2.1.202 之前,每次重新调用都会追加另一份完整的技能指令副本。

自动压缩会在一定的 Token 预算内延续已调用的技能。当对话被摘要以释放上下文时,Claude Code 会在摘要之后重新附加每个技能最近一次的调用内容,各保留前 5,000 个 Token。重新附加的技能共享一个 25,000 个 Token 的总预算。Claude Code 会从最近调用的技能开始填充这个预算,因此如果你在一个会话中调用了很多技能,较旧的技能在压缩后可能会被完全丢弃。

如果某个技能在第一次回复之后似乎不再影响行为,通常是因为其内容仍然存在,只是模型选择了其他工具或方法。可以强化该技能的 description 和指令,让模型持续偏好使用它,或使用钩子以确定性方式强制执行该行为。如果该技能内容较大,或你在它之后又调用了几个其他技能,可以在压缩后重新调用它来恢复完整内容。

为某个技能预先批准工具

allowed-tools 字段会在该技能处于活动状态期间,为列出的工具授予权限,因此 Claude 可以使用它们而不必征得你的批准。它并不限制哪些工具可用:每个工具仍可被调用,你的权限设置仍对未列出的工具生效。

对于纳入项目 .claude/skills/ 目录的技能,allowed-tools 会在你接受该文件夹的工作区信任对话框之后生效,与 .claude/settings.json 中的权限规则相同。在信任某个仓库之前请先审阅其中的项目技能,因为一个技能可以为自己授予广泛的工具访问权限。

以下技能让 Claude 在每次你调用它时都能运行 git 命令,无需逐次批准:

---
name: commit
description: Stage and commit the current changes
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---

要在某个技能处于活动状态期间从 Claude 的可用工具池中移除某些工具,请在该技能 frontmatter 的 disallowed-tools 中列出它们。该限制会在你发送下一条消息时清除。要在所有技能和提示词中屏蔽某些工具,请在你的权限设置中添加拒绝规则。

向技能传递参数

你和 Claude 都可以在调用某个技能时传递参数。参数可以通过 $ARGUMENTS 占位符获取。

以下技能通过编号修复一个 GitHub issue。$ARGUMENTS 占位符会被替换为技能名称之后的任何内容:

---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---

Fix GitHub issue $ARGUMENTS following our coding standards.

1. Read the issue description
2. Understand the requirements
3. Implement the fix
4. Write tests
5. Create a commit

当你运行 /fix-issue 123 时,Claude 会收到“Fix GitHub issue 123 following our coding standards...”

如果你带参数调用了某个技能,但该技能没有包含 $ARGUMENTS,Claude Code 会在技能内容末尾追加 ARGUMENTS: <your input>,让 Claude 仍能看到你输入的内容。

你也可以在一条消息的开头叠加多个技能。从 v2.1.199 开始,输入 /code-review /fix-issue 123 会加载这两个技能,并将末尾的文本 123 作为 $ARGUMENTS 传给它们各自。在更早的版本中,只会加载第一个技能,并把 /fix-issue 123 作为字面参数文本接收。

Claude Code 会展开第一个技能,以及之后最多叠加五个。展开会在遇到第一个不是内联、可由用户调用的技能的词元时停止,因此一个以分叉子智能体方式运行的技能,或一个参数本身可能以斜杠命令开头的技能(例如 /loop),也会在那里结束展开;那个词元及其之后的一切,都会成为所有已展开技能共同的参数文本。

要按位置访问某个具体参数,使用 $ARGUMENTS[N] 或更简短的 $N

---
name: migrate-component
description: Migrate a component from one framework to another
---

Migrate the $ARGUMENTS[0] component from $ARGUMENTS[1] to $ARGUMENTS[2].
Preserve all existing behavior and tests.

运行 /migrate-component SearchBar React Vue 会将 $ARGUMENTS[0] 替换为 SearchBar$ARGUMENTS[1] 替换为 React$ARGUMENTS[2] 替换为 Vue。同一个技能使用 $N 简写形式:

---
name: migrate-component
description: Migrate a component from one framework to another
---

Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests.

进阶模式

注入动态上下文

!`<command>` 语法会在该技能内容发送给 Claude 之前运行 shell 命令。命令的输出会替换该占位符,因此 Claude 收到的是实际数据,而不是命令本身。

以下技能通过 GitHub CLI 获取实时 PR 数据来总结一个 pull request。!`gh pr diff` 及其他命令会先运行,其输出会被插入提示词中:

---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---

## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`

## Your task
Summarize this pull request...

该技能运行时:

  1. 每个 !`<command>` 会立即执行(在 Claude 看到任何内容之前)
  2. 输出会替换该技能内容中的占位符
  3. Claude 收到的是包含实际 PR 数据的、已完全渲染的提示词

这是一个预处理步骤,不是 Claude 执行的操作。Claude 只会看到最终结果。

替换只在原始文件上运行一次。命令输出会以纯文本形式插入,不会再被重新扫描以寻找进一步的 !`<command>` 占位符,因此一个命令无法输出一个占位符供后续步骤展开。

只有当 ! 出现在一行的开头、或紧跟在空白字符之后时,才会识别这种内联形式。如果 ! 跟在另一个字符之后,例如 KEY=!`cmd`,该占位符会被当作字面文本保留,命令也不会运行。

对于多行命令,请使用以 ```! 开头的围栏代码块,而不是内联形式:

## Environment
```!
node --version
npm --version
git status --short
```

要为来自用户、项目、插件或额外目录来源的技能和自定义命令关闭这种行为,请在设置中设置 "disableSkillShellExecution": true。此后每个命令都会被替换为 [shell command execution disabled by policy],而不会运行。内置技能和统一管理的技能不受影响。这个设置在统一管理设置中最有用,用户无法覆盖它。

要在某个技能运行时请求更深入的推理,可在该技能内容的任意位置加入 ultrathink。请参阅用 ultrathink 进行一次性深度推理

在子智能体中运行技能

当你希望某个技能在隔离环境中运行时,在其 frontmatter 中加入 context: fork。该技能的内容会成为驱动该子智能体的提示词。它不会访问你的对话历史。

context: fork 只对带有明确指令的技能才有意义。如果你的技能包含类似“使用这些 API 约定”这样没有具体任务的指导性内容,该子智能体会收到这些指导,却没有可执行的提示词,最终返回时不会有实质性的输出。

技能和子智能体可以按两个方向协同工作:

方式系统提示词任务还会加载
context: fork 的技能来自智能体类型SKILL.md 内容CLAUDE.md,除非该智能体是 Explore 或 Plan
skills 字段的子智能体子智能体的 markdown 正文Claude 的委派消息预加载的技能 + CLAUDE.md

使用 context: fork 时,你在技能中编写任务,并选择一个智能体类型来执行它。内置的 Explore 和 Plan 智能体会跳过 CLAUDE.md 和 git 状态以保持其上下文精简,因此使用 agent: Explore 的分叉技能只会看到 SKILL.md 内容和该智能体自己的系统提示词。反过来,如果你想定义一个把技能当作参考资料使用的自定义子智能体,请参阅子智能体

示例:使用 Explore 智能体的调研技能

以下技能在一个分叉的 Explore 智能体中运行调研。该技能内容会成为任务,该智能体提供了为代码库探索优化过的只读工具:

---
name: deep-research
description: Research a topic thoroughly
context: fork
agent: Explore
---

Research $ARGUMENTS thoroughly:

1. Find relevant files using Glob and Grep
2. Read and analyze the code
3. Summarize findings with specific file references

该技能运行时:

  1. 会创建一个新的隔离上下文
  2. 该子智能体会将该技能的内容作为其提示词收到(“Research $ARGUMENTS thoroughly...”)
  3. agent 字段决定执行环境(模型、工具和权限)
  4. 结果会被汇总并返回到你的主对话中

agent 字段指定使用哪种子智能体配置。选项包括内置智能体(ExplorePlangeneral-purpose)或来自 .claude/agents/ 的任何自定义子智能体。如果省略,则使用 general-purpose

限制 Claude 的技能访问权限

默认情况下,Claude 可以调用任何没有设置 disable-model-invocation: true 的技能。定义了 allowed-tools 的技能,会在其处于活动状态期间,让 Claude 无需逐次批准即可访问那些工具。你的权限设置仍对所有其他工具的基线批准行为生效。少数内置命令也可以通过 Skill 工具使用,包括 /init/review/security-review。其他内置命令(例如 /compact)则不能。

控制 Claude 可以调用哪些技能有三种方式:

禁用所有技能,在 /permissions 中拒绝 Skill 工具:

# Add to deny rules:
Skill

允许或拒绝特定技能,使用权限规则

# Allow only specific skills
Skill(commit)
Skill(review-pr *)

# Deny specific skills
Skill(deploy *)

权限语法:Skill(name) 表示精确匹配,Skill(name *) 表示带任意参数的前缀匹配。

隐藏个别技能,在其 frontmatter 中加入 disable-model-invocation: true。这会把该技能完全从 Claude 的上下文中移除。

user-invocable 字段只控制菜单可见性,不控制 Skill 工具的访问权限。使用 disable-model-invocation: true 来阻止程序化调用。

从设置中覆盖技能可见性

skillOverrides 设置从你的设置中控制技能可见性,而不是通过该技能自己的 frontmatter。适用于你不想编辑其 SKILL.md 的技能,例如纳入共享项目仓库的技能,或由某个 MCP 服务器提供的技能。/skills 菜单会替你写入这个设置:高亮一个技能并按 Space 循环切换状态,然后按 Enter 保存到 .claude/settings.local.json

每个键是一个技能名称,每个值是以下四种状态之一:

对 Claude 可见的内容/ 菜单中
"on"名称和描述
"name-only"仅名称
"user-invocable-only"隐藏
"off"隐藏隐藏

从 v2.1.199 开始,"off" 也会把该技能从提供给远程控制客户端和 Agent SDK 调用方的命令列表中隐藏,而不仅仅是从终端的 / 菜单中隐藏。用完整名称调用一个隐藏的技能,仍会返回 skillOverrides 错误,而不会运行它。

一个不在 skillOverrides 中的技能会被当作 "on" 处理。以下示例把一个技能折叠为仅显示名称,把另一个完全关闭:

{
  "skillOverrides": {
    "legacy-context": "name-only",
    "deploy": "off"
  }
}

插件技能不受 skillOverrides 影响。请改用 /plugin 管理它们。

评估和迭代一个技能

看到某个技能被触发,只能说明 Claude 找到了它,不能说明它做的正是你想要的。要知道一个技能是否有效,需要分别衡量两件事:Claude 是否在应该触发的提示词上调用了它,以及它调用时的输出是否符合你的预期。

对这两点的检验,都是一次基线对比。收集几个真实的提示词,在一个全新会话中分别用启用该技能和禁用该技能各运行一次,然后比较结果。全新会话很重要,因为编写该技能过程中留下的上下文,会掩盖书面指令中的缺口。

用 skill-creator 运行评测

skill-creator 插件可以在 Claude Code 内部自动化这个对比循环。从官方市场安装它:

/plugin install skill-creator@claude-plugins-official

如果 Claude Code 报告在任何市场中都找不到该插件,说明你的市场缺失或已过期。运行 /plugin marketplace update claude-plugins-official 刷新它,如果你之前没有添加过,运行 /plugin marketplace add anthropics/claude-plugins-official。然后重试安装。

安装后,运行 /reload-plugins,让该插件的技能在当前会话中可用。然后让 Claude 评估某个已有的技能,例如 evaluate my summarize-changes skill with skill-creator。该插件会引导你编写测试用例,并运行这个循环:

  • 测试用例:将提示词、输入文件和预期行为存储在该技能目录内的 evals/evals.json
  • 隔离运行:为每个测试用例生成一个子智能体,使每次运行都从干净的上下文开始,并记录 Token 数量和耗时
  • 评分:对照输出检查每条断言,并将通过或失败的结果连同证据写入 grading.json
  • 基准对比:将带技能与不带技能的通过率、耗时和 Token 汇总到 benchmark.json,方便你比较通过率的提升与 Token、时间开销之间的权衡
  • 版本对比:在该技能的两个版本之间运行一次盲测 A/B 对比,让你在提交某次编辑之前先确认它确实是一次改进
  • 描述调优:生成应触发和不应触发的提示词,测量命中率,并在该技能对错误的请求触发时提出描述修改建议
  • 审查查看器:打开一份 HTML 报告,你可以在其中检查每个输出,并记录定性反馈,供下一次迭代读取

关于评测文件格式和完整的迭代工作流,请参阅 agentskills.io 上的评估技能输出质量。关于基准对比和版本对比模式的背景信息,请参阅skill-creator 发布公告

分享技能

根据你的目标受众,技能可以在不同范围内分发:

  • 项目技能:将 .claude/skills/ 提交到版本控制
  • 插件:在你的插件中创建一个 skills/ 目录
  • 统一管理:通过统一管理设置在整个组织范围内部署

生成可视化输出

技能可以打包并运行任意语言的脚本,赋予 Claude 单条提示词无法实现的能力。一种强大的模式是生成可视化输出:在浏览器中打开的交互式 HTML 文件,用于探索数据、调试或生成报告。

以下示例创建一个代码库浏览器:一个交互式树状视图,你可以展开和折叠目录,一眼看到文件大小,并通过颜色识别文件类型。

创建该 Skill 目录:

mkdir -p ~/.claude/skills/codebase-visualizer/scripts

将以下内容保存到 ~/.claude/skills/codebase-visualizer/SKILL.md。该描述告诉 Claude 何时激活这个 Skill,指令告诉 Claude 运行打包的脚本。脚本路径使用了 ${CLAUDE_SKILL_DIR},因此无论该技能安装在个人级、项目级还是插件级,都能正确解析:

---
name: codebase-visualizer
description: Generate an interactive collapsible tree visualization of your codebase. Use when exploring a new repo, understanding project structure, or identifying large files.
allowed-tools: Bash(python3 *)
---

# Codebase Visualizer

Generate an interactive HTML tree view that shows your project's file structure with collapsible directories.

## Usage

Run the visualization script from your project root:

```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/visualize.py .
```

This creates `codebase-map.html` in the current directory and opens it in your default browser.

## What the visualization shows

- **Collapsible directories**: Click folders to expand/collapse
- **File sizes**: Displayed next to each file
- **Colors**: Different colors for different file types
- **Directory totals**: Shows aggregate size of each folder

将以下内容保存到 ~/.claude/skills/codebase-visualizer/scripts/visualize.py。这个脚本会扫描一个目录树,并生成一个自包含的 HTML 文件,包含:

  • 一个摘要侧边栏,显示文件数、目录数、总大小和文件类型数量
  • 一个条形图,按文件类型细分代码库构成(按大小排前 8 位)
  • 一个可折叠的树状视图,可以展开和折叠目录,并带有按颜色区分的文件类型标记

该脚本需要 Python 3,但只使用内置库,因此无需安装任何软件包:

#!/usr/bin/env python3
"""Generate an interactive collapsible tree visualization of a codebase."""

import json
import sys
import webbrowser
from html import escape
from pathlib import Path
from collections import Counter

IGNORE = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'dist', 'build'}

def scan(path: Path, stats: dict) -> dict:
    result = {"name": path.name, "children": [], "size": 0}
    try:
        for item in sorted(path.iterdir()):
            if item.name in IGNORE or item.name.startswith('.'):
                continue
            if item.is_file():
                size = item.stat().st_size
                ext = item.suffix.lower() or '(no ext)'
                result["children"].append({"name": item.name, "size": size, "ext": ext})
                result["size"] += size
                stats["files"] += 1
                stats["extensions"][ext] += 1
                stats["ext_sizes"][ext] += size
            elif item.is_dir():
                stats["dirs"] += 1
                child = scan(item, stats)
                if child["children"]:
                    result["children"].append(child)
                    result["size"] += child["size"]
    except PermissionError:
        pass
    return result

def generate_html(data: dict, stats: dict, output: Path) -> None:
    ext_sizes = stats["ext_sizes"]
    total_size = sum(ext_sizes.values()) or 1
    sorted_exts = sorted(ext_sizes.items(), key=lambda x: -x[1])[:8]
    colors = {
        '.js': '#f7df1e', '.ts': '#3178c6', '.py': '#3776ab', '.go': '#00add8',
        '.rs': '#dea584', '.rb': '#cc342d', '.css': '#264de4', '.html': '#e34c26',
        '.json': '#6b7280', '.md': '#083fa1', '.yaml': '#cb171e', '.yml': '#cb171e',
        '.mdx': '#083fa1', '.tsx': '#3178c6', '.jsx': '#61dafb', '.sh': '#4eaa25',
    }
    lang_bars = "".join(
        f'<div class="bar-row"><span class="bar-label">{ext}</span>'
        f'<div class="bar" style="width:{(size/total_size)*100}%;background:{colors.get(ext,"#6b7280")}"></div>'
        f'<span class="bar-pct">{(size/total_size)*100:.1f}%</span></div>'
        for ext, size in sorted_exts
    )
    def fmt(b):
        if b < 1024: return f"{b} B"
        if b < 1048576: return f"{b/1024:.1f} KB"
        return f"{b/1048576:.1f} MB"

    html = f'''<!DOCTYPE html>
<html><head>
  <meta charset="utf-8"><title>Codebase Explorer</title>
  <style>
    body {{ font: 14px/1.5 system-ui, sans-serif; margin: 0; background: #1a1a2e; color: #eee; }}
    .container {{ display: flex; height: 100vh; }}
    .sidebar {{ width: 280px; background: #252542; padding: 20px; border-right: 1px solid #3d3d5c; overflow-y: auto; flex-shrink: 0; }}
    .main {{ flex: 1; padding: 20px; overflow-y: auto; }}
    h1 {{ margin: 0 0 10px 0; font-size: 18px; }}
    h2 {{ margin: 20px 0 10px 0; font-size: 14px; color: #888; text-transform: uppercase; }}
    .stat {{ display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #3d3d5c; }}
    .stat-value {{ font-weight: bold; }}
    .bar-row {{ display: flex; align-items: center; margin: 6px 0; }}
    .bar-label {{ width: 55px; font-size: 12px; color: #aaa; }}
    .bar {{ height: 18px; border-radius: 3px; }}
    .bar-pct {{ margin-left: 8px; font-size: 12px; color: #666; }}
    .tree {{ list-style: none; padding-left: 20px; }}
    details {{ cursor: pointer; }}
    summary {{ padding: 4px 8px; border-radius: 4px; }}
    summary:hover {{ background: #2d2d44; }}
    .folder {{ color: #ffd700; }}
    .file {{ display: flex; align-items: center; padding: 4px 8px; border-radius: 4px; }}
    .file:hover {{ background: #2d2d44; }}
    .size {{ color: #888; margin-left: auto; font-size: 12px; }}
    .dot {{ width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; }}
  </style>
</head><body>
  <div class="container">
    <div class="sidebar">
      <h1>📊 Summary</h1>
      <div class="stat"><span>Files</span><span class="stat-value">{stats["files"]:,}</span></div>
      <div class="stat"><span>Directories</span><span class="stat-value">{stats["dirs"]:,}</span></div>
      <div class="stat"><span>Total size</span><span class="stat-value">{fmt(data["size"])}</span></div>
      <div class="stat"><span>File types</span><span class="stat-value">{len(stats["extensions"])}</span></div>
      <h2>By file type</h2>
      {lang_bars}
    </div>
    <div class="main">
      <h1>📁 {escape(data["name"])}</h1>
      <ul class="tree" id="root"></ul>
    </div>
  </div>
  <script>
    const data = {json.dumps(data)};
    const colors = {json.dumps(colors)};
    function fmt(b) {{ if (b < 1024) return b + ' B'; if (b < 1048576) return (b/1024).toFixed(1) + ' KB'; return (b/1048576).toFixed(1) + ' MB'; }}
    function esc(s) {{ return s.replace(/[&<>"']/g, c => ({{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}}[c])); }}
    function render(node, parent) {{
      if (node.children) {{
        const det = document.createElement('details');
        det.open = parent === document.getElementById('root');
        det.innerHTML = `<summary><span class="folder">📁 ${{esc(node.name)}}</span><span class="size">${{fmt(node.size)}}</span></summary>`;
        const ul = document.createElement('ul'); ul.className = 'tree';
        node.children.sort((a,b) => (b.children?1:0)-(a.children?1:0) || a.name.localeCompare(b.name));
        node.children.forEach(c => render(c, ul));
        det.appendChild(ul);
        const li = document.createElement('li'); li.appendChild(det); parent.appendChild(li);
      }} else {{
        const li = document.createElement('li'); li.className = 'file';
        li.innerHTML = `<span class="dot" style="background:${{colors[node.ext]||'#6b7280'}}"></span>${{esc(node.name)}}<span class="size">${{fmt(node.size)}}</span>`;
        parent.appendChild(li);
      }}
    }}
    data.children.forEach(c => render(c, document.getElementById('root')));
  </script>
</body></html>'''
    output.write_text(html)

if __name__ == '__main__':
    target = Path(sys.argv[1] if len(sys.argv) > 1 else '.').resolve()
    stats = {"files": 0, "dirs": 0, "extensions": Counter(), "ext_sizes": Counter()}
    data = scan(target, stats)
    out = Path('codebase-map.html')
    generate_html(data, stats, out)
    print(f'Generated {out.absolute()}')
    webbrowser.open(f'file://{out.absolute()}')

要测试,在任意项目中打开 Claude Code,问它“Visualize this codebase.”。Claude 会运行该脚本,生成 codebase-map.html,并在你的浏览器中打开它。

这种模式适用于任何可视化输出:依赖关系图、测试覆盖率报告、API 文档,或数据库模式可视化。打包的脚本负责具体工作,Claude 负责编排。

故障排查

技能没有被触发

如果 Claude 在预期情况下没有使用你的技能:

  1. 检查描述是否包含了用户会自然说出的关键词
  2. 确认该技能出现在 What skills are available? 的结果中
  3. 尝试改写你的请求,使其更贴近该描述
  4. 如果该技能是用户可调用的,直接用 /skill-name 调用它

如果 frontmatter 的 YAML 格式有误,Claude Code 会以空元数据加载该技能正文,因此 /skill-name 仍然有效,但 Claude 没有 description 可供匹配。用 --debug 运行即可看到解析错误。

技能触发过于频繁

如果 Claude 在你不想要的时候使用了你的技能:

  1. 让描述更具体
  2. 如果你只想手动调用,加入 disable-model-invocation: true

技能描述被截断

Claude Code 会把技能名称和描述的列表加载到上下文中,让 Claude 知道有哪些可用。这份列表总是包含每个技能的名称,但如果你有很多技能,Claude Code 会缩短描述以适应列表的字符预算,这可能会去掉 Claude 匹配你的请求所需的关键词。这个预算按模型上下文窗口的 1% 计算。当列表溢出时,Claude Code 会从你调用最少的技能开始丢弃描述,因此你最常用的技能会保留完整文本。

运行 /doctor 可以估算该列表的上下文成本及其最大的占用来源。当列表超出预算时,Claude Code 还会向调试日志写入一条警告,可通过 --debug 查看。

/context 中的 Skills 行会报告应用预算之后该列表的大小,因此它与模型实际收到的内容一致。在 v2.1.196 之前,该行统计的是每个描述的完整文本,显示的值可能比配置的预算大好几倍。

要提高这个预算,可以设置 skillListingBudgetFraction 设置(例如 0.02 表示 2%),或将 SLASH_COMMAND_TOOL_CHAR_BUDGET 环境变量设为一个固定的字符数。要为其他技能释放预算,可以在 skillOverrides 中将低优先级条目设为 "name-only",使其只列出名称、不带描述。你也可以从源头精简 descriptionwhen_to_use 文本:把关键使用场景放在最前面,因为无论预算如何,每个条目合并后的文本都上限为 1,536 个字符。这个上限可以用 skillListingMaxDescChars 配置。

  • 调试你的配置:诊断某个技能为什么没有出现或没有触发
  • 评估技能输出质量:agentskills.io 上的评测文件格式和迭代工作流
  • 技能编写最佳实践:适用于所有 Claude 产品的编写指导
  • 子智能体:将任务委派给专属智能体
  • 插件:将技能与其他扩展打包分发
  • 钩子:围绕工具事件自动化工作流
  • 记忆:管理用于持久化上下文的 CLAUDE.md 文件
  • 命令:内置命令和内置技能的参考文档
  • 权限:控制工具和技能的访问权限
  • Claude Tag skills:纳入某个仓库的项目技能,在该仓库用于某个 Claude Tag Channel 时也会加载

博极客AI是专业人工智能学习平台,提供通俗易懂的AI入门教程、大模型应用、实战项目与行业动态,全站内容免费阅览,零基础也能轻松学AI,适配学生、职场新人及技术爱好者。

© 版权所有 2026 博极客AI,保留一切权利。 | 桂ICP备2026007205号 | 桂公网安备45010502001169号