Files
skills-win/tools-sync/SKILL.md
T

465 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: tools-sync
description: 在本机统一同步并配置 Claude Code / Codex / OpenCode 的 skills(~\.claude\skills / ~\.codex\skills / ~\.agents\skills)、用户级 MCP 注册和开发环境:主副本与 git.rpchannel.com 上的 bowei/skills-win 仓库保持同步(其余目录一律是 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://git.rpchannel.com/bowei/skills-win.git`(remote 名 `origin`)。
`~\.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 条目",不写死路径。
- **凭据不写进任何会被同步的文件**。git.rpchannel.com 的凭据优先用 Windows
凭据管理器(`cmdkey /list` 里应有 `git:http://git.rpchannel.com` 条目);
只有在新机器首次配置时,才从 `~\skills_env.md` 读取用户名/密码完成首次
clone(之后 credential helper 会缓存)。
完整跑一遍下面各阶段(阶段 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 <skill目录>\scripts\sync_repos.ps1 -RepoUrl <origin_url>
```
脚本对主副本做的事:`git add -A` → 有变更则 commit(消息格式
`sync: <机器名> <日期> <一句话摘要>`)→ `git pull --rebase` → `git push` →
**`ls-remote` 闸门**:比对 `origin/<branch>` 的实际 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 <repo_dir> <sha>...` 按**父在前**的顺序
经 API 逐字节重建那几个 commit:它只在重建对象的 SHA-1 与服务端报的 sha 完全一致时才
写入,所以是可证同一份历史,不是近似复制。重建后
`git update-ref refs/remotes/origin/main <sha>` + `git rebase <sha>`,本地历史就归位了,
服务恢复后一条 `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 <skill目录>\scripts\gitea_doctor.ps1 # 只检查并报告
powershell -File <skill目录>\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 <skill目录>\scripts\sync_opencode_skills.ps1
# 补上该条目(幂等,保留 model/plugins/agents/mcp 等其余键,写前自动备份)
powershell -File <skill目录>\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
& <CONDA_LANGGRAPH_PYTHON> -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 <skill目录>\scripts\sync_mcp_registrations.ps1
# 双向补齐单边缺失项;同名漂移默认只报告,不覆盖
powershell -File <skill目录>\scripts\sync_mcp_registrations.ps1 -Apply
# 经核实后才可选择一侧作为同名漂移项的权威配置
powershell -File <skill目录>\scripts\sync_mcp_registrations.ps1 -Apply -ConflictAuthority Claude
# 或:-ConflictAuthority Codex
```
脚本必须遵守以下规则:
- Claude 独有的用户 MCP 补到 Codex;Codex 独有的用户 MCP 补到 Claude,实现双向同步。
- 云端账号连接器、插件动态 MCP、Codex 自带运行时/插件 MCP 不属于可移植用户配置,
必须排除,不能复制到另一侧。额外的机器特有项可用 `-ExcludeName <name>` 排除。
- 用户级配置才自动双向同步。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 <name> ...` 注册,命令、解释器
路径和 env 变量都取自 `~\skills_env.md` 的 MCP 配置节。
2. **Codex 侧**:`codex mcp list --json` 确认存在对应条目。缺失的用
`codex mcp add <name> --env K=V -- <command> <args...>` 注册,配置取自 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("<installer绝对路径>");
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: <pull/push 结果,commit 摘要>
- 远端校验: origin/main = <sha>,与本地一致 / **不一致:N 个 commit 滞留本机**
- 服务端: 传输两面均正常 / 医生修复了 <哪几项> / **仍不可用:<RESULT 行>**
- 从 registry ingest: <被自动收进主副本的 codex/agents 侧 skill;没有则写"无">
- junction 对账: <修复的 MISDIRECTED、塌缩的重复、清理的失效链接、镜像新建数>
- OpenCode: 自动读 ~\.claude\skills 与 ~\.agents\skills / ~\.codex\skills 已登记(或本轮 -Apply 补上)/ OpenCode 自有目录待 ingest 的 skill
## 硬编码审计
- 本次修复: <清单>
- 遗留待改: <清单>
## 环境
- langgraph env: OK / 已重装 / 失败原因
- MCP 注册: <Claude 独有、Codex 独有、同名漂移、已补齐、排除项、清单查漏、MATLAB/Simulink 专项检查、抽查结果>
- MCP 已注册但不完整: <codex-cli 无接口可设的键;没有则写"无">
## 需要用户决定的事项
- <CONFLICT 分叉、密码泄露、local-only 的 ingest 项、registry 里的竞争检出……没有则写"无">
```
## 边界与安全
- 本 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 路径)。