SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-25

如何跨仓库共享 Agent Skills 并避免版本漂移

8 个仓库复制同一 skill 会在第 60 天开始分叉。本文介绍共享 skills 仓库、项目固定标签、冒烟测试和版本评审流程。

如何在多个仓库之间共享 agent skill

要在多个仓库之间共享 agent skill,不要继续复制文件,而应改为依赖该文件。维护一个 skills 仓库,为其创建标签,然后让每个项目固定使用某个标签。随后为每个 skill 添加冒烟测试,并按照评审依赖项版本升级的方式评审每次版本更新。

这包括四个部分:共享的事实来源、每个仓库固定的版本、每个 skill 的冒烟测试,以及评审流程。下文将说明每个部分存在的原因、2026 年发布的工具如何处理这些问题,以及如何在不依赖任何外部服务的情况下,基于自托管 git 远程仓库构建完整方案。

agent skill 是一个目录,其中包含 SKILL.md 文件,以及它所需的脚本和参考文件。如果这是一个新单元,请先阅读什么是 agent skill,以及 SKILL.md 如何工作。本页介绍的是围绕该单元的供应链。

技能存放位置,以及共享为何困难

Claude Code 会从 3 个位置加载技能,技能文档列出了每个路径。

  • ~/.claude/skills/<skill-name>/SKILL.md 是个人级路径。它会加载到您的所有项目中,其他人无法使用。
  • .claude/skills/<skill-name>/SKILL.md 是项目级路径。检出该仓库的用户都可以加载它。
  • <plugin>/skills/<skill-name>/SKILL.md 随插件一起发布。在启用该插件的所有位置都会加载它。

对团队而言,中间这个位置最有用,因为它会提交到仓库,所有克隆该仓库的人都能获得它。但问题也从这里开始。.claude/skills/ 中的技能属于一个仓库。您有 8 个仓库,因此需要复制 8 份技能。

frontmatter 无法解决这个问题。Agent Skills 规范允许使用 6 个键,而强制执行该规范的分发路径会在使用其他键时打印以下列表:

Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name

请注意缺少的内容:没有 version 键。文件中没有记录哪个副本更新。这很合理,因为技能是文档,而不是软件包。但这意味着版本控制必须由文件外层负责,而这一层需要由您管理。

问题一:8 份副本在不知不觉中逐渐分叉

复制粘贴在第一天有效。到第60天就会失效。有人修正了 payments 仓库中的错误说明,却没有修改其他7份副本。另一个人在 orders 中添加了分页规则。现在,相同的技能名称会产生两种不同的审查结果,具体取决于代理从哪个目录启动,而两个开发者都不知道原因。

这种故障不会显式报错,因为不存在错误状态。技能的内容是散文。过时的说明会导致代理自信地给出错误答案,而这类错误代价最高。代理不会将你的副本与其他人的副本进行比较,因此唯一的信号就是有人发现两个仓库的内容不一致。

问题二:没有固定版本

即使团队将技能集中存放,通常也只是通过复制步骤来共享:设置脚本、入职文档中的一行 curl,或用于同步目录的 shell 别名。这些方式安装的都是当前分支最新提交中的内容。

这意味着,同一应用处于同一提交时,两个开发人员运行的指令仍可能不同,因为他们在不同日期执行了同步。发生代理运行异常后,还有一个关键问题无法回答:产生该结果的技能版本是什么?如果没有记录修订版本,这次运行就无法复现,错误报告也无法用于定位问题。

问题三:没人知道技能是否仍然有效

技能没有编译器。它是提供给模型的指令,因此即使文件逐字节保持不变,也可能停止工作。模型升级会改变其严格遵循长指令的程度。技能调用的命令行工具可能会重命名某个标志。参考文件中的 URL 可能开始返回 404,代理则会根据错误页面继续工作。

这些情况都不会明确报告失败。代理仍然会回答。只是答案比上个月更差,而这种变化很难通过一次次 pull request 发现。

2026 发布的工具解决了什么问题

目前已经出现了几种方案,但它们对于版本应该存放在哪里,意见并不一致。

