同时用 Claude Code 和 Codex,我做了个 Agent 资产库:能预览、能投递、能追更新

0 阅读10分钟

Context Hub:技能库、媒体预览和更新检测

技能库首页:封面/视频预览、来源、安装位置,以及「需要更新 5」和「检查更新」

先说结论

我同时用 Claude Code 和 Codex,也经历过只用其中一个的阶段。两种情况下,真正麻烦的都不是模型能力,而是同一份资产散落在不同目录、格式不同、更新不可见。

同时用两个 Agent 时,一个 MCP server 要在 Claude Code 的 .mcp.json 和 Codex 的 config.toml 里各写一次;一个技能要放进 .claude/skills、.agents/skills 或对应 Agent 的技能目录;一条提示词在 Claude Code 里是 command,在 Codex 里可能是指令文件里的一段。改了一个,忘了另一个,是常态。

只用一种 Agent 时,问题不是同步,而是另外两个:你的技能、提示词、插件散在几个目录里,很多时候你只记得名字,不记得它长什么样、能做什么;上游更新了没有,也没有一个地方能看见。

Context Hub 就是为这两类问题做的:一个本地资产库,能预览截图、视频和 GIF,能按 Agent 投递,也能检查上游更新。插件包和环境变量也在里面,但主线是资产库、预览和更新。

这篇文章不讲功能清单,讲三个问题:

  1. 跨 Agent 的配置维护为什么没有想象中简单;
  2. JSONC/TOML 无损编辑、幂等投递、更新追踪怎么做;
  3. 为什么只用一种 Agent,它也值得用。

一、先看一个具体场景

前段时间我给项目加一个 GitHub MCP server。

以前的操作是:

  1. 打开 Claude Code 的 .mcp.json,加一段 mcpServers.github;
  2. 打开 Codex 的 config.toml,用 TOML 再写一遍同样的命令、参数和环境变量;
  3. 发现 GITHUB_TOKEN 的变量名写错了,两边各改一次;
  4. 过两周,其中一个文件被格式化工具重排,注释没了,diff 里全是噪声。

这不是“多写几行”的问题,而是三件事叠在一起:

  • 格式不同:一个是 JSONC,一个是 TOML;
  • 位置不同:工作区一份、全局一份,作用域不同;
  • 语义相同:它们描述的是同一个 MCP server,应该只有一份事实来源。

我试过几种偷懒办法,都不太成立。

软链接。 技能目录可以软链接,但 MCP 配置不行:两个客户端的文件格式不一样,不能共享同一个文件。Windows 上创建软链接还需要开发者模式或管理员权限,团队里没法要求每个人都开。

复制脚本。 写个脚本把一份配置复制到两边,能解决大部分场景。但复制是整文件覆盖,会吃掉对方文件里的注释、键顺序和尾逗号;而且没法判断“这份文件我手改过,要不要覆盖”。

dotfiles 管理器。 适合管理 shell 配置和编辑器配置,但它不理解 Agent 语义:不知道什么叫“这个技能装到工作区还是全局”,也不知道“这个 MCP server 在某个客户端被禁用了”。

云同步。 对个人用户来说,没必要把 Agent 配置和 token 放到云端;本地文件本来就应该留在本地。

所以我想要的是一个本地库 + 按 Agent 投递的模型:资产只在库里维护一份,每个 Agent 通过一个适配器声明“我能接收什么、写到哪里、用什么格式”。

二、难点一:不要重新格式化用户的配置

这是整个项目里最容易被低估的部分。

很多工具处理 JSON 的方式是 JSON.parse 再 JSON.stringify。对普通配置没问题,但 Agent 的配置文件里经常有注释:

{
  // 团队共享的 GitHub MCP
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
    }
  }
}

一旦 parse + stringify,这行注释就没了,键顺序也可能变,原本手写的紧凑排版会被重排成两空格缩进。用户打开 diff,看到的是整文件重写,而不是“加了一个 server”。

TOML 更麻烦。重新序列化会改写引号风格、丢掉注释、把内联表展开成多行表。

我的做法是不做整体序列化,只改目标节点所在的文本区间:

  1. 先解析,确认目标节点在语法树里的位置;
  2. 在原始文本里找到这个节点对应的行区间;
  3. 只替换或插入这一段;
  4. 写回之前重新解析一遍,确认“改完的结果”和“我想改的结果”一致;
  5. 不一致就拒绝写入,而不是写一个半坏的文件。

这套做法不优雅,但它能同时满足“改对”和“不破坏用户文件”。代价是要为 JSONC 和 TOML 各写一套逐节点编辑器,测试里要覆盖注释、尾逗号、内联表、嵌套表这些边界。

三、难点二:投递必须幂等

技能和 MCP 的投递相对简单:一个目录、一段配置,装一次和装两次结果一样。

提示词不一样。它经常要写进一个已经存在的指令文件,比如 CLAUDE.md 或 AGENTS.md。如果每次安装都追加一段,装三次就会有三段重复内容;如果删除时直接删文件,又会把用户自己写的内容一起删掉。

我的做法是给每一节加一对不会在渲染时显示的注释标记:

<!-- context-hub:code-review -->
## Code review

- 先看 diff,再看测试
- 不要只根据文件名猜行为
<!-- /context-hub:code-review -->

规则只有三条:

  • 安装时,如果同名标记已经存在,替换这一节,而不是追加;
  • 删除时,只删两个标记之间的内容,标记外的字节原样保留;
  • 如果删完文件变空,才删文件;否则保留空行和用户的其他内容。

这样同一个提示词装十次也只有一节,卸载时也不会误伤别人的内容。

四、难点三:更新时不要覆盖手改

“从上游更新”是另一个容易想当然的地方。

