Claude Code Skills 用 SKILL.md 把可复用的知识和多步骤工作流封装成技能,适合反复贴同一段指令、部署、提交、排查 diff 等场景。通过 description、disable-model-invocation、allowed-tools、context: fork 和 paths 控制自动触发、手动调用与运行范围;/doctor 可检查描述预算和技能是否被截断。
Claude Code Skills 怎么配置和排查
Skills 是 Claude Code 扩展能力的方式。一个技能通常是一个目录里的 SKILL.md,Claude 只有在需要时才加载它,所以比把长流程常驻写进 CLAUDE.md 更省上下文。
Claude Code Skills 遵循 Agent Skills 开放标准,并额外支持调用控制、子代理执行和动态上下文注入。
内置命令如
/help、/compact,以及 bundled skills 如/debug、/code-review,见 Commands reference。自定义命令已经并入 skills。
.claude/commands/deploy.md和.claude/skills/deploy/SKILL.md都会创建/deploy,行为一致。现有.claude/commands/文件仍可继续使用。Skills 额外支持支持文件目录、frontmatter 控制由谁调用,以及在相关时让 Claude 自动加载。
Bundled skills
Claude Code 在每个会话里都包含一组 bundled skills,包括 /code-review、/batch、/debug、/loop 和 /claude-api。它们和大多数内置命令不同,不是固定逻辑直接执行,而是基于 prompt 的技能:Claude 会根据详细指令,调用自己的工具完成工作。用法和其他 skill 一样,输入 / 加技能名即可。
Bundled skills 和内置命令会一起出现在 Commands reference 里,Purpose 列会标成 Skill。
运行并验证你的应用
下面三个 bundled skills 配合使用,可以启动应用并直接对运行中的程序做验证,而不是只看测试结果:
| Skill | Purpose |
|---|---|
/run |
启动并驱动你的应用,观察改动是否真正生效 |
/verify |
构建并运行你的应用,确认代码改动符合预期,不回退到测试或类型检查 |
/run-skill-generator |
教会 /run 和 /verify 如何构建并启动你的项目 |
{/* min-version: 2.1.145 */}这三个技能都要求 Claude Code v2.1.145 或更高版本。
/run 和 /verify 不需要额外配置。它们会根据项目类型推断启动方式,比如 CLI、server、TUI 或浏览器驱动项目,也会参考 README、package.json 或 Makefile。如果项目不是标准启动流程,比如需要数据库、.env 文件、图形会话或多步构建,这种推断就容易失准。
/run-skill-generator 的作用是把启动配方记录下来。它会在干净环境里把应用跑起来,记录真正可用的安装命令、环境变量和启动脚本,然后把结果保存成项目级技能,放到 .claude/skills/run-<name>/。之后 /run、/verify 以及仓库里的其他 agent 都会按这个记录好的配方执行,而不是重新猜。每个项目运行一次即可;如果构建或启动流程变了,再跑一次。
如何开始使用
创建你的第一个 skill
下面的例子会创建一个 skill,用来总结 git 仓库里未提交的改动,并标出风险。它会在 Claude 读取之前把实时 diff 注入到 prompt 里,这样回答会基于当前工作区,而不是 Claude 从打开的文件里猜出来的内容。Claude 会在你询问改动时自动加载这个 skill,你也可以直接用 /summarize-changes 调用。
- 在个人 skills 目录里创建技能目录。个人技能对你的所有项目都可用。
mkdir -p ~/.claude/skills/summarize-changes
- 每个 skill 都需要一个
SKILL.md,里面有两部分:---之间的 YAML frontmatter,用来告诉 Claude 什么时候使用这个 skill;后面是 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 会先运行命令,再把输出替换进 skill 内容,Claude 看到的是已经展开的当前 diff。
- 打开一个 git 项目,改动任意文件,然后运行
claude启动 Claude Code。你可以用两种方式测试这个 skill。
让 Claude 自动调用:说一段和 description 匹配的话。
What did I change?
直接手动调用:
/summarize-changes
不管哪种方式,Claude 都应该返回一个简短的改动总结和风险列表。
Skills 放在哪里
skill 存放的位置决定谁能使用它:
| Location | Path | Applies to |
|---|---|---|
| Enterprise | See managed settings | All users in your organization |
| Personal | ~/.claude/skills/<skill-name>/SKILL.md |
All your projects |
| Project | .claude/skills/<skill-name>/SKILL.md |
This project only |
| Plugin | <plugin>/skills/<skill-name>/SKILL.md |
Where plugin is enabled |
同名 skill 在不同层级同时存在时,优先级是 enterprise 覆盖 personal,personal 覆盖 project。Plugin skills 使用 plugin-name:skill-name 命名空间,因此不会和其他层级冲突。.claude/commands/ 里的文件也遵循同样的工作方式,但如果 skill 和 command 同名,skill 优先。
实时变更检测
Claude Code 会监视 skill 目录中的文件变化。你在 ~/.claude/skills/、项目 .claude/skills/,或者 --add-dir 目录下的 .claude/skills/ 中新增、编辑或删除 skill,当前会话里会自动生效,不用重启。
如果是在会话开始后才创建了一个新的顶层 skills 目录,就需要重启 Claude Code,新的目录才会被监视。
从父目录和嵌套目录自动发现
项目 skill 会从你启动目录里的 .claude/skills/ 加载,并向上查找父目录,直到仓库根目录。所以即使你在子目录里启动 Claude,也会加载根目录定义的 skills。
当你处理启动目录下更深层的文件时,Claude Code 还会按需发现嵌套的 .claude/skills/ 目录。例如你正在编辑 packages/frontend/ 里的文件,Claude Code 也会查找 packages/frontend/.claude/skills/。这适合 monorepo,每个 package 有自己的技能集。
每个 skill 都是一个目录,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 必须存在,其他文件都是可选的。你可以把模板、示例输出、可执行脚本或详细参考文档放进去,再在 SKILL.md 中引用它们,让 Claude 知道这些文件的用途以及什么时候加载。更多细节见 Add supporting files。
.claude/commands/下的文件仍然可用,也支持同样的 frontmatter。但推荐使用 skills,因为它支持附属文件等更多能力。
来自额外目录的 skills
--add-dir 标志是授予文件访问权限,而不是配置发现权限,但 skills 是例外:额外目录里的 .claude/skills/ 会自动加载。会话中如何拾取这些变更,见 实时变更检测。
不过,其他 .claude/ 配置不会从额外目录加载,比如 subagents、commands 和 output styles 都不会。完整的加载与不加载清单,见 exceptions table,以及跨项目共享配置的推荐方式。
来自
--add-dir的 CLAUDE.md 默认不会加载。若要加载,请设置CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1。见 Load from additional directories。
配置 skills
Skills 通过 SKILL.md 顶部的 YAML frontmatter 和后面的 Markdown 内容来配置。
Skill 内容类型
skill 文件可以写任何指令,但先想清楚它是怎么被调用的,会帮助你决定该写什么:
参考型内容:给 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,还是两者都可以)以及要在哪里运行(内联还是子代理)。复杂 skill 还可以配合 附属文件 保持主文件简洁。
保持 body 简短。skill 一旦加载,它的内容会在后续回合继续留在上下文里,所以每一行都会反复消耗 token。写法要像给 CLAUDE.md 写规则一样:只写该做什么,不写太多过程解释。
Frontmatter 参考
除了 Markdown 内容,你还可以在 SKILL.md 顶部用 YAML frontmatter 配置 skill 行为:
---
name: my-skill
description: What this skill does
disable-model-invocation: true
allowed-tools: Read Grep
---
Your skill instructions here...
所有字段都是可选的,只有 description 是推荐项,这样 Claude 才知道什么时候该用这个 skill。
| Field | Required | Description |
|---|---|---|
name |
No | 技能显示名;省略时使用目录名。只能包含小写字母、数字和连字符,最长 64 字符。 |
description |
Recommended | 这个技能做什么、什么时候用。Claude 会用它决定是否应用该技能。若省略,则使用 Markdown 内容的第一段。把核心用途放在前面:description 和 when_to_use 合计在技能列表里最多截断到 1,536 字符。 |
when_to_use |
No | 额外的触发上下文,比如触发短语或示例请求。会追加到 description 后面,也计入 1,536 字符上限。 |
argument-hint |
No | 自动补全时展示的参数提示,例如 [issue-number] 或 [filename] [format]。 |
arguments |
No | 用于技能内容里 $name substitution 的命名位置参数。可以是空格分隔字符串,也可以是 YAML 列表。名字会按顺序映射到参数位置。 |
disable-model-invocation |
No | 设为 true 可禁止 Claude 自动加载该 skill。适合你想手动用 /name 触发的流程。它也会阻止该 skill 被 preloaded into subagents。默认值:false。 |
user-invocable |
No | 设为 false 后会从 / 菜单隐藏。适合不应该由用户直接执行的背景知识。默认值:true。 |
allowed-tools |
No | skill 激活时,Claude 可在不请求确认的情况下使用的工具。可以是空格分隔字符串,也可以是 YAML 列表。 |
model |
No | skill 激活时使用的模型。这个覆盖只对当前回合有效,不会写入设置;下一次提示会恢复会话模型。可取值与 /model 相同,也可写 inherit。 |
effort |
No | skill 激活时的 努力级别,覆盖会话级设置。默认继承会话设置。可选 low、medium、high、xhigh、max;可用等级取决于模型。 |
context |
No | 设为 fork,让 skill 在分叉的子代理上下文中运行。 |
agent |
No | 在 context: fork 下使用哪个子代理类型。 |
hooks |
No | 仅作用于该 skill 生命周期的 hooks。配置格式见 Hooks in skills and agents。 |
paths |
No | 限定 skill 何时自动激活的 Glob 模式。可以是逗号分隔字符串,也可以是 YAML 列表。设置后,Claude 只会在处理匹配文件时自动加载该 skill。格式与 path-specific rules 相同。 |
shell |
No | 供 !`command` 和 ```! 块使用的 shell。可选 bash(默认)或 powershell。设为 powershell 时,Windows 上的内联 shell 命令通过 PowerShell 运行。需要 CLAUDE_CODE_USE_POWERSHELL_TOOL=1。 |
可用字符串替换
Skills 支持在内容里做字符串替换:
| Variable | Description |
|---|---|
$ARGUMENTS |
调用 skill 时传入的全部参数。如果内容里没有 $ARGUMENTS,参数会以 ARGUMENTS: <value> 追加到末尾。 |
$ARGUMENTS[N] |
访问指定位置的参数,使用 0 开始的索引,例如第一个参数是 $ARGUMENTS[0]。 |
$N |
$ARGUMENTS[N] 的简写,例如第一个参数写 $0,第二个参数写 $1。 |
$name |
在 arguments frontmatter 中声明的命名参数。名字按顺序映射位置,例如 arguments: [issue, branch] 时,$issue 展开为第一个参数,$branch 展开为第二个参数。 |
${CLAUDE_SESSION_ID} |
当前会话 ID。适合记录日志、创建会话专属文件,或把 skill 输出和会话关联起来。 |
${CLAUDE_EFFORT} |
当前努力级别:low、medium、high、xhigh 或 max。可据此调整 skill 指令。 |
${CLAUDE_SKILL_DIR} |
包含该 skill SKILL.md 的目录。对于 plugin skills,这个路径是插件内该技能的子目录,而不是插件根目录。可在 bash 注入命令里引用脚本或随 skill 打包的文件,不受当前工作目录影响。 |
索引参数使用 shell 风格引用规则,所以多词值要加引号,才能作为一个参数传入。例如 /my-skill "hello world" second 会让 $0 变成 hello world,$1 变成 second。$ARGUMENTS 始终会展开成你输入的完整参数字符串。
替换示例:
---
name: session-logger
description: Log activity for this session
---
Log the following to logs/${CLAUDE_SESSION_ID}.log:
$ARGUMENTS
添加附属文件
skills 可以在目录里放多个文件。这样 SKILL.md 只保留核心内容,Claude 只有在需要时才访问更详细的参考材料。大的参考文档、API 规范、示例集合都不用每次运行 skill 时塞进上下文。
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 行以内。把详细参考内容拆到独立文件里。
控制谁来调用 skill
默认情况下,你和 Claude 都可以调用任何 skill。你可以直接输入 /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对用户来说不是一个有意义的操作。
这个例子创建一个只能由你触发的 deploy skill。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 | You can invoke | Claude can invoke | When loaded into context |
|---|---|---|---|
| (default) | Yes | Yes | Description always in context, full skill loads when invoked |
disable-model-invocation: true |
Yes | No | Description not in context, full skill loads when you invoke |
user-invocable: false |
No | Yes | Description always in context, full skill loads when invoked |
在普通会话里,skill descriptions 会加载到上下文里,让 Claude 知道有哪些技能可用,但完整内容只有在调用时才加载。预加载了 skills 的 subagents 行为不同:完整 skill 内容会在启动时直接注入。
Skill 内容生命周期
当你或 Claude 调用某个 skill 时,渲染后的 SKILL.md 内容会作为一条消息进入对话,并在整个会话中保留。Claude Code 在后续回合不会重新读取 skill 文件,所以应该把它写成能贯穿整个任务的持续指令,而不是一次性步骤。
Auto-compaction 会在 token 预算内继续保留已调用的 skills。对话被总结以腾出上下文时,Claude Code 会在总结后重新附加每个 skill 最近一次调用的内容,每个 skill 保留前 5,000 token。重新附加的 skills 总预算是 25,000 token,Claude Code 从最近调用的 skill 开始填充,所以如果你在一个会话里调用了很多 skill,较早的 skill 可能会在压缩后被完全丢掉。
如果某个 skill 看起来在第一轮后就不再影响行为,通常不是内容丢了,而是模型选择了别的工具或方法。你可以加强 skill 的 description 和指令,让模型更倾向使用它;如果需要确定性行为,就用 hooks 强制执行。如果 skill 太大,或者后面又调用了很多其他 skill,压缩后可以再手动调用一次,恢复完整内容。
预先批准 skill 可用工具
allowed-tools 会在 skill 激活时授予列出的工具权限,让 Claude 不用每次都请求批准。但这并不限制它还能调用什么——所有工具仍然可用,只是你在权限设置里没允许的工具,仍然受 permission settings 约束。
对于提交到项目 .claude/skills/ 的 skills,在你接受该工作区的信任对话后,allowed-tools 才会生效,这和 .claude/settings.json 里的权限规则一致。在信任仓库之前,请先审查项目 skill,因为 skill 可以给自己授予很宽的工具访问权限。
下面这个 skill 在你调用它时,可以免确认运行 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 *)
---
如果要阻止 skill 使用某些工具,请在 permission settings 里添加 deny 规则。
给 skill 传参数
你和 Claude 调用 skill 时都可以传参数,参数通过 $ARGUMENTS 占位符可用。
这个 skill 根据 issue 编号修复 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…”
如果调用 skill 时传了参数,但内容里没有 $ARGUMENTS,Claude Code 会在 skill 内容末尾追加 ARGUMENTS: <your input>,这样 Claude 仍然能看到你输入了什么。
如果要按位置访问单个参数,可以用 $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。同样的 skill 也可以用 $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>` 语法会在 skill 内容发送给 Claude 之前运行 shell 命令。命令输出会替换占位符,所以 Claude 看到的是实际数据,而不是命令本身。
下面这个 skill 会通过 GitHub CLI 拉取实时 PR 数据来总结 pull request。!`gh pr diff` 和其他命令会先运行,输出再插入 prompt:
---
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...
运行这个 skill 时:
- 每个
!`<command>`会立刻执行,发生在 Claude 看到内容之前 - 输出替换掉 skill 内容中的占位符
- Claude 收到的是已经渲染好的 prompt,里面包含真实 PR 数据
这是预处理,不是让 Claude 自己执行命令。Claude 只会看到最终结果。
替换只会在原始文件上执行一次。命令输出会作为纯文本插入,不会再次扫描其中的其他 !`<command>` 占位符,所以一个命令不能生成另一个占位符让后续 pass 展开。
内联写法只有在 ! 位于行首,或紧跟空白时才会被识别。如果 ! 前面还有别的字符,比如 KEY=!`cmd`,这个占位符会被当作普通文本,命令不会执行。
多行命令请用以 ```! 开头的 fenced code block,而不是内联形式:
## Environment
```!
node --version
npm --version
git status --short
```
如果你想在用户、项目、插件或 additional-directory 来源的 skills 和 custom commands 中禁用这种行为,可以在 settings 里设置 "disableSkillShellExecution": true。每个命令都会被 [shell command execution disabled by policy] 替换,而不会真的执行。Bundled 和 managed skills 不受影响。这个设置在 managed settings 中最有用,因为用户在那里不能覆盖它。
如果你希望 skill 运行时需要更深推理,可以在 skill 内容里任意位置加入
ultrathink。见 Use ultrathink for one-off deep reasoning。
在子代理中运行 skills
当你希望 skill 在隔离环境中执行时,在 frontmatter 里加上 context: fork。skill 内容会变成驱动子代理的 prompt,它不会访问你的对话历史。
context: fork只适合带明确任务的 skill。如果你的 skill 只是写了“使用这些 API 约定”之类的指导,没有实际任务,子代理会拿到规则但没有可执行目标,通常不会返回有意义的结果。
Skills 和 subagents 可以双向配合:
| Approach | System prompt | Task | Also loads |
|---|---|---|---|
Skill with context: fork |
From agent type | SKILL.md content | CLAUDE.md, except when the agent is Explore or Plan |
Subagent with skills field |
Subagent’s markdown body | Claude’s delegation message | Preloaded skills + CLAUDE.md |
用 context: fork 时,你把任务写进 skill,再选择一个 agent type 去执行。内置的 Explore 和 Plan agent 会 跳过 CLAUDE.md 和 git status 来保持上下文小,所以使用 agent: Explore 的 forked skill 只会看到 SKILL.md 内容和 agent 自己的 system prompt。反过来,如果你要定义一个自定义 subagent,把 skills 作为参考材料,见 Subagents。
示例:使用 Explore agent 的 research skill
这个 skill 会在 forked Explore agent 中执行研究任务。skill 内容本身就是任务,agent 提供只读工具,适合代码库探索:
---
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
运行这个 skill 时:
- 会创建一个新的隔离上下文
- 子代理拿到 skill 内容作为 prompt(例如 “Research $ARGUMENTS thoroughly…”)
agent字段决定执行环境(模型、工具和权限)- 结果会被总结后返回到主对话
agent 字段指定要使用哪种子代理配置。可选 built-in agents 包括 Explore、Plan、general-purpose,也可以是 .claude/agents/ 里的自定义 subagent。省略时默认 general-purpose。
限制 Claude 可访问的 skill
默认情况下,Claude 可以调用任何没有设置 disable-model-invocation: true 的 skill。定义了 allowed-tools 的 skill 在激活时可以免确认使用这些工具,但其他工具仍然受你的 permission settings 控制。通过 Skill tool 也能访问少量内置命令,包括 /init、/review 和 /security-review;但像 /compact 这样的其他内置命令不行。
有三种方式控制 Claude 能调用哪些 skill:
禁用所有 skills:在 /permissions 里拒绝 Skill 工具。
# Add to deny rules:
Skill
允许或拒绝特定 skills:使用 permission rules。
# Allow only specific skills
Skill(commit)
Skill(review-pr *)
# Deny specific skills
Skill(deploy *)
权限语法:Skill(name) 表示精确匹配,Skill(name *) 表示匹配带任意参数的前缀。
隐藏单个 skill:在 frontmatter 中加入 disable-model-invocation: true。这会让这个 skill 完全不进入 Claude 的上下文。
user-invocable只控制菜单可见性,不控制 Skill 工具访问。要阻止程序化调用,请用disable-model-invocation: true。
从 settings 覆盖 skill 可见性
skillOverrides 设置会从 settings 里控制 skill 的可见性,而不是依赖 skill 自己的 frontmatter。适合那些你不想直接编辑 SKILL.md 的 skill,比如共享仓库里提交的技能,或者 MCP server 提供的技能。/skills 菜单也会帮你写:高亮一个 skill,按 Space 切换状态,再按 Enter 保存到 .claude/settings.local.json。
每个 key 是技能名,每个 value 是四种状态之一:
| Value | Listed to Claude | In / menu |
|---|---|---|
"on" |
Name and description | Yes |
"name-only" |
Name only | Yes |
"user-invocable-only" |
Hidden | Yes |
"off" |
Hidden | Hidden |
如果某个 skill 没出现在 skillOverrides 里,默认按 "on" 处理。下面这个例子把一个 skill 只显示名称,另一个彻底关闭:
{
"skillOverrides": {
"legacy-context": "name-only",
"deploy": "off"
}
}
Plugin skills 不受 skillOverrides 影响。要管理它们,请用 /plugin。
分享 skills
根据受众不同,skills 可以用不同范围分发:
- Project skills:把
.claude/skills/提交到版本控制 - Plugins:在你的 plugin 里创建
skills/目录 - Managed:通过 managed settings 做组织级部署
生成可视化输出
skills 可以打包并运行任意语言的脚本,让 Claude 获得单个 prompt 做不到的能力。一个很实用的模式是生成可视化输出:打开浏览器就能看的交互式 HTML 文件,用来浏览数据、排查问题或生成报告。
下面这个例子会创建一个代码库浏览器:一个交互式树状视图,可以展开和收起目录,快速看到文件大小,并用颜色区分文件类型。
创建 Skill 目录:
mkdir -p ~/.claude/skills/codebase-visualizer/scripts
把下面内容保存到 ~/.claude/skills/codebase-visualizer/SKILL.md。description 告诉 Claude 什么时候激活这个 skill,指令告诉 Claude 去运行随 skill 打包的脚本。脚本路径使用 ${CLAUDE_SKILL_DIR},所以不管这个 skill 装在个人、项目还是 plugin 层级,都能正确解析:
---
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 文件,里面包括:
- summary sidebar:显示文件数、目录数、总大小和文件类型数
- bar chart:按文件类型展示代码库分布,取前 8 个最大项
- collapsible tree:可以展开和收起目录,并用颜色标出文件类型
这个脚本需要 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 => ({{"&":"&","<":"<",">":">",'"':""","'":"'"}}[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 文档或数据库结构图。真正干活的是 bundled script,Claude 负责调度。
排查 Skills 不生效怎么解决
Claude 没有触发 skill
如果 Claude 没有在预期时使用你的 skill:
- 检查
description里是否包含用户会自然说出来的关键词 - 确认这个 skill 是否出现在
What skills are available? - 尝试把你的请求改写得更接近 description
- 如果这个 skill 允许用户调用,就直接用
/skill-name
skill 触发太频繁
如果 Claude 在你不想要的时候使用了这个 skill:
- 把 description 写得更具体
- 如果你只想手动触发,就加
disable-model-invocation: true
skill 描述被截断
skill description 会加载进上下文,让 Claude 知道有哪些 skill。所有 skill 名称都会加载,但如果 skill 数量太多,description 会为了适应字符预算而被截断,这可能把 Claude 需要的关键词截掉。预算大小是模型上下文窗口的 1%。当预算溢出时,最少使用的 skills 会先丢掉 description,所以你最常用的 skill 会保留完整文本。运行 /doctor 可以查看预算是否溢出,以及哪些 skills 受影响。
要提高预算,可以设置 skillListingBudgetFraction(例如 0.02 表示 2%),或者把 SLASH_COMMAND_TOOL_CHAR_BUDGET 环境变量设成固定字符数。要给其他 skills 腾预算,可以把低优先级项在 skillOverrides 里设为 "name-only",这样它们只显示名称不显示描述。你也可以从源头缩短 description 和 when_to_use:把核心用途放在最前面,因为无论预算多少,每个条目的合计长度都最多 1,536 字符。这个上限可以通过 maxSkillDescriptionChars 配置。
相关资源
- Debug your configuration:排查 skill 为什么不显示或不触发
- Subagents:把任务委派给专门 agent
- Plugins:把 skills 和其他扩展一起打包分发
- Hooks:围绕工具事件自动化工作流
- Memory:管理用于持久上下文的 CLAUDE.md 文件
- Commands:内置命令和 bundled skills 参考
- Permissions:控制工具和 skill 访问权限
常见问题
Skills 和 CLAUDE.md 有什么区别
CLAUDE.md 会在每次会话开始时自动加载,适合每次都要遵守的规则。Skills 是按需加载,只在匹配或调用时进入上下文,更省 token。反复出现的多步骤流程放 Skills,始终要执行的规则放 CLAUDE.md。
disable-model-invocation 和 user-invocable 分别适合什么场景
disable-model-invocation: true 适合有副作用、需要你控制时机的流程,比如部署、提交、发消息,防止 Claude 自己决定执行。user-invocable: false 适合背景知识类技能,Claude 需要知道,但用户不应该直接从 / 菜单里触发。
技能多了会不会撑爆上下文
技能名称会始终加载,描述会按预算加载,完整内容只在调用时加载。disable-model-invocation: true 的技能在调用前甚至不会把描述放进上下文,所以不会消耗自动加载预算。