目录
有一次做板级移植,驱动代码其实早就写完了。
同一颗外设,从一块 STM32 板子搬到另一块 i.MX RT 板子,寄存器表没有变,时序图没有变,datasheet 也没有变。真正消耗时间的是另一堆东西:I2C 初始化位置换了,DMA cache 策略换了,中断优先级要跟 RTOS 重新对齐,linker script 里还有一段原来没人注意的保留 RAM。
那天晚上我盯着启动日志,突然意识到一件事。我们平时说代码复用,其实复用的从来不是一整个工程。能复用的是被拆干净的知识,是边界清楚的接口,是那些知道什么时候该插手、什么时候不该插手的经验。
后来我看 Cursor、Codex、Claude Code 这些 AI 编程工具里的 Skill,感觉非常像这件事。
很多人把 Skill 当成更长的提示词。这当然能用,但很快就会遇到问题。今天团队有人用 Cursor,明天换 Codex 跑代码审查,另一个同事又在 Claude Code 里调 HardFault。每个平台都能写指令,每个平台都有自己的目录和配置。结果经验又散了,像早年每个芯片工程都维护一套私有 BSP,移植一次,手工改一次,坑也重新踩一次。
我更愿意把 Skill 看成 AI 时代的 BSP。
它不是一段漂亮话,而是一套可移植的工程能力。它把团队里那些反复发生的工作流沉淀下来,比如 bring-up 检查、HardFault 分析、DMA cache 排查、release 前检查、map 文件分析。然后让 Cursor、Codex、Claude Code 在合适的时候拿起来用。

