这次同步事故的教训不是服务器坏了,而是坏了没人知道:pull --rebase 一失败脚本 就退出,push 从没走到,3 个 commit(含整个 bmem skill)在本机躺了 8 天,报告里 却算作"已知故障、待恢复"。而 ls-remote 这 8 天一直是好的。 - sync_repos.ps1 增 Assert-RemoteInSync:push 后(及 -DryRun 时)用 ls-remote 比对 origin/<branch> 的实际 sha,不一致就 FAIL 并报出滞留/落后的 commit 数。 远端分支不存在时单独报错,避免那两个计数被静默地按 HEAD 算出误导性数字。 - SKILL.md 阶段 1 写明闸门是硬性的:服务端故障是原因不是结果,不得报成已同步。 - SKILL.md 新增 bad pack header / early EOF 的排查阶梯(三步各排掉一层),并记下 根因:全局 gitconfig 被注入 [uploadpack] packObjectsHook,把 pack-objects 换成 了空转脚本。附带提醒 HOME 是 /data/gitea/home 而非 /data/git,以及这是一条 完整 RCE 链、要连 hook 与 /tmp 载荷一起清。 - bmem/SKILL.md 的"已知边界"改为已修复,避免继续把普通的没 pull 误判成服务端故障。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015tMFjKGgxdmxJ28dyXdx1u
286 lines
18 KiB
Markdown
286 lines
18 KiB
Markdown
---
|
||
name: tools-sync
|
||
description: 在本机统一同步并配置 Claude Code 与 Codex 的 skills、MCP server 和开发环境:将 ~\.claude\skills 和 ~\.codex\skills 与 git.rpchannel.com 上的 bowei/skills-win 仓库保持同步,审计 skills 中的硬编码参数并迁移到本机 ~\skills_env.md,校验 langgraph conda 环境,双向发现并补齐 Claude Code 与 Codex 的用户 MCP 注册,同时确保 E:\MCP_design 下所有 MCP 均已注册。当用户提到"同步skills"、"skills同步"、"tools sync"、"tools-sync"、"同步codex"、"同步MCP"、"环境同步"、"skills-win"、"把skills推上去/拉下来"、"新电脑配置claude/codex环境"、"检查MCP注册"、"skills_env"时,必须使用此技能,即使用户没有明确说"skill"二字。
|
||
---
|
||
|
||
# tools-sync:Claude Code / Codex 工具链同步
|
||
|
||
## 目标与总体思路
|
||
|
||
一台开发机上有两个 AI 编码工具(Claude Code 和 Codex),它们各自有 skills 目录和 MCP
|
||
注册表。这个 skill 的职责是让它们保持一致,并且可以在一台新电脑上从零复原整套配置:
|
||
|
||
- **skills 的唯一真源**是 `http://git.rpchannel.com/bowei/skills-win.git`(remote 名 `origin`)。
|
||
`~\.claude\skills` 是唯一的 git 检出(主副本);`~\.codex\skills` 下的每个 skill
|
||
是指向主副本对应目录的 **junction(目录联接)**——两个工具共享同一份物理文件,
|
||
从机制上杜绝两侧漂移,git 操作只发生在主副本一处。
|
||
Codex 没有注册命令——它在会话启动时自动扫描 `~\.codex\skills` 下所有含 `SKILL.md`
|
||
的目录(junction 对它透明),所以"注册到 Codex" = "junction 存在且指向的目录含
|
||
SKILL.md",新开的 Codex 会话才会扫到新 skill。
|
||
- **机器相关的参数不进 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 会缓存)。
|
||
|
||
完整跑一遍下面 6 个阶段就是一次"同步"。用户只要求其中一部分(比如"把 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`,但 `git ls-remote` 和 Gitea 网页/API 正常**:
|
||
服务端把 pack 生成外包给了一个不产出内容的程序。**2026-09-02 实际发生过一次**,
|
||
症状是 `POST /git-upload-pack` 记 200 OK、耗时也正常,响应体却只有 `0008NAK`。
|
||
别去猜版本或网络,按这个阶梯两三步就能定位(每一步都排掉一层):
|
||
1. 容器/宿主机内直连 gitea 端口 clone —— 还失败就与 frp、反向代理无关;
|
||
2. `git upload-pack --stateless-rpc <repo裸路径> < 一个手写的 want/done 请求 | wc -c`
|
||
—— 出得来几 MB 就说明 git 本身没病,**注意必须带 `--stateless-rpc`**,
|
||
用 `clone file://` 走的不是 HTTP 那条码路,测了不算数;
|
||
3. 在 gitea 和 git 之间插一个 wrapper(把 `/usr/bin/git` 换成脚本,`tee` 下 argv/env/
|
||
stdin/stdout)跑一次 clone。**上次就是这一步定的案:upload-pack 只吐了 17 字节、
|
||
stderr 全空**——不是 gitea 丢包,是 git 自己被指使着不干活。
|
||
根因在 gitea 的全局 gitconfig(`$HOME/.gitconfig`,docker 下 HOME=`/data/gitea/home`,
|
||
**不是** `/data/git`)里被注入了 `[uploadpack] packObjectsHook`。该配置会把
|
||
`git pack-objects` 整个替换掉,指向的脚本不产出 pack 就得到空包。
|
||
处置:备份该文件取证 → 移走 → 重启 gitea(它启动时会重建一份干净的)→
|
||
在容器内和公网各 clone 一次验证。同时检查 hook 脚本有没有被追加执行语句、
|
||
`/tmp` 有没有配套载荷——这是一条完整的 RCE 链,不是配置写错。
|
||
- **push 被拒(认证)**:检查凭据管理器条目;新机器上用
|
||
`git config --global credential.helper manager` 后重试一次,让 credential
|
||
helper 弹窗或从 skills_env.md 读凭据拼一次性 URL 完成首次认证。
|
||
认证成功后确认凭据已被缓存(再 fetch 一次不应再要密码)。
|
||
- **不该入库的文件**:commit 前扫一眼 `git status`,运行时产物(.sqlite、.pckl、
|
||
日志、缓存目录)应加进 `.gitignore` 而不是提交。看到就顺手补 `.gitignore`。
|
||
|
||
## 阶段 2 — 对齐 ~\.codex\skills(junction 对账)
|
||
|
||
同一个脚本接着处理 codex 侧。它对主副本的每个顶层 skill 目录做三件事:
|
||
|
||
- codex 侧没有同名条目 → 创建 junction(`New-Item -ItemType Junction`,
|
||
Windows 上不需要管理员权限,Codex 对 junction 完全透明)。
|
||
- 已是 junction → 跳过(内容天然一致,无需任何拷贝)。
|
||
- 是**真实目录**(挡住了链接)→ 脚本停下来列出这些目录。停的原因:真实目录
|
||
里可能有 codex 独有的、还没入库的内容。处理原则——有价值的内容先复制进
|
||
`~\.claude\skills` 走阶段 1 入库,确认无需保留后用 `-Force` 重跑,脚本会
|
||
删掉真实目录并建 junction。
|
||
|
||
脚本还会清理"指向已删除主副本目录"的失效 junction(skill 在仓库里被删除或
|
||
改名后残留的链接)。codex 自带的系统 skills(`.system\`)永远不动。
|
||
|
||
校验注册:主副本每个 skill 目录在 codex 侧都应有同名 junction 且透出
|
||
`SKILL.md`(脚本已内置该检查)。提醒用户:Codex 要开新会话才会扫到新 skills。
|
||
|
||
## 阶段 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)
|
||
|
||
先运行双向审计脚本。它读取 Claude Code 用户级 `~\.claude.json` 的 `mcpServers` 和
|
||
Codex 用户级 `~\.codex\config.toml` 的 `[mcp_servers.*]`,对用户可配置 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\config.toml` 的 `[mcp_servers.<name>]` 节。
|
||
缺失的按 skills_env.md 里的同一份配置补写 TOML 节(stdio 类型,
|
||
记得带 `PYTHONIOENCODING=utf-8`、`PYTHONUTF8=1`,Codex 在 Windows 上
|
||
没有这两个变量时中文输出会乱码)。
|
||
3. **可启动性抽查**:对新注册的 MCP,直接运行其 command + args 数秒,
|
||
确认进程能起来、没有立刻报 import/路径错误(stdio MCP 起来后会静默等
|
||
输入,这就算通过,Ctrl-C 掉即可)。
|
||
|
||
注意两侧配置语义相同但格式不同(JSON vs TOML)。先由双向脚本发现差集和漂移,再用
|
||
`mcp_list.md` 查漏,最后以 skills_env.md 和实际可启动结果判定正确值。不要因为某项当前
|
||
只在一侧出现,就假设另一侧没有安装需求。
|
||
|
||
### 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 `~\.codex\config.toml` 均有用户级 `matlab` MCP,
|
||
command 指向同一二进制,args 同时包含 `--matlab-session-mode=existing` 和正确的
|
||
`--extension-file`。Claude 条目应有 `alwaysLoad=true`。
|
||
4. Codex 的 `[mcp_servers.matlab]` 还必须有 `tool_timeout_sec = 600` 和 Windows 所需的
|
||
`env_vars = ["WINDIR"]`;缺少 `WINDIR` 时 Simulink 子进程可能无法正常工作。
|
||
5. `~\.agents\skills` 中存在指向两套 Toolkit 的 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。再直接解析两边配置,确认
|
||
Simulink extension、Codex timeout 与 `WINDIR` 未被规范化比较遗漏。
|
||
- 使用 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 滞留本机**
|
||
- ~\.codex\skills: <pull 结果,注册差集>
|
||
## 硬编码审计
|
||
- 本次修复: <清单>
|
||
- 遗留待改: <清单>
|
||
## 环境
|
||
- langgraph env: OK / 已重装 / 失败原因
|
||
- MCP 注册: <Claude 独有、Codex 独有、同名漂移、已补齐、排除项、清单查漏、MATLAB/Simulink 专项检查、抽查结果>
|
||
## 需要用户决定的事项
|
||
- <冲突、密码泄露、codex 独有 skill 去留……没有则写"无">
|
||
```
|
||
|
||
## 边界与安全
|
||
|
||
- 本 skill 会 push 到远端仓库——commit 前把将要提交的文件列表给用户过目
|
||
(自动化场景下至少写进报告)。绝不 `push --force`。
|
||
- `~\skills_env.md` 含凭据,任何情况下不将其内容或凭据字段写入仓库内文件、
|
||
commit message 或对外输出。
|
||
- 不动 `~\.codex\skills\.system\`(Codex 自带系统 skills)和
|
||
`~\.claude\skills\.system\`。
|
||
- 修改 `~\.codex\config.toml` 前先备份为 `config.toml.bak-<日期>`,TOML 写坏
|
||
会导致 Codex 起不来。
|