sync: WIN10-I9-64G 2026-07-20
This commit is contained in:
@@ -348,3 +348,6 @@ web-design-guidelines/SKILL.md
|
||||
*.mjs
|
||||
*.pckl
|
||||
*.pyc
|
||||
|
||||
*.sqlite
|
||||
**/.langgraph_api/
|
||||
|
||||
Binary file not shown.
@@ -7,31 +7,38 @@ description: Use the LangGraph plan-review workflow from the current Claude Code
|
||||
|
||||
Use this skill to run the LangGraph plan-review workflow. All scripts are bundled under the skill's own `scripts\` directory (`C:\Users\bowei\.claude\skills\langgraph-plan-review\scripts\`) — no dependency on `E:\LangGraph_Design\lab00`.
|
||||
|
||||
## ⚠️ 强制执行规则(MANDATORY — read first)
|
||||
## 运行方式:进程内驱动,无 dev server(2026-07 起)
|
||||
|
||||
**plan-review 工作流必须、且只能通过 `langgraph dev` 服务器来驱动。**
|
||||
**本 skill 已去掉 `langgraph dev` 服务器**。工作流由进程内 runner **`scripts\clean_run.py`** 直接驱动:
|
||||
它 `build_workflow()` 编译图、用 **SqliteSaver**(`scripts\.plan_review_state.sqlite`)做跨调用持久化、
|
||||
自己处理 `interrupt`/`Command(resume=...)`。不再需要 `manage_dev_server.ps1` / `run_langgraph.ps1` /
|
||||
`run_workflow.py`(这些是旧的 dev-server 路径,保留仅作参考,勿再使用)。
|
||||
|
||||
- ✅ **唯一允许的入口**:`run_langgraph.ps1` → `run_workflow.py`。后者是一个**纯 REST/SDK 客户端**
|
||||
(`langgraph_sdk.get_sync_client(url=http://127.0.0.1:2024)`),每次 start/resume 都把 run
|
||||
投递到 `langgraph dev` 服务器执行。先用 `manage_dev_server.ps1 start` 把服务器拉起。
|
||||
这样 LangGraph Studio 才能实时追踪线程、节点、中断与状态。
|
||||
- **入口**:用 `langgraph` conda 环境的 python 跑 `clean_run.py`:
|
||||
```powershell
|
||||
$PY = "C:\Users\bowei\.conda\envs\langgraph\python.exe"
|
||||
$RUN = "C:\Users\bowei\.claude\skills\langgraph-plan-review\scripts\clean_run.py"
|
||||
# 首次(到 write_plan 中断):
|
||||
& $PY $RUN --task-id 10 --work-dir "<work_dir>" --notes-dir Notes --prompt "<原始请求>"
|
||||
# resume(写完/改完 Task10.md 后;resume 值 base64(JSON)):
|
||||
$resume = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('true'))
|
||||
& $PY $RUN --task-id 10 --work-dir "<work_dir>" --notes-dir Notes --resume-b64 $resume --codex-review-command-b64 $CMD --timeout 900
|
||||
```
|
||||
每次调用推进到下一个 `interrupt` 或结束,打印一行 JSON:`{"status":"waiting","action_required":{...}}`
|
||||
或 `{"status":"done","has_serious_issue":...}`。`action_required` 的处理同下(write_plan / decide /
|
||||
revise / write_rejection),resume 值:`write_plan`/`revise`/`write_rejection` 用 `true`;`decide` 用
|
||||
`{"accepts":true}` 或 `{"accepts":false,"reason":"..."}`。
|
||||
|
||||
- 🚫 **绝对禁止**:直接 `import` / 在进程内运行本 skill 的图定义模块
|
||||
(`scripts\graph.py`、`scripts\workflow.py`、`scripts\agents.py`)来跑工作流。包括但不限于:
|
||||
- `python -c "from graph import ...; graph.invoke(...)"` 或 `.stream(...)`
|
||||
- 写临时脚本 build 编译图后在进程内调用
|
||||
- 任何绕过 `http://127.0.0.1:2024` 的「就地执行」方式
|
||||
- **codex 评审的两个必备条件**(本次踩坑记录):
|
||||
1. **codex CLI 要足够新**。config.toml 若配了较新模型(如 `gpt-5.6-sol`),旧 codex 会返回 400
|
||||
`requires a newer version of Codex`。升级:`npm i -g @openai/codex@latest`(≥0.144.5 实测可用)。
|
||||
2. **codex 子进程要能到 chatgpt.com**。`clean_run.py` 会自动读 Windows 系统代理(HKCU Internet
|
||||
Settings 的 `ProxyServer`)设进 `HTTP(S)_PROXY`,让 codex 走代理;也可用 `PLAN_REVIEW_PROXY` 覆盖。
|
||||
3. **codex 超时**:`review_task.py` 的内层 codex 超时取 argv[5](默认 300s,评审常不够)。用
|
||||
`--codex-review-command-b64` 传一条**末尾带超时秒数**的命令(见 Workflow),并把 `--timeout` 设一致。
|
||||
|
||||
这类调用在 dev server 进程**之外**运行,Studio 追踪不到状态,**一律不允许**——
|
||||
即使只是想「快速验证一个节点」也不行。
|
||||
|
||||
- ℹ️ **例外说明**:`review_task.py`(Codex 评审 shim)会直接调 `codex`,这是**合规的**——
|
||||
它是由运行在 dev server 内的图通过 `codex_review_command` 调起的评审步骤,不是绕过
|
||||
dev server 去驱动工作流本身。区别在于:驱动图 = 必须走 dev server;图内部调用的工具 = 正常。
|
||||
|
||||
- 🛠 **改了 `graph.py`/`workflow.py`/`agents.py` 后**:dev server 内存里仍是旧代码。先
|
||||
`manage_dev_server.ps1 stop` 再 `start`(或让 `--reload` 生效),再经 `run_langgraph.ps1`
|
||||
重跑,使改动在被追踪的运行中生效。
|
||||
- **改了 `workflow.py`/`agents.py`/`clean_run.py` 后**:无 dev server 缓存,下次 `clean_run.py` 直接生效。
|
||||
若图结构改到与旧 checkpoint 不兼容,删 `scripts\.plan_review_state.sqlite` 重新开始该 thread。
|
||||
|
||||
## Pre-flight: Environment Setup
|
||||
|
||||
@@ -75,7 +82,10 @@ conda run -n langgraph langgraph --version
|
||||
- The workflow uses LangGraph `interrupt()` and persists state via the langgraph dev server.
|
||||
- Each `run_langgraph.ps1` invocation advances the graph to the next interrupt or completion. Use `-Resume` to continue the same thread after completing the requested Claude Code action.
|
||||
|
||||
## Dev Server Lifecycle
|
||||
## Dev Server Lifecycle ⛔ DEPRECATED
|
||||
|
||||
> **已弃用(2026-07 起)。** 本 skill 现用 `clean_run.py` 进程内驱动,**不再需要 dev server**。
|
||||
> 下面这套 `manage_dev_server.ps1` / `langgraph dev` / Studio 仅作历史参考,正常流程不要用。
|
||||
|
||||
The `langgraph dev` server must be running for the entire duration of a workflow session.
|
||||
Use `manage_dev_server.ps1` to start it before the first `run_langgraph.ps1` call and stop it after the workflow completes.
|
||||
@@ -99,6 +109,10 @@ C:\Users\bowei\.claude\skills\langgraph-plan-review\scripts\manage_dev_server.ps
|
||||
|
||||
## Workflow
|
||||
|
||||
> **注意**:下列步骤是**旧的 dev-server 流程**(`manage_dev_server.ps1` + `run_langgraph.ps1`)。
|
||||
> 正常请用上方"**运行方式:进程内驱动**"节的 `clean_run.py`。以下 action 语义(write_plan / decide /
|
||||
> revise / write_rejection 及其 resume 值)两种流程通用,可参考。
|
||||
|
||||
1. Determine `TaskId` from the user. If missing, ask for it.
|
||||
2. Start the dev server (idempotent):
|
||||
```powershell
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
"""In-process driver for the plan-review graph — NO langgraph dev server.
|
||||
|
||||
Builds the compiled graph in-process (workflow.build_workflow) with a SqliteSaver so
|
||||
state survives between invocations, and drives interrupts directly. Sets HTTP(S)_PROXY
|
||||
from the Windows system proxy (or PLAN_REVIEW_PROXY) so the codex subprocess spawned by
|
||||
the review node can reach its backend through the proxy.
|
||||
|
||||
CLI (mirrors the old run_workflow.py):
|
||||
--task-id 10 [--work-dir DIR] [--notes-dir Notes] [--prompt "..."]
|
||||
[--resume-b64 <b64 json>] [--codex-review-command-b64 <b64 json>] [--timeout 600]
|
||||
Prints one JSON line: {"status":"waiting","action_required":{...}} or {"status":"done",...}.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import os, sys, json, base64, argparse, sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPTS = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
|
||||
|
||||
def resolve_proxy() -> str:
|
||||
px = os.environ.get("PLAN_REVIEW_PROXY", "").strip()
|
||||
if not px:
|
||||
try:
|
||||
import winreg
|
||||
k = winreg.OpenKey(
|
||||
winreg.HKEY_CURRENT_USER,
|
||||
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
||||
)
|
||||
enable, _ = winreg.QueryValueEx(k, "ProxyEnable")
|
||||
server, _ = winreg.QueryValueEx(k, "ProxyServer")
|
||||
if enable and server and "=" not in str(server): # simple host:port, not per-scheme
|
||||
px = "http://" + str(server)
|
||||
except Exception:
|
||||
px = ""
|
||||
if px:
|
||||
for v in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY",
|
||||
"http_proxy", "https_proxy", "all_proxy"):
|
||||
os.environ[v] = px
|
||||
return px
|
||||
|
||||
|
||||
def open_saver(db_path: str):
|
||||
from langgraph.checkpoint.sqlite import SqliteSaver
|
||||
conn = sqlite3.connect(db_path, check_same_thread=False)
|
||||
saver = SqliteSaver(conn)
|
||||
try:
|
||||
saver.setup()
|
||||
except Exception:
|
||||
pass
|
||||
return saver
|
||||
|
||||
|
||||
def extract_interrupt(result):
|
||||
if not isinstance(result, dict):
|
||||
return None
|
||||
ints = result.get("__interrupt__")
|
||||
if not ints:
|
||||
return None
|
||||
first = ints[0] if isinstance(ints, (list, tuple)) else ints
|
||||
return getattr(first, "value", first)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--task-id", required=True)
|
||||
ap.add_argument("--work-dir", default=".")
|
||||
ap.add_argument("--notes-dir", default="Notes")
|
||||
ap.add_argument("--prompt", default="")
|
||||
ap.add_argument("--resume-b64", default="")
|
||||
ap.add_argument("--codex-review-command-b64", default="")
|
||||
ap.add_argument("--timeout", type=int, default=600)
|
||||
ap.add_argument("--db", default=str(SCRIPTS / ".plan_review_state.sqlite"))
|
||||
args = ap.parse_args()
|
||||
|
||||
proxy = resolve_proxy()
|
||||
|
||||
from workflow import build_workflow
|
||||
from langgraph.types import Command
|
||||
|
||||
codex_cmd = None
|
||||
if args.codex_review_command_b64:
|
||||
codex_cmd = json.loads(base64.b64decode(args.codex_review_command_b64).decode("utf-8"))
|
||||
|
||||
saver = open_saver(args.db)
|
||||
graph = build_workflow(checkpointer=saver)
|
||||
|
||||
config = {"configurable": {"thread_id": str(args.task_id), "codex_timeout": args.timeout}}
|
||||
if codex_cmd:
|
||||
config["configurable"]["codex_review_command"] = codex_cmd
|
||||
|
||||
if args.resume_b64:
|
||||
resume_val = json.loads(base64.b64decode(args.resume_b64).decode("utf-8"))
|
||||
result = graph.invoke(Command(resume=resume_val), config)
|
||||
else:
|
||||
init = {
|
||||
"task_id": str(args.task_id),
|
||||
"user_prompt": args.prompt,
|
||||
"work_dir": args.work_dir,
|
||||
"notes_dir": args.notes_dir,
|
||||
"codex_session_id": "",
|
||||
}
|
||||
result = graph.invoke(init, config)
|
||||
|
||||
payload = extract_interrupt(result)
|
||||
if payload is not None:
|
||||
out = {"status": "waiting", "thread_id": str(args.task_id),
|
||||
"action_required": payload, "proxy": proxy}
|
||||
else:
|
||||
out = {"status": "done", "thread_id": str(args.task_id),
|
||||
"has_serious_issue": bool(result.get("has_serious_issue")),
|
||||
"review_comment": result.get("review_comment", ""),
|
||||
"proxy": proxy}
|
||||
print(json.dumps(out, ensure_ascii=False))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
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 环境,并确保 E:\MCP_design 下所有 MCP 都已在 Claude Code 和 Codex 中注册。当用户提到"同步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` 是主工作副本,`~\.codex\skills` 是同一仓库的第二个检出(镜像)。
|
||||
Codex 没有注册命令——它在会话启动时自动扫描 `~\.codex\skills` 下所有含 `SKILL.md`
|
||||
的目录,所以"注册到 Codex" = "目录出现在那里且含 SKILL.md"。
|
||||
- **机器相关的参数不进 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`。
|
||||
|
||||
人工介入的情形:
|
||||
|
||||
- **rebase 冲突**:逐个文件看冲突双方,skills 文本冲突通常保留两边语义合并;
|
||||
解不了就 `git rebase --abort` 并向用户报告冲突文件清单,让用户裁决。
|
||||
- **push 被拒(认证)**:检查凭据管理器条目;新机器上用
|
||||
`git config --global credential.helper manager` 后重试一次,让 credential
|
||||
helper 弹窗或从 skills_env.md 读凭据拼一次性 URL 完成首次认证。
|
||||
认证成功后确认凭据已被缓存(再 fetch 一次不应再要密码)。
|
||||
- **不该入库的文件**:commit 前扫一眼 `git status`,运行时产物(.sqlite、.pckl、
|
||||
日志、缓存目录)应加进 `.gitignore` 而不是提交。看到就顺手补 `.gitignore`。
|
||||
|
||||
## 阶段 2 — 同步 ~\.codex\skills(镜像检出)
|
||||
|
||||
同一个脚本会接着处理 codex 侧。逻辑和原因:
|
||||
|
||||
- 已是 git 仓库 → `git pull --rebase` 即可。
|
||||
- 还不是 git 仓库(首次转换)→ 脚本执行 `git init` + `remote add origin` +
|
||||
`fetch` + `git reset origin/main`,然后**停下来**列出"本地与仓库不一致"的文件。
|
||||
这一步停是有原因的:codex 目录里可能有比仓库新的本地修改或 codex 独有的
|
||||
skill。处理原则——codex 独有且有价值的 skill,先复制到 `~\.claude\skills`
|
||||
走阶段 1 入库,再回来;确认没有需要保留的差异后,用 `-Force` 重跑让脚本
|
||||
`git checkout -f` 对齐到仓库版本。codex 自带的系统 skills(`.system/`、
|
||||
vercel 系列等)已在 `.gitignore` 里,不会被动。
|
||||
- 同步完成后校验注册:仓库里每个顶层 skill 目录都应在 `~\.codex\skills`
|
||||
出现且含 `SKILL.md`。列出两侧目录做差集,差集应为空(忽略 .gitignore
|
||||
中的 codex 专属条目)。提醒用户: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 一次,并在 codex 侧 pull,保证两边一致。
|
||||
|
||||
## 阶段 4 — langgraph 开发环境校验
|
||||
|
||||
很多 skills(vivado-mcp、fpga 系列、langgraph 系列)依赖 conda 的 `langgraph` 环境。
|
||||
校验方式(路径以 skills_env.md 的 `CONDA_LANGGRAPH_PYTHON` 为准):
|
||||
|
||||
```powershell
|
||||
& <CONDA_LANGGRAPH_PYTHON> -c "import langgraph, langchain; print('langgraph env OK')"
|
||||
```
|
||||
|
||||
失败(环境不存在或包缺失)→ 调用 `langgraph-vivado-install` skill 完成安装,
|
||||
装好后把实际的 python 路径回填到 `~\skills_env.md`。
|
||||
|
||||
## 阶段 5 — MCP server 注册校验(Claude Code + Codex 两边)
|
||||
|
||||
MCP 的清单真源是 `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 在两边的
|
||||
command/args/env 应当一致;发现两边漂移(比如只有一边改了 VIVADO_PATH)时,
|
||||
以 skills_env.md 记录的值为准改齐两边。
|
||||
|
||||
## 阶段 6 — 输出同步报告
|
||||
|
||||
最后用这个结构向用户汇报(有问题的条目放前面):
|
||||
|
||||
```
|
||||
# tools-sync 同步报告 <日期>
|
||||
## skills 同步
|
||||
- ~\.claude\skills: <pull/push 结果,commit 摘要>
|
||||
- ~\.codex\skills: <pull 结果,注册差集>
|
||||
## 硬编码审计
|
||||
- 本次修复: <清单>
|
||||
- 遗留待改: <清单>
|
||||
## 环境
|
||||
- langgraph env: OK / 已重装 / 失败原因
|
||||
- MCP 注册: <两侧各缺了什么、补了什么、抽查结果>
|
||||
## 需要用户决定的事项
|
||||
- <冲突、密码泄露、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 起不来。
|
||||
@@ -0,0 +1,68 @@
|
||||
# skills_env — 本机环境档案(模板)
|
||||
|
||||
> 复制本文件到 `~\skills_env.md` 后逐项核对。此文件**只留在本机,不进 git**:
|
||||
> 每台电脑的路径/IP/凭据不同。skills 中凡是机器相关的参数都从这里取值。
|
||||
> 值为 `KEY = value` 形式,方便脚本和模型直接检索。
|
||||
|
||||
## 同步仓库
|
||||
|
||||
```
|
||||
SKILLS_REPO_URL = http://git.rpchannel.com/bowei/skills-win.git
|
||||
SKILLS_REPO_USER = bowei
|
||||
SKILLS_REPO_PASS = <填写,或留空表示已存入 Windows 凭据管理器>
|
||||
CLAUDE_SKILLS_DIR = C:\Users\<user>\.claude\skills
|
||||
CODEX_SKILLS_DIR = C:\Users\<user>\.codex\skills
|
||||
```
|
||||
|
||||
凭据说明:首次 clone 成功后运行 `git config --global credential.helper manager`,
|
||||
让 Windows 凭据管理器接管,此后 `SKILLS_REPO_PASS` 可以清空。
|
||||
|
||||
## Python / Conda
|
||||
|
||||
```
|
||||
CONDA_ROOT = C:\ProgramData\miniconda3
|
||||
CONDA_LANGGRAPH_PYTHON = C:\Users\<user>\.conda\envs\langgraph\python.exe
|
||||
```
|
||||
|
||||
## EDA / 硬件工具
|
||||
|
||||
```
|
||||
VIVADO_PATH = D:\Xilinx\Vivado\2018.1\bin\vivado.bat
|
||||
KICAD_PYTHONPATH = C:\Program Files\KiCad\10.0\bin\Lib\site-packages
|
||||
MATLAB_MCP_EXE = C:\Users\<user>\.matlab\agentic-toolkits\bin\matlab-mcp-server.exe
|
||||
```
|
||||
|
||||
## 板卡 / 仪器 IP
|
||||
|
||||
```
|
||||
N9320B_HOST = 192.168.1.147 # Keysight N9320B 频谱仪
|
||||
SMOD_BOARD_IP = 192.168.1.143 # SMOD 板(ATSC 激励器远程升级)
|
||||
```
|
||||
|
||||
## MCP 注册配置(真源:MCP_DESIGN_ROOT\mcp_list.md)
|
||||
|
||||
```
|
||||
MCP_DESIGN_ROOT = E:\MCP_design
|
||||
```
|
||||
|
||||
每个 MCP 在 Claude Code(`~\.claude.json` 的 `mcpServers`)和 Codex
|
||||
(`~\.codex\config.toml` 的 `[mcp_servers.*]`)中应使用**同一份** command/args/env。
|
||||
标准配置如下(路径变量引用上文条目):
|
||||
|
||||
| name | command | args | env |
|
||||
|------------|----------------------------------|---------------------------------------------------|-----|
|
||||
| vivado-mcp | {CONDA_LANGGRAPH_PYTHON} | E:\MCP_design\vivado-mcp-main\run_server.py(Codex)/ -m vivado_mcp(Claude) | VIVADO_PATH={VIVADO_PATH} |
|
||||
| mcp_n9320b | {CONDA_LANGGRAPH_PYTHON} | E:\MCP_design\mcp_n9320b\n9320b_mcp.py | N9320B_HOST={N9320B_HOST} |
|
||||
| cubemx | E:\MCP_design\MCP_cubemx\cubemx-mcp\.venv\Scripts\python.exe | E:\MCP_design\MCP_cubemx\cubemx-mcp\server\main.py | PYTHONPATH=E:\MCP_design\MCP_cubemx\cubemx-mcp\server |
|
||||
| kicad | node | E:\MCP_design\KiCAD-MCP-Server\dist\index.js | NODE_ENV=production; PYTHONPATH={KICAD_PYTHONPATH}; KICAD_AUTO_LAUNCH=false |
|
||||
| simetrix | <conda env>\Scripts\mcp-simetrix.exe | | SIMETRIX_PROJECT_ROOT=E:\ |
|
||||
| matlab | {MATLAB_MCP_EXE} | --matlab-session-mode=existing --extension-file=...\simulink\tools\tools.json | |
|
||||
|
||||
Codex 侧所有 stdio MCP 额外加 `PYTHONIOENCODING=utf-8`、`PYTHONUTF8=1`。
|
||||
|
||||
## 本机状态备注(同步时更新)
|
||||
|
||||
```
|
||||
LAST_SYNC = <日期>
|
||||
TODO = <遗留事项>
|
||||
```
|
||||
@@ -0,0 +1,113 @@
|
||||
# tools-sync: mechanical git sync of the two skills checkouts.
|
||||
# Claude side (~\.claude\skills) is the primary working copy: commit -> pull --rebase -> push.
|
||||
# Codex side (~\.codex\skills) is a mirror checkout of the same repo: bootstrap if needed, then pull.
|
||||
# Judgment calls (conflicts, codex-only skills worth keeping) are left to the caller.
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$RepoUrl,
|
||||
[string]$ClaudeSkills = "$env:USERPROFILE\.claude\skills",
|
||||
[string]$CodexSkills = "$env:USERPROFILE\.codex\skills",
|
||||
[string]$Branch = "main",
|
||||
# Bootstrap of the codex checkout stops when local files differ from the repo;
|
||||
# -Force lets it overwrite them (caller must have rescued codex-only edits first).
|
||||
[switch]$Force,
|
||||
# Report what would happen without committing/pushing/checking out.
|
||||
[switch]$DryRun
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Invoke-Git {
|
||||
param([string]$Dir, [string[]]$GitArgs)
|
||||
# PS 5.1: git writes progress/warnings to stderr; 2>&1 wraps them in ErrorRecords
|
||||
# which terminate under EAP=Stop. Relax EAP locally and stringify every line.
|
||||
$prev = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
$out = & git -C $Dir @GitArgs 2>&1 | ForEach-Object { "$_" }
|
||||
$code = $LASTEXITCODE
|
||||
$ErrorActionPreference = $prev
|
||||
return @{ Code = $code; Out = (($out -join "`n")).Trim() }
|
||||
}
|
||||
|
||||
function Fail([string]$Msg) { Write-Host "FAIL: $Msg"; exit 1 }
|
||||
|
||||
# ---------- Phase 1: primary checkout (~\.claude\skills) ----------
|
||||
Write-Host "=== [1/2] primary: $ClaudeSkills ==="
|
||||
if (-not (Test-Path (Join-Path $ClaudeSkills ".git"))) {
|
||||
Fail "$ClaudeSkills is not a git repo. Clone $RepoUrl there first (new machine: git clone $RepoUrl `"$ClaudeSkills`")."
|
||||
}
|
||||
|
||||
$st = Invoke-Git $ClaudeSkills @("status", "--porcelain")
|
||||
if ($st.Out) {
|
||||
Write-Host "local changes:"; Write-Host $st.Out
|
||||
if ($DryRun) {
|
||||
Write-Host "(dry-run) would commit these changes"
|
||||
} else {
|
||||
Invoke-Git $ClaudeSkills @("add", "-A") | Out-Null
|
||||
$msg = "sync: $env:COMPUTERNAME $(Get-Date -Format yyyy-MM-dd)"
|
||||
$r = Invoke-Git $ClaudeSkills @("commit", "-m", $msg)
|
||||
if ($r.Code -ne 0) { Fail "commit failed:`n$($r.Out)" }
|
||||
Write-Host "committed: $msg"
|
||||
}
|
||||
} else {
|
||||
Write-Host "working tree clean"
|
||||
}
|
||||
|
||||
if (-not $DryRun) {
|
||||
$r = Invoke-Git $ClaudeSkills @("pull", "--rebase", "origin", $Branch)
|
||||
if ($r.Code -ne 0) {
|
||||
Write-Host $r.Out
|
||||
Fail "pull --rebase failed on primary. Resolve conflicts (or 'git rebase --abort') then re-run."
|
||||
}
|
||||
Write-Host $r.Out
|
||||
$r = Invoke-Git $ClaudeSkills @("push", "origin", $Branch)
|
||||
if ($r.Code -ne 0) { Write-Host $r.Out; Fail "push failed (auth? see skill Phase 1)." }
|
||||
Write-Host "primary in sync with origin/$Branch"
|
||||
}
|
||||
|
||||
# ---------- Phase 2: mirror checkout (~\.codex\skills) ----------
|
||||
Write-Host "=== [2/2] mirror: $CodexSkills ==="
|
||||
if (-not (Test-Path $CodexSkills)) { New-Item -ItemType Directory -Force $CodexSkills | Out-Null }
|
||||
|
||||
if (-not (Test-Path (Join-Path $CodexSkills ".git"))) {
|
||||
Write-Host "bootstrapping mirror as a checkout of $RepoUrl"
|
||||
if ($DryRun) { Write-Host "(dry-run) would git init + fetch + align to origin/$Branch"; exit 0 }
|
||||
Invoke-Git $CodexSkills @("init") | Out-Null
|
||||
Invoke-Git $CodexSkills @("remote", "add", "origin", $RepoUrl) | Out-Null
|
||||
$r = Invoke-Git $CodexSkills @("fetch", "origin", $Branch)
|
||||
if ($r.Code -ne 0) { Write-Host $r.Out; Fail "fetch failed during bootstrap." }
|
||||
# Point index at origin/main without touching files, so status shows local-vs-repo drift.
|
||||
Invoke-Git $CodexSkills @("reset", "origin/$Branch") | Out-Null
|
||||
Invoke-Git $CodexSkills @("branch", "-f", $Branch, "origin/$Branch") | Out-Null
|
||||
Invoke-Git $CodexSkills @("checkout", $Branch) 2>$null | Out-Null
|
||||
$drift = Invoke-Git $CodexSkills @("status", "--porcelain")
|
||||
$modified = ($drift.Out -split "`n" | Where-Object { $_ -match '^\s*(M|D)' })
|
||||
if ($modified -and -not $Force) {
|
||||
Write-Host "these tracked files differ locally from the repo:"
|
||||
$modified | ForEach-Object { Write-Host " $_" }
|
||||
Fail "codex dir has local drift. Rescue anything worth keeping into the primary checkout, then re-run with -Force."
|
||||
}
|
||||
$r = Invoke-Git $CodexSkills @("checkout", "-f", $Branch)
|
||||
if ($r.Code -ne 0) { Write-Host $r.Out; Fail "checkout failed during bootstrap." }
|
||||
Invoke-Git $CodexSkills @("branch", "--set-upstream-to=origin/$Branch", $Branch) | Out-Null
|
||||
Write-Host "mirror bootstrapped at origin/$Branch (untracked codex-local files left untouched)"
|
||||
} else {
|
||||
if ($DryRun) {
|
||||
Invoke-Git $CodexSkills @("fetch", "origin", $Branch) | Out-Null
|
||||
$r = Invoke-Git $CodexSkills @("log", "--oneline", "HEAD..origin/$Branch")
|
||||
Write-Host "(dry-run) mirror is behind by:"
|
||||
if ($r.Out) { Write-Host $r.Out } else { Write-Host " (nothing, or not yet a checkout)" }
|
||||
} else {
|
||||
$st2 = Invoke-Git $CodexSkills @("status", "--porcelain")
|
||||
if ($st2.Out) {
|
||||
Write-Host "WARNING: mirror has local changes (should be edited on the primary side instead):"
|
||||
Write-Host $st2.Out
|
||||
}
|
||||
$r = Invoke-Git $CodexSkills @("pull", "--rebase", "origin", $Branch)
|
||||
if ($r.Code -ne 0) { Write-Host $r.Out; Fail "pull failed on mirror." }
|
||||
Write-Host $r.Out
|
||||
Write-Host "mirror in sync with origin/$Branch"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "=== sync_repos done ==="
|
||||
Reference in New Issue
Block a user