Claude Code Skills 在单体仓库中采用“按需发现”机制,它与 CLAUDE.md 的“逐级向上加载”行为截然不同。当您编辑某个子目录下的文件时,Claude Code 会自动从该路径的 .claude/skills/ 目录中发现并加载对应技能的描述,实现上下文的精准聚焦。本文将详细拆解此机制,并提供在大型项目中高效管理 Skills 的实践方案。

Claude Code Skills 在大型单体仓库中的发现机制与最佳实践

在大型单体仓库(Monorepo)中,不同的代码包(package)通常有各自独特的开发约定、框架模式或部署流程。将这些领域知识封装为 Claude Code Skills 是实现结构化代理工作流的关键一步。理解 Skills 的发现与加载机制,能帮助你精准控制 Claude 的上下文,避免信息过载,并支持多团队独立维护专属技能。

Skills 发现机制与 CLAUDE.md 的根本区别

许多用户熟悉 CLAUDE.md 文件的加载行为:Claude Code 会从当前工作目录开始,逐级向上查找父目录中的 CLAUDE.md 并加载内容。这是一种“祖先加载”模式,确保项目根目录的全局指令始终可用。

然而,Skills 的发现机制恰恰相反。它采用 “子孙发现” 模式。Skills 不会向上查找,而是从项目根目录或当前文件所在路径向下探索。这意味着,只有当您开始编辑特定子目录下的文件时,该目录及更深层目录中的 Skills 才会被发现和加载。

行为 CLAUDE.md Skills
祖先加载 (向上)
嵌套/子孙发现 (向下) 是 (惰性) 是 (自动发现)
全局位置 ~/.claude/CLAUDE.md ~/.claude/skills/
项目位置 .claude/ 或仓库根目录 .claude/skills/
内容加载 完整内容加载 仅加载描述 (调用时加载完整内容)

表格:Skills 与 CLAUDE.md 加载行为对比

Skills 的自动嵌套发现(Automatic Discovery from Nested Directories)

这是 Skills 在单体仓库中实现模块化的核心能力。考虑以下典型的单体仓库结构:

/mymonorepo/
├── .claude/
│   └── skills/
│       └── shared-conventions/SKILL.md    # 项目级技能
├── packages/
│   ├── frontend/
│   │   ├── .claude/
│   │   │   └── skills/
│   │   │       └── react-patterns/SKILL.md  # 前端专属技能
│   │   └── src/
│   │       └── App.tsx
│   ├── backend/
│   │   ├── .claude/
│   │   │   └── skills/
│   │   │       └── api-design/SKILL.md      # 后端专属技能
│   │   └── src/
│   └── shared/
│       ├── .claude/
│       │   └── skills/
│       │       └── utils-patterns/SKILL.md  # 共享工具库技能
│       └── src/

场景一:启动于根目录,未编辑任何文件 当您在 /mymonorepo/ 启动 Claude Code 会话,但尚未编辑任何文件时,只有根目录下的 shared-conventions 技能会被加载。因为其位于项目作用域的 .claude/skills/ 中,始终可用。其他包目录下的技能由于没有对应的文件操作触发,不会被发现。

场景二:编辑特定包中的文件 当您请求 Claude 编辑 packages/frontend/src/App.tsx 时,情况发生变化。由于您开始操作 packages/frontend/ 目录下的文件,Claude Code 会自动从该路径开始向下搜索,发现并加载 packages/frontend/.claude/skills/ 中的 react-patterns 技能。此时,shared-conventionsreact-patterns 两个技能的描述都会出现在上下文中。而后端和共享库的技能则保持未发现状态。

核心结论:嵌套 Skills 的发现是 “按需”“基于上下文” 的。它们不会在会话启动时全部预加载,而是随着您工作的深入,相关领域的技能描述才会进入上下文。这为大型仓库提供了极佳的上下文隔离和优化。

描述(Description) vs 完整内容(Content)的加载策略