锁定文件。 Vercel Labs 提供的 skills 命令行工具(vercel-labs/skills,采用 MIT 许可证,截至 2026 年 8 月 5 日为 v1.5.22)可以从 git 仓库将技能安装到代理所需的目录,并且了解七十多个代理的目录布局。npx skills add <repo> 用于安装,npx skills update 用于升级,npx skills list 用于显示已安装内容。安装记录按用户保存,而不是按仓库保存。该项目有一个公开请求(issue 283),要求增加 skills install 命令,从锁定文件重新安装所有已跟踪的技能,使第二台机器获得相同的技能集合。应将这一请求视为当前状态的说明。锁定文件的思路已经确定,但按项目管理的部分仍在开发中。

规范和测试。 SkillSpec 采用另一种思路。它将 SKILL.md 视为需要检查的契约,而不是可以直接信任的说明文字,目标是让技能“可遵循、可测试、可证明”。skillspec doctor <path> 报告代理可能中断执行的位置。skillspec boundary map <path> 报告技能可以访问的内容,skillspec boundary assess <path> 则按风险对这些发现进行排序。它是一个 Rust crate,采用 MIT 或 Apache 2.0 双许可证,截至 2026 年 7 月 29 日为 0.2.2 版本。应安装固定版本,而不是最新版本:

cargo install skillspec --version 0.2.2 --locked
skillspec --version

--locked 使用该 crate 发布时的依赖版本进行构建,因此构建过程不会在后台发生版本漂移。skillspec --version 应输出 0.2.2。如果显示其他数字,说明 PATH 中有更早的旧二进制文件优先被执行。

供应商实践。 Google 在一篇介绍其构建、测试和扩展代理技能方式的文章中,说明了它如何构建 google/skills 中的技能。去掉规模因素后,这套机制就是常规的持续集成(CI)。每项技能在合并前,都必须通过 frontmatter 元数据、行数、目录布局和命名检查。链接检查器会在任意 URL 返回 404 时使构建失败,从而捕获代理生成的看似合理但实际无效的链接。作者必须同时为技能提供评估提示词套件和评分规则。定期评估作业每周针对整个技能库运行,以发现回归问题;每项技能还都有一名指定负责人,质量下降时应由其负责修复。

三个答案背后的共同模式

您不必从中选择一个。它们背后是同一种结构,而原生 git 已经提供了完整支持。

  1. 单一事实来源。每个 skill 只有一个存放位置,每个仓库都引用该位置,而不是保存副本。
  2. 每个仓库固定一个版本。每个项目记录所使用的确切修订版本,因此升级就是在该项目中提交一次变更,并带有作者和日期。
  3. 每个 skill 一个冒烟测试。通过一个可运行的检查,验证该 skill 仍能产生其承诺的结果。
  4. 评审流程。共享 skill 的变更必须经过评审,每个使用方在采用变更前都能看到差异。

这就是依赖项的结构。skill 成为共享工件的速度超过了配套工具的发展速度,因此,优先使用您已经信任的工具是最安全的做法。

面向小型团队的自托管 Git 远程仓库布局

一个仓库存放这些技能文件。除此之外不存放任何内容,因此其历史记录就是指令的变更日志。

agent-skills/
  skills/
    api-review/
      SKILL.md
    release-notes/
      SKILL.md
  tests/
    api-review.sh
    release-notes.sh
  CHANGELOG.md

发布版本使用标签。请使用附注标签,因为它们包含消息和日期。消息应说明使用者为什么需要升级到该版本:

git tag -a v1.4.0 -m "api-review: require pagination on list endpoints"
git push origin v1.4.0

如果远程仓库使用 Gitea、Forgejo、GitLab,或使用您自己的 VPS 上通过 SSH 提供的裸仓库,下面的内容都不需要修改。这里涉及的只有 git 和一个符号链接。

使用 git 子模块固定版本

子模块会在当前仓库中记录另一个仓库的一个确切提交。这个记录就是固定版本。在每个使用该模块的项目中:

