--- name: tools-sync description: 在本机统一同步并配置 Claude Code / Codex / OpenCode 的 skills(~\.claude\skills / ~\.codex\skills / ~\.agents\skills)、用户级 MCP 注册和开发环境:主副本与 tailscale 直连的 bowei/skills-win 仓库保持同步(公网 git.rpchannel.com 已于 2026-10-01 关闭;其余目录一律是 junction),审计 skills 中的硬编码参数并迁移到本机 ~\skills_env.md,校验 langgraph conda 环境,双向发现并补齐 Claude Code 与 Codex 的用户 MCP 注册,并把 Claude Code / Codex 的 skills 注册进 OpenCode(OpenCode 直接读 ~\.claude\skills 与 ~\.agents\skills,~\.codex\skills 通过 opencode.jsonc 登记),同时确保 E:\MCP_design 下所有 MCP 均已注册。同步的传输一旦出问题,本 skill 还负责登录 gitea 服务器(上海 mac 的 Docker 容器)诊断并修复,直到同步真正成功。当用户提到“同步skills”、“skills同步”、“tools sync”、“tools-sync”、“同步codex”、“codex 里的 skill 怎么进仓库”、“把 codex 新写的 skill 收进来”、“同步MCP”、“MCP 注册/更新/补齐”、“检查MCP注册”、“codex mcp 没了”、“环境同步”、“skills-win”、“把skills推上去/拉下来”、“新电脑配置claude/codex环境”、“skills_env”、“skills 硬编码路径”、“注册opencode skills”、“同步opencode”、“opencode 里没有 codex 的 skill”、“把 skills 注册到 opencode”,或者 git.rpchannel.com 出现“clone/fetch 失败”、“bad pack header”、“early EOF”、“RPC failed”、“推不上去/拉不下来”、“gitea 坏了/连不上/要修”时,必须使用此技能,即使用户没有明确说“skill”二字。也用于处理 junction 指错、两侧 skill 分叉、以及 skills 里的硬编码路径/密码需要迁进 ~\skills_env.md 的场合。 --- # tools-sync:Claude Code / Codex 工具链同步 ## 目标与总体思路 一台开发机上有三个 AI 编码工具(Claude Code、Codex 和 OpenCode),它们各自有 skills 目录和 MCP 注册表。这个 skill 的职责是让它们保持一致,并且可以在一台新电脑上从零复原整套配置: - **skills 的唯一真源**是 `http://100.88.102.18:3000/bowei/skills-win.git`(remote 名 `origin`, **tailscale 直连**上海 mac 的 gitea Docker)。公网 `git.rpchannel.com` 已于 2026-10-01 被主动关闭(mac 的 frpc 删掉了 gitea-web/gitea-ssh 代理),对外一律 403,不要再走公网。 新机器必须先装 tailscale、登录同一 tailnet,才能 clone / push。 `~\.claude\skills` 是唯一的 git 检出(主副本);其余目录里的每个 skill 都是指向主副本 对应目录的 **junction(目录联接)**——所有工具共享同一份物理文件,从机制上杜绝漂移, git 操作只发生在主副本一处。 受管的 registry 有两个:`~\.codex\skills`(Codex 自己的目录,主副本在这里做全量镜像)和 `~\.agents\skills`(Codex / Copilot CLI / Gemini CLI 都会扫的跨工具别名目录)。 **`~\.agents\skills` 只做发现与修复,不做全量镜像**:Codex 两个目录都扫,全量铺进去会让 同一个 skill 注册两遍。 - **同步是双向的**。Codex 侧新建的 skill(真实目录、含 SKILL.md、主副本里没有同名)会被 **自动 ingest**:移进 `~\.claude\skills` 成为主副本,原地换成 junction,随后跟着阶段 1 入库。 这是"Codex 里写的 skill 如何进仓库"的唯一正规路径,不需要人工搬运。 Codex 没有注册命令——它在会话启动时自动扫描上述目录下所有含 `SKILL.md` 的目录 (junction 对它透明),所以"注册到 Codex" = "junction 存在且指向的目录含 SKILL.md", 新开的 Codex 会话才会扫到新 skill。 - **Codex 插件(`codex plugin list`)自带的 skill 和 MCP 归 marketplace 所有**,不属于可移植 用户配置,任何阶段都不要 ingest、不要复制到 Claude 侧。同理 `~\.agents\skills` 里指向 `~\.matlab\agentic-toolkits` 的 junction 是 MathWorks 装的,脚本只计数不动它。 - **机器相关的参数不进 git**。每台电脑的软件安装路径、IP、账号等都记录在 `~\skills_env.md`(用户主目录下,天然在两个仓库检出之外,不会被 git 同步)。 skills 里引用参数时写"见 ~\skills_env.md 的 XXX 条目",不写死路径。 - **凭据不写进任何会被同步的文件**。gitea(tailscale 地址 `100.88.102.18:3000`)的凭据优先用 Windows 凭据管理器(`cmdkey /list` 里应有 `git:http://100.88.102.18:3000` 条目); 只有在新机器首次配置、或 push 报 `could not read Username` 时,才从 `~\skills_env.md` 读取用户名/密码,用 `git credential-manager store`(stdin 给 protocol/host/username/password) 写入凭据管理器,**不要**把密码拼进 remote URL。换地址后 origin 要同步改: `git -C ~\.claude\skills remote set-url origin `。 push/fetch 报 403 且连根页面都 403,多半是还在走已关闭的公网地址,先查 remote URL。 完整跑一遍下面各阶段(阶段 2 之后还有 2.5 OpenCode,阶段 5 之后是报告)就是一次 "同步"。用户只要求其中一部分(比如"把 skills 推上去")时,跑对应阶段即可,但 阶段 0 永远先做。 ## 阶段 0 — 读取本机环境档案 ~\skills_env.md 先读 `~\skills_env.md`。后续所有阶段的路径、URL、账号都以它为准。 - 如果文件不存在(通常是新电脑):从本 skill 的 `references/skills_env_template.md` 复制一份到 `~\skills_env.md`, 然后逐项核对模板里的路径在本机是否真实存在(`Test-Path`),把不符的改成实际值, 缺失的软件记为"未安装"。凭据字段让用户填写或从用户处确认,不要猜。 - 如果存在:快速通读,注意 `status` 类标记和上次同步遗留的 TODO。 ## 阶段 1 — 同步 ~\.claude\skills(主副本) 用 `scripts/sync_repos.ps1` 做机械部分;它失败或报冲突时再人工介入。 ```powershell # 从 skill 目录运行;RepoUrl 取自 skills_env.md powershell -File \scripts\sync_repos.ps1 -RepoUrl ``` 脚本对主副本做的事:`git add -A` → 有变更则 commit(消息格式 `sync: <机器名> <日期> <一句话摘要>`)→ `git pull --rebase` → `git push` → **`ls-remote` 闸门**:比对 `origin/` 的实际 sha 与本地是否一致,不一致就 `FAIL` 并报出有几个 commit 滞留本机。 这个闸门是硬性的,不要绕过。**服务端故障是原因,不是结果**:传输坏了、commit 只躺在本地,这一轮同步就是失败的,不能在报告里写成"已同步(远端故障,待恢复)"。 2026-09-02 就是这么丢了 8 天——本地 3 个 commit(含一整个新 skill)以为存下了, 其实哪台机器都看不到;而 `ls-remote` 那 8 天一直是好的,这一句比对随时能发现。 人工介入的情形: - **rebase 冲突**:逐个文件看冲突双方,skills 文本冲突通常保留两边语义合并; 解不了就 `git rebase --abort` 并向用户报告冲突文件清单,让用户裁决。 - **fetch / push 报 `RPC failed; HTTP 504`(服务端 pack 进程卡死)**:先分清哪一半坏了 —— `git ls-remote` 与 Gitea HTTP API 正常、只有 `git-upload-pack` / `git-receive-pack` 504, 就不是认证也不是网络。**`--depth=1` 不能绕过,POST 体只有几十 KB 也照样 504**,所以 不要在"减小传输量"上浪费时间。此时若远端有本机没有的 commit(无法 rebase 就无法 push), 用 `scripts/recover_commits_via_gitea_api.py ...` 按**父在前**的顺序 经 API 逐字节重建那几个 commit:它只在重建对象的 SHA-1 与服务端报的 sha 完全一致时才 写入,所以是可证同一份历史,不是近似复制。重建后 `git update-ref refs/remotes/origin/main ` + `git rebase `,本地历史就归位了, 服务恢复后一条 `git push` 即可。**任何情况下都不要 force push**(会丢掉别的机器的 commit)。 - **clone/fetch 报 `bad pack header` / `early EOF` / `RPC failed`,而 `ls-remote` 和网页正常**: 服务端传输故障,**脚本会自动调用阶段 1.5 的医生修复并重试**,通常你什么都不用做。 自动修复没搞定时按阶段 1.5 的手工梯子往下走。绝不要因为"服务端坏了"就把这轮 记成已同步。 - **push 被拒(认证)**:检查凭据管理器条目;新机器上用 `git config --global credential.helper manager` 后重试一次,让 credential helper 弹窗或从 skills_env.md 读凭据拼一次性 URL 完成首次认证。 认证成功后确认凭据已被缓存(再 fetch 一次不应再要密码)。 - **不该入库的文件**:commit 前扫一眼 `git status`,运行时产物(.sqlite、.pckl、 日志、缓存目录)应加进 `.gitignore` 而不是提交。看到就顺手补 `.gitignore`。 ## 阶段 1.5 — 服务端诊断与修复(gitea 坏了就上去修) 同步的前提是传输是通的。**本 skill 有权登录 gitea 服务器并做修复**,连接参数取自 `skills_env.md` 的 `GITEA_*` 段(阶段 0 已读;缺就先补,模板里有占位符和填写要点)。 `sync_repos.ps1` 已经把这件事接进主流程,正常情况下**你不需要手动做任何事**: - `pull` / `push` 报传输类错误(`bad pack header`、`early EOF`、`RPC failed`、 `remote end hung up`…)→ 自动调 `gitea_doctor.ps1 -Fix` → 修好后重试一次; - 每轮同步收尾还有一次 **fetchability 探针**:push 成功不等于同步成功, 服务端发不出 pack 时别的机器照样拉不到。探针不通就判本轮失败。 - `-NoDoctor` 关掉这套自动行为,`-AllowDestructive` 放开破坏性修复(见下)。 单独跑: ```powershell powershell -File \scripts\gitea_doctor.ps1 # 只检查并报告 powershell -File \scripts\gitea_doctor.ps1 -Fix # 检查→登录修复→复验 ``` 医生先在本机分两个面探测——**refs 面**(`ls-remote`)和 **pack 面**(一次 `--filter=tree:0 --depth 1` 的极小 clone)。两个面独立坏,区别就是整个诊断: 2026-08 那次只有 pack 面死了,push 和 `ls-remote` 全程正常,所以故障藏了 8 天。 两面都好就直接返回,不会去连服务器。 坏了才 ssh 上去跑 `gitea_triage.sh`(scp 过去执行,不走 stdin 管道——PS 5.1 会给 管道内容加 UTF-8 BOM,糊在 shebang 上,报出来的错会指向解释器而不是真原因)。 服务端按这个梯子逐层排查,每项打 `PASS/FAIL`: | 检查 | 看什么 | `--fix` 时怎么修 | |---|---|---| | container | 容器在不在、起没起 | `docker start` | | disk | `/data` 剩余空间 | 只报告 | | **gitconfig** | 全局 gitconfig 有没有 `packObjectsHook` / `core.hooksPath` / `sshCommand`,文件是不是被撑大 | 隔离该文件 + 重启容器,gitea 自己重建干净的 | | hooks | 仓库 hook 里有没有引用 `/tmp` 的执行语句 | 用 git 模板还原 | | payload | `/tmp` 下有没有配套载荷 | 删除 | | gitbin | `/usr/bin/git` 是不是被包了一层(`git.real` 存在即是) | 还原真 git | | **packgen** | `git upload-pack --stateless-rpc` 能否产出 pack | 只报告(它说明 git 被指使着不干活) | | httpclone | 容器内直连 3000 端口能否 clone | 只报告 | 两条排查纪律,都是 2026-09-02 用一周代价换来的: - **`packgen` 必须带 `--stateless-rpc`**。`clone file://` 走的不是 HTTP 那条码路, 测通了不能说明任何问题——上一轮排查就是被这个假阴性带偏,误判成 gitea 版本 bug。 - **`gitconfig` 在 `/data/gitea/home/.gitconfig`,不是 `/data/git/.gitconfig`**。 查错路径会得到"文件不存在",从而排除掉真凶。 医生只做**可逆**修复;`-AllowDestructive` 才放开可能丢数据的操作(重建容器、仓库手术), 默认路径一个都不需要。修完不信服务端的自述,回到客户端重新探一次 pack 面才算数。 **梯子走完仍未修好**,说明是这套已知故障之外的新东西。此时再上手工三步(每步排掉一层): 容器内直连 3000 端口 clone → `upload-pack --stateless-rpc` 手喂 want/done 数字节 → 把 `/usr/bin/git` 换成 `tee` 掉 argv/env/stdin/stdout 的 wrapper 跑一次 clone。 第三步能直接看出是 gitea 丢了 pack 还是 git 自己没产出——上次就是这一步定的案。 定位到新根因后,把它补进 `gitea_triage.sh` 的检查表,别只修这一次。 ## 阶段 2 — registry 对账(ingest + 修复 + 镜像) 同一个脚本接着扫 `~\.codex\skills` 和 `~\.agents\skills`,对每个顶层条目分类处理: | 现状 | 脚本动作 | |------|----------| | junction 指向主副本,目标存在 | 跳过(正确状态) | | junction 指向主副本,目标已消失 | 删除失效链接(skill 在仓库里被删或改名后的残留) | | junction 指向受管 registry 之外(如 `~\.matlab\agentic-toolkits`) | 只计数,不动(宿主自己装的) | | junction 指向**另一个 registry** 里的副本 | 报 MISDIRECTED,解析那份副本后把链接改指主副本 | | 真实目录,含 SKILL.md,主副本**无**同名 | **INGEST**:移进主副本 + 原地建 junction(`-NoIngest` 可关闭) | | 真实目录,与主副本同名且内容**相同** | 直接塌缩成 junction(丢不了东西) | | 真实目录,与主副本同名但内容**不同** | 报 CONFLICT,两边都不动,退出码 2 | | 目录里没有 SKILL.md | 忽略并说明 | | **主副本里的同名条目本身就是链接** | 归宿主所有,只计数不动(它的物理家在 registry,方向与本仓库相反) | | 真实目录,且 `~\.*` 下其他工具的 skills 目录有链接指向它 | 判为宿主自装,**不** ingest | 最后把主副本每个 skill 镜像进 `-MirrorRegistry`(默认 `~\.codex\skills`)。 `.system\` 等点开头目录永远不动。 判断力要点: - **宿主自装的 skill 可能把物理副本放在 registry,方向和本仓库相反。** Orca 就是这样:真目录在 `~\.agents\skills`,再把 `.claude` / `.codex` / `.augment` / `.factory` / `.hermes` / `.pi` 等每个工具都链过去。这种时候主副本里的条目本身是链接, 把 registry 里的真目录“塌缩成 junction”会让链接指向自己,**唯一的真副本就没了** (2026-09-07 实跑就这么弄丢了 orca-cli)。脚本现在对两种形状都会认出并让开, 镜像阶段也会跳过它们(否则 Codex 会把同一个 skill 注册两遍)。 弄坏了用它自己的安装器恢复,例如 `orca skills install --skill orca-cli`。 - **CONFLICT 是唯一需要人裁决的分支。** 内容指纹排除了 `.git`、`__pycache__`、 `.langgraph_api`、`*.pckl`、`*.sqlite` 这类运行时产物,所以报出来的分叉是真分叉。 处理方式:把想留的内容并进主副本,再带 `-Force` 重跑(`-Force` 一律主副本胜出)。 - **ingest 进来的 skill 可能命中 `.gitignore` 的 local-only 段**(仓库里有一段 `/orca-cli/` 之类的本机专用规则)。脚本会 `git check-ignore` 并在报告里点名—— 这种 skill 留在本机、不会推上远端,换电脑时不会出现。要它进仓库就删掉那条规则。 - **registry 自带 `.git` 是历史遗留的竞争检出。** 本机 `~\.agents\skills` 就有一个指向 github 镜像的检出。脚本只报告不动手;等它的独有内容都 ingest 完,再由用户决定删。 校验注册:主副本每个 skill 在镜像 registry 都应有同名 junction 且透出 `SKILL.md` (脚本已内置该检查)。提醒用户:Codex 要开新会话才会扫到新 skills。 ## 阶段 2.5 — OpenCode skills 注册 OpenCode 的 skill 发现模型和 Claude Code / Codex 不同,而且更省事——它**直接读目录, 不需要 junction**(详见 OpenCode 官方 V2 skills 文档的 Discovery 一节): - **OpenCode 自动发现 `~\.claude\skills` 和 `~\.agents\skills`**(官方叫 "global compatibility" 源)。也就是说,主副本和 MATLAB / 跨工具别名目录对 OpenCode 是**零 junction、零镜像、零额外配置**——阶段 1(clone 主副本)+ 阶段 2(MATLAB junction) 做完,OpenCode 就自动拿到了全部 Claude + MATLAB 技能,永远和主副本同步。 - **OpenCode 不自动发现 `~\.codex\skills`**。要让 OpenCode 也能看到 Codex 独有的 skill (如工程级 `dpd-ctl-flow`),需要在 OpenCode 全局配置里显式登记这个目录。用 `scripts/sync_opencode_skills.ps1` 做,配置落在 `~\.config\opencode\opencode.jsonc` 的 `skills` 数组: ```powershell # 只审计;缺少 ~/.codex/skills 条目时退出码为 2,不改配置 powershell -File \scripts\sync_opencode_skills.ps1 # 补上该条目(幂等,保留 model/plugins/agents/mcp 等其余键,写前自动备份) powershell -File \scripts\sync_opencode_skills.ps1 -Apply ``` 脚本只补缺失的 `~/.codex/skills` 条目(`~` 相对主目录,跨机器可移植),其余键原样 保留,写前备份 `opencode.jsonc.bak-<时间戳>`。改完 `opencode reload` 或开新会话才生效。 - **副作用要知情**:登记 `~\.codex\skills` 时 OpenCode 会递归子目录,把 Codex 自己的 `~\.codex\skills\.system\` 内置技能(imagegen / openai-docs / review-agent / skill-creator / skill-installer)也暴露出来。这是接受并已记录的副作用——本 skill 仍然 **永不编辑** `.system`(与阶段 2 的规矩一致),只是 OpenCode 会读到它们。若用户不想要 这些 Codex 内置技能,用 OpenCode 的 `permissions` 按 id 逐条 deny,或干脆不登记 `~\.codex\skills`(代价是 `dpd-ctl-flow` 这类 Codex 独有 skill 对 OpenCode 不可见)。 - **OpenCode 自己的全局技能目录** `~\.config\opencode\skills` 若出现真实目录(含 SKILL.md、 非 junction),说明有技能是在 OpenCode 侧新建的,应当像 Codex 侧一样 ingest 进主副本。 `sync_opencode_skills.ps1` 只报告不动手;真正 ingest 交给阶段 2 的流程。更省事的做法是 引导用户直接在 `~\.claude\skills` 里建技能——OpenCode 直接读那里,无需任何额外登记。 ## 阶段 3 — 硬编码审计与 skills_env.md 回填 目的:让仓库里的 skills 在任何一台按 `~\skills_env.md` 配置好的电脑上都能用。 在 `~\.claude\skills`(排除 `.system`)里搜这几类硬编码: - 盘符绝对路径:正则 `[A-Za-z]:[\\/]`(如 `D:\Xilinx\Vivado`、`E:\MCP_design`) - IP 地址:`\d+\.\d+\.\d+\.\d+` - 明文凭据:`password`、`密码`、常见口令字样 对每个命中做判断,不要机械替换: - **机器相关的值**(安装路径、板卡 IP、conda 路径)→ 确认已记录在 `~\skills_env.md` 对应条目(没有就补上),然后把 skill 原文改成引用式写法, 例如"Vivado 路径见 `~\skills_env.md` 的 `VIVADO_PATH`"。skill 的执行逻辑 改为:运行时先读 skills_env.md 取值。 - **明文密码出现在会被 git 同步的文件里** → 这是必须当场修复的问题: 从文件里删掉,移入 `~\skills_env.md`,并向用户报告(密码已进过 git 历史的话,建议用户改密码)。 - **协议性/普适的值**(如标准端口号、公开 URL)→ 保留,不算硬编码。 一次审计不必把所有历史 skills 全部重构完;至少保证:新增/本次改动的 skill 零硬编码, 存量 skill 的命中清单写进最终报告,让用户决定分批改。改动之后回到阶段 1 再 commit + push 一次(junction 模型下 codex 侧自动同步,无需额外操作)。 ## 阶段 4 — langgraph 开发环境校验 很多 skills(vivado-mcp、fpga 系列、langgraph 系列)依赖 conda 的 `langgraph` 环境。 校验方式(路径以 skills_env.md 的 `CONDA_LANGGRAPH_PYTHON` 为准): ```powershell & -c "import langgraph, langgraph.graph, langchain_core; print('langgraph env OK')" ``` 失败(环境不存在或包缺失)→ 调用 `langgraph-vivado-install` skill 完成安装, 装好后把实际的 python 路径回填到 `~\skills_env.md`。 ## 阶段 5 — MCP server 双向发现、注册与校验(Claude Code ↔ Codex) ### Codex 的 MCP 存储模型(codex-cli 0.150.1 / 0.153.4 实测,官方无文档) 动手前必须知道这三条,否则会写出"看起来成功、实际无效"的配置: - **`codex mcp list --json` / `add` / `remove` 是唯一有效接口。** 审计读它,注册写它。 - **`~\.codex\config.toml` 的 `[mcp_servers.*]` 节是惰性的,Codex 不读。** 实测:把 `node_repl` 的 `startup_timeout_sec` 从 120 改成 137,`codex mcp list` 仍报 120; 手写一整节则完全不出现。config.toml 里现存的那几节是历史遗留。 **本 skill 旧版"缺失的补写 TOML 节"是错的,别再那样做——它静默地什么也没干。** - **这个存储不跨 CLI 升级存活。** 2026-08-29 注册好的 7 个用户 MCP,在升级到 0.150.1 后全部消失;2026-09-07 升到 0.153.4 后**又全没了一次**。所以每次同步都要重新审计 Codex 侧,`~\skills_env.md` 的 MCP 配置节才是可重建的真源。 更要命的是**消失并不需要升级**:2026-09-07/08 一次同步内复现了三次,每次都是补齐、 验证通过,几分钟后再查就只剩 Codex 自带的 `cua_repl` / `node_repl`。已排除 cwd 作用域、 Syncthing、以及 `codex mcp add/remove/list` 本身(单独测都正常,能持续数分钟); `config.toml` 经 0.153.4 复验仍然惰性——手写一节 `[mcp_servers.tomlprobe]` 后 `codex mcp list` 完全看不到,所以别再想靠改 TOML 绕过。add 之后在 `~\.codex`、 `AppData\Local\OpenAI\Codex`、`AppData\Roaming\OpenAI` 下都找不到被写过的文件, 怀疑该存储是账号侧下发、本地增补会被后台同步覆盖;活下来的两个恰好是 Codex 自己重新 播种的自带运行时。 **2026-09-23 实测定量(win-hp-bowei,codex-cli 0.150.1,全程未升级)**:回退窗口 **小于 25 秒**,不是此前记的"几分钟"。做法是改完立即验证,再按 25 s 间隔轮询: | 时刻 | 条目数 | 本次新增的 mcp_dsa815 | 本次改过的 stm32cubemx | |---|---|---|---| | T+0 | 14 | 在 | `C:\Python314\python.exe`(已修) | | T+25s | 13 | **没了** | **退回 `.venv\Scripts\python.exe`(坏的)** | | T+50s | 13 | 没了 | `.venv`(同上) | | T+75s | 13 | 没了 | `.venv`(同上) | 而且**它不是"清空到只剩自带运行时",是"回退到一份陈旧的账号侧快照"**:13 个条目里 altium / kicad / matlab / simetrix / mcp_n9320b 等全都还在,只有本次的增改被抹掉, 且 stm32cubemx 精确地退回到修改之前那一版配置。这比"被清空"更能说明问题——本地 `codex mcp add` 的写入会被一份**有内容的**远端快照覆盖,而不是被清零。 **由此修正实践结论**:在出现这种回退的机器上,**Codex 侧的 MCP 改动是徒劳的**, 每轮同步重新 `-Apply` 只会在报告里留下一行假的"已修复"。正确做法是:照常 `-Apply` (成本低,万一这台机器没这毛病就生效了),**但必须在 25 s 后复验**,没活下来就在 报告里写明"Codex 侧改动未能持久,该机 Codex 实际仍在用 <旧配置>",不要写成已修复。 真正的修复要从账号侧那份快照入手(目前无已知入口)。 由此还有一个已知缺口:`codex mcp add` 没有 `--tool-timeout-sec` / `env_vars` 之类的参数, config.toml 又不生效,所以这两个键**目前无任何设置入口**。需要它们的 MCP 记在 `references/codex_mcp_wanted_keys.json`,脚本注册完会单独报"registered, but incomplete", 不要把它当失败,也不要假装已配好。每次 codex-cli 升级后复查是否已有新接口。 另:这两个脚本都要用 PowerShell 跑,不要用 git-bash——MSYS 会改写 `/c` 形式的参数和 Windows 路径(实测 `codex mcp add ... /c echo hi` 的 `/c` 被改成了 `C:/`)。 ### 双向审计 先运行双向审计脚本。它读取 Claude Code 用户级 `~\.claude.json` 的 `mcpServers` 和 `codex mcp list --json`,对用户可配置 MCP 做并集: ```powershell # 只审计;发现差集或漂移时退出码为 2,不修改配置 powershell -File \scripts\sync_mcp_registrations.ps1 # 双向补齐单边缺失项;同名漂移默认只报告,不覆盖 powershell -File \scripts\sync_mcp_registrations.ps1 -Apply # 经核实后才可选择一侧作为同名漂移项的权威配置 powershell -File \scripts\sync_mcp_registrations.ps1 -Apply -ConflictAuthority Claude # 或:-ConflictAuthority Codex ``` 脚本必须遵守以下规则: - Claude 独有的用户 MCP 补到 Codex;Codex 独有的用户 MCP 补到 Claude,实现双向同步。 - 云端账号连接器、插件动态 MCP、Codex 自带运行时/插件 MCP 不属于可移植用户配置, 必须排除,不能复制到另一侧。额外的机器特有项可用 `-ExcludeName ` 排除。 - 用户级配置才自动双向同步。Claude 的项目级 `.mcp.json` / local scope 不自动提升成 Codex 全局配置;如确需提升,先向用户说明作用域扩大,再手工处理。 - 同名 MCP 要规范化比较 transport、command、args、env、URL 和 headers。 Codex stdio 专用的 `PYTHONIOENCODING=utf-8`、`PYTHONUTF8=1` 是允许的平台差异, 比较时忽略;Claude → Codex 时自动补上,Codex → Claude 时自动移除。 - 同名配置漂移时默认只报告,不能猜哪侧正确。结合 `~\skills_env.md`、工程实际路径和 用户意图核实后,才可显式指定 `-ConflictAuthority Claude` 或 `Codex` 覆盖另一侧。 - 修改 Codex 配置前脚本必须创建带时间戳的备份。含无法无损转换的 HTTP headers、 bearer-token 设置等配置时应停止该项并报告,不得静默丢字段。 然后用 `E:\MCP_design\mcp_list.md`(路径见 skills_env.md 的 `MCP_DESIGN_ROOT`)做 第二层完整性校验。每行一个 MCP 工程目录;它能发现“工程存在但两侧都没注册”的项目, 这是仅比较两个注册表的并集无法发现的。对清单里的每一项: 1. **Claude Code 侧**:`claude mcp list` 或读 `~\.claude.json` 的 `mcpServers`, 确认存在对应条目。缺失的用 `claude mcp add ...` 注册,命令、解释器 路径和 env 变量都取自 `~\skills_env.md` 的 MCP 配置节。 2. **Codex 侧**:`codex mcp list --json` 确认存在对应条目。缺失的用 `codex mcp add --env K=V -- ` 注册,配置取自 skills_env.md 的同一份表(stdio 类型记得带 `PYTHONIOENCODING=utf-8`、`PYTHONUTF8=1`,Codex 在 Windows 上没有这两个变量时中文输出会乱码)。 3. **可启动性抽查**:对新注册的 MCP,直接运行其 command + args 数秒, 确认进程能起来、没有立刻报 import/路径错误(stdio MCP 起来后会静默等 输入,这就算通过,Ctrl-C 掉即可)。 先由双向脚本发现差集和漂移,再用 `mcp_list.md` 查漏,最后以 skills_env.md 和实际可启动 结果判定正确值。不要因为某项当前只在一侧出现,就假设另一侧没有安装需求——尤其 Codex 侧 可能只是被上一次 CLI 升级清空了。 ### MATLAB / Simulink Agentic Toolkit 专项检查与安装 每次执行 MCP 同步时,还要专项检查 MathWorks Agentic Toolkits。它们不一定位于 `MCP_DESIGN_ROOT`,因此不能只依赖 `mcp_list.md`。MATLAB 与 Simulink 共用一个名为 `matlab` 的 MCP server;Simulink 能力通过 `--extension-file=.../simulink/tools/tools.json` 加载,不应错误地要求再注册一个独立的 `simulink` MCP。 检查以下项目,任一缺失均视为未完整安装: 1. 本机存在 MATLAB;MATLAB Toolkit 要求 R2020b+,Simulink Toolkit 要求 R2023a+ 且已安装 Simulink。实际 MATLAB 根目录记录到 `~\skills_env.md` 的 `MATLAB_ROOT`。 2. `~\.matlab\agentic-toolkits\bin\matlab-mcp-server.exe` 存在且 `--version` 可运行; `config.json` 的 `toolkits` 同时包含 `matlab` 与 `simulink`。 3. Claude `~\.claude.json` 与 `codex mcp list --json` 均有用户级 `matlab` MCP, command 指向同一二进制,args 同时包含 `--matlab-session-mode=existing` 和正确的 `--extension-file`。Claude 条目应有 `alwaysLoad=true`。 4. Codex 侧本应还有 `tool_timeout_sec = 600` 和 Windows 所需的 `env_vars = ["WINDIR"]` (缺 `WINDIR` 时 Simulink 子进程可能起不来),但 0.150.1 **没有任何接口能设置它们** (见本阶段开头的存储模型)。所以这一条是**已知缺口,不作为安装完整性的判定项**: 脚本报出 "registered, but incomplete" 即可,写进报告的遗留事项,不要标记为失败, 也不要试图靠改 config.toml 绕过——那不生效。 5. `~\.agents\skills` 中存在指向两套 Toolkit 的 junction(阶段 2 会把这类指向 `~\.matlab\agentic-toolkits` 的 junction 识别为宿主自有,只计数不动);Claude Code 的 MATLAB / Simulink plugin 或等效 skill 注册存在。 缺失时使用 MathWorks 官方统一安装器,不使用旧版 agent-driven 安装法: ```powershell $installer = Join-Path $env:TEMP "mathworks-agentic-installer\agenticToolkitInstaller.mltbx" Invoke-WebRequest -UseBasicParsing ` "https://github.com/matlab/simulink-agentic-toolkit/releases/latest/download/agenticToolkitInstaller.mltbx" ` -OutFile $installer # MATLAB 中先安装 installer,再分别执行 install 和 configure;Prompt=false 可无交互运行。 matlab.addons.toolbox.installToolbox(""); setupAgenticToolkit("install", Toolkit=["matlab","simulink"], ... SessionMode="existing", Prompt=false); setupAgenticToolkit("configure", Agents=["claude-code","codex"], ... Scope="global", SessionMode="existing", Prompt=false); ``` 注意事项: - `install` 与 `configure` 是两个独立动作,两者都必须成功;仅看到 Toolkit 文件落盘不代表 MCP 已注册。每次修改 agent 配置前先备份 Claude JSON 与 Codex TOML。 - 如果新 MATLAB 进程找不到 `setupAgenticToolkit`,先确认 “MathWorks Agentic Toolkit Setup” Add-On 已启用;必要时把其实际 Toolbox 安装目录 `addpath` 后再执行。如果 R2025b 的 Add-On 管理器能列出 Toolbox 却无法启用,则将 “MathWorks Agentic Toolkit Setup” 与 “MATLAB MCP Server Toolbox” 两个官方 Toolbox 根目录加入 path 并 `savepath`,再用全新 MATLAB 进程通过 `which setupAgenticToolkit`、 `which shareMATLABSession` 验证。 - 安装后重新运行双向审计脚本;`matlab` 必须显示为 Matched。再直接比对两边的 command/args, 确认 Simulink extension 未被规范化比较遗漏(Codex timeout 与 `WINDIR` 见上面第 4 条, 当前无法设置,不必核对)。 - 使用 Simulink 工具前,每个 MATLAB 会话运行 `addpath("~/.matlab/agentic-toolkits/simulink"); satk_initialize`,然后重启 Claude Code / Codex 会话。MCP 健康检查在没有共享 MATLAB 会话时可能显示连接失败,这不等于未安装。 ## 阶段 6 — 输出同步报告 最后用这个结构向用户汇报(有问题的条目放前面): ``` # tools-sync 同步报告 <日期> ## skills 同步 - ~\.claude\skills: - 远端校验: origin/main = ,与本地一致 / **不一致:N 个 commit 滞留本机** - 服务端: 传输两面均正常 / 医生修复了 <哪几项> / **仍不可用:** - 从 registry ingest: <被自动收进主副本的 codex/agents 侧 skill;没有则写"无"> - junction 对账: <修复的 MISDIRECTED、塌缩的重复、清理的失效链接、镜像新建数> - OpenCode: 自动读 ~\.claude\skills 与 ~\.agents\skills / ~\.codex\skills 已登记(或本轮 -Apply 补上)/ OpenCode 自有目录待 ingest 的 skill ## 硬编码审计 - 本次修复: <清单> - 遗留待改: <清单> ## 环境 - langgraph env: OK / 已重装 / 失败原因 - MCP 注册: - MCP 已注册但不完整: ## 需要用户决定的事项 - ``` ## 边界与安全 - 本 skill 会 push 到远端仓库——commit 前把将要提交的文件列表给用户过目 (自动化场景下至少写进报告)。绝不 `push --force`。 - `~\skills_env.md` 含凭据,任何情况下不将其内容或凭据字段写入仓库内文件、 commit message 或对外输出。 - 不动 `~\.codex\skills\.system\`(Codex 自带系统 skills)和 `~\.claude\skills\.system\`;也不动 `codex plugin` 装的 marketplace 插件。 - 修改 `~\.codex\config.toml` 前先备份为 `config.toml.bak-<日期>`,TOML 写坏 会导致 Codex 起不来。 - 修改 `~\.config\opencode\opencode.jsonc` 时用 `sync_opencode_skills.ps1 -Apply` (脚本自己会备份 `opencode.jsonc.bak-<时间戳>`);别手改坏 JSONC 让 OpenCode 起不来。 - **ingest 会 `Move-Item`,`-Force` 会 `Remove-Item -Recurse`。** 先 `-DryRun` 看清单, 确认没有误判再实跑;CONFLICT 项在用户裁决前脚本不会碰。 - 两个脚本都从 PowerShell 运行,不要用 git-bash(MSYS 会改写参数里的 `/c` 和 Windows 路径)。