ISEP 0.1.0:環境設定收成一個 plugin,本機與雲端共用一份

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>
This commit is contained in:
2026-08-20 11:41:46 +08:00
commit c2638668e3
85 changed files with 11879 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env bash
# wiki-first-police.sh — 記憶警察(Stop / SubagentStop hook
#
# 病根(leo 2026-07-26 兩句原話):
# ①「你應該隨時記錄進 wiki,然後用記憶回答,而不是每次都去實測,除非 wiki 沒做好記錄找不到」
# ②「你要把不憑記憶回答改成檢查 wiki……不依賴你自己的記憶,那個過 session 就死了」
# ③「我看 CP 也沒用,因為你執行時沒去更新」
#
# 兩個病,同一根:**做完不落帳** → 下次(或下個 session)只好重新實測/憑會死的記憶亂答。
# - 病 A:驗完就往下做,CP/wiki 事後才補 → leo 看 CP 永遠落後現況=「看了也沒用」。
# - 病 B:被問狀態時憑對話記憶或重跑實測,而不是查 wiki → 過 session 就失憶。
#
# 為什麼要 hook 而不是寫規則:CLAUDE.md 早就寫了「tasks.md 是唯一進度來源,不靠對話記憶」,
# 照樣違反——自評的人跟做事的人是同一個。同 delivery-police:在「要收工」那刻攔截。
#
# 機制:這回合有「實測/修好/部署」這類狀態變更 → 檢查有沒有動過 wiki/CP/tasks → 沒動就 exit 2。
# 豁免:純查詢/純對話回合、已動過帳、撞權限閘。
input="$(cat)"
stop_active="$(printf '%s' "$input" | python3 -c '
import sys, json
try: print(json.load(sys.stdin).get("stop_hook_active", False))
except Exception: print(False)
' 2>/dev/null)"
[ "$stop_active" = "True" ] && exit 0
transcript="$(printf '%s' "$input" | python3 -c '
import sys, json
try: print(json.load(sys.stdin).get("transcript_path", ""))
except Exception: print("")
' 2>/dev/null)"
[ -z "$transcript" ] || [ ! -f "$transcript" ] && exit 0
# 取本回合(最後一次 user 訊息之後)的所有 assistant 文字 + 工具輸入
turn="$(python3 -c '
import sys, json
path = sys.argv[1]
out = []
try:
with open(path) as f: lines = f.readlines()
# 從尾巴往回走到最近一則 user 訊息為止
buf = []
for line in reversed(lines):
try: ev = json.loads(line)
except Exception: continue
msg = ev.get("message", ev)
role = msg.get("role")
if role == "user":
break
if role == "assistant":
content = msg.get("content", "")
if isinstance(content, list):
for b in content:
if not isinstance(b, dict): continue
if b.get("type") == "text":
buf.append(b.get("text", ""))
elif b.get("type") == "tool_use":
buf.append(json.dumps(b.get("input", {}), ensure_ascii=False))
elif isinstance(content, str):
buf.append(content)
out = list(reversed(buf))
except Exception: pass
print("\n".join(out))
' "$transcript" 2>/dev/null)"
[ -z "$turn" ] && exit 0
# ── 豁免一:撞閘/明說停等 → 放行 ──
if printf '%s' "$turn" | grep -qiE '權限閘|classifier|分類器擋|四題第|人閘|denied by'; then
exit 0
fi
# ── 這回合有沒有「狀態變更」?(值得落帳的事)──
# 部署、修好、實測通過、端到端驗證、立案——這些都是下次會被問到的狀態。
if ! printf '%s' "$turn" | grep -qiE '部署|deploy|已上線|修好|修掉|真兇|實測|端到端|驗證通過|全綠|通了|立案'; then
exit 0
fi
# ── 豁免〇(2026-08-08 新增):**這回合根本沒有動過任何 repo → 純討論,不必落帳** ──
#
# 🔴 為什麼補這道(leo 08-08 問「為什麼花了一整天」時查出來的):
# 上面那個「有沒有狀態變更」的判斷,比對的是**我回覆裡的散文**。
# 於是我只要在回答中**談到**「修好/實測/全綠/部署」——例如向 leo 解釋
# 今天做了什麼、或提出改善流程的建議——就會被判成「有狀態變更卻沒落帳」。
# ⇒ **把「談論」當成「執行」**。這是同一天第三個同款 bug
# (前兩個在我自己寫的 stage-before-prod-guard:讀腳本、commit 訊息提到關鍵字都被誤攔)。
# ⇒ 代價很實在:leo 每插話一次,我就被判一次「沒落帳」,於是又寫一段落帳、
# commit、push。今天 65 個 commit 裡 41 個是落帳,這是其中一個來源。
#
# 判準改成看**事實**:這 30 分鐘內有沒有任何 repo 真的產生 commit。
# 沒有 ⇒ 這回合是討論/回答,沒有東西需要被記住,放行。
ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
recent_commits=0
for r in "$ROOT" "$ROOT/products/arcrun-rag" "$ROOT/matrix/arcrun"; do
[ -d "$r/.git" ] || continue
n=$(git -C "$r" log --since="30 minutes ago" --oneline 2>/dev/null | wc -l | tr -d ' ')
recent_commits=$((recent_commits + n))
done
[ "$recent_commits" -eq 0 ] && exit 0
# ── 豁免二:這回合確實動過帳(wiki / CRITICAL-PATH / tasks.md / memory)──
#
# 🔴 同上,這裡原本也是比對散文——**只要回覆裡「提到」wiki 路徑就算落帳**,
# 等於「說了就算做了」。改成先看 git:這 30 分鐘的 commit 有沒有真的動到帳本檔。
doc_commits=0
for r in "$ROOT" "$ROOT/products/arcrun-rag" "$ROOT/matrix/arcrun"; do
[ -d "$r/.git" ] || continue
n=$(git -C "$r" log --since="30 minutes ago" --name-only --pretty=format: 2>/dev/null \
| grep -cE 'system-dev/wiki/|CRITICAL-PATH\.md|critical-paths/|tasks\.md|memory/.*\.md' || true)
doc_commits=$((doc_commits + n))
done
if [ "$doc_commits" -gt 0 ]; then
# 🔗 落帳了,但有沒有標「這件事牽涉哪些 repo」?
# leo 2026-08-01:「你需要在每次記錄你自己的 wiki 時建立連到發生此事件的 hyperlink 或三元組,
# 每件事牽涉到 2 個 repo,就可以在該事件查到那兩個 repo,細節記錄在該 repo 裡。」
# 病根:08-01 實測 status.md 今日 34 段落帳中,只有 2 段標題提到 repo、
# 0 筆有結構化 repo 欄位、僅 2 行三元組 ⇒ 下次仍要重查「這件事在哪」。
# 只在「跨 repo 事件」時要求(單 repo 的事不必標)。
if printf '%s' "$turn" | grep -qiE 'arcrun-rag|matrix/arcrun|console-ui|installer|portal|bundle|kbdb|cypher|跨 ?repo'; then
# 🔴 2026-08-08 第二刀:這一段原本也在比對**我的回覆散文**——
# 於是 wiki 條目裡明明寫了 📍 repo 與三元組,只因為**回覆裡沒提**就被判「沒標 repo」。
# (早上已把上層觸發條件改看 git,這裡是同一個病漏掉的另一半。)
# ⇒ 改成看**剛剛真的寫進帳本的內容**:近 30 分鐘 commit 新增的行有沒有 repo 指針。
doc_added=""
for r in "$ROOT" "$ROOT/products/arcrun-rag" "$ROOT/matrix/arcrun"; do
[ -d "$r/.git" ] || continue
doc_added="$doc_added$(git -C "$r" log --since='30 minutes ago' -p --unified=0 \
-- 'system-dev/wiki/*' 'system-dev/docs/3-specs/*' 2>/dev/null \
| grep '^+' || true)"
done
# ⚠️ 樣式別寫死 '📍 repo'——實際格式是 `📍 **repo**:`(粗體),
# 寫死會永遠對不上(08-08 實撞:條目明明有指針卻一直被判沒有)。
if ! printf '%s' "$doc_added$turn" | grep -qE '牽涉 repo|📍|真身在|真兇|位於|\[\[.*\]\]'; then
cat >&2 << 'EOF2'
🔗 記憶警察(連結層):你落帳了,但**沒標這件事牽涉哪些 repo**。
【leo 2026-08-01】「你需要在每次記錄你自己的 wiki 時建立連到發生此事件的 hyperlink 或三元組,
**每件事牽涉到 2 個 repo,就可以在該事件查到那兩個 repo**,細節記錄在該 repo 裡。」
【為什麼】08-01 實測:status.md 當日 34 段落帳,只有 2 段標題提到 repo、
**0 筆有結構化 repo 欄位** ⇒ 下次查「這件事在哪」還是要從頭找,
這就是同一天猜錯真身 3 次的根源。
補一行就好(擇一格式,放在該段落帳裡):
📍 repo`products/arcrun-rag`landing/worker.js)+`matrix/arcrun`console-ui/public/portal/
或三元組:`portal 視覺 >> 真身在 >> matrix/arcrun:console-ui/public/portal/index.html`
原則:**頂層 wiki 記「事件+牽涉哪些 repo」,細節記在該 repo 自己的 wiki**。
EOF2
exit 2
fi
fi
exit 0
fi
cat >&2 << 'EOF'
🚓 記憶警察:這回合有狀態變更(實測/修復/部署),但我沒看到你落帳。
【病根】leo 2026-07-26
「我看 CP 也沒用,因為你執行時沒去更新。」
「不依賴你自己的記憶——那個過 session 就死了。」
→ 你現在腦裡有的東西,下個 session 一個字都不剩。沒寫進檔案=沒發生過。
收工前補完這兩件(擇需要的做,一句話也算):
1. **狀態變更落帳**
- CP 關卡狀態變了 → 改 `system-dev/docs/3-specs/CRITICAL-PATH.md`(✅/◐/❌+實測輸出)
- 任務進度/新發現 → 改對應 `tasks.md`(含真兇、殘項、下一步)
- 跨 session 要記得的操作知識 → `system-dev/wiki/`status/mistakes/agent-memory
2. **下次別再重測**:把「這件事現在是什麼狀態、證據在哪」寫成一句話,
讓下個 session(或被 leo 問時)**查得到就不用重跑**。
⚠️ 落帳=寫檔案,不是寫在回覆裡。回覆會消失,檔案不會。
EOF
exit 2