Featured image of post 用一个轻量脚本统一管理多个 AI 工具的 Skills

用一个轻量脚本统一管理多个 AI 工具的 Skills

前言

最近在不同的 AI Coding 工具之间切换得比较多:Claude Code、Codex、Qoder,还有一些项目里用到的 Gemini CLI、Copilot 等。

Skill 的格式现在已经逐步收敛了。基本都是一个目录,入口叫 SKILL.md,里面放 namedescription 和具体的工作流说明;需要时还可以带脚本、模板和参考资料。

问题不在 Skill 怎么写,而在它们放在哪里

同一个 release-note Skill,如果希望 Claude Code、Qoder、Cline、Kiro 都能发现,最直接的做法往往是复制四份:

.claude/skills/release-note/
.qoder/skills/release-note/
.cline/skills/release-note/
.kiro/skills/release-note/

刚开始只有一个 Skill 的时候没有感觉。后面一旦改了脚本、补了参考资料,或者调整触发描述,就很容易漏改其中一份。更麻烦的是,用户级 Skill 和项目级 Skill 又是两套目录,久而久之根本不知道哪一份才是“最新的”。

所以我写了一个很小的脚本原型:不发明新的 Skill 格式,也不检查 SKILL.md 内容,只做一件事——把自己的 Skills 集中放好,再让不同工具能找到它们。

完整脚本:agent-skills-manager.sh

GitHub:仓库准备中,后续会补在这里。

以 .agents/skills 为唯一维护源,直接扫描或通过原生目录软链接让不同工具发现

问题:目录不统一,不等于格式不统一

现在主流工具对 SKILL.md 的理解已经比较接近,不过发现目录各有一套。

工具用户级目录项目级目录是否直接扫描 .agents/skills
Claude Code~/.claude/skills/.claude/skills/官方文档未声明
Codex~/.codex/skills/.agents/skills/当前项目环境已验证
Gemini CLI~/.gemini/skills/.gemini/skills/
GitHub Copilot~/.copilot/skills/.github/skills/
Qoder~/.qoder/skills/.qoder/skills/官方文档未声明
Cline~/.cline/skills/.cline/skills/官方文档未声明
Kiro~/.kiro/skills/.kiro/skills/官方文档未声明
Windsurf / Amp / OpenCode / Roo / OpenClaw各有原生目录各有原生目录

这里不再把每个产品的差异全部展开。关键是:.agents/skills/ 已经成了一个很有价值的跨工具约定,但还不能替代所有原生路径。

比如 Gemini CLI 明确把 .agents/skills/ 作为 .gemini/skills/ 的兼容别名;Copilot 同时接受 .github/skills/.claude/skills/.agents/skills/;Windsurf、Amp、OpenCode、Roo Code、OpenClaw 也都支持这个共享入口。反过来,Claude Code、Qoder、Cline、Kiro 仍然应该保留原生目录适配。 Gemini CLI GitHub Copilot Windsurf Amp OpenCode Roo Code OpenClaw

先确定边界:这个脚本不管理 Skill 内容

一开始很容易把这个工具做成“Skill 管理平台”:解析 frontmatter、校验规范、扫描脚本安全性、做一个注册表、记录遥测数据……

这些事情当然都有人在做,但不是我这次要解决的问题。

这里的前提很简单:进入管理目录的 Skill 默认已经符合规范,也由使用者自己负责内容。 脚本不读取和修改 SKILL.md,自然也不对“写得好不好”“会不会触发”做判断。

它只管理四类关系:

统一来源     .agents/skills/
目录迁移     原生目录 -> 统一目录
目录发现     原生目录 -> 软链接 -> 统一目录
来源同步     Git checkout -> git pull --ff-only
状态检查     已链接 / 未配置 / 冲突 / 本地有修改

这也是它保持轻量的关键。

和现有 Skill 工具有什么不同

其实业界并不缺 Skill 安装工具。

  • skills.sh 提供了发现、安装、榜单和 Skill Packs,更像一个公共目录与分发入口。
  • GitHub CLI 的 gh skill 可以搜索、预览、安装、更新和发布,并记录来源仓库、ref、tree SHA,支持指定 agent host 和版本固定。 GitHub Docs
  • OpenSkill 则是面向多个 coding agent 的 Git 型 Skill 包管理器,覆盖发现、安装、更新和发布。

