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>
153 lines
9.4 KiB
Bash
Executable File
153 lines
9.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# SessionStart hook — 開 session 就把「全局」推到眼前(InkStoneCo#17 / D69 / D70 / D71)
|
||
#
|
||
# 掛在 settings.json 的 SessionStart(matcher: startup|resume|clear)。stdout 會被當成 context 注入。
|
||
#
|
||
# ── 這支在整個機制裡的位置(leo 2026-08-11 拍板)─────────────────────────────
|
||
# 算 = 雲端的 Arcrun 工作流 `global_index`(讀 Gitea 五個 repo 的票 + 讀 KBDB 藏書地圖)
|
||
# 存 = 雲端(總圖住雲端,不推回地端;沒有東西需要同步,所以沒有漂移源)
|
||
# 拿 = 本檔。**由機制保證取一次**,不靠 AI 記得去打 MCP
|
||
# (openhuman SuperContext 的教訓:靠模型自己選擇要不要查,就是會漏)
|
||
#
|
||
# ⚠️ 例外=下面 push 2/5 的「各 repo wiki 全景」:那段**算在地端**。
|
||
# 不是不想放雲端,是雲端拿不到 repo 樹(只有 API 列檔一條路,撞 principles 紅線)。
|
||
#
|
||
# ── 為什麼不再 cat status.md ────────────────────────────────────────────────
|
||
# 2026-08-11 實測:舊版這支吐 235,880 bytes(因為它 cat 整份 216 KB 的落帳流水),
|
||
# 真正進到 context 的只有 2 KB 預覽 + 一個要自己去 Read 的檔名。
|
||
# ⇒ 「開場就知道」等於沒發生。**注入通道的預算是有限的,落帳流水不該佔著它。**
|
||
# 進度/狀態一律去 Gitea(D52 + D72),不在地端養第二份會漂的快照。
|
||
#
|
||
# 順序=重要性:全局總圖最前面(它最可能被截斷保護到),其次原則,最後踩坑標題。
|
||
|
||
set -euo pipefail
|
||
|
||
GLOBAL_INDEX_URL="https://arcrun-cypher-executor.leo21c.workers.dev/webhooks/named/leo/global_index/query"
|
||
PANORAMA_FILE="system-dev/wiki/PANORAMA.md"
|
||
PRINCIPLES_FILE="system-dev/wiki/principles.md"
|
||
MISTAKES_FILE="system-dev/wiki/mistakes.md"
|
||
MISTAKES_TAIL=20 # 只推最近 N 條標題(全份 96 條/11 KB 會吃掉總圖的預算)
|
||
|
||
# ── push 1/5:全局總圖(雲端現算)────────────────────────────────────────────
|
||
# 降級規格(D71,票與知識不對稱):
|
||
# 算得出來 → 注入
|
||
# 算不出來/破損/空 → **完全不注入內容**,只誠實說一句「這次沒有」
|
||
# (寧可冷啟動,不要注入垃圾;票尤其不能給一份看起來像現況的舊東西)
|
||
GLOBAL_MD=""
|
||
GLOBAL_ERR=""
|
||
if RESP=$(curl -sS -m 45 -X POST "$GLOBAL_INDEX_URL" \
|
||
-H 'Content-Type: application/json' -d '{}' 2>/dev/null); then
|
||
GLOBAL_MD=$(printf '%s' "$RESP" | python3 -c '
|
||
import sys, json
|
||
try:
|
||
d = json.load(sys.stdin)
|
||
except Exception:
|
||
sys.exit(0)
|
||
def find_md(x, depth=0):
|
||
# 工作流的最終輸出可能被包一層 data(code 零件的回應信封),兩種形狀都收
|
||
if depth > 3 or not isinstance(x, dict):
|
||
return None
|
||
if x.get("success") is True and isinstance(x.get("md"), str) and x["md"].strip():
|
||
return x["md"]
|
||
return find_md(x.get("data"), depth + 1)
|
||
md = find_md(d)
|
||
if md:
|
||
sys.stdout.write(md)
|
||
' 2>/dev/null || true)
|
||
if [ -z "$GLOBAL_MD" ]; then
|
||
GLOBAL_ERR=$(printf '%s' "$RESP" | python3 -c '
|
||
import sys, json
|
||
try:
|
||
d = json.load(sys.stdin)
|
||
except Exception:
|
||
sys.stdout.write("回應不是 JSON"); sys.exit(0)
|
||
sys.stdout.write(str((d or {}).get("error") or "工作流沒有交出 md")[:200])
|
||
' 2>/dev/null || echo "回應解析失敗")
|
||
fi
|
||
else
|
||
GLOBAL_ERR="連不上雲端(curl 失敗或逾時 45 秒)"
|
||
fi
|
||
|
||
echo "════════════════════════════════════════════════"
|
||
if [ -n "$GLOBAL_MD" ]; then
|
||
printf '%s\n' "$GLOBAL_MD"
|
||
else
|
||
echo "⚠️ 這次拿不到全局總圖:${GLOBAL_ERR}"
|
||
echo " 刻意不給你一份舊的——票的狀態放舊的比沒有更危險(會去重做已經做完的事)。"
|
||
echo " ⇒ 你現在**不知道有哪些票、庫裡有哪些知識**。動手前先自己撈:"
|
||
echo " Gitea:GET https://git.uncle6.me/api/v1/repos/Leo/<repo>/issues?state=open"
|
||
echo " (五個 repo:InkStoneCo / Arcrun / arcrun-rag / mira / system-dev-template)"
|
||
echo " 知識:kbdb_get_map()"
|
||
echo " 修這條管路:工作流 leo:wf:global_index(雲端),本 hook 只負責取來印。"
|
||
fi
|
||
echo "════════════════════════════════════════════════"
|
||
echo ""
|
||
|
||
# ── push 2/5:各 repo 的 wiki 全景(leo 2026-08-12「一次看到全景」的另一半)──
|
||
# 上面那段是**票**(雲端現算)+ KBDB 藏書地圖;這段是**各 repo 真正的 wiki**。
|
||
# 為什麼不塞進雲端那支工作流:工作流跑在 Cloudflare 上,要拿 repo 樹只有
|
||
# 「Gitea contents API 列檔」一條路,而 principles 紅線寫死
|
||
# 「讀 repo 走 git clone/fetch,不走 API 列檔」。
|
||
# ⇒ 在有 git 的地方算好(`scripts/wiki-panorama.sh`,人發起、不輪詢),
|
||
# 存成 repo 裡的一份 md,本檔只負責印。
|
||
# 只印到 `panorama:inject-end` 為止——標記以下是給 grep 的完整卡片清單,
|
||
# 全倒進來會撐爆 context(全檔 ~28 KB,注入段 ~6 KB)。
|
||
if [ -f "$PANORAMA_FILE" ]; then
|
||
echo "════════════════════════════════════════════════"
|
||
awk '/panorama:inject-end/{exit} {print}' "$PANORAMA_FILE"
|
||
# 過期就講出來(不自動重算:重算要連網 clone 十幾個 repo,不該卡在開場)
|
||
P_GEN=$(grep -m1 -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} 產生' "$PANORAMA_FILE" | cut -d' ' -f1 || true)
|
||
W_LAST=$(git log -1 --format=%ad --date=short -- system-dev/wiki 2>/dev/null || true)
|
||
if [ -n "$P_GEN" ] && [ -n "$W_LAST" ] && [ "$W_LAST" \> "$P_GEN" ]; then
|
||
echo ""
|
||
echo "⚠️ 這張全景圖是 ${P_GEN} 算的,但 wiki 在 ${W_LAST} 又被改過 ⇒ 它已經不準了。"
|
||
echo " 重算:bash scripts/wiki-panorama.sh --write"
|
||
fi
|
||
echo "════════════════════════════════════════════════"
|
||
echo ""
|
||
else
|
||
echo "────────────────────────────────────────────────"
|
||
echo "⚠️ 沒有各 repo 的 wiki 全景圖($PANORAMA_FILE)⇒ 你**不知道哪些知識記在哪個 repo**。"
|
||
echo " 產生(約一分鐘,git clone 各 repo 的 wiki 目錄,不走 API):"
|
||
echo " bash scripts/wiki-panorama.sh --write"
|
||
echo ""
|
||
fi
|
||
|
||
# ── push 3/5:進度不在地端(D72)────────────────────────────────────────────
|
||
echo "────────────────────────────────────────────────"
|
||
echo "📍 進度/狀態一律去 Gitea 撈,不要在任何檔案裡找(D52 + D72)"
|
||
echo " 等 leo = labels=s/stage | 可開工 = s/todo | 進行中 = s/doing"
|
||
echo " 知識庫(system-dev/wiki/)只放**事實**:踩過的坑、決策、憑證在哪。**不放進度**。"
|
||
echo " 🔴 不要在對話或任何檔案裡另養一份「等 leo 的清單」——它一定會漂。"
|
||
echo ""
|
||
|
||
# ── push 4/5:principles(全文,行動前必服從)────────────────────────────────
|
||
if [ -f "$PRINCIPLES_FILE" ] && grep -q '^- ' "$PRINCIPLES_FILE" 2>/dev/null; then
|
||
echo "════════════════════════════════════════════════"
|
||
echo "📐 設計原則(行動前必服從,來自 principles.md)"
|
||
echo "════════════════════════════════════════════════"
|
||
grep '^- ' "$PRINCIPLES_FILE"
|
||
P_COUNT=$(grep -c '^- ' "$PRINCIPLES_FILE")
|
||
if [ "$P_COUNT" -gt 15 ]; then
|
||
echo ""
|
||
echo "(⚠️ principles 已 ${P_COUNT} 條 > 15:請考慮合併相近原則、或把較細的下放成 card)"
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
# ── push 5/5:mistakes(只推最近 N 條標題)──────────────────────────────────
|
||
if [ -f "$MISTAKES_FILE" ] && grep -q 'MISTAKE' "$MISTAKES_FILE" 2>/dev/null; then
|
||
M_TOTAL=$(grep -cE 'MISTAKE' "$MISTAKES_FILE")
|
||
echo "────────────────────────────────────────────────"
|
||
echo "⚠️ 已知踩坑:最近 ${MISTAKES_TAIL} 條(全部 ${M_TOTAL} 條在 $MISTAKES_FILE,撞到就 grep 全文)"
|
||
grep -E 'MISTAKE' "$MISTAKES_FILE" | tail -n "$MISTAKES_TAIL" | sed 's/^/ /'
|
||
echo ""
|
||
fi
|
||
|
||
echo "────────────────────────────────────────────────"
|
||
echo "以上是 index,不是內容——它只讓你知道「有這件事、去哪裡找」。"
|
||
echo "要完整脈絡(decisions / mistakes / SDD)→ 執行 /wiki-recall"
|
||
echo "════════════════════════════════════════════════"
|
||
|
||
exit 0
|