#!/usr/bin/env bash # 實測:scripts/isep-doctor 對**真的** Claude Code(這台的 CLI 與 Desktop 內建那一份)判得對不對(inkstone/ISEP#150) # # 不動 ~/.claude:把 inkstone marketplace 與 isep 安裝紀錄複製到 mktemp 的隔離 HOME, # 分別放「有 path」「沒有 path」兩份 settings.json,用真的可執行檔跑 doctor。 # 會打網路:只有 Claude Code 自己可能匿名讀 git.uncle6.me(Gitea),不碰 GitHub、不寫任何遠端。 # # 期望(09-13 在 CLI 2.1.220/Desktop 2.1.266 上實測過的行為): # 沒有 path ⇒ 兩個都 ✅,離開碼 0 (真陰性) # 有 path、Desktop 2.1.266 ⇒ ❌ 並點名 path (真陽性) # 有 path、CLI 2.1.220 ⇒ ✅,不因設定長相被判 ❌ (舊版 doctor 的假陽性) # 版本不是這兩個時,該版本那一格印 SKIP(行為可能不同,不假裝驗過)。 set -u HERE="$(cd "$(dirname "$0")" && pwd)" R="$HOME/.claude/plugins" [ -d "$R/cache/inkstone" ] && [ -d "$R/marketplaces/inkstone" ] || { echo "SKIP:這台沒有安裝 isep@inkstone,無從複製"; exit 0; } CLI=$(command -v claude || true) DESK=$(ls -d "$HOME/Library/Application Support/Claude/claude-code/"*/claude.app/Contents/MacOS/claude 2>/dev/null \ | python3 -c 'import sys,re;L=[l.strip() for l in sys.stdin if l.strip()];k=lambda p:tuple(int(x) for x in re.search(r"/claude-code/([0-9.]+)/",p).group(1).split(".") if x);print(sorted(L,key=k)[-1] if L else "")') BINS=""; [ -n "$CLI" ] && BINS="CLI=$CLI"; [ -n "$DESK" ] && BINS="${BINS:+$BINS:}Desktop=$DESK" [ -n "$BINS" ] || { echo "SKIP:找不到任何 Claude Code"; exit 0; } vcli=$([ -n "$CLI" ] && "$CLI" --version | awk '{print $1}'); vdesk=$([ -n "$DESK" ] && "$DESK" --version | awk '{print $1}') echo "CLI=${CLI:-無} ${vcli:-}|Desktop=${DESK:-無} ${vdesk:-}" T=$(mktemp -d); trap 'rm -rf "$T"' EXIT C="$T/home/.claude"; mkdir -p "$C/plugins/cache" "$C/plugins/marketplaces" cp -R "$R/cache/inkstone" "$C/plugins/cache/" && cp -R "$R/marketplaces/inkstone" "$C/plugins/marketplaces/" python3 - "$R" "$C" <<'EOF' import json, sys R, C = sys.argv[1], sys.argv[2] km = json.load(open(R + "/known_marketplaces.json")) json.dump({"inkstone": json.loads(json.dumps(km["inkstone"]).replace(R, C + "/plugins"))}, open(C + "/plugins/known_marketplaces.json", "w")) ip = json.load(open(R + "/installed_plugins.json")) ip["plugins"] = {"isep@inkstone": ip["plugins"]["isep@inkstone"]} json.dump(json.loads(json.dumps(ip).replace(R, C + "/plugins")), open(C + "/plugins/installed_plugins.json", "w")) src = {"source": "git", "url": "https://git.uncle6.me/inkstone/ISEP.git"} json.dump({"enabledPlugins": {"isep@inkstone": True}, "extraKnownMarketplaces": {"inkstone": {"source": src}}}, open(C + "/settings.nopath.json", "w")) src["path"] = "/Users/youlinhsieh/Documents/tech_projects/ISEP" json.dump({"enabledPlugins": {"isep@inkstone": True}, "extraKnownMarketplaces": {"inkstone": {"source": src}}}, open(C + "/settings.path.json", "w")) EOF cp -R "$C" "$T/pristine" pass=0; fail=0; skip=0 doctor() { # settings 變體 rm -rf "$C" && cp -R "$T/pristine" "$C" && cp "$C/settings.$1.json" "$C/settings.json" OUT=$(cd "$T" && env -u CLAUDE_CODE_CHILD_SESSION HOME="$T/home" ISEP_DOCTOR_CLAUDE_BINS="$BINS" python3 "$HERE/isep-doctor" 2>&1); CODE=$? echo "── settings=$1(exit=$CODE)"; printf '%s\n' "$OUT" | sed 's/^/ /' } has() { printf '%s' "$OUT" | grep -q -- "$1"; } judge() { if eval "$2"; then pass=$((pass+1)); echo "ok $1"; else fail=$((fail+1)); echo "FAIL $1"; fi; } doctor nopath judge "真陰性:沒有 path → 離開碼 0、沒有 ❌" '[ "$CODE" = 0 ] && ! has "❌"' doctor path if [ "$vdesk" = 2.1.266 ]; then judge "真陽性:Desktop 2.1.266 有 path → ❌ 並點名 path、離開碼 1" '[ "$CODE" = 1 ] && has "❌ Desktop 2.1.266" && has "多了 path"' else skip=$((skip+1)); echo "SKIP 真陽性:Desktop 是 ${vdesk:-無},不是 2.1.266"; fi if [ "$vcli" = 2.1.220 ]; then judge "假陽性:CLI 2.1.220 有 path 但實際載入 → ✅" 'has "✅ CLI 2.1.220"' else skip=$((skip+1)); echo "SKIP 假陽性:CLI 是 ${vcli:-無},不是 2.1.220"; fi echo "pass=$pass fail=$fail skip=$skip"; [ "$fail" = 0 ]