这些方案并不是“太重所以不能用”。如果团队要分发公共 Skill、维护版本、管理第三方来源,它们解决的问题更完整。

我这里的场景更靠前一步:本机已经有一批自己写的、或者团队仓库里已有的 Skills,只希望它们不再复制多份,并且能在多个 AI 工具里被立即发现。为这件事情引入一个市场、中心服务或新的包格式,有点绕了。

简单来说:

需求更合适的方案
发现和安装第三方 Skillskills.shgh skill、OpenSkill
发布团队公共 Skill 包Git 仓库、gh skill publish、平台能力
管理自己电脑和当前项目已有的目录本文的软链接脚本

统一目录:用户态和项目态各一份

脚本选择下面两个目录作为唯一真实来源:

~/.agents/skills/                  # 用户态:自己的通用工作流
<project>/.agents/skills/          # 项目态:随仓库提交的团队工作流

项目态和用户态不要混在一起。比如发布博客、生成 changelog 这一类个人习惯,放用户态比较合适;某个仓库的发布流程、测试约定、代码审查模板,则应该放进项目态,和代码一起演进。

多数支持 .agents/skills/ 的工具不需要额外工作,直接能扫描到。只有不扫描这个路径的宿主才创建适配链接:

<project>/.agents/skills/          # 唯一真实目录
<project>/.claude/skills -> ../.agents/skills
<project>/.qoder/skills  -> ../.agents/skills
<project>/.cline/skills  -> ../.agents/skills
<project>/.kiro/skills   -> ../.agents/skills

用户态也是同样的逻辑:

~/.agents/skills/                  # 唯一真实目录
~/.claude/skills -> ../.agents/skills
~/.codex/skills  -> ../.agents/skills
~/.qoder/skills  -> ../.agents/skills
~/.cline/skills  -> ../.agents/skills
~/.kiro/skills   -> ../.agents/skills

注意相对路径的写法。链接文件在 .claude/ 内,所以项目级应该是:

mkdir -p .claude
ln -s ../.agents/skills .claude/skills

而不是 ln -s .agents/skills .claude/skills。后者会被解析为 .claude/.agents/skills,路径就错了。

另外也不要给所有工具无脑创建原生链接。比如 Copilot、OpenCode、Amp 同时会扫描多个兼容目录;把同一目录再链接过去,可能造成同名 Skill 被重复发现。脚本只给“没有 .agents/skills 兼容入口”的工具建链接。

脚本流程

脚本是一个单文件 Bash 工具,迁移和链接不依赖额外运行时;如果要同步 Git 来源,则需要本机有 Git。macOS/Linux 可以直接运行:

chmod +x agent-skills-manager.sh
./agent-skills-manager.sh

默认在当前 Git 项目根目录工作;没有 Git 仓库时就使用当前目录。也可以用 --root 明确指定项目目录。加 --global 则管理 ~/.agents/skills/

./agent-skills-manager.sh --project
./agent-skills-manager.sh --root ~/Code/my-project
./agent-skills-manager.sh --global

交互菜单目前是这样:

Agent Skills Manager (project)

  1) Migrate existing Skills to .agents/skills
  2) Check tool and directory compatibility
  3) Configure or repair native Skill links
  4) Sync Git-backed shared Skill sources
  5) Show status
  q) Exit

各菜单的责任刻意分得比较开:

1. 迁移

迁移时只看目录,不校验里面的 SKILL.md

例如项目里原来有:

.claude/skills/release-note/
.qoder/skills/api-review/

脚本会逐个询问,确认后移动为:

.agents/skills/release-note/
.agents/skills/api-review/

对应原生 skills/ 目录在确认已经空了以后,才会被替换为软链接。

如果统一目录已经存在同名 Skill,脚本不会猜哪一份应该保留,也不会覆盖,而是报告冲突并把原目录留在原地。这个地方宁愿麻烦一点,也不能把别人本地改过的 Skill 静默丢掉。

