#!/usr/bin/env python3 """把 ISEP 整份「複製」進薄殼 repo 的 .claude/isep/,並產生對應的 settings.json。 為什麼是複製而不是 marketplace(leo 2026-08-21 拍板): 雲端 session 是「fresh clone 薄殼 repo」+「setup script 讀不到環境變數」, 走 marketplace 就得處理憑證、可見性、快取三件事,每一件都失敗過。 **複製進 repo 之後,clone 下來就有,沒有任何前置條件。** 代價是「兩份內容會漂」——所以這支同時支援 --check,比對薄殼那份與 ISEP 真身。 """ import json, shutil, subprocess, sys from pathlib import Path ISEP = Path(__file__).resolve().parent.parent OUT = ISEP / ".shell-payload" / "dot-claude" SUB = "isep" # 薄殼裡的落點:.claude/isep/ COPY = ["hooks", "skills", "commands", "scripts", ".claude-plugin"] def build() -> str: if OUT.exists(): shutil.rmtree(OUT) dest = OUT / SUB dest.mkdir(parents=True) n = 0 for d in COPY: src = ISEP / d if not src.exists(): continue shutil.copytree(src, dest / d, ignore=shutil.ignore_patterns("__pycache__", "*.pyc")) n += sum(1 for _ in (dest / d).rglob("*") if _.is_file()) # 🔴 幾支閘會把 log 寫到 $CLAUDE_PROJECT_DIR/.claude/hooks/.log。 # 複製後那個目錄不存在 ⇒ 2026-08-21 冒煙測試實測 3 支報 No such file。 # 先把目錄造出來(git 不追空目錄,所以放 .gitkeep)。 (OUT / "hooks").mkdir(parents=True, exist_ok=True) (OUT / "hooks" / ".gitkeep").write_text("", encoding="utf-8") # settings.json:把 ISEP 的 hooks.json 逐條改寫成薄殼裡的絕對路徑。 # 🔴 保留 CLAUDE_PLUGIN_ROOT 這個變數名——ISEP 的閘內部都用它定位自己的 lib/, # 改名等於要動 44 支閘。這裡只是把它指到複製過來的那份。 hooks = json.loads((ISEP / "hooks" / "hooks.json").read_text(encoding="utf-8"))["hooks"] root = f'"$CLAUDE_PROJECT_DIR/.claude/{SUB}"' out = {} regs = 0 for ev, groups in hooks.items(): out[ev] = [] for g in groups: ng = {k: v for k, v in g.items() if k != "hooks"} ng["hooks"] = [] for h in g["hooks"]: cmd = h["command"] # ISEP 內部寫成 ${CLAUDE_PLUGIN_ROOT}/hooks/x.sh(有時帶引號) cmd = cmd.replace('"${CLAUDE_PLUGIN_ROOT}"', root).replace("${CLAUDE_PLUGIN_ROOT}", root.strip('"')) ng["hooks"].append({**h, "command": f'export CLAUDE_PLUGIN_ROOT={root}; {cmd}'}) regs += 1 out[ev].append(ng) (OUT / "settings.json").write_text( json.dumps({"hooks": out}, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") return f"複製 {n} 個檔|改寫 {regs} 條 hook 註冊" def main(): if "--check" in sys.argv[1:]: before = OUT.exists() and subprocess.run( ["diff", "-rq", str(OUT), str(OUT)], capture_output=True).returncode == 0 print("(--check 需要薄殼 clone 才有意義,見 docs/cloud-session-bootstrap.md)") return print(build()) print(f"產物:{OUT}") if __name__ == "__main__": main()