假设你从某个仓库装了一个技能,过了一周上游更新了。直接覆盖当然简单,但用户可能已经在本地改过这个技能;直接覆盖等于把人的修改扔掉。

所以要区分两个问题:

  • 上游是否前进:记录的版本/哈希和上游当前值是否不同;
  • 本地是否被改:当前目录的哈希和安装时记录的哈希是否不同。

只有“上游前进了,且本地没改”才自动更新:

const canUpdate = outdated(record, latest) && !modified(record, currentHash);

技能这边,skills CLI 会在安装目录旁写一个 skills-lock.json,里面已经有上游哈希。我复刻了它的目录哈希算法(相对路径 + 字节 + 按路径序喂进一个 SHA-256),只补它答不上的那一问:安装之后文件有没有被改过。插件包同理,用记录的 revision 判断上游,用目录哈希判断本地。

这个策略不追求“永远最新”,追求的是不静默覆盖人的修改。手改过的资产会一直显示“有更新”,但不会自动动它。

五、难点四:每个 Agent 的差异,不应该写进核心

如果核心代码里写满 if (agent === "claude-code"),每加一个 Agent 就是一次核心改动,测试面会越来越大。

所以我把 Agent 差异收进扩展:

  • 一个 Agent 扩展声明自己有哪些位置(工作区/全局)、每种位置能接收什么形式(command、rule、指令文件分节、agent 文件)、每个形式的落点在哪里;
  • 页面只画这个位置声明过的形式,核心不认识任何具体 Agent;
  • 新增一个 Agent 是加一个扩展,不是改页面代码。

这和“插件市场”是同一套机制:语言包、搜索源、Agent 集成都是扩展。代价是扩展 API 必须稳定,否则每次破坏性变更都会让生态重新来一遍——这也是我现在最谨慎的地方。

Context Hub 的扩展页:Agent 集成、资产模块、全局搜索和语言包都通过扩展接入

六、现在它能做什么

目前官方集成的是 Claude Code 和 Codex,主要资产四类:

资产库里存什么能看到什么投递到哪里
MCP server一份声明:命令、参数、环境变量命令、参数、环境变量、运行状态Claude Code 的 .mcp.json、Codex 的 config.toml
技能目录 + 元数据封面、截图、GIF、视频、来源、更新时间工作区或全局的技能目录,含共享的 .agents/skills
提示词正文 + 元数据正文、封面/媒体、大小、更新时间command、rule、agent 文件,或指令文件里的一节
插件包包文件 + 来源/版本封面、展示图、文件树、更新状态Agent 的插件目录

Context Hub 的技能库:封面、媒体预览、来源和更新时间

媒体不是附属品。每个技能、提示词、插件都可以带一组资产:截图、GIF、视频。列表卡片直接用第一张资产做封面;详情页是可滑动的画廊;视频只在可见时自动播放,同时最多 6 个预览在解码,打开灯箱时全部暂停。GIF 按图片处理,视频支持 mp4/webm/mov 等。上传可以拖拽、粘贴或选择文件,也可以填远程地址。

技能和插件包会记录来源和版本。检查上游后,列表里会显示是否落后;自动更新只覆盖未手改的资产,手改过的仍显示有更新,但不会自动动它。检查时机可选手动、启动时或每天,自动更新默认关闭。

环境变量更像系统级配置:库只存名字和分组,值写进系统环境。

三种形态共用同一套本地库:Electron 桌面端、独立 CLI、本地网页。资产最终仍然落在 .claude、.codex、.agents 这些 Agent 自己的目录里,没有云端黑盒。项目是 MIT 协议。

七、只用一种 Agent,为什么也值得用

跨 Agent 同步是起点,但不是唯一的价值。只用 Claude Code 或只用 Codex 的人,也会遇到另外两个问题。

第一,你不知道库里有什么。 技能和提示词不只是 Markdown;它们经常带截图、演示视频、GIF。打开 Context Hub 的首页,先看到的不是一串文件名,而是每个资产的封面和预览;点开详情,是完整的画廊,视频在可见时自动播放。你不用打开文件夹一个个读 SKILL.md,先看它长什么样、能做什么。

第二,你不知道上游变了没有。 技能来自不同仓库,插件包也各有来源。库里能看到来源、版本和上次检查时间;检查后发现落后,可以一键更新,并且自动更新只碰没手改过的内容。手改过的资产会保留,不会被静默覆盖。

再加上本地库本身的价值:所有资产在一个地方,按工作区/全局、按 Agent、按类型筛选;要投递时选一个位置,不用记路径。即使只用一个 Agent,这也是一个比文件夹好用的资产管理器。

Context Hub 的 MCP 页面:命令、参数、环境变量、运行状态和安装目标

八、为什么值得做

因为它解决的不是“少点几下鼠标”,而是同一份事实只维护一次,以及不用来回折腾。

即使只用一个 Agent,预览和更新追踪也足够让它有价值:你不用翻目录猜一个技能是干什么的,也不用隔一段时间手动去上游仓库看有没有更新。

当你有 10 个 MCP server、20 个技能、几十条提示词,并且同时在两个 Agent 里用时,真正消耗时间的不是添加,而是:

  • 这个 server 两边参数是不是一致?
  • 这个技能上游更新了,我本地改过没有?
  • 这条提示词在 Claude Code 里是 command,在 Codex 里到底落在哪?
  • 我删掉这一节,会不会把 AGENTS.md 里别人的内容一起删了?

这些问题都有确定答案,也应该由工具来回答,而不是靠人记。

项目还在早期,代码在 GitHub:

github.com/bb-boy680/c…

如果你也在用 Claude Code 或 Codex,我最想知道的是:你现在怎么管理技能、提示词和插件?有没有哪个资产,你根本不知道它更新了? 评论区聊聊,也欢迎直接提 issue。