git submodule add https://git.example.com/team/agent-skills.git vendor/agent-skills
git -C vendor/agent-skills fetch --tags
git -C vendor/agent-skills checkout v1.4.0
mkdir -p .claude/skills
ln -s ../../vendor/agent-skills/skills/api-review .claude/skills/api-review
git add .gitmodules vendor/agent-skills .claude/skills/api-review
git commit -m "Pin shared agent skills to v1.4.0"

符号链接是实现这一机制的关键。项目级别的技能条目可以是指向磁盘上其他位置的目录的符号链接,Claude Code 会跟随该链接,并从目标位置读取 SKILL.md。因此,该技能会作为普通的项目技能加载,而实际文件内容位于你指定提交的子模块中。

检查固定的提交:

git submodule status

正常的输出行以一个空格开头,后面依次是提交、路径和最近的标签:

 4d1a7c2f0b93e5a1c8d6f2b40e7a95c3d1f8b602 vendor/agent-skills (v1.4.0)

开头的 - 表示子模块从未初始化,因此 .claude/skills/api-review 指向空位置,技能也不会加载,并且不会显示错误。使用 git submodule update --init 修复。开头的 + 表示当前检出的提交与记录的提交不同,因此该开发者使用的是其他人没有的指令。新克隆的仓库需要执行 git clone --recurse-submodules。这条命令应写入 README,因为普通克隆会使 vendor/agent-skills 为空,且不会显示错误。

升级必须经过明确操作,这正是固定版本的意义:

git -C vendor/agent-skills fetch --tags
git -C vendor/agent-skills diff v1.4.0 v1.5.0 -- skills/
git -C vendor/agent-skills checkout v1.5.0
git add vendor/agent-skills
git commit -m "Bump shared agent skills to v1.5.0"

diff 行是审查路径。它会显示所有其他使用该模块的仓库将看到的相同变更,并且可以直接放入拉取请求。

改用插件市场固定版本

如果您不希望要求每位开发者学习 submodule,可以使用 Claude Code 插件系统完成分发,并从自托管远程仓库获取内容。在 skills 仓库的 .claude-plugin/marketplace.json 中创建目录:

{
  "name": "acme-agents",
  "owner": { "name": "Platform team", "email": "platform@example.com" },
  "plugins": [
    {
      "name": "team-skills",
      "description": "Shared review and release skills",
      "version": "1.4.0",
      "source": {
        "source": "url",
        "url": "https://git.example.com/team/agent-skills.git",
        "ref": "v1.4.0",
        "sha": "4d1a7c2f0b93e5a1c8d6f2b40e7a95c3d1f8b602"
      }
    }
  ]
}

这里涉及两个不同的源,混淆它们是最常见的错误。市场源指获取目录本身的位置,接受用于指定分支或标签的 ref,但不接受 sha。目录中的插件源同时接受这两个参数;如果两者都已设置,sha 才是实际生效的固定版本。因此,精确提交固定版本应写在目录条目中。

随后,每个使用该插件的仓库都在已提交的 .claude/settings.json 中声明该市场:

{
  "extraKnownMarketplaces": {
    "acme-agents": {
      "source": {
        "source": "url",
        "url": "https://git.example.com/team/agent-skills.git",
        "ref": "v1.4.0"
      }
    }
  },
  "enabledPlugins": {
    "team-skills@acme-agents": true
  }
}

信任项目目录的团队成员会收到安装市场的提示,插件也会自动为其启用,无需通过 wiki 页面说明安装步骤。此后,skills 使用 /team-skills:api-review 进行调用,因为插件 skills 按插件名称划分命名空间,不会与同名项目 skill 冲突。推送新标签后,使用者通过 /plugin marketplace update acme-agents 刷新内容;如果安装摘要要求执行 /reload-plugins,再运行该命令。

为一个技能编写冒烟测试

冒烟测试是针对包含已知故障的 fixture 执行一次脚本化代理运行,并进行一项断言。Claude Code 通过 -p 以非交互方式运行,用户调用的技能也可在此模式下工作:将 /skill-name 放入提示字符串中,它会在运行开始前展开。

#!/usr/bin/env bash
set -euo pipefail

