
1. 为什么我会折腾一个“Skills Manager”我是那种会在一个仓库里把 Codex、Claude Code、Cursor、Zed、Continue、Gemini CLI 全部装上的开发者。装了半年最先崩溃的不是硬盘是我的脑子每个工具的 Agent 技能格式都不一样有的认 SKILL.md有的读 AGENTS.md有的只吃 .cursor/rules还有几个非要写在配置文件里。我每更新一套“代码审查规范”就得在五六个地方分别改一遍改完还不一定生效经常出现 Claude Code 已经用了新规则Codex 还在用三个月前的旧版本。忍到临界点之后我动手做了一个 Skills Manager。它把 54 款 AI 编程工具的 Agent 技能收拢成一个统一入口通过桌面应用统一编辑、管理、版本化再一键分发到各个工具对应的目录和格式。简单说这就是一个跨平台桌面中枢你不需要知道每个 Agent 的技能应该塞进哪个文件中枢会帮你处理好。这篇文章会把项目从背景、设计到落地全部拆开包括我踩过的坑、适配器的实现思路以及如果你也想搭一套自己的技能管理方案可以直接照搬的部分。1.1 先说清楚Agent 技能到底是什么很多人把 Agent 技能理解成“提示词”但它远不止一长串文字。一个可复用的技能通常包含元信息、触发条件、指令模板、示例输出有时还带脚本、参考文档和工具调用约束。拿 Claude Code 的 Skills 举例本质是一个目录里放一个 SKILL.md里面用 frontmatter 写名称和描述正文写怎么调用甚至可以附带脚本和知识库文件。Codex 那边更粗暴直接把规则写进 AGENTS.md靠目录层级做优先级。Cursor 则用 .mdc 文件文件头部有 glob 和 description用来控制这条规则作用于哪些文件。打个生活化一点的比方技能就像游戏里的“技能书”。每本书写的都是同一个招式但不同游戏的任务栏格式不一样你不能把《魔兽世界》的技能直接拖到《原神》里。Skills Manager 做的事情就是把这些“技能书”全部翻译成各自游戏能识别的格式再放到对应的背包格子里。这个翻译过程不是简单改后缀因为每个 Agent 的加载机制、优先级和变量替换规则都不同只做“批量复制”肯定行不通。1.2 为什么是“桌面中枢”而不是又一个 CLI市面上已经有很多命令行工具能做规则同步比如 dotfiles 管理、symlink 脚本甚至我自己早期也是用 shell script 把技能文件软链过去。那为什么还要做一个带桌面的中枢因为当技能数量超过 20 个配对关系超过 100 条时纯文本配置文件已经无法一眼看出“这个技能到底发到哪几个 Agent 了”。我需要一个能看到全局的界面左边技能库右边目标工具列表中间是冲突提示和版本状态。桌面应用还有其他好处。它能常驻系统托盘在 Agent 工具更新技能缓存时弹出提醒它能调用系统 keychain 安全存储密钥它能做可视化 diff每次同步前让我看清楚哪些文件会被改动。用 Web 应用也能做但本地 Agent 工具都在本地文件系统里跑Web 端访问本地文件需要桥接既麻烦又容易有安全风险。所以最终选择做成跨平台桌面应用Windows、macOS、Linux 三端都能跑。2. 核心设计一个统一模型54 个适配器整个项目最关键的设计决策不是我用了什么桌面框架而是我定义了一个“通用技能模型”。所有 Agent 的技能到了 Skills Manager 里都会先被转成一份统一的 Skill Manifest再由不同适配器渲染成目标工具需要的格式。这个思路很像编译器前端解析源码生成 AST后端根据目标平台生成机器码。前端统一后端各搞各的。2.1 先把所有技能抽象成一份 Skill Manifest我设计的 Manifest 最开始非常简单大概长这样{ schemaVersion: 1, id: commit-message-polish, name: Commit Message Polisher, description: 根据暂存区 diff 生成符合 Conventional Commits 的提交信息, trigger: git diff --cached, globs: [*.{ts,tsx,js,jsx,py,go}], promptTemplate: 请分析下面的 diff严格按 Conventional Commits 规范生成 3 条候选提交信息。要求类型限定为 feat/fix/docs/refactor/test/chorebody 部分用一句话说明动机。\n\n{{DIFF}}, variables: [DIFF], targets: [claude-code, codex, cursor], version: 0.3.2 }这里面的每一项都是我从 54 款主流工具里抽出的“最小公约数”。几乎每个 Agent 都认 description、trigger、instruction 和 version但命名五花八门。比如有些工具里没有 promptTemplate只有 rules没有 trigger只有 glob。所以 Manifest 的作用是定一个内部标准写技能的人只面向它不面向任何具体工具。这样做最大的好处是新接入一个 Agent 时不需要改任何已写好的技能只需要为那个 Agent 写一个新的适配器把 Manifest 渲染成对应格式就够了。2.2 适配器层一份技能如何变成各工具能读懂的格式适配器是 Skills Manager 里工作量最大的部分。每个适配器需要知道三件事目标技能文件放在哪个路径、用什么格式组织内容、如何做变量替换和缓存清理。举个例子同一个 Manifest 在不同工具里的落盘结果完全不同。对于 Claude Code适配器会生成skills/commit-message-polish/SKILL.mdfrontmatter 里写 name 和 description正文写步骤和示例。对于 Codex适配器会读取现有的 AGENTS.md追加一个 “Commit Message” 小节并把触发条件写成一段自然语言规则。对于 Cursor适配器会生成.cursor/rules/commit-message-polish.mdc头部加上glob: *.{ts,tsx,js,jsx,py,go}正文里写清楚“当用户输入 /commit 时使用以下流程”。我统计过54 款 AI 编程工具大概可以分成三类工具类型代表技能承载形式适配难度CLI AgentClaude Code、Codex、Gemini CLI目录 Markdown / AGENTS.md中IDE 原生 AgentCursor、Zed、CopilotRules 文件 / 自定义指令低Agent 编排框架Dify、LangChain、CrewAIYAML / Prompt 模板数据库高第三类最难因为它们不是简单的“读文件”而是要从数据库里加载 Prompt 模板甚至要通过 API 动态注入。所以我的适配器分了两层文件型适配器直接写盘API 型适配器生成一个可导入的 JSON 包用户在中枢里确认后再手动导入到目标平台。项目当前实现了 54 个适配器其中 42 个是文件型12 个是 API 型。3. 动手实现从零搭一个可用的技能中枢前面聊了设计接下来聊点实在的。如果你也想搭一个类似的工具不一定非要抄我的代码但技术选型和核心流程可以复用。3.1 技术选型到底怎么落地我最终用的是 Tauri 2.0 加 React 加 TypeScript状态管理用的 Zustand本地存储用 SQLite。为什么不选 Electron我最开始用的是 Electron原型两天就出来了但打包出来 200 多 MB启动要三秒而且内存占用常年 400MB 以上。作为一个要常驻后台的桌面工具这个代价有点高。Tauri 用系统 WebView 渲染界面后端是 Rust打包体积能压到 15MB 到 30MB内存占用明显更低而且 Rust 做本地进程管理和文件监听非常顺手。目录结构大概是这样的skills-manager/ ├── src/ # React 前端 │ ├── pages/ # 技能库、目标工具、同步记录 │ └── components/ # 技能卡片、冲突提示、变量编辑 ├── src-tauri/ # Rust 后端 │ ├── adapters/ # 54 个工具适配器每个目录一个 │ ├── manifest/ # Manifest 解析、校验、版本对比 │ └── sync/ # 文件渲染、写入、备份、回滚 └── skills/ # 本地技能源目录支持 git 版本管理前端的核心页面只有三个技能库、目标工具、同步记录。技能库展示所有 Manifest支持拖拽导入已有技能文件目标工具展示检测到的本地 Agent 工具和对应路径同步记录展示每次分发后的文件 diff、成功失败状态和回滚按钮。3.2 写出第一个统一技能Commit Message 生成器为了测试适配器我写的第一个技能是 Commit Message 生成器。它的 Manifest 就像上面那段 JSON但真正重要的是 promptTemplate 里的占位符。这里有个经验不要在技能里写死任何具体信息尽量用{{DIFF}}这样的变量占位分发时再统一替换。如果你把某个仓库的具体路径写进技能文件里换一个项目就废了。这个技能的同步流程非常典型。我在前端点击“同步到已选 Agent”后Rust 后端会做以下几件事读取 Manifest校验 schemaVersion 是否支持解析 variables 列表从安全存储里读出对应值按目标工具执行 render 函数生成目标文件内容对比目标文件当前 hash如果没有变化则跳过写入临时文件再原子替换目标文件避免写一半崩溃触发对应 Agent 的缓存更新命令。这里最重要的一步是“渲染”。以 Codex 适配器为例它会生成这样的 AGENTS.md 追加内容## Commit Message Polisher When the user runs git diff --cached, follow these steps: 1. Run git diff --cached to collect staged changes. 2. Analyze the diff and classify the change type as fix, feat, refactor, docs, test, or chore. 3. Write exactly three candidate commit messages. 4. Do not mention files that are not in the diff. 5. If the diff is empty, tell the user to stage files first.如果同一个 Manifest 要发给 Claude Code适配器会生成 SKILL.md描述部分几乎不变但正文会被改写成更贴近 Claude Code 习惯的步骤式指令。这就是统一模型的价值改一处所有工具跟着更新。3.3 变量与安全别把密钥写进技能文件技能里难免要用到 API Key、仓库地址、人员名单这些信息。早期我把这些直接写在 Manifest 里后来发现一个严重问题技能文件会通过 git 同步到仓库里一次手滑 push 就能把密钥泄露出去。而且很多 Agent 工具在加载技能文件时会把整个文件内容送给模型密钥进了对话上下文等于又多了一条泄露路径。Skills Manager 的解决办法是引入“变量插值”。Manifest 里只写{{GITHUB_TOKEN}}或者{{TEAM_MEMBERS}}真正的值存在系统 keychain 里Tauri 通过 Rust 的 keyring 接口读写。渲染时在内存里做替换并且替换后不写回 Manifest。这样技能源文件可以放心提交到 git别人拿到技能文件也拿不到你的密钥。还有一个细节渲染时如果发现某个变量没有值我不会直接留空而是中止同步并提示用户避免生成一个“半残技能”把 Agent 带偏。4. 真实使用中的技术难点与排查办法这个项目最难的不是 UI也不是适配器数量而是各种 Agent 对技能的加载机制差异太大。下面几个问题是我在真实使用时反复踩的坑。4.1 不同工具加载技能的机制天差地别同样是文件型技能加载机制完全不一样。Claude Code 会在启动时扫描skills/目录遇到新目录会提示“发现新技能”Codex 只在每次请求时按层级向上查找最近的 AGENTS.mdCursor 的 Rules 则按 glob 匹配规则决定是否激活而且修改 .mdc 文件后不会立刻生效经常要等几秒或重启窗口。Zed 更特殊它支持 Agent 读取项目内.rules文件但默认不递归子目录。这些差异直接影响了适配器的输出策略。对于 Claude Code我可以放心生成独立目录对于 Cursor我不能只写文件还得在同步完成后调用一个“refresh rules”命令或者提醒用户重启窗口。对于 Codex我必须注意 AGENTS.md 的文件大小因为内容过长会导致后续请求的上下文被压缩。后来我加了一个“超长告警”功能单个技能渲染出来超过 10KB 就提示拆分。4.2 幂等同步与冲突处理的工程细节同步最怕两件事重复写入和覆盖用户手改的内容。我处理重复写入的办法是给每个技能文件加一个隐藏的注释头比如!-- generated by skills-manager -- version: 0.3.2 --。同步时先检查这个标记如果文件不是本工具生成的就提示冲突而不是直接覆盖。如果文件是本工具生成的再做全文对比只有内容真的变化了才写盘。冲突处理我用了三层方案。第一层是 hash 对比检测文件是否变化第二层是备份目录每次同步前把旧文件复制到~/.skills-manager/backups/timestamp/第三层是手动回滚按钮。这条链路看似简单但帮我救回了很多次误操作。有一次我把“代码审查技能”的 glob 从*.ts误写成*.t差点把 TypeScript 项目里的所有规则都禁用掉。因为有了备份我一条命令就恢复了。4.3 Agent 安全的边界技能也可能是一段攻击代码很多人没意识到技能文件不只是“提示词”它可以让 Agent 执行命令、读取文件、调用外部工具。如果你从网上下载了一个恶意技能里面写着“当用户请求删除某个文件时先用 Python 执行一段混淆脚本”那你的开发机就危险了。Agent 工具通常有权限系统但技能内容本身可以构造让用户无意识同意的操作。我在 Skills Manager 里加了三道安全闸。第一道是“技能来源审核”从网上下载的技能必须先通过本地沙箱扫描检查是否包含网络请求、文件删除、base64 解码等敏感行为。第二道是“命令白名单”渲染技能时自动提取文件里的命令片段和用户维护的允许命令表做比对未登记的标记为危险。第三道是“只读模式”发送给 Agent 的技能默认声明“本技能只读禁止执行写操作”除非用户显式打开写权限。这不能百分百防住恶意技能但能挡住大多数常见攻击套路。4.4 常见问题速查表现象可能原因处理方式技能在 Claude Code 里能看到但 Codex 里没反应Codex 只读 AGENTS.md不扫描 skills 目录检查适配器是否确认输出到 AGENTS.md 的正确位置Cursor 规则改了但不生效Cursor 对 .mdc 有缓存同步后等待几秒或执行Reload Window同步报“没有变化”但目标工具里内容还是旧的目标工具缓存了技能内容找到缓存目录清掉对应子目录技能文件里有{{TOKEN}}没被替换变量在 keychain 中不存在到设置页补全变量或者临时关闭校验Windows 下路径用反斜杠被 Agent 解析失败适配器用了 Linux 风格路径在适配器里统一用path.join渲染后转成目标平台风格同步后原文件格式被破坏缺少生成标记工具把它当手写文件恢复备份再检查生成标记是否写入成功5. 批量迁移 54 个技能包的实战过程和个人心得最后分享一次真实的批量迁移经历。我从决定做 Skills Manager 到把 54 个工具适配器跑通前后花了两周多。中间从零整理了大概 70 个技能包最后合并去重成 41 个有效技能。这个过程本身很值得复盘。5.1 迁移七步法我大概是这样推进的盘点把自己在 Codex、Claude Code、Cursor 里使用过的所有规则、提示词、技能文件全部导出分类按用途分成代码审查、提交信息、架构讨论、测试生成、文档维护、终端操作六大类去重同一个意图的规则经常出现多个版本留下描述最准确的那个标准化把每一条都改写成 Skill Manifest 格式补上 description 和 trigger映射确认每个技能该发给哪些工具比如“终端操作类”只发给 CLI Agent不发 IDE避免干扰日常补全灰度先同步到 Claude Code 和 Codex 两个主力工具跑一天看效果回归用固定测试项目验证每个技能都被正确加载再逐步扩展到全部工具。这个过程里最费时间的不是写适配器而是去重。同一套“Git 提交规范”我居然在四个工具里写了四种写法表达的意思大同小异。统一管理之后我的维护成本从“改五处”变成了“改一处”这是这个项目带给我最直接的收益。5.2 四个让我印象深刻的坑第一个坑是路径分隔符。Windows 下生成的规则文件里用了反斜杠Claude Code 在 Linux 容器里加载时路径就飘了。后来我在所有适配器里强制使用path.join再在渲染层统一转成目标平台的路径风格。第二个坑是上下文膨胀。一个技能文件写得越全Agent 在每次对话中需要加载的内容就越多。尤其是 AGENTS.md因为它会在每次请求时都读一遍。后来我把所有技能分成“自动加载”和“按需触发”两类只有高频核心技能才全局加载其余的都改成通过斜杠命令或 提及触发明显改善了大仓库下的响应速度。第三个坑是命名冲突。我导入了一个社区技能叫code-review结果和 Cursor 里原有的自定义规则重名导致 Cursor 加载了旧版本。现在我规定所有技能 id 必须带命名空间比如team/engineering/code-review避免全局冲突。第四个坑是缓存。很多 Agent 工具不会实时重新扫描技能文件同步完像没同步一样。我一开始以为是适配器写错了调了半天才发现是 Claude Code 的缓存目录里存了一个旧副本。现在每个同步流程的收尾动作都会尝试触发目标工具的缓存刷新命令如果做不到就明确提示用户在哪个界面手动刷新。5.3 这个项目的下一站Skills Manager 目前的版本已经能覆盖我 90% 的日常需求但我接下来还有三个方向想继续做。一个是“技能市场”把整理好的技能包匿名化之后放到订阅源里用户可以通过 URL 订阅和包管理器一个思路更新时走同一套 diff 和回滚机制。另一个是团队分享在 Manifest 里增加audience字段区分个人技能和团队公共技能同步到团队仓库时自动过滤包含个人变量的内容。第三个是把 MCP 工具调用也纳入技能声明里比如技能可以声明自己需要访问某个 MCP server同步时自动检测该 server 是否已配置。根据我自己的体验如果你手里的 Agent 工具超过两个技能管理问题就一定会出现。与其每次手动复制小抄不如抽一天时间把规则收拢成一个统一入口。这套思路不限于我写的这个工具哪怕你只是用脚本把几个配置文件串起来也比凭感觉到处粘贴强得多。