#!/usr/bin/env bash # isep-presence-beacon.sh — SessionStart:報出「ISEP 真的載入了,幾版、幾支閘」 # # 這不是閘,是**信標**。存在的理由是 2026-08-20 的雲端事故: # 雲端 session 的閘全滅,而三個驗證步驟全部回綠——因為它們沒有鑑別力 # (`git tag` 在三支閘的白名單裡;`Skills(9)/Hooks(5)` 剛好是薄殼自己的 .claude/ 產生的數字)。 # # 🔴 鑑別力就是這支的全部意義: # 這行出現 ⇒ plugin 一定載入了(因為它自己就住在 plugin 裡) # 這行不見 ⇒ plugin 沒載入,那個 session 是零閘狀態 # ——沒有第三種情況,也沒有「剛好也會過」的巧合。 set -uo pipefail ROOT="${CLAUDE_PLUGIN_ROOT:-}" [ -n "$ROOT" ] || exit 0 VER="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \ "$ROOT/.claude-plugin/plugin.json" 2>/dev/null | head -1)" VER="${VER:-未知}" # 🔴 數「真的被註冊的」,不是數目錄裡有幾個 .sh(leo 的雲端驗收 2026-08-23 抓到): # 舊寫法 `ls hooks/*.sh` 把 `pre-write-guard.template.sh`(樣板,不是閘) # 與兩支沒掛註冊的輔助檔一起算進去 ⇒ 報 45,實際註冊 42。 # 這個數字是 leo 判斷「這個 session 有沒有閘」的唯一介面——**多報就是假綠**。 # (查過歷史:本檔自 daa1674 建立以來只有那一版,沒有別的分支修過這段。) GATES="$(grep -oE 'hooks/[a-zA-Z0-9._-]+\.sh' "$ROOT/hooks/hooks.json" 2>/dev/null \ | sort -u | wc -l | tr -d ' ')" case "$GATES" in ''|*[!0-9]*|0) GATES="$(ls "$ROOT"/hooks/*.sh 2>/dev/null | grep -cv '\.template\.sh$' | tr -d ' ')" ;; esac # 🔴 講清楚「這一份是誰」(leo 2026-08-23 的雲端驗收卡在這裡整整一輪): # 同一台機器上 ISEP 可能有兩份——marketplace 裝的 plugin 快取, # 與 repo 裡 vendor 進去的複製本。**兩份都會印這一行**,而版本號一樣時分不出誰在說話。 # 「閘到底從哪一份走」正是決定「另一份能不能拆」的唯一判準 # ⇒ 讓路徑自己講:快取在 plugins/cache/ 底下,vendor 的在 repo 的 .claude/ 底下。 # (查過歷史:本檔只有 daa1674 建立、b1f399f 改閘數兩輪,沒有人試過標示來源,也沒有被否決的方案。 # KBDB 語意搜尋這段開發史:0 命中,該餵。) case "$ROOT" in */plugins/cache/*) SRC="plugin(marketplace 裝的)" ;; */.claude/isep*) SRC="vendor(repo 裡的複製本)" ;; *) SRC="來源不明" ;; esac MSG="🟢 ISEP v${VER} 已載入(${GATES} 支閘|來源:${SRC}|${ROOT})" printf '%s\n' "{ \"systemMessage\": \"${MSG}\", \"hookSpecificOutput\": { \"hookEventName\": \"SessionStart\", \"additionalContext\": \"${MSG}。這行是 ISEP plugin 自己發的——看得到它就表示閘真的生效了。若某個 session 從頭到尾沒有這行,那個 session 是零閘狀態,先修 plugin 再做事,不要用『跑得動』當證據。\" } }"