1. 为什么我在三个 AI 编程工具之间来回折腾后,最终用 skills.sh 收编一切
先讲个真实场景。手头同时用 Claude Code、Cursor、Codex CLI 干活,这是很多 AI 编程重度用户的常态。装好三个工具倒不费劲,真正的麻烦在于每个工具都有自己的"规则文件":Claude Code 认CLAUDE.md,Cursor 读.cursor/rules/下的.mdc文件,Codex CLI 则看项目根目录的AGENTS.md。你以为是同一套工程规范,结果要维护三份内容相似、格式各异的文件。改一个规则,得手动同步三处,漏掉任何一个,对应的工具就会按旧规则干活。
我最初的想法很简单:搞一个脚本,把一份 Markdown 复制到这三个位置。试了两周就意识到不对。Claude Code 的CLAUDE.md是纯 Markdown,可以写很自然的指令;Cursor 的.mdc文件需要在 frontmatter 里声明description、globs、appliesTo之类的元信息;Codex 的AGENTS.md虽然也是 Markdown,但它对结构、优先级、引用方式有自己的一套理解。硬复制不是不行,但等于是把三套格式揉成一团,最后写出来的规则文件既不够"贴合平台语义",又会因为格式差异反复折腾。
后来找到 skills.sh 这个 CLI 工具,思路一下就顺了。它做的不是简单的文件复制,而是让你维护一份统一的技能定义,再由工具本身负责把这份定义"翻译"成各个平台需要的格式。用了一段时间后,我觉得这种"一份源、多端编译"的思路,正好解决了多平台 AI 工具规则管理的核心痛点。这篇文章就把我踩过的坑、用下来的经验,以及完整的操作流程整理出来,给同样在多个 AI 编程工具之间切换的人做个参考。
1.1 三套规则文件的"精神分裂"现场
先列个表,把各平台规则文件的核心差异摊开看:
| 平台 | 默认规则文件位置 | 格式 | 规则粒度 | 重要特点 |
|---|---|---|---|---|
| Claude Code | 项目根目录CLAUDE.md | Markdown | 整文件读取 | 支持项目级和用户级,Markdown 章节越靠前越容易被模型关注 |
| Cursor | .cursor/rules/目录下的.mdc文件 | Markdown + frontmatter | 可按文件粒度控制 | frontmatter 里可以配globs指定作用范围,alwaysApply控制是否总是生效 |
| Codex CLI | 项目根目录AGENTS.md | Markdown | 整文件读取,但支持层级引用 | 也支持codex目录下的分文件配置,适合大型项目拆分 |
三套体系各有各的脾气。Cursor 的.mdc文件是最"结构化"的,frontmatter 里写错了字段不会报错,但规则可能静默失效,这点后文会详细说。Claude Code 的CLAUDE.md则是"自然语言优先",你把它当作文档来写,它读起来就很舒服,但正因为自由度高,团队里不同成员写出来的风格可能天差地别。Codex 的AGENTS.md目前越来越像一种"标准"——很多其他工具也开始兼容它,但它对嵌套、引用的解析又与 Claude 不完全一样。
1.2 一次真实改动引发的连锁事故
让我痛下决心找解决方案的,是一次很低级的错误。项目里原来的规则是"提交代码之前必须跑测试",某天负责人说改一下措辞,把"必须跑"改成"变更涉及核心模块时,必须跑全量测试;只改样式时,允许只跑 lint"。我在CLAUDE.md里改完,顺手提交了事。第二天同事用 Cursor 打开项目,AI 助手还在按旧规则办事,他按照旧规则理解代码,给出的重构建议里把"可以跳过测试"当成既定约束,搞得我花了一个下午排查为什么测试覆盖面突然缩水。
问题不在"人的记忆",而在"规则文件的维护天然是分布式任务"。三份文件,三个位置,没有一份权威副本,任何一次局部改动都是在为未来的不一致埋雷。我当时想的很简单:能不能用一套命令,让我只编辑一份"源",然后一键同步到所有平台?skills.sh 就是冲着这个诉求来的。
2. skills.sh 的统一技能格式与多端适配机制,它凭什么能当"中央厨房"
把 skills.sh 想成一个"中央厨房"可能不准确,更合适的比喻是"编译管道"。它先读取你定义的一份结构化的技能描述文件,然后根据目标平台的配置格式差异,生成对应的规则文件写入各自的目录。整个过程是单向的:skills.yaml是源头,CLAUDE.md、.cursor/rules/*.mdc、AGENTS.md是产物。你不需要直接手改产物,即使手改了,下次同步也会被覆盖。
2.1 一份技能定义长什么样
一个最小化的技能文件大概是下面这样:
project: ts-backend-ai-guide vars: language: "TypeScript" test_command: "npm test" skills: code-review: about: "代码评审的重点关注项" applies_to: [claude, cursor, codex] content: | # Code Review Rules 优先检查安全问题和数据校验; 任何涉及数据库变更的逻辑,必须显式说明会影响哪些表; Promise 必须处理 rejection,不允许静默吞错。 测试豁免:仅当改动为纯类型调整或注释变更时,可跳过全量测试。 全文引用项目语言:{{ language }} 提交前必须执行:{{ test_command }}拆解一下结构:
project:技能库所属项目的描述信息,会作为规则文件的"元信息"写入产物。vars:变量区。文件里用双花括号{{ }}引用的地方,在同步时会被替换成真实值。这个机制的价值,后面单独讲。skills:真正的技能列表。每个技能有about(说明)、applies_to(适用平台列表)、content(规则正文)。content支持多行字符串,内部写的是 Markdown,将来同步到哪个平台,skills.sh 会尽量保留 Markdown 语义。
2.2 平台适配层是怎么工作的
每个平台的生成器都可以看作一个独立的"渲染器"。skills.sh 在做sync时,会做这么几件事:
- 解析
skills.yaml,校验 YAML 结构和字段合法性。 - 根据
applies_to判断当前技能要不要落到某个平台。 - 读取项目里已有的目标文件(比如已有
CLAUDE.md),把不属于 skills.sh 管理的历史内容保留下来,只更新由它托管的那一段。 - 按平台的格式规范渲染成最终文本,写回对应文件。
以 Cursor 为例,它把规则拆成多个.mdc文件。skills.sh 会为每个技能生成一个.cursor/rules/your-skill-name.mdc文件,并在 frontmatter 里写入必要的元信息:
--- description: code-review globs: "**/*.ts" alwaysApply: true --- # Code Review Rules ...Claude Code 和 Codex 则不同,它们读取的是单一 Markdown 文件。skills.sh 会把所有applies_to包含claude的技能合并渲染进CLAUDE.md,把这个文件做成一个标准 Markdown 文档;同理,codex的技能合并成AGENTS.md。合并的时候,还会插入一段管理标记,方便下次同步时定位"哪些内容是 tools 生成的"。
2.3 为什么"编译"比"复制"靠谱
很多人第一反应是:我不需要这么复杂的工具,写个 shell 脚本把一份文件复制三份不就行了?我最初也是这么干的。区别在于"复制"只能解决文件内容一致性问题,解决不了格式差异问题。
CLAUDE.md需要的是自然的语言流,不应该出现 frontmatter;.mdc文件必须有 frontmatter,否则 Cursor 不识别;AGENTS.md对层级和引用有自己的解析偏好,纯复制过来的内容可能没有发挥它该有的检索效率。
"编译"的优势还体现在改动规则时,不用手动处理多个文件中的折叠关系。比如往code-review技能里加一条规则,只需要改 YAML 里那一处,然后skills sync—— 所有平台的产物都会更新。这种"唯一事实来源"的模式,对多项目、多团队协作尤其重要。
3. 安装、初始化与第一次同步:15 分钟跑通完整链路的实操记录
工具再怎么设计得巧,上手如果太复杂,还是会劝退大多数人。好在 skills.sh 的安装成本很低,核心链路也不长。下面按我实际操作的顺序走一遍。
3.1 安装:三种方式任选
skills.sh 提供了三种安装途径,覆盖常见环境。我用 macOS,直接走 Homebrew:
brew install skills-sh/tap/skillsLinux 环境下,官方更推荐的是用预编译二进制:
curl -sSf https://install.skills.sh | sh这个脚本会把二进制装到~/.local/bin,并让它在当前 shell 生效。Windows 上可以用 Scoop:
scoop bucket add skills-sh https://github.com/skills-sh/scoop-bucket.git scoop install skills装完验证一下版本:
skills --version如果输出版本号,说明装好了。
注意:安装脚本只是把二进制放到 PATH,不会动你的任何项目文件。怕它改东西的话,可以先在一个空白目录里做试验。
3.2 初始化技能仓库
进入一个实际项目目录,执行:
skills init这个命令会做几件事:
- 检查当前目录有没有
skills.yaml,没有则创建一个带注释的模板; - 创建
.skills/目录,用来放模板文件、本地缓存、同步状态记录; - 扫描目录下是否已有
CLAUDE.md、.cursor/rules/、AGENTS.md等文件,并在.skills/state.json里记录现状。
init只会初始化,不会立即写入任何内容,所以可以放心跑。初始化完成后,先看一下生成的skills.yaml里有什么注释说明,再删掉注释改成自己的规则。
如果你只是想先体验,可以直接用官方提供的一个示例项目:
git clone https://github.com/skills-sh/skills-demo.git cd skills-demo skills sync --dry-run3.3 写一个最小技能,并同步到三个平台
在skills.yaml里只写一个技能,内容务求简单,方便观察同步效果:
project: demo vars: owner: "Team Bot" skills: no-debug-log: about: "禁止遗留调试日志" applies_to: [claude, cursor, codex] content: | 禁止在提交代码中遗留 console.log、println、print 等调试日志。 如需临时调试,请用日志库并标记 TODO。 项目负责人:{{ owner }}保存后执行:
skills sync --previewpreview会打印出将要写入哪些文件、每个文件里将包含哪些内容,但不会真的写入。看到输出里有CLAUDE.md、.cursor/rules/no-debug-log.mdc、AGENTS.md三个目标,确认无误:
skills sync执行完,打开这三个文件看一眼。你会在CLAUDE.md和AGENTS.md里看到统一的 Markdown 段落,并在.cursor/rules/no-debug-log.mdc里看到带 frontmatter 的独立文件。至此,完整链路已经跑通:改 YAML、预览、同步、各平台生效。
4. 日常维护里的差异化:同一份技能在不同平台如何各取所需
同步只是基础能力,真正让 skills.sh 好用的是它处理"差异化"的方式。实际项目中,三个平台的使用场景不完全一样:Claude Code 多用于终端里的长链路重构,Cursor 偏向交互式补全和局部编辑,Codex CLI 在自动化流水线里跑任务比较多。同一套规则,硬性统一反而别扭。
4.1 用条件标记区分平台行为
一个常见需求:有些规则只对 Cursor 生效,因为 Claude Code 和 Codex 根本用不到,比如"补全时不要重排 import 顺序",这类 UI 层面的编辑器行为,对 CLI 工具没有意义。
skills.sh 的applies_to字段天然支持这种区分:
skills: editor-specific: about: "仅 Cursor 生效的编辑器行为约束" applies_to: [cursor] content: | 自动补全时不得重排 import 顺序; 重命名符号时,必须同步更新测试文件中的引用; 禁止自动折叠超过 200 行的函数。同步后你会发现,CLAUDE.md和AGENTS.md里完全没有这段内容,只有 Cursor 的.mdc文件里出现了它。这种"按平台过滤"的能力,比把规则全部塞进所有文件然后靠模型自己判断要可靠得多。
4.2 变量注入:同一套规则,不同环境变量
多项目场景是变量注入最实用的地方。假设你有几个后端项目,用的语言不同,一个 Python、一个 Go。技能"mock 接口时必须注明类型"在两个项目里的表达方式略有差异——Python 项目要写typing,Go 项目要写interface。
在 skills.sh 里,你不需要复制两份技能:
vars: language: "Python" mock_hint: "类型标注必须使用 typing 模块"同一份技能文件,拿到 Go 项目里把 vars 改一下再 sync,输出就变成对应的语言约束。这个机制特别适合团队里的多项目模板。我个人的习惯是,每个项目的skills.yaml只保留项目相关的vars和少数特色技能,通用技能全放进全局模板里,同步时用变量做差异化。
4.3 平台级 override:在统一与差异之间找平衡
有时候你确实希望某个技能在大多数平台保持一致,但某个平台上需要额外补充约束。skills.sh 提供了 overrides 机制:
skills: testing: about: "测试相关约定" applies_to: [claude, cursor, codex] content: | 修改核心逻辑时必须新增测试用例。 测试命令必须保持全绿。 overrides: codex: content: | 修改核心逻辑时必须新增测试用例。 测试命令必须保持全绿。 CI 流水线中若测试失败,应立即切换到调试模式定位根因,不能通过重跑掩盖问题。这个 override 会把 codex 平台的渲染内容替换成overrides.codex.content,claude 和 cursor 仍然使用顶层content。它解决的是"大多数一致、个别补充"的需求,不至于因为一个平台特殊,就要复制整份技能。
5. 实测一个月后,我总结的四个容易踩的坑和对应排错方法
工具用起来顺手是一回事,真正放进工作流里,哪些地方会出问题,只有经过一段时间的实际使用才能暴露。下面这些坑不是看文档能提前避开的,每一条都是我在真实项目里撞出来的。
5.1 同步顺序:先改源,再 sync,最后再提交
最早期我犯过一个低级错误:在skills.yaml里改了规则,然后直接一起git add -A提交了。结果就是 YAML 更新了,但生成的CLAUDE.md还是旧版——因为忘了skills sync。这个错误看起来蠢,但很容易犯,尤其是改完规则顺手就提交的肌肉记忆。
正确的顺序永远是:
skills sync git add -A git commit -m "chore: update code review rules"如果嫌麻烦,可以在skills.yaml所在目录配一个 Git hook,提交前自动 sync。后面会说怎么配。
如果你发现自己提交后,某个平台的规则没有跟上游保持一致,先检查一下是不是漏跑了sync。
5.2 路径解析:绝对路径 vs 项目相对路径
content里如果写文件路径,一定要用项目相对路径,不要用绝对路径。Claude Code 在子目录启动时,对绝对路径和相对路径的行为很不一样。实测中,/Users/me/project/src/foo.ts这种绝对路径,一旦换机器或者项目位置变化,就会失效,而且模型会拿着旧路径反复检索。相对路径src/foo.ts则没有这个问题。
同样的道理也适用于.cursor/rules/里的globs字段:
# 推荐 globs: "src/**/*.ts" # 不推荐 globs: "/Users/me/project/src/**/*.ts"Cursor 的 globs 如果写绝对路径,在某些版本里不会报错,但规则就一直不生效。
5.3 Cursor 的 frontmatter 静默失效
这是所有坑里最隐蔽的一个。Cursor 的.mdc文件 frontmatter 只支持特定字段,具体字段名和取值在不同 Cursor 版本里还略有调整。早期我把alwaysApply写成了alwaysApply: true和appliesTo: "always"两种风格混用,结果一部分文件生效,一部分没有。问题是,Cursor 并不会在你写错字段时给出任何提示——规则文件静静躺在.cursor/rules/里,但 AI 完全不读它。
排查办法是先用skills preview看生成结果,再检查.mdc文件前几行:
cat .cursor/rules/no-debug-log.mdc正常输出应该包含:
--- description: no-debug-log globs: "**/*" alwaysApply: true ---如果看到description为空,或者globs字段缺失,基本就是生成器版本和当前 Cursor 版本不兼容。这时升级 skills.sh 并重新 sync 即可。更稳妥的方案是,在技能的content开头把作用范围用 Markdown 注释写清楚,这样即使 frontmatter 失效,至少规则正文里还有提示信息。
5.4 团队协作场景下的覆盖冲突
多人同时在一个项目里维护规则,最容易出现的是"覆盖冲突"。假设 A 在本地改了skills.yaml并 sync,生成了新的AGENTS.md;B 也改了skills.yaml,但他没拉到 A 的修改,直接同步,把 A 的部分改动覆盖掉了。
这个问题在 Git 合作中很难完全避免,只能通过流程来缓解:
skills.yaml必须走代码评审,不要直接推到主分支;- 生成的
CLAUDE.md、.cursor/rules/、AGENTS.md可以纳入版本管理,但要约定由sync统一生成,禁止手改; - 提交前用
skills list --diff之类的命令确认本次改动影响了哪些平台。
我现在的做法是,在 CI 里加一道检查,如果发现 YAML 和生成文件不一致,就让流水线失败。这样所有改动必须先过一遍 sync,才能合并进主线。
6. 让 skills.sh 真正融入工作流的几个小技巧
工具链这种东西,能不能坚持用下去,往往取决于它跟现有工作流贴合得紧不紧密。最后分享几个我用起来很顺手的小技巧。
6.1 把高频技能固化成模板
如果你经常要新建项目,可以把通用技能抽成模板,skills init的时候直接带上:
skills init --template gh:skills-sh/base-node-server模板里可以预设网络安全基线、提交信息规范、测试要求、环境变量命名规范等。这样新项目一开始就有一份比较像样的规则体系,而不是从零开始写。我自己的模板里放了一套"安全生产基线",包含认证鉴权、日志脱敏、依赖审查三块内容,新项目初始化后基本不用再改,就能直接约束住 AI 工具的行为。
6.2 用 Git Hook 自动同步,减少手工负担
前面说的"先 sync 再提交",配置成 hook 之后就不用操心了。在项目.git/hooks/pre-commit里写入:
#!/bin/bash if command -v skills >/dev/null 2>&1; then skills sync >/dev/null 2>&1 || echo "skills sync failed" fi exit 0并给它加执行权限:
chmod +x .git/hooks/pre-commit这样每次git commit之前,skills.sh 都会自动把 YAML 里的最新内容同步到各个平台文件。如果 YAML 没改动,sync 会很快结束,不会对提交速度造成什么影响。
如果你用了团队级配置,可以把这个 hook 的安装命令写进项目文档,或者在skills init的时候自动生成。
6.3 从 0 到 1 建立自己的技能库,建议分三步走
想要让这套体系真正发挥价值,不建议一上来就试图穷举所有规则。我建议的顺序是:
第一步,先只维护一个项目里的 3 到 5 条核心规则,比如"禁止调试日志""变更必须补测试""数据库变更必须显式说明影响"。用一两周时间,观察 AI 工具是否稳定遵守。
第二步,在确认核心规则稳定生效后,把项目里遇到的"AI 反复犯的错"沉淀成新技能。比如某次 AI 在修改代码时频繁引入类型错误,就可以加一条"修改类型定义时必须同步更新使用处的类型声明"。这类规则最好一次只加一条,观察效果后再加更多,避免一次加太多导致模型上下文过于拥挤。
第三步,当多个项目都用同一套规则后,把它们抽成通用模板,用变量区分项目差异。到这一步,你的技能库就从一个项目的零散配置,变成可以复用的组织级资产了。
我个人在实际使用中最深的体会是,skills.sh 的价值不在于"自动生成文件"这个动作本身,而在于它逼着你把规则结构化、把来源集中化。过去我依赖"在 AI 工具对话框里临时打字纠正它"的方式,对话框一关,约束就消失;现在所有约束都以技能文件的形式固化下来,而且一次修改、全局生效。这种"规则可版本化、可评审、可历史回溯"的工作方式,对我来说,比任何 AI 模型本身的进步都更管用。如果你也同时有几个 AI 编程工具要伺候,不妨花一个下午把 skills.yaml 搭起来,体验一下"只改一处,到处生效"的清爽感。