claude -p "/api-review Read fixtures/orders-api.md and list the rule ids it breaks." \
  --allowedTools "Read" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"rule_ids":{"type":"array","items":{"type":"string"}}},"required":["rule_ids"]}' \
  | jq -e '.structured_output.rule_ids | index("pagination-required")' > /dev/null

fixtures/orders-api.md 是一个包含一个故意故障的短文件。断言内容是技能会指出该故障。当过滤器产生 null 时,jq -e 会以非零状态退出,因此无法再捕获种子故障的技能会导致脚本失败。运行失败时,claude 自身会以非零状态退出,而 set -euo pipefail 会将任一失败转换为失败的测试。

模型在不同运行中可能会改写答案,因此不要对完整句子进行断言。应断言技能预期输出的标识符,或所要求 schema 中的字段,并保持 fixture 简短,以降低运行成本。

在 CI 中添加 --bare。否则,claude -p 会加载与交互式会话相同的上下文,包括运行机器上的钩子、插件和 CLAUDE.md,因此团队成员的个人配置可能改变结果。裸模式会跳过所有自动发现,因此也会跳过正在测试的技能;请显式加载该技能。裸模式也不会读取订阅登录信息,因此应先在环境中设置 ANTHROPIC_API_KEY:

claude --bare -p "/team-skills:api-review Read fixtures/orders-api.md and list the rule ids it breaks." \
  --plugin-dir vendor/agent-skills \
  --allowedTools "Read" \
  --output-format json

使用 --output-format stream-json 时,运行的第一个事件会报告已加载的插件,并为未加载的插件提供 plugin_errors 数组。若 plugin_errors 非空,则让 CI 作业失败。这样可以捕获指向已不存在修订版本的固定配置;否则,代理可能只是静默忽略你的统一规则。

共享技能就是可执行指令

有两个特性使这句话成为字面事实,而且当文件来自其他团队时,这两个特性都很重要。

首先,SKILL.md 可以在模型读取任何内容前运行 shell 命令。正文中的以下行就是预处理指令:

- Current branch: !`git rev-parse --abbrev-ref HEAD`

命令会在加载该技能的机器上运行,其输出会替换模型收到的文本中的占位符。以三个反引号开头、后跟 ! 的围栏代码块也会以相同方式运行多个命令。运行时不会有人批准这些操作。读取共享技能,就等于读取其中的命令替换内容。

其次,frontmatter 可以预先批准工具。allowed-tools 会在调用该技能的当前轮次中授予列出的工具,且不会显示权限提示。对于项目技能,用户接受该文件夹的 workspace trust 对话框后,这项授权才会生效。Claude Code 文档明确说明了后果:信任代码仓库前,应先审查项目技能,因为技能可以自行授予广泛的工具访问权限。

因此,应像处理依赖项升级一样处理技能升级。在机制允许时,使用精确的 commit 固定版本,因为 tag 可能被移动,而 branch 按定义会持续移动。在受限机器上,设置中的 "disableSkillShellExecution": true 会将每个命令替换为字面文本 [shell command execution disabled by policy],而不是执行命令;通过 managed settings 应用后,用户无法覆盖该设置。Bundled skill 和 managed skill 不受此设置影响。

对于技能读取的内容,也应采取同样的谨慎态度。运行 env 或打开配置文件的技能,会将找到的所有内容载入模型上下文。这正是 防止机密信息进入所运行的代理 所讨论的故障。获取页面或运行查询的技能,则是将同样的暴露面扩展到外部,因为获取的文本进入上下文后,看起来与您编写的指令完全相同。在 将代理指向自己的 SearXNG 实例进行网页搜索 之前,应先了解这一边界。

版本升级时需要阅读的内容

  • 每个 SKILL.md 正文的差异,因为其中的文本就是代理将遵循的指令。
  • 每个命令替换,因为技能加载时会在您的计算机上执行这些命令。
  • 对 allowed-tools 的任何更改,因为该行无需提示即可授予工具访问权限。
  • 标签背后的测试运行结果。如果共享仓库在 CI 中运行自己的冒烟测试,则您固定使用的标签应附带一次通过的运行结果。

