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
+182
View File
@@ -0,0 +1,182 @@
#!/bin/bash
# wiki-first-search.sh — PreToolUse hook:要去翻原文/程式碼前,先把 wiki 命中結果推到眼前
#
# 病根(2026-07-20 leo 點破,mistakes 第一鐵律):
# 總管 session 開頭讀了 agent-memory 前 50 行就開工,關鍵那條在第 56 行 → 拿過期記憶擋了 leo 三輪。
# leo:「如果你不是讀而是**搜尋** wiki 就不會只讀 50 行就下定論,
# 而是就像我直接在頁面 cmd+F,那些都會高亮。」
#
# 設計要點(為什麼是這個形狀):
# 1. **搜尋 ≠ 通讀**:開場 push 全文(session-start-recall.sh)解決不了這題——量大必然只讀開頭。
# 這支反過來:在「你正要去查 code/原文」的當下,用你自己的關鍵字 grep wiki,只推命中行。
# 2. **時機是關鍵**:不是開場推、不是寫入時擋,而是**查詢動作發生的那一刻**介入。
# 3. 🔴 **硬擋,不是提醒**leo 2026-08-05 改):
# 「改成首先查 wiki,如果 wiki 沒有則放行其他搜尋,要擋」
# 原版全程 exit 0(只印字),實測擋不住——我照樣一路 curl/grep code
# 連 wiki 都沒查就下結論,還把 401 誤判給不相干的 commit。
# 現在:**本輪沒查過 wiki ⇒ exit 2 擋下**;查過(不論有無命中)⇒ 放行。
# 放行條件刻意寬鬆——目的是「逼你先看一眼 wiki」,不是攔住你查原文。
#
# 觸發:Grep / Glob / Read 打向 code 或 docs 時(見下方 should_check)。
# 輸出:stdout 注入 context(命中的 wiki 行 + 檔名:行號)。
set -euo pipefail
INPUT=$(cat)
TOOL=$(printf '%s' "$INPUT" | python3 -c "import json,sys;print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null || echo "")
# 取出這次查詢的關鍵字:Grep 用 patternGlob/Read 用路徑的檔名部分
QUERY=$(printf '%s' "$INPUT" | python3 -c "
import json,sys,os,re
try:
d=json.load(sys.stdin); ti=d.get('tool_input',{})
q = ti.get('pattern') or ''
if not q:
p = ti.get('file_path') or ti.get('path') or ''
q = os.path.splitext(os.path.basename(p))[0] if p else ''
if not q:
# Bash2026-07-21 補的破口——原版只掛 Grep|Glob|Read
# 但「用 curl/wrangler 亂試部署方法」走的是 Bash,整支 hook 不觸發。
# leo 當場點破:wiki 早記著「寄信已驗證可用」,我卻沒查又自創方法。
# 只認「會動到外部系統/部署」的高風險指令,避免每個 ls 都洗版。
cmd = ti.get('command') or ''
# 2026-08-05 再補一個破口(leo 當場點破:「你應該做任何查詢先去查 KBDB,
# 但剛剛這一輪你都沒有查,但 hooks 沒攔阻?」):
# 原本只認「動外部系統」的動詞,但**查證類**指令(git log / grep / unzip /
# 讀 manifest)完全不觸發 ⇒ 我查「release 版本號怎麼算」整輪沒查過 KBDB/wiki。
# ⇒ 補上查證類動詞。判準:**會讓我形成結論的指令**都該先問記憶,
# 不是只有「會改壞東西的指令」。
# 2026-08-05 第三次補(leo:「我看你剛剛 bash 裡很多 grep,為什麼不是查 wiki?」):
# 再補「讀檔形成結論」的動詞——head/cat/sed/tail/awk/find/jq。
# 之前只認「動外部系統」與部分查證動詞,`head changelog.md` 這種
# **直接讀檔下判斷**的完全不觸發。
if re.search(r'\b(wrangler|curl|npx|acr|gh|deploy|push|git|grep|unzip|manifest|version'
r'|head|cat|sed|tail|awk|find|jq)\b', cmd):
# 取指令中最具識別度的詞(worker 名/資源名/子命令)當搜尋詞
cand = re.findall(r'[A-Za-z_][A-Za-z0-9_-]{4,}', cmd)
skip = {'https','http','client','accounts','workers','scripts',
'application','content','Authorization','Bearer','python3',
'curl','npx','bash','echo','grep','local','branch','origin'}
cand = [c for c in cand if c not in skip and not c.startswith('-')]
q = max(cand, key=len) if cand else ''
# grep pattern 常含 regex 元字元;取最長的英數/底線詞當搜尋詞
# 🔴 2026-08-05 leo 點破:「最常做的就是 grep,為什麼沒在裡面」
# ——Grep 其實有註冊,壞在**這行取詞規則**:
# ① 連字號被當分隔 ⇒ `bge-m3` 只取到 `bge`(3 字)不足 4 字 ⇒ 整支不觸發
# ② **中文完全不匹配** ⇒ 查「版本號」「出貨」這類詞一律不觸發
# 而我日常查的關鍵字大量正是這兩類 ⇒ hook 形同虛設。
# ⇒ 容許 `-`/`.`,並支援 CJK;中文 2 字即算一個詞。
words = re.findall(r'[A-Za-z_][A-Za-z0-9_.-]{2,}', q)
cjk = re.findall(r'[\u4e00-\u9fff]{2,}', q)
words = words + cjk
print(max(words, key=len) if words else '')
except Exception:
print('')
" 2>/dev/null || echo "")
[ -z "$QUERY" ] && exit 0
WIKI_DIR="system-dev/wiki"
[ -d "$WIKI_DIR" ] || exit 0
# 只在「查程式碼/文件」時提醒;查 wiki 本身就不用了(已經在讀了)
TARGET=$(printf '%s' "$INPUT" | python3 -c "
import json,sys
try:
d=json.load(sys.stdin); ti=d.get('tool_input',{})
print(ti.get('file_path') or ti.get('path') or '')
except Exception: print('')
" 2>/dev/null || echo "")
# ── wiki-first 閘門(leo 2026-08-05)────────────────────────────
# 記號檔:本輪(本 session)是否已經查過 wiki。放 /tmp 依 session 隔離,
# 新 session 自動重置——每輪工作都要重新先看一眼 wiki。
STAMP="/tmp/.wiki-first-$(id -u)-${CLAUDE_SESSION_ID:-nosession}"
# 這次動作**本身就是在查 wiki** → 蓋章放行(之後才允許查別的)
case "$TARGET" in
*system-dev/wiki*) : > "$STAMP"; exit 0 ;;
esac
# Bash 指令裡直接 grep/read wiki 的也算(例:grep -rn xxx system-dev/wiki/
if printf '%s' "$INPUT" | grep -q "system-dev/wiki"; then
: > "$STAMP"; exit 0
fi
# 已經查過 wiki → 放行,後面照舊只做提醒
if [ ! -f "$STAMP" ]; then
# 🔴 還沒查過 wiki 就想翻原文/打外部系統 → 擋(exit 2)
PRE_HITS=$(grep -rin --include="*.md" -- "$QUERY" "$WIKI_DIR" 2>/dev/null | head -8 || true)
{
echo "════════════════════════════════════════════════"
echo "⛔ 先查 wiki,才准查別的(leo 2026-08-05 立)"
echo "════════════════════════════════════════════════"
printf '你正要查「%s」,但這一輪還沒查過 wiki。\n\n' "$QUERY"
# 🔴 成本順序(leo 2026-08-05):「**成本最低的查就是 KBDB**,整了所有 repo 的 wiki
# 查詢應該效果最好最簡單,但你會去先查成本高的,甚至查不到後去查源碼,成本很高。」
# ⇒ 一律 **KBDB 先**(跨全部 repo、語意搜尋、一次呼叫),
# 再 grep 本 repo wiki(只認字面、只有這個 repo),最後才翻源碼。
echo "查詢成本由低到高——**照順序來,別跳過前面直接翻源碼**:"
echo ""
echo " 1️⃣ KBDB**最便宜也最強**:整合**所有 repo** 的 wiki,且有三種檢索)"
echo " kbdb_search(q=\"…\", mode=\"semantic\") ← 語意:問句、想不到精確字眼時"
echo " kbdb_search(q=\"…\", mode=\"keyword\") ← 關鍵字:確定有某個詞"
echo " kbdb_graph_neighbors(...) ← 關係:沿「A >> 關係 >> B」展開"
echo " kbdb_get_map() ← 不知道該查哪個庫時先看藏書地圖"
echo ""
echo " 2️⃣ 本 repo wiki**只有 grep 字面、只有這一個 repo** ⇒ 比 KBDB 弱很多)"
if [ -n "$PRE_HITS" ]; then
echo " ⚠️ **這裡已經有命中了**,先讀這幾行:"
printf '%s\n' "$PRE_HITS" | sed 's|^system-dev/wiki/| |'
else
echo " Grep/Read system-dev/wiki/ 用你自己的關鍵字"
echo " (本閘 grep 沒命中,但它只認字面、搜尋詞還是從你指令猜的)"
fi
echo ""
echo " 3️⃣ 源碼/原文(**最貴**:要讀很多、容易讀到已作廢的實作)"
echo ""
echo "wiki 沒有答案 → 那時再翻原文,本閘就會放行(查過 wiki 即解鎖本輪)。"
} >&2
exit 2
fi
# grep wiki(不分大小寫、含行號),最多 12 行避免洗版
HITS=$(grep -rin --include="*.md" -- "$QUERY" "$WIKI_DIR" 2>/dev/null | head -12 || true)
# 🔴 grep 零命中時**不能靜默退出**——那正是今天失敗的模式(2026-07-21):
# grep 查不到 → 以為 wiki 沒記載 → 結論「這件事沒查過」。
# 但 grep 只認字面,查不到往往只代表「沒猜中用詞」。
# → 零命中反而是**最該改用語意搜尋**的時刻,必須出聲。
if [ -z "$HITS" ]; then
echo "════════════════════════════════════════════════"
printf '🔍 grep 在 wiki 找不到「%s」——但這**不代表沒記載**\n' "$QUERY"
echo "════════════════════════════════════════════════"
echo "grep 只認字面,查不到通常只是「沒猜中用詞」。**改用語意搜尋再確認一次**:"
echo " kbdb_search(q=\"<用一句話描述你要找什麼>\", mode=\"semantic\")"
echo " 不知道該查哪個庫 → kbdb_get_map()|要沿關係展開 → kbdb_graph_neighbors()"
echo ""
echo "實例:查「CF 上的 git 託管」時 grep 全零命中,語意搜尋第一筆就命中"
echo "Cloudflare Artifacts >> 若提供 git 倉庫則可取代 >> Gitea,負責人 15 天前就記了)。"
echo ""
exit 0
fi
COUNT=$(printf '%s\n' "$HITS" | wc -l | tr -d ' ')
echo "════════════════════════════════════════════════"
printf '📚 wiki 已有「%s」的記載(%s 處,先看這裡再翻原文)\n' "$QUERY" "$COUNT"
echo "════════════════════════════════════════════════"
printf '%s\n' "$HITS" | sed 's|^system-dev/wiki/| |'
echo ""
echo "⚠️ wiki 是判準,程式碼與歷史文件只是稿子(mistakes 第一鐵律)。"
echo " • 上面若與你將要查的原文衝突 → **以 wiki 為準**,別用 code 推翻 wiki。"
echo " • 看到「不可動/待廢除/進行中」→ 先讀它的**解除條件**並逐條核對,"
echo " 那是當時狀態不是永久禁令;條件已滿足就是可動。"
echo " • wiki 沒答案才值得翻原文——翻完若得到新結論,**回頭更新 wiki**。"
echo ""
echo "🔎 以上是 **grep(最弱的查法)** 的結果,只認字面,且搜尋詞是從你的指令**猜**出來的"
echo " (很可能太籠統而命中一堆無關的,同時漏掉真正的主題詞)。"
echo " **重要判斷一律補一次語意搜尋**——它不需要你猜對用詞:"
echo " kbdb_search(q=\"<一句話描述你要找什麼>\", mode=\"semantic\")"
echo " 實例:查「CF 的 git 託管」時 grep 猜到的詞是 cloudflare → 命中 12 處全無關、"
echo " 真正的答案(Artifacts)一筆沒撈到;語意搜尋第一筆就命中。"
echo ""
exit 0