这篇文章只讨论一个具体问题。
同一份 Skill,怎样尽量兼容 Cursor、Codex 和 Claude Code。
1、工具会变,经验别被工具锁住
做嵌入式的人应该很理解平台绑定的痛。
一段外设驱动,如果从一开始就把业务逻辑、寄存器访问、时钟配置、OS 适配和板级差异混在一起,第一次写的时候可能很快。等第二块板子来了,问题就开始了。你分不清哪些代码代表芯片本身,哪些只是那块开发板的历史包袱。
Skill 也一样。
如果一个 Skill 里写满了某个平台的私有字段、私有路径、私有命令,它在当前工具里可能很好用,但很难带走。换到另一个 Agent,轻则触发不稳定,重则脚本跑不起来,甚至某些安全策略完全失效。
我现在写 Skill,会先按三层拆。
| 层次 | 放什么 | 不放什么 |
|---|---|---|
| 核心 Skill | 检查顺序、判断标准、输入证据、输出格式 | 平台私有路径、个人机器路径、只对某工具生效的字段 |
| 可移植资源 | references/、scripts/、assets/ |
需要登录某个工具才能读到的资料 |
| 平台适配 | Cursor、Codex、Claude Code 各自发现和触发 Skill 的胶水 | 真正的工程规则和领域经验 |
这和嵌入式分层很像。
| 嵌入式里的概念 | Skill 里的对应物 | 设计原则 |
|---|---|---|
| datasheet 和 errata | 领域检查规则和背景知识 | 尽量平台无关 |
| HAL 驱动 | SKILL.md 里的工作流 |
保持接口清楚 |
| BSP | .agents/skills、.cursor/skills、.claude/skills |
负责接入平台 |
| SDK 私有扩展 | 平台元数据、工具授权、触发策略 | 可以用,但别污染核心 |
| 产测脚本 | scripts/ |
做确定性检查,不替代判断 |
我见过不少 Skill 写得很努力,但移植性很差。问题通常不在内容不专业,而是它一开始就没分层。
2、三个平台其实在靠近同一种东西
截至 2026 年 5 月 30 日,Cursor、Codex、Claude Code 都已经支持围绕 SKILL.md 组织能力,只是入口和增强能力不完全一样。
可以先看一张压缩表。
| 平台 | 项目级常见入口 | 用户级常见入口 | 核心文件 | 比较有特点的能力 |
|---|---|---|---|---|
| Cursor | .cursor/skills/,必要时用 .cursor/rules/ 做桥接 |
~/.cursor/skills/ |
SKILL.md |
Agent 可按场景发现 Skill,也可以用斜杠菜单显式调用 |
| Codex | .agents/skills/ |
$HOME/.agents/skills |
SKILL.md |
渐进式加载、agents/openai.yaml、显式或隐式调用 |
| Claude Code | .claude/skills/ |
~/.claude/skills/ |
SKILL.md |
allowed-tools、context: fork、动态上下文注入 |
这张表的重点不是让你背路径,而是看清趋势。
SKILL.md 正在变成 AI Agent 能力包的公共入口。目录可以不同,字段可以扩展,触发机制也会各有实现,但一份写得克制的 Skill,已经可以被多种工具理解。
真正要小心的是第二层差异。
比如 Claude Code 支持在 Skill 里写 allowed-tools,让某些工具在 Skill 激活时少走确认流程。这个能力很强,但你不能假设 Cursor 和 Codex 会按同样方式处理。
再比如 Codex 支持 agents/openai.yaml,可以配置显示信息、调用策略和依赖。这个文件对 Codex 很有价值,但其他平台可能直接忽略。
Cursor 现在也支持 Agent Skills,适合放过程型的工作流;但团队里如果还有旧的 Cursor Rules,也没必要一夜之间全删。更稳的做法是把 Rules 当成桥接层,让它指向同一份核心 Skill。
所以跨平台兼容的写法,不是找一个字段让所有平台都完全支持。更现实的做法是:
先写一个不依赖私有字段也能工作的核心 Skill,再给各个平台补增强。
3、一份可移植 Skill 应该长什么样
如果让我给嵌入式团队设计一个共享 Skill 仓库,我不会直接把 Skill 散落在三个工具目录下。我会先维护一份源目录。
bash
repo/
├── ai-skills/
│ └── embedded-bringup-review/
│ ├── SKILL.md
│ ├── references/
│ │ ├── linker-checklist.md
│ │ ├── dma-cache-notes.md
│ │ └── rtos-porting-notes.md
│ ├── scripts/
│ │ ├── parse-map.py
│ │ └── check-vector-table.py
│ └── assets/
│ └── review-template.md
├── .agents/
│ └── skills/
├── .cursor/
│ └── skills/
└── .claude/
└── skills/
实际分发时,可以复制,也可以用脚本同步。软链接在个人机器上很好用,但在团队里我会谨慎。Windows 权限、WSL、CI 容器、企业终端安全策略,都可能让 symlink 变成不确定因素。
对团队来说,最稳的方式是把 ai-skills/ 当成单一源,然后同步到各个平台入口。
bash
ai-skills/embedded-bringup-review
-> .agents/skills/embedded-bringup-review
-> .cursor/skills/embedded-bringup-review
-> .claude/skills/embedded-bringup-review
这和固件里维护一个 drivers/,再由不同 BSP 去挂载,是一个道理。
真正应该被代码审查的是 ai-skills/embedded-bringup-review/SKILL.md,不是三份拷贝。三份入口只是为了让工具找到它。
4、Cursor 里不要只靠一条长规则
Cursor 的 Rules 适合放项目长期约束,比如目录边界、代码风格、生成文件不要改、测试命令怎么跑。
但 Skill 更适合放过程型工作流,比如怎么评审 Bootloader 跳转,怎么分析 HardFault,怎么检查 DMA cache。
如果团队已经有成熟的 Cursor Rules,不需要一夜之间全部推翻。可以先保留一条桥接规则,把 Cursor 拉到同一份核心 Skill 上。
bash
---
description: 修改底层固件、板级支持包、启动文件、链接脚本、中断、DMA、时钟树或 RTOS 移植代码时,按照仓库里的新板点亮评审 Skill 执行。
globs:
- "firmware/**/*.c"
- "firmware/**/*.h"
- "boards/**/*.c"
- "boards/**/*.h"
- "linker/**/*.ld"
alwaysApply: false
---
请读取并遵守 `ai-skills/embedded-bringup-review/SKILL.md`。
评审时优先关注以下问题:
- 启动入口、中断向量表、链接脚本和 Bootloader 跳转地址是否一致。
- DMA 缓冲区、cache 维护、内存对齐和生命周期是否安全。
- NVIC 优先级是否破坏 RTOS 可调用 API 的边界。
- map 文件中的 Flash、RAM、栈和堆是否还有余量。
- 输出必须给出可验证的最小测试项。
这段规则的目的不是复制整个 Skill,而是把 Cursor 的触发机制接到核心 Skill 上。
以后真正维护的仍然是 ai-skills/embedded-bringup-review/SKILL.md。规则只是入口。
5、Codex 里把 .agents 当作一等入口
Codex 对 .agents/skills 比较友好。它会先读取 Skill 的 name、description 和路径,等任务匹配后再加载完整 SKILL.md。这正好适合把项目级 Skill 放进仓库。
一个固件项目可以这样放:
bash
.agents/
└── skills/
└── embedded-bringup-review/
├── SKILL.md
├── references/
└── scripts/
使用时可以显式说:
bash
请使用 $embedded-bringup-review 帮我看这次启动文件和 linker script 的改动。
也可以让 Codex 按 description 自动判断。
如果想让 Codex 里展示得更清楚,可以加一个平台增强文件。
bash
interface:
display_name: "新板点亮评审"
short_description: "检查启动路径、链接脚本、时钟、中断、DMA cache 和最小上板验证"
brand_color: "#2563EB"
policy:
allow_implicit_invocation: true
注意,这个文件是 Codex 适配层,不是核心 Skill。不要把工程流程只写在这里,否则 Cursor 和 Claude Code 看不到。
6、Claude Code 里工具授权要谨慎
Claude Code 的 Skill 很适合做命令型工作流,比如读取 git diff、分析日志、跑一个检查脚本。它还支持 allowed-tools,可以在 Skill 激活时给某些工具授权。
这件事在嵌入式场景里要特别克制。
读 map 文件、读日志、跑只读解析脚本,可以考虑放宽。
刷固件、擦 Flash、改证书、提交代码、发 release 包,不要自动触发。
一个只读分析 Skill 可以这样写:
bash
---
name: hardfault-analysis
description: 分析 Cortex-M HardFault 日志。用户提供 stacked PC、LR、xPSR、MSP、PSP、CFSR、HFSR、BFAR、MMFAR、map 文件或反汇编片段时使用。缺少寄存器现场时先要求补充信息,不要凭感觉猜测。
allowed-tools: Read Grep
---
# HardFault 现场分析
先确认 fault 寄存器和栈帧是否完整。缺少 stacked PC、LR、xPSR、CFSR 或 HFSR 时,先列出缺失项,不要直接给结论。
如果有 map 文件,查找 stacked PC 是否落在合法代码区。如果有 BFAR 或 MMFAR,有效位为 1 时再解释地址含义。
输出必须包含:
- fault 类型判断
- 证据来源
- 最可能的两到三个原因
- 下一步最小复现实验
如果 Skill 会产生副作用,比如刷板,宁愿让它只能手动调用。
bash
---
name: flash-dev-board
description: 只有当用户明确要求刷写开发板,并提供目标探针序列号、固件路径和板卡型号时才使用。不要自动调用。
disable-model-invocation: true
---
# 开发板刷写
刷写前必须复述目标板卡、探针序列号、固件路径和擦除范围,并等待用户确认。
用户没有明确确认时,不要执行任何会写 Flash、擦除 Flash 或复位目标板的命令。
嵌入式里,有些命令看起来只是工具命令,实际会改变硬件状态。这个边界不能交给模型临场发挥。
7、一个可以长期维护的中文模板
最后放一份我会拿来做跨平台起点的模板。
它没有用太多平台私有字段。Cursor、Codex、Claude Code 都能从 name、description 和正文里理解核心意图。平台增强可以之后再加。
bash
---
name: your-skill-name
description: 执行一个具体工程流程。用户提到明确的触发词、文件类型、日志、工具输出或故障现象时使用。任务不属于这个流程,或者缺少必要证据时不要使用。
metadata:
owner: your-team
version: "0.1.0"
---
# 你的工程流程名称
你是一名负责该领域的工程师。处理任务时优先看证据和可验证检查,不要输出泛泛的建议。
## 适用范围
当用户的问题与本 Skill 的 description 匹配,并且能拿到相关文件、日志或命令输出时使用。
不要把本 Skill 用在普通格式整理、大范围重构,或任何没有得到用户明确确认就会产生副作用的动作上。
## 需要检查的输入
- 源代码文件
- 配置文件
- 编译日志
- 生成报告
- 硬件日志
- 用户给出的约束条件
## 工作流程
1. 先确认任务边界和目标硬件。
2. 先看证据,再给建议。
3. 只运行安全的只读脚本;有副作用的动作必须先问用户。
4. 按领域检查清单逐项对照。
5. 输出风险时,按硬件风险、数据风险、发版风险、维护风险排序。
## 输出要求
必须返回:
- 发现的问题
- 对应证据
- 建议修复方式
- 最小验证步骤
## 支撑文件
- 详细检查清单见 `references/checklist.md`
- 只有在输入文件存在时,才运行 `scripts/analyze.py`
如果要给 Codex 加显示和策略,再放一个可选文件。
bash
interface:
display_name: "工程流程名称"
short_description: "一句话说明这个 Skill 解决什么问题"
brand_color: "#2563EB"
policy:
allow_implicit_invocation: true
如果这个 Skill 有副作用,把隐式触发关掉。
Cursor 和 Claude Code 侧可以在 SKILL.md 里写:
bash
disable-model-invocation: true
Codex 侧可以在 agents/openai.yaml 里写:
bash
policy:
allow_implicit_invocation: false
这几行配置看起来不起眼,但在嵌入式里很关键。因为一次错误刷写,可能不是回滚一个文件那么简单。
8、最后真正要留下来的不是工具配置
Cursor 会继续变,Codex 会继续变,Claude Code 也会继续变。
目录可能会调整,字段会增加,触发策略会更聪明。今天写在某个工具里的配置,明天可能就换了名字。
但有些东西不会那么快过时。
比如 HardFault 没有寄存器现场,就不应该开始编故事。 比如能刷板、能发版、能提交代码的动作,必须显式触发。
这些才是 Skill 里最值得沉淀的东西。
我不太愿意把 Skill 写成某个 AI 工具的使用说明。我更愿意把它写成团队经验的封装层。外面接 Cursor、Codex、Claude Code 都可以,里面真正稳定的是工程判断。
做嵌入式的人都知道,好的 BSP 不是把所有平台差异抹掉,而是把差异隔离在该隔离的位置。
Skill 也是。
把核心经验写干净,把平台胶水放边缘,把能验证的部分交给脚本,把有风险的动作锁到显式调用。
这样写出来的 Skill,才有机会从一个人的提示词,变成一个团队能带走、能迁移、能长期维护的工程资产。
最后,如果实在没精力自己做跨平台适配,也可以把现有 Skill 交给当前正在使用的 AI 工具,直接告诉它:请将当前 Skill 改成适合当前工具识别和调用的形式,同时保留原有工程流程和安全边界。