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>
166 lines
9.5 KiB
Bash
Executable File
166 lines
9.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# self-drive-police.sh — 自走警察(Stop hook)
|
||
#
|
||
# 病根(leo 2026-06-29 一個 session 點破三次):我「做一條停一條」「把該自己做的推給 leo」
|
||
# ——不是缺 loop,是每個決策點退回「推給 leo」的省事預設。CLAUDE.md 寫規則沒用(我會不讀)。
|
||
# leo 的解法:在我「要停下來」那一刻攔截 + 反問,逼我先窮盡查證才准停。
|
||
# 靈感=leo 之前做的「Haiku 警察 Anna」(檢查回話、犯規就抓)。先用輕的(純 bash grep),不夠再升級。
|
||
#
|
||
# 機制(官方 Stop hook,核實 code.claude.com/docs/en/hooks):
|
||
# Claude 結束回合想停 → 本 hook 觸發 → 讀 transcript 我這回合輸出 → grep 三類推卸句式:
|
||
# ① 憑記憶問「要不要/該選/哪個」 → 反問「查 wiki/internet 官方了嗎」
|
||
# ② 問「下一步/接下來做什麼」 → 反問「查 SDD/派工表/北極星了嗎」
|
||
# ③ 推卸「交給你/這是你的/你去開」 → 反問「規劃做完了嗎、能 spawn subagent 嗎、為何停」
|
||
# 命中 → exit 2 + stderr(reason) → Claude 不停、收到反問繼續做。
|
||
# 沒命中 → exit 0 放行(真的該停,不擾)。
|
||
#
|
||
# 防無限迴圈(官方):stop_hook_active=true(已 block 過一輪)→ 放行。
|
||
# + Claude Code 內建 block cap(連續 8 次自動放行)。
|
||
#
|
||
# 誠實限制:靠句式 grep,擋得住「明顯推卸句」,擋不住「換句話說的推卸」。是底線不是萬能。
|
||
# 不夠時升級成 type:prompt(Haiku 判語意) 或 type:agent(查 wiki/SDD 客觀驗),見 hook 末註。
|
||
|
||
input="$(cat)"
|
||
|
||
# 防無限迴圈:已經 block 過一輪(Claude 正在回應上一次 block)→ 放行
|
||
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)"
|
||
if [ "$stop_active" = "True" ]; then
|
||
exit 0
|
||
fi
|
||
|
||
# 取 transcript 路徑
|
||
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
|
||
|
||
# 抽「我這回合最後的 assistant 文字輸出」(transcript 是 JSONL,每行一事件)
|
||
# 取最後一個 assistant 訊息的 text,避免掃到歷史
|
||
last_text="$(python3 -c '
|
||
import sys, json
|
||
path = sys.argv[1]
|
||
texts = []
|
||
try:
|
||
with open(path) as f:
|
||
lines = f.readlines()
|
||
# 從尾往前找最後一個 assistant 訊息的 text 內容
|
||
for line in reversed(lines):
|
||
try:
|
||
ev = json.loads(line)
|
||
except Exception:
|
||
continue
|
||
msg = ev.get("message", ev)
|
||
if msg.get("role") == "assistant":
|
||
content = msg.get("content", "")
|
||
if isinstance(content, list):
|
||
for b in content:
|
||
if isinstance(b, dict) and b.get("type") == "text":
|
||
texts.append(b.get("text", ""))
|
||
elif isinstance(content, str):
|
||
texts.append(content)
|
||
break
|
||
except Exception:
|
||
pass
|
||
print("\n".join(texts))
|
||
' "$transcript" 2>/dev/null)"
|
||
|
||
[ -z "$last_text" ] && exit 0
|
||
|
||
# ── 豁免:撞真閘就別逼它做不可能的事(2026-07-22 補)──
|
||
# 病例:雲端總管有 token、備好 patch,但 harness classifier 擋掉 mutating 寫入 →
|
||
# 舊版警察會抓「Which way do you want it?」逼它繼續,它撞牆到 block cap 才放行=空轉。
|
||
# 明說「四題第X命中」也放行(本 hook 註解原本就要求這格式,但沒實作豁免)。
|
||
if printf '%s' "$last_text" | grep -qiE '權限閘|classifier|分類器擋|denied by|四題第|需要你放行|需要 leo 放行'; then
|
||
exit 0
|
||
fi
|
||
|
||
# ── 三類推卸句式偵測 ──────────────────────────────────────────────
|
||
# ③ 推卸給 leo(最該抓)——「交給你/這是你的/你去/請你去開/leo 去做」
|
||
if printf '%s' "$last_text" | grep -qiE '交給(你|leo)|這是你的|是你的工作|你(去|來)(開|做|跑|查|執行)|請你去|leo (去|來)(開|做)|由你(去|來)|該你'; then
|
||
cat >&2 << 'EOF'
|
||
🚓 自走警察:偵測到「推卸給 leo」。停下前先答這三題:
|
||
1. 所有規劃工作做完了嗎?(查 autonomy-dispatch/tasks.md 派工表 + 北極星)
|
||
2. 這件事我能不能 spawn 一個 subagent 去做?(子 repo 寫 code 走 guard 白名單 CHILD_SESSION;偵察/文書/診斷都能 spawn)
|
||
3. 為什麼要停下來?——只有四題命中(花錢/不可逆/品味方向/物理人閘如 terminal consent、merge 批准)才輪到 leo。
|
||
「做 vs 問」是假二選一,真三選一:自己做 / spawn subagent 做 / 才輪到 leo。叫 leo 當人肉中繼=親手裝回剛拔掉的瓶頸。
|
||
若確認真四題命中才需 leo,下一回合明說「四題第X命中:<理由>」再停。
|
||
EOF
|
||
exit 2
|
||
fi
|
||
|
||
# ② 問「下一步做什麼」——答案在規劃文件裡
|
||
if printf '%s' "$last_text" | grep -qiE '下一步(要|該)?做什麼|接下來(要)?做什麼|接下來呢|要做什麼好|現在(要|該)做什麼'; then
|
||
cat >&2 << 'EOF'
|
||
🚓 自走警察:偵測到「問下一步做什麼」。答案多半在規劃文件裡,先查再說:
|
||
1. 查 SDD 了嗎?(system-dev/docs/3-specs/*/tasks.md — 唯一進度來源)
|
||
2. 查派工表了嗎?(system-dev/docs/3-specs/autonomy-dispatch/tasks.md — issue 開哪、誰做、狀態)
|
||
3. 查北極星了嗎?(system-dev/docs/6-user/20260627-northstar.md §8.4 順序)
|
||
查完下一條自然浮現,自己往前推,別把判斷推給 leo。若真查過且確認無下一條/撞人類閘,下一回合明說再停。
|
||
EOF
|
||
exit 2
|
||
fi
|
||
|
||
# ⓪ 🔴 請示句:「要我 X 嗎/我可以開始嗎/需要我 X 嗎」——**已交代過的事不必再請示**
|
||
# 2026-08-04 leo 兩度點破:「不要停下來問要不要繼續,**這是前面明說要做的事**」
|
||
# 「都列表了你還要問這個問題?」「**就是個自問自答功能,你問的問題明明有答案**」
|
||
# ⇒ 舊 ① 只認「要不要/該選哪個」,**漏掉最常見的請示句型「要我…嗎」**。
|
||
# 判準不是句型漂亮與否,而是:**這個問題的答案,是不是已經存在於既有資料裡?**
|
||
if printf '%s' "$last_text" | grep -qiE '要我(現在|先|繼續|開始)?[^??]{0,12}嗎|需要我[^??]{0,12}嗎|我(現在|可以|該)(開始|繼續|動手)[^??]{0,8}嗎|(繼續|開工|動手)嗎'; then
|
||
cat >&2 << 'EOF'
|
||
🚓 自走警察(請示句):你在問「要我 X 嗎」——**先自問自答:這個問題有答案嗎?**
|
||
|
||
【leo 2026-08-04】「不要停下來問要不要繼續,**這是前面明說要做的事**」
|
||
「都列表了你還要問這個問題?」
|
||
「**就是個自問自答功能,你問的問題明明有答案,應該就可以找到答案了**」
|
||
|
||
━━━ 送出前先在這三處找答案(找到就別問,直接做)━━━
|
||
A. **leo 這個 session 說過的話**——他是不是已經給了優先序/清單/「開工」?
|
||
(已交代過的事再請示=把他當按鈕按,正是自走規則要拔掉的瓶頸)
|
||
B. **你自己剛列的 todo/任務表**——下一項就是答案,照著做
|
||
C. **wiki/tasks.md**——`system-dev/wiki/status.md`、對應 tasks.md 有沒有定案
|
||
|
||
━━━ 只有這種情況才可以問(且要明說已查過)━━━
|
||
真命中四題人閘:花錢/不可逆(push main、刪資料、部署上線)/
|
||
跨專案結構決策/品味方向。**其餘一律自己裁、往前推,錯了被擋回再修。**
|
||
|
||
📌 想收尾時不要問「要我繼續嗎」,直接**做下一項並回報做了什麼**。
|
||
EOF
|
||
exit 2
|
||
fi
|
||
|
||
# ① 憑記憶問「要不要/該選哪個」——先查證再決定
|
||
if printf '%s' "$last_text" | grep -qiE '要不要|該不該|該選(哪|什麼)|哪個(比較)?好|選哪(個|條)|是不是要|可不可以'; then
|
||
# 但若這回合已有查證跡象(提到查了 wiki/官方/grep/WebFetch/出處),放行——別擾正常提問
|
||
if printf '%s' "$last_text" | grep -qiE '查(了|過)|官方(確認|核實|出處)|核實|grep|WebFetch|出處|wiki 寫|記憶(卡|寫)'; then
|
||
exit 0
|
||
fi
|
||
cat >&2 << 'EOF'
|
||
🚓 自走警察:偵測到「憑判斷問要不要/該選哪個」,但沒看到查證跡象。停下問 leo 前先窮盡:
|
||
1. 查 wiki/記憶了嗎?(system-dev/wiki/ + MEMORY.md — 這事可能已有定案,別重議)
|
||
2. 查 internet 官方說明了嗎?(LLM/平台/API 機制憑記憶會錯,查官方原文 + 給出處)
|
||
查完答案多半自己浮現,四題全否就自己裁、不問 leo。若查過仍需 leo 的品味/方向判斷(第④題),下一回合明說「查過,這是品味題」再問。
|
||
EOF
|
||
exit 2
|
||
fi
|
||
|
||
# 沒命中任何推卸句式 → 真的該停,放行
|
||
exit 0
|
||
|
||
# ── 升級路徑(不夠用時,leo 點頭)──────────────────────────────────
|
||
# 輕→重三級:
|
||
# L1(現在):command bash grep 句式三分流。最省,擋明顯推卸句。
|
||
# L2:改 type:prompt(Haiku 判語意),settings.json 寫 {"type":"prompt","prompt":"判斷這回合是否在推卸/未查證就停…"}。
|
||
# 擋「換句話說的推卸」,但每次停叫一次 Haiku(便宜)。
|
||
# L3:改 type:agent,spawn subagent 真查 wiki/SDD/派工表客觀驗「規劃是否做完」(像 Ralph 的 passes)。最強最貴。
|