2. 检查

检查只做两件事:统一目录是否存在;各工具的原生目录是缺失、正确链接、错误链接,还是普通目录。

它也会尝试在 PATH 里查找 claudecodexgeminiqoderclinekiro-cliopencodeopenclaw。这只是一个提示,不把“命令不存在”当作兼容性结论——很多 IDE 工具并不一定提供命令行入口。

3. 链接

链接阶段不会覆盖目标目录:

  • 目标不存在,创建链接;
  • 已经是正确链接,跳过;
  • 是普通目录,提示先迁移;
  • 是指向别处的链接,原样保留。

这样脚本可以重复执行。反复运行不会不断修改用户环境,也不会吞掉手工配置。

核心实现其实没有多少东西:

# 原生目录都位于 <scope>/.工具名/skills,统一回到 <scope>/.agents/skills
link_value='../.agents/skills'

mkdir -p "$(dirname "$target")"
ln -s "$link_value" "$target"

真正需要多写一点的是状态判断和冲突保护,而不是 ln -s 本身。

4. 更新

“统一目录”解决的是一处修改、到处生效;它并不等于自动知道所有 Skill 的上游版本。

这个原型没有再设计一个 Manifest。当前约定很简单:如果 .agents/skills/ 本身是一个 Git checkout,或者它下面某个 Skill 目录本身是 Git checkout,菜单 4 会在工作区干净时执行:

git pull --ff-only

本地有未提交修改时直接跳过,不使用 reset、不覆盖。不是 Git 目录则提示“无需同步”。

这已经能覆盖一个很常见的长期维护方式:把个人 Skills 或团队 Skills 放在一个 Git 仓库里,然后链接到各个工具。以后如果确实需要锁定版本、多来源依赖、安装第三方 Skill,再接入 gh skill 或其他包管理工具也不晚。

代码结构

目前脚本内部按下面的层次拆分:

agent-skills-manager.sh
├── 参数解析
│   ├── --project
│   ├── --global
│   ├── --root DIRECTORY
│   └── --action migrate|diagnose|link|update
├── Provider 路径表
│   ├── 原生发现 .agents/skills 的工具
│   └── 需要创建原生目录链接的工具
├── 状态判断
│   ├── missing
│   ├── linked
│   ├── other-link
│   ├── directory
│   └── file
├── 迁移与链接
├── Git 同步
└── 交互菜单

脚本不把所有已知工具都硬编码成“必须创建目录”。Provider 表最重要的字段其实只有两个:原生路径,以及它是否已经扫描 .agents/skills。以后增加新工具,只要补一条路径映射和兼容判断即可。

一点限制

软链接最适合本机环境。用户态目录基本没有问题,改一处立即生效。

项目态则要注意 Git checkout、Windows 和云端 sandbox 对 symlink 的支持差异。如果团队环境不稳定,推荐把 <project>/.agents/skills/ 作为真实目录提交;在每个开发者机器上运行脚本生成原生适配链接。这样仓库本身仍然是普通文件结构,链接只属于本地运行环境。

还有一点:Cursor、Aider、Continue 等工具目前主要使用 Rules、AGENTS.md 或自定义命令体系,不能因为它们也能写 Markdown 就强行创建 .cursor/skills/。脚本应该明确显示“不在 Skill provider 表中”,而不是创建一个看起来合理、实际上不会被发现的目录。

总结

这套方案没有试图统一所有 AI 工具,也没有试图成为新的 Skill 平台。

它只做一件很实际的事:

一份 Skill 内容
.agents/skills 作为唯一来源
按需生成各个工具的原生目录链接
一次修改,多个 AI 工具立即看到

对个人开发者来说,先把目录关系理顺,比先选择一个复杂的注册表更有用。等 Skills 多起来、需要分发给团队或维护第三方来源时,再把它交给 Git、gh skill、skills.sh 之类的工具即可。

至此,这个小脚本最核心的实现点就是这些。后面准备把脚本单独整理到 GitHub 仓库,再持续补 Provider 映射和实际使用中的兼容问题。~~~

参考