在使用 Claude Code 处理 monorepo 时,理解 skills 是如何被发现并加载到上下文中的,对于有效地组织项目特定的能力至关重要。
| ← 返回 Claude Code 最佳实践 |
Skills 的加载行为与 CLAUDE.md 文件不同。 虽然 CLAUDE.md 文件会向上遍历目录树(祖先加载),但 skills 使用一种不同的发现机制,专注于项目内的嵌套目录。
Skills 根据作用域从这些固定位置加载:
| 位置 | 路径 | 适用范围 |
|---|---|---|
| Enterprise | 托管设置 | 组织内所有用户 |
| Personal | ~/.claude/skills/<skill-name>/SKILL.md |
你的所有项目 |
| Project | .claude/skills/<skill-name>/SKILL.md |
仅当前项目 |
| Plugin | <plugin>/skills/<skill-name>/SKILL.md |
启用 plugin 的位置 |
当你处理子目录中的文件时,Claude Code 会自动发现嵌套的 .claude/skills/ 目录中的 skills。例如,如果你在编辑 packages/frontend/ 中的文件,Claude Code 也会查找 packages/frontend/.claude/skills/ 中的 skills。
这支持 monorepo 设置,其中各个包可以拥有自己的 skills。
考虑一个典型的包含多个独立包的 monorepo:
/mymonorepo/
├── .claude/
│ └── skills/
│ └── shared-conventions/SKILL.md # 项目级 skill
├── packages/
│ ├── frontend/
│ │ ├── .claude/
│ │ │ └── skills/
│ │ │ └── react-patterns/SKILL.md # 前端专用 skill
│ │ └── src/
│ │ └── App.tsx
│ ├── backend/
│ │ ├── .claude/
│ │ │ └── skills/
│ │ │ └── api-design/SKILL.md # 后端专用 skill
│ │ └── src/
│ └── shared/
│ ├── .claude/
│ │ └── skills/
│ │ └── utils-patterns/SKILL.md # 共享工具 skill
│ └── src/
当你在 /mymonorepo/ 运行 Claude Code 且尚未编辑任何文件时:
cd /mymonorepo
claude
# 刚启动 - 尚未编辑任何文件| Skill | 是否在上下文中? | 原因 |
|---|---|---|
shared-conventions |
是 | 根目录 .claude/skills/ 中的项目级 skill |
react-patterns |
否 | 未发现 - 尚未处理 packages/frontend/ 中的文件 |
api-design |
否 | 未发现 - 尚未处理 packages/backend/ 中的文件 |
utils-patterns |
否 | 未发现 - 尚未处理 packages/shared/ 中的文件 |
在你要求 Claude 编辑 packages/frontend/src/App.tsx 后:
| Skill | 是否在上下文中? | 原因 |
|---|---|---|
shared-conventions |
是 | 根目录 .claude/skills/ 中的项目级 skill |
react-patterns |
是 | 处理 packages/frontend/ 中文件时发现 |
api-design |
否 | 仍未发现 - 尚未处理 packages/backend/ 中的文件 |
utils-patterns |
否 | 仍未发现 - 尚未处理 packages/shared/ 中的文件 |
关键洞察:嵌套 skills 是按需发现的,当你处理这些目录中的文件时才会加载。它们不会在会话开始时预加载。
Skill 描述会被加载到上下文中,让 Claude 知道有哪些可用技能,但 完整 skill 内容只在调用时加载。这是一个重要的优化:
- 描述:始终在上下文中(在字符预算范围内)
- 完整内容:skill 被调用时按需加载
注意:预加载 skills 的子代理工作方式不同 - 完整 skill 内容会在启动时注入。
当 skills 在不同层级共享相同名称时,高优先级位置优先:
| 优先级 | 位置 | 作用域 |
|---|---|---|
| 1 (最高) | Enterprise | 组织范围 |
| 2 | Personal (~/.claude/skills/) |
你的所有项目 |
| 3 (最低) | Project (.claude/skills/) |
仅当前项目 |
Plugin skills 使用 plugin-name:skill-name 命名空间,因此不会与其他层级冲突。
-
包特定的 skills 保持隔离 - 在
packages/frontend/工作的前端开发者可以获得前端专用 skills,而不会让后端 skills 干扰上下文。 -
自动发现减少配置 - 无需显式注册包级 skills;它们会在你处理这些目录时被发现。
-
上下文经过优化 - 只有 skill 描述会在初始加载,嵌套 skills 按需发现。
-
团队可以维护自己的 skills - 每个包团队可以定义自己领域的 skills,无需与其他团队协调。
Skill 描述会被加载到上下文中,直到达到字符预算限制(默认 15,000 字符)。在拥有多个包和 skills 的大型 monorepo 中,你可能会达到这个限制。
- 运行
/context检查关于被排除 skills 的警告 - 设置
SLASH_COMMAND_TOOL_CHAR_BUDGET环境变量来增加限制
-
将共享工作流放在根目录
.claude/skills/- 仓库范围的约定、提交工作流和共享模式。 -
将包特定的 skills 放在包的
.claude/skills/- 框架特定的模式、组件约定、该包独有的测试工具。 -
对危险的 skills 使用
disable-model-invocation: true- 部署或破坏性 skills 应需要显式用户调用。 -
保持 skill 描述简洁 - 描述始终在上下文中(直到字符预算),因此冗长的描述会浪费上下文空间。
-
在 skill 名称中使用命名空间 - 考虑使用包名前缀(例如
frontend-review、backend-deploy)以避免混淆。
| 行为 | CLAUDE.md | Skills |
|---|---|---|
| 祖先加载(向上遍历目录树) | 是 | 否 |
| 嵌套/后代发现(向下遍历目录树) | 是(惰性) | 是(自动发现) |
| 全局位置 | ~/.claude/CLAUDE.md |
~/.claude/skills/ |
| 项目位置 | .claude/ 或仓库根目录 |
.claude/skills/ |
| 内容加载 | 完整内容 | 仅描述(调用时加载完整内容) |