如果审阅者无法在十分钟内读完整个差异,说明该技能已经过于庞大。请将其拆分。同样的原则也适用于代理读取的仓库文档:将持久规则保存在 AGENTS.md 与 HUMAN.md 的拆分所描述的文件中,将架构方面的理由写入面向代理编写的 DESIGN.md,而让技能只包含范围明确的操作流程。

当模型或工具变更破坏技能

技能本身没有人编辑,但其底层的多个组件可能发生变化。模型升级会改变长指令的执行可靠性,因此依赖模型执行到第九步的技能可能不再执行到那里。命令行工具重命名了某个 flag,代理仍使用旧 flag,读取错误后自行推断。被引用的 URL 开始返回 404。代理框架改变了技能的选择方式,因此原本能够匹配成功的 description 不再胜出。当某个流程开始提前结束时,增加版本号无法修复问题;必须调整指令结构,强制执行最后几个步骤。这正是unlazy 技能及其 Depth Tree 方法所采用的方式。

因此,在这种安排中,冒烟测试承担了主要作用。除了在 push 时运行每个技能的测试,还应按计划运行。Google 每周都会针对整个技能库运行评估任务,原因就在于此。对于只有十个技能的团队,在一台小型 VPS 上设置每周 cron 任务就足够了。这是开发人员发现故障前获知问题的唯一方法。

可移植性同样有帮助。Agent Skills 规范将 frontmatter 限制为 6 个键,因此按照该规范编写的技能可以加载到编写目标工具之外的其他工具中;而每增加一个特定于框架的键,都是在押注某个供应商。编写能够适应模型更换的技能本身是一项专门实践,详见让技能适用于任何模型。

FAQ

如何在多个仓库之间共享一个 agent skill?

将 skill 放在专用 git 仓库中,为其发布版本打 tag,然后让每个使用方项目引用 tag,而不是复制文件。有两种机制可用。git submodule 记录精确的 commit,从 .claude/skills/<name> 指向该 submodule 的符号链接会使它作为普通项目 skill 加载。插件 marketplace 通过 /plugin 完成同样的工作,并在使用方仓库的 .claude/settings.json 中声明固定版本。两种方式都会将版本记录在 git 历史中,因此可以确定某次 agent 运行使用了哪些指令。

可以将 agent skill 固定到特定版本吗?

不能在 SKILL.md 内完成,因为其中的 frontmatter 没有 version 键。固定版本必须由文件外层的机制提供。git submodule 的设计就是固定精确的 commit。在 Claude Code 插件 marketplace 中,插件源接受 ref 来指定分支或 tag,接受 sha 来指定精确的 commit;两者同时存在时,sha 优先。marketplace 源本身只接受 ref。优先使用 commit 固定,因为在审核后 tag 仍可能被移动。

skill 的 smoke test 应断言什么?

应断言稳定的结果。针对包含已知故障的 fixture,以非交互方式运行 skill,然后检查输出中是否出现特定标识符,例如 skill 应报告的规则 id。使用 --output-format json 和 --json-schema 请求结构化输出,可以使检查精确;jq -e 会在缺少该值时使脚本失败。不要断言完整句子,因为模型在不同运行之间可能改写答案。

从其他团队的仓库安装共享 skill 是否安全?

应将其视为代码依赖,因为它属于可执行指令。SKILL.md 可以在加载时通过 ! 命令替换形式运行 shell 命令,frontmatter 的 allowed-tools 字段可以在无需提示的情况下预先批准工具。在每次升级时查看 diff,固定到精确的 commit 而不是分支,并优先使用由本团队控制的源。在受管控的机器上,设置中的 "disableSkillShellExecution": true 会完全阻止命令替换运行。

共享 skill 能否在 Claude Code 以外的 agent 中运行?

这取决于使用了哪些 frontmatter。Agent Skills 规范定义了 6 个键:name、description、license、compatibility、metadata 和 allowed-tools。仅使用这些键的 skill 可以在实现该规范的工具之间加载,也可以不做修改地加载到 Claude Code 中。其他工具特有的键以及规范之外的正文功能,可能会在其他工具中被忽略或拒绝,因此计划广泛共享的 skill 不应包含这些内容。