Files
ISEP/hooks/delivery-police.sh
Leo c2638668e3 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>
2026-08-20 11:41:46 +08:00

136 lines
7.2 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# delivery-police.sh — 交付警察(Stop / SubagentStop hook
#
# 病根(leo 2026-07-22 原話):「它是我的助理,如果都是我檢查,那我就是它的助理。」
# 一天內三次都是 leo 先發現:①雲端沒查文件就問 ②OAuth 做好但前端還是舊按鈕、網址沒設 DNS
# ③連補規則也是 leo 講了才補。→ 角色顛倒=根本失敗。
#
# 為什麼清單沒用:CRITICAL-PATH 使用規則 4/5v2 覆蓋前為 6/7)寫了「要驗前端」,CLAUDE.md 規則二寫了四題公式,
# 照樣被違反——因為**自評的人跟做事的人是同一個**,任何品管制度都不成立。
# 解法同 self-drive-police:在「要標完成/要收工」那一刻攔截 + 反問。
#
# 機制:Claude 想停 → 掃這回合輸出有沒有「完成宣稱」→ 有、但沒看到實測證據 → exit 2 反問。
# 豁免:輸出已含實測證據(curl/dig 實際輸出、HTTP 碼、grep 到畫面字串、檔案:行號)→ 放行。
# 撞權限閘/明說四題命中 → 放行(不逼它做沒權限的事)。
#
# 判準(leo 2026-07-22):「友善=前端。人去按了連到錯的機制、安裝失敗,不算友善。」
# → 有前端的東西,驗收只認用戶真的會走的那條路。
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
last_text="$(python3 -c '
import sys, json
path = sys.argv[1]
texts = []
try:
with open(path) as f: lines = f.readlines()
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
# ── 豁免一:撞權限閘/明說四題命中 → 真的停不下來,放行 ──
if printf '%s' "$last_text" | grep -qiE '權限閘|classifier|分類器擋|四題第|人閘|需要你放行|denied by'; then
exit 0
fi
# ── 是否有「完成宣稱」? ──
if ! printf '%s' "$last_text" | grep -qiE '✅|完成了|已完成|通了|部署成功|已上線|已部署|搞定|做完了|全綠|收工'; then
exit 0
fi
# ── 豁免二:已有「夠格的」實測證據 → 放行 ──
#
# ⚠️ 本段自身踩過的雷(2026-07-22 實測 t2 抓到):
# 舊版寫 `HTTP [0-9]{3}` 就放行 → 「curl 回 HTTP 200,這關通了」直接過關。
# 那正是要防的假綠原句(landing 回 200 但按鈕 href 還是舊的)。
# **把要防的東西寫成通行證**=規則 6「HTTP 200 不算驗過」被自己的 hook 架空。
#
# 現在的判準:2xx/3xx 一律不算證據;只有下列才算——
# ① 抓到畫面內容(curl|grep / href= / <title>)② 壞消息碼(4xx/5xx,誠實回報不罰)
# ③ DNS 實查 ④ 測試數字 ⑤ 檔案:行號 ⑥ 明確標 ❌/◐(沒宣稱通就不必擋)
if printf '%s' "$last_text" | grep -qiE 'curl -s[^|]*\| *grep|href=|<title>|HTTP (4[0-9]{2}|5[0-9]{2})|dig \+short|[0-9]+/[0-9]+ (通過|綠|passed)|\.md:[0-9]+|❌ 斷|◐ 半通'; then
exit 0
fi
cat >&2 << 'EOF'
🚓 交付警察:你宣稱完成,但我沒看到「實測證據」。標 ✅ 之前先答:
【最高規範】leo 2026-08-17(他當場更正自己前一句的範圍):
「我剛剛講錯一件事,我說你推給我的版本要測過,我錯了——
**不是版本要測過,是任何東西你交的貨都要測過,一個 PR 也要測過,
你不允許交出你不知道能不能用的東西。**
**這不是對版本的規範,這是對你所有東西的規範。**」
⇒ 「交貨」=**任何交到別人手上的東西**:版本/PR 分支/票上的結論/
派工單裡寫的「現況」/回報的每一句。
⇒ **判準只有一句:交出去之前,我知不知道它能不能用?**
不知道 → 它還沒到可以交的狀態,不是「交了再說」。
⚠️ **「我沒有說它能用」不構成豁免**——接收方還是得自己驗,成本一樣轉嫁出去了。
🔴 **`report` 與 `deliver` 是兩個字**leo 2026-08-17 當場糾正總管的後門寫法):
「你可以說你有進度,**這是 report**;**但驗完那一格才能交,這是 deliver**。」
report 有進度、有發現、有卡點 ← 可以有沒驗的部分,標明即可
deliver 交到別人手上的東西 ← 每一格都驗過
⇒ **「有一格沒驗」不是交付的例外,它是「還沒到交付」的定義。**
那個狀態要說「我報告一下進度」,不是「我交給你但有一格沒驗」。
【判準】leo 2026-07-22:「它是我的助理,如果都是我檢查,那我就是它的助理。」
→ leo 如果現在去測,會不會發現你沒發現的東西?會 → 現在就去測,別交。
【判準】leo 2026-07-22:「友善=前端。人去按了連到錯的機制、安裝失敗,不算友善。」
→ 驗收只認**用戶真的會走的那條路**,不是你做的那個零件。
1. 有前端嗎?貼「抓實際畫面內容」的輸出:
curl -s <public 網址> | grep <該出現的字串>
⚠️ HTTP 200 不算驗過——200 只證明伺服器活著,不證明用戶看到對的東西。
(真實踩雷:landing 回 200,但按鈕 href 還是舊的 deploy.workers.cloudflare.com
2. 對外網址四件驗了嗎?
① custom domain 綁對專案?② DNS 解析得到?③ 前端 apiBase 對?④ CORS 放行?
(真實踩雷:install.arcrun.dev 有 DNS 但 HTTP 522、畫面全空=網域建了沒綁 worker)
3. 網址還是 *.workers.dev / *.pages.dev
→ 那叫「僅預設網域」,用戶拿不到,不准標 ✅,最多 ◐ 半通。
🔵 **例外:stage/測試環境不受此條約束**leo 2026-08-07
「Stage 直接用 workers.dev 不行?**反正只有你我用**」)。
本條的目的是「用戶拿不到=沒交付」——stage 的用戶就是 leo 與總管,
*.workers.dev 我們拿得到 ⇒ 前提不成立。
而且 stage 不掛 custom domain 反而是優點(不會有人誤入/被搜尋引擎索引/
把 stage 網址當正式的傳出去)。
⇒ **stage 的驗收標準=「leo 與總管能不能在上面測到真東西」,不是網域形式。**
4. 你自己找到並修掉了哪些問題?一項都沒有=多半沒認真找。
答不出來就是還沒完成——現在去測。規格見 CRITICAL-PATH.md 使用規則 4、5。
EOF
exit 2