c2638668e3
leo 2026-08-20:「同一個 plugin 你用,薄殼也用,保證兩邊同步」
「我要你幫雲端做薄殼,永遠都有問題,你要做的就是這組設定
你自己可以 dogfooding」
搬進來:41 支 hook(51 條註冊)/7 支 command/2 支 skill/23 支腳本。
不搬 .env、wiki、docs——那些是知識不是環境。
51 條 hook 路徑全部從 $CLAUDE_PROJECT_DIR/.claude/hooks/ 改成 ${CLAUDE_PLUGIN_ROOT}/hooks/,
零漏網。那正是薄殼一直壞掉的根:雲端 cwd 不是真身,寫死路徑就斷。
尚未驗證:Claude Code 能不能從私有 Gitea repo 裝 marketplace(要憑證)。
下一步就是在本機實際裝一次,通了才動雲端 bootstrap.sh。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
94 lines
4.2 KiB
Bash
Executable File
94 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# guard-cross-project.sh — 頂層總管 guardrail
|
||
#
|
||
# 職責邊界(InkStoneCo CLAUDE.md D9):頂層只做「跨專案的整理與安排」(總管),
|
||
# 不進單一子 repo 做實作。子 repo 的程式碼/設定實作 = 在那個子 repo 的專案裡執行,
|
||
# 頂層只負責「交棒」(寫 docs/HANDOFF-*.md)並記錄安排。
|
||
#
|
||
# 本 hook 攔 PreToolUse 的 Write/Edit/MultiEdit:
|
||
# - 目標路徑落在子 repo 目錄(matrix/ polaris/ products/ _archive/)內
|
||
# - 且不是 .md 文件
|
||
# → 擋下(exit 2),提示改用交棒(寫 HANDOFF)。
|
||
# 放行:頂層自己的檔(CLAUDE.md / .claude/ / docs/ / MEMORY.md…)、
|
||
# 以及子 repo 內的 .md(交棒 HANDOFF / 筆記 / 文件——leo 2026-06-15 拍板放行所有 .md)。
|
||
#
|
||
# 誠實限制(arcrun mindset §7):本 hook 擋的是「路徑語法層」——能擋「寫程式碼進子 repo」,
|
||
# 擋不了「把實作偽裝成 .md」或「該交棒卻判斷成可直改」這種語意層越界。它是底線,不是萬能。
|
||
|
||
input="$(cat)"
|
||
|
||
# 取 file_path(Write/Edit/MultiEdit 都用 tool_input.file_path)
|
||
file_path="$(printf '%s' "$input" | python3 -c '
|
||
import sys, json
|
||
try:
|
||
d = json.load(sys.stdin)
|
||
print(d.get("tool_input", {}).get("file_path", ""))
|
||
except Exception:
|
||
print("")
|
||
' 2>/dev/null || true)"
|
||
|
||
# 拿不到路徑就放行(不誤擋;hook 不該因解析失敗卡死正常操作)
|
||
[ -z "$file_path" ] && exit 0
|
||
|
||
# 頂層 repo 根(hook 檔位於 <root>/.claude/hooks/)
|
||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||
|
||
# 轉成相對 repo_root 的路徑(file_path 可能是絕對或相對)
|
||
case "$file_path" in
|
||
/*) rel="${file_path#"$repo_root"/}" ;; # 絕對路徑 → 去掉 repo_root 前綴
|
||
*) rel="$file_path" ;; # 相對路徑 → 假定相對 repo_root
|
||
esac
|
||
|
||
# 不在子 repo 目錄內 → 放行(頂層自己的檔)
|
||
case "$rel" in
|
||
matrix/*|polaris/*|products/*|_archive/*) : ;; # 落在子 repo,繼續判斷
|
||
*) exit 0 ;;
|
||
esac
|
||
|
||
# 自動派工放行(2026-06-28 leo 拍板上線,實測通過):
|
||
# 子 repo 的 subagent 可直接寫該 repo code(繞 D9)——因 subagent 心智已是該 repo
|
||
# (A/B 實驗證 ≈ claude -p),讓「心智是該 repo 的小弟寫該 repo」符合 context 隔離。
|
||
# 雙重夾:① CHILD_SESSION=1(harness 內建 subagent 標籤,總管主 session 無此標籤仍被擋)
|
||
# ② 路徑在下面白名單 array 內。
|
||
# 隔離保證=prompt reset(軟,靠監測)非結構級;要絕對純走 claude -p。心法見 wiki。
|
||
#
|
||
# 🔧 leo 維護:要放開一個 repo 給 subagent 自動寫 code,就在這 array 加一行路徑前綴。
|
||
# ⚠️ 加 = 放權,想清楚私人/共用(改它影響誰)再加。array 空 = 誰都不放(安全預設)。
|
||
AUTODISPATCH_ALLOW=(
|
||
"polaris/mira" # 私人環境(只影響 leo)
|
||
"matrix/arcrun" # 共用框架(影響全部人,走 PR 把關)
|
||
"products/arcrun-rag" # 產品組裝 repo(2026-07-13 leo 放行 G8:回覆「放行」)
|
||
)
|
||
if [ "$CLAUDE_CODE_CHILD_SESSION" = "1" ]; then
|
||
for allowed in "${AUTODISPATCH_ALLOW[@]}"; do
|
||
case "$rel" in
|
||
"$allowed"/*) echo "🤝 [guard] subagent 放行:$allowed(自動派工)" >&2; exit 0 ;;
|
||
esac
|
||
done
|
||
fi
|
||
|
||
# 在子 repo 內:.md 放行(交棒 / 文件),其餘擋
|
||
case "$rel" in
|
||
*.md|*.MD|*.markdown) exit 0 ;;
|
||
esac
|
||
|
||
# 走到這 = 寫非 .md 檔進子 repo 目錄 = 越界實作 → 擋
|
||
sub="${rel%%/*}"
|
||
cat >&2 <<EOF
|
||
❌ BLOCKED by InkStoneCo 總管 guardrail(職責邊界 D9)
|
||
|
||
違反:頂層總管直接寫實作檔進子 repo($sub/)。
|
||
路徑:$rel
|
||
|
||
頂層只做「跨專案的整理與安排」,不進單一 repo 做實作。
|
||
子 repo 的程式碼 / 設定 = 在該子 repo($sub)的專案裡改,不在頂層。
|
||
|
||
正確做法:
|
||
• 要子 repo 改什麼 → 寫一份交棒文件:$sub/docs/HANDOFF-*.md(.md 放行),
|
||
給判準與論證,成品由該 repo 的 CC 按它自己的 rules 產出。
|
||
• 真要由你(總管)動這個子 repo → 切換到那個子 repo 的工作脈絡再做,不在頂層越界。
|
||
|
||
參考:CLAUDE.md「職責邊界(核心原則)」+ decisions-summary D9。
|
||
EOF
|
||
exit 2
|