Skills 的加载是一个分两阶段的过程,这是优化上下文窗口使用的关键设计:

  1. 第一阶段:描述常驻 当技能被发现后,只有其 SKILL.md 文件中的 description 字段(或前 N 个字符)会被加载到当前会话的上下文中。这使得 Claude 知道该技能的存在及其大概用途,但不会占用大量宝贵的上下文空间。

  2. 第二阶段:内容按需加载 只有当用户显式调用该技能(例如,通过斜杠命令或子代理触发),或者 Claude 基于当前任务判断需要使用该技能时,其完整的 SKILL.md 内容才会被加载到上下文中,以指导具体的执行步骤。

这种设计确保了在单体仓库中即使存在大量技能,会话初期的上下文也能保持轻盈,只加载最相关的技能概要。

15K 字符预算限制与上下文管理

所有技能的描述文本会共享一个字符预算,默认为 15,000 个字符。在拥有众多技能的大型单体仓库中,这个限制很容易被触及。

验证方法

  1. 运行 /context 命令。
  2. 检查输出中是否有警告信息,提示部分技能的描述因超出字符预算而未被加载。

解决方案

  • 环境变量覆盖:通过设置环境变量 SLASH_COMMAND_TOOL_CHAR_BUDGET 可以临时提高此预算。例如,export SLASH_COMMAND_TOOL_CHAR_BUDGET=25000
  • 优化技能描述:这是根本的解决之道。保持每个 SKILL.mddescription 字段简洁明了,只包含核心功能概述,将详细用法和示例保留给 content 部分,确保在预算内能加载更多技能。

单体仓库中的 Skills 组织最佳实践

基于上述发现机制,以下是组织大型单体仓库 Skills 的实践建议:

  1. 项目根目录:存放通用、跨团队技能 将适用于整个仓库的规范(如代码风格、提交信息格式、通用部署流程)定义为根目录下的 Skills。例如:shared-conventionsrepo-commit-flow

  2. 各包目录:存放领域专属技能 将特定于某个前端框架、后端服务或共享库的模式、组件规范或部署命令放在对应包目录的 .claude/skills/ 下。例如:react-patternsapi-designutils-patterns。这实现了技能的自动发现和上下文隔离。

  3. 危险技能:强制用户确认 对于执行删除、发布或访问生产环境等破坏性操作的技能,务必在其 SKILL.md 的 Frontmatter 中设置 disable-model-invocation: true。这将禁止 Claude 自动调用,必须由用户显式确认后才能执行,增强安全性。

  4. 命名空间:避免冲突与混淆 虽然技能的命名优先级已定(企业级 > 个人 > 项目),但在大型仓库中,为技能名称添加包前缀是更清晰的做法。例如:frontend-deploybackend-migrate,这能有效避免名称冲突,并让技能用途一目了然。

  5. 定期审查上下文 养成在复杂任务开始前使用 /context 命令的习惯,它能清晰地显示当前加载了哪些技能及其来源,帮助你判断是否需要手动调整工作目录或触发文件操作来发现更多相关技能。

通过遵循这些模式,你可以让 Claude Code 的技能系统在大型单体仓库中既强大又可控,为不同的开发场景提供精准的领域知识支持,真正实现从“凭感觉编码”到“结构化代理工程”的转变。如需进一步了解技能字段的详细配置,可参考 Claude Code Skills 深度解析

FAQ

Q: 如何查看当前会话中加载了哪些 Skills? A: 在 Claude Code 会话中直接运行 /context 命令,输出结果会列出所有已加载技能的名称、来源路径及其状态,可以清晰看到哪些技能因字符预算等原因未被加载。

Q: 如果我同时编辑 packages/frontendpackages/backend 的文件,两个目录下的技能会同时加载吗? A: 会的。只要您在同一个会话中操作了这些目录下的文件,Claude Code 就会分别从这些路径向下发现并加载对应的 Skills 描述。它们会同时出现在上下文中,共享字符预算。

Q: 子代理(Subagents)加载技能的行为有何不同? A: 当子代理在其定义中预加载了特定技能时,该技能的完整内容会在子代理启动时就注入其上下文,而非仅加载描述。这适用于子代理必须严格遵循某技能全部细节才能完成任务的场景。详见 Claude Code Subagents 指南