Files
ISEP/hooks/mistake-needs-ticket-guard.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

109 lines
5.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.
#!/bin/sh
# mistake-needs-ticket-guard.sh — PreToolUseWrite|Edit|MultiEdit):
# **往 mistakes.md 寫一條新教訓,如果機制擋得住,就必須附票號。**
#
# 🔴 立這條的原話(leo 2026-08-16,一口氣講了三次,因為前兩次我又拿去寫成文件):
# 「以後你自己寫入 mistake 的東西,**如果是機制可以防止的都要貼上票號**。」
# 「要不然你的 mistake 根本**不會協助你迭代**。」
# 「**沒迭代寫個 mistake 做什麼?下一次又犯同樣錯誤,你就說上次寫過。**」
#
# 【當天的活教材,就是這條規則本身要治的病】
# `mistakes.md:220` 那條(08-14)結尾自己寫著:
# 「📌 **待修**:`check` 應該要有唯讀模式,否則這個坑對每一個第一次用它的人都會再發生一次。」
# 它**沒有票號** ⇒ 沒有任何人/任何看板會撈到它 ⇒ 躺了兩天 ⇒
# 08-16 總管**原封不動照撞一次**,還在回覆裡說「這個我兩天前寫過」。
# ⇒ 那句「我寫過」正是 leo 預言的那句話。**寫下來不是迭代,開票才是。**
#
# 【判準:什麼叫「機制可以防止」】
# 不是所有教訓都要票。要票的是**下一次可以被機器擋下來**的那種:
# · 用錯工具/用錯順序/漏做一步 → 可以做成 hook 或 preflight
# · 兩份真相漂移/欄位漏填 → 可以做成不變式
# · 「我以為 X 其實 Y」的事實誤解 → 通常**不需要**票(那是知識,不是閘)
# 本閘不猜,用一個便宜的近似:**新增的段落裡出現「待修/應該要/下次要/
# 要改成/可以做成」這類「有人得動手」的字樣,就要求同段落有票號。**
#
# 【放行的寫法】同一段裡出現任一種票號定址即可:
# inkstone/arcrun-rag#88 Leo/Arcrun#136 本 repo 的 #142
# 真的不需要票(純知識型教訓):該段加 `no-ticket-needed` 一詞(留痕,說明理由)。
set -eu
PAYLOAD=$(cat 2>/dev/null || echo '{}')
RESULT=$(printf '%s' "$PAYLOAD" | python3 -c '
import sys, json, re
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
ti = d.get("tool_input") or {}
path = ti.get("file_path") or ""
if not path.endswith("mistakes.md"):
sys.exit(0)
# 收集這次「新寫進去」的文字(Write 全文;Edit 只看 new_string
chunks = []
if "content" in ti:
chunks.append(ti["content"])
if "new_string" in ti:
chunks.append(ti["new_string"])
for e in (ti.get("edits") or []):
if isinstance(e, dict) and "new_string" in e:
chunks.append(e["new_string"])
text = "\n".join(c for c in chunks if isinstance(c, str))
if not text.strip():
sys.exit(0)
# 「有人得動手」的訊號——出現這些就代表它是可被機制防止的
ACTION = re.compile(r"待修|應該要|下次要|要改成|可以做成|該做成|還沒做|尚未修|TODO|待辦")
# 票號定址:owner/repo#N 或裸 #N
TICKET = re.compile(r"(?:[A-Za-z0-9._-]+/[A-Za-z0-9._-]+)?#\d+")
# 逐「段」判斷(空行分段),只報第一個有問題的段落
for para in re.split(r"\n\s*\n", text):
if not ACTION.search(para):
continue
if "no-ticket-needed" in para:
continue
if TICKET.search(para):
continue
hit = ACTION.search(para).group(0)
snippet = " ".join(para.split())[:120]
print(json.dumps({"hit": hit, "snippet": snippet}, ensure_ascii=False))
break
' 2>/dev/null || true)
[ -n "$RESULT" ] || exit 0
HIT=$(printf '%s' "$RESULT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["hit"])' 2>/dev/null || echo "待修")
SNIP=$(printf '%s' "$RESULT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["snippet"])' 2>/dev/null || echo "")
cat >&2 <<EOF
🎫 這條教訓是「機制擋得住」的,但沒有票號——**沒有票,它不會被迭代**
觸發字樣:「$HIT」
那一段: $SNIP
【leo 2026-08-16,一口氣講了三次】
「以後你自己寫入 mistake 的東西,**如果是機制可以防止的都要貼上票號**。」
「要不然你的 mistake 根本**不會協助你迭代**。」
「**沒迭代寫個 mistake 做什麼?下一次又犯同樣錯誤,你就說上次寫過。**」
【當天的活教材(就是這條規則的來由)】
\`mistakes.md\` 那條 08-14 的結尾自己寫著「📌 待修:check 應該要有唯讀模式」,
**沒有票號** ⇒ 沒有看板撈得到 ⇒ 躺了兩天 ⇒ 08-16 總管原封不動照撞一次,
然後在回覆裡說「這個我兩天前寫過」。**那句話正是 leo 預言的那一句。**
【現在怎麼做(擇一)】
① **先開票,再寫教訓**(正解,順序不能反):
scripts/ticket where <關鍵字> # 先查這件事該掛哪張既有票
scripts/ticket say <owner/repo#N> -F <檔> # 有既有票就貼進去
scripts/ticket new <repo> -F <檔> # 真的沒有才開新票
然後把票號寫進這一段:\`inkstone/arcrun-rag#88\`
② **這條純粹是知識型教訓、沒有東西要動手**(例如「我以為 X 其實 Y」):
該段加一詞 \`no-ticket-needed\`,並在旁邊寫一句為什麼不需要閘
EOF
exit 2