#!/usr/bin/env sh # plugins-root.sh — 推導 jsc 技能組工作目錄的根,meta/tools 六支腳本共用同一套規則。 # # 用法: # . "$(dirname -- "$0")/plugins-root.sh" # 以 source 載入,取得 jsc_plugins_root() # ROOT=$(jsc_plugins_root) || exit {該腳本的碼} # sh plugins-root.sh # 直接執行,把推導結果印到 stdout # # 為什麼要抽出來: # 舊寫法一律取「本腳本位置的上兩層」。技能組以 plugin 形式安裝時,腳本落在 # ~/.claude/plugins/cache/jsc/jsc-meta/{版本}/tools/,上兩層是 .../cache/jsc/jsc-meta, # 那是 plugin 快取,不是放各 domain 存取庫的工作目錄,**一定推錯**。 # 2026-08 例行稽核第一步就實際踩到:不手動設 JSC_PLUGINS_ROOT 就跑不動, # 而呼叫這些腳本的 SKILL.md 都沒提要設。 # # 推導順序(取第一個帶得出 gitea.sh 的候選): # 1. JSC_PLUGINS_ROOT # 2. 從 $PWD 逐層往上,找含 meta/.claude-plugin/marketplace.json # (或 jsc-meta/.claude-plugin/marketplace.json)的目錄 # 3. 本腳本位置的上兩層({root}/meta/tools -> {root}) # 4. $HOME/plugins # # 判準: 候選目錄下要有 gitea/tools/gitea.sh 或 jsc-gitea/tools/gitea.sh。 # marketplace.json 每個 domain 存取庫都帶一份,單看它會把某個 domain 存取庫自己 # 誤判成根;gitea.sh 只存在於 jsc-gitea 存取庫裡,拿它當標記才分得出根與 domain。 # # 逃生門: 候選都不帶 gitea.sh,但 JSC_PLUGINS_ROOT 有設且是目錄時,照設定值使用, # 並在 stderr 提醒。只 clone plugins/meta 的環境(CI、單存取庫維護)本來就沒有 # jsc-gitea;這時硬擋會讓「請設 JSC_PLUGINS_ROOT」變成解不開的死路。 # # 結束碼(直接執行時): 0=推導成功,根目錄印在 stdout # 1=推導不出來,訊息會指名要設 JSC_PLUGINS_ROOT 並列出試過的候選 # 環境變數: JSC_PLUGINS_ROOT(根目錄,最優先) set -u _jsc_walk_up() { # 從 $PWD 逐層往上,印出第一個含 meta 存取庫的目錄 _jpr_d=$(pwd -P 2>/dev/null) || return 1 while [ -n "$_jpr_d" ]; do if [ -f "$_jpr_d/meta/.claude-plugin/marketplace.json" ] || [ -f "$_jpr_d/jsc-meta/.claude-plugin/marketplace.json" ]; then printf '%s\n' "$_jpr_d" return 0 fi [ "$_jpr_d" = / ] && break _jpr_d=$(dirname -- "$_jpr_d") done return 1 } _jsc_has_gitea() { # $1=候選根目錄;jsc-gitea 存取庫是「這是根」的唯一可靠標記 [ -f "$1/gitea/tools/gitea.sh" ] || [ -f "$1/jsc-gitea/tools/gitea.sh" ] } jsc_plugins_root() { # 印出根目錄(stdout);推不出來回 1,說明走 stderr _jpr_tried='' for _jpr_src in env pwd here home; do case "$_jpr_src" in env) _jpr_c="${JSC_PLUGINS_ROOT:-}" ;; pwd) _jpr_c=$(_jsc_walk_up 2>/dev/null) || _jpr_c='' ;; here) _jpr_c=$(CDPATH= cd -- "$(dirname -- "$0")/../.." 2>/dev/null && pwd) || _jpr_c='' ;; home) if [ -n "${HOME:-}" ]; then _jpr_c="$HOME/plugins"; else _jpr_c=''; fi ;; *) _jpr_c='' ;; esac [ -n "$_jpr_c" ] || continue _jpr_tried="$_jpr_tried $_jpr_src: $_jpr_c " [ -d "$_jpr_c" ] || continue _jsc_has_gitea "$_jpr_c" || continue _jpr_c=$(CDPATH= cd -- "$_jpr_c" && pwd) || continue printf '%s\n' "${_jpr_c%/}" return 0 done if [ -n "${JSC_PLUGINS_ROOT:-}" ] && [ -d "$JSC_PLUGINS_ROOT" ]; then echo "JSC_PLUGINS_ROOT 底下找不到 gitea/tools/gitea.sh,仍照設定值使用:$JSC_PLUGINS_ROOT" >&2 _jpr_c=$(CDPATH= cd -- "$JSC_PLUGINS_ROOT" && pwd) || return 1 printf '%s\n' "${_jpr_c%/}" return 0 fi { echo '推導不出 jsc 技能組的根目錄。' echo '以 plugin 形式安裝時,腳本位在 ~/.claude/plugins/cache/jsc/jsc-meta/{版本}/tools/,' echo '上兩層落在 plugin 快取目錄,不是放各 domain 存取庫的工作目錄,所以推不出來。' echo '請設 JSC_PLUGINS_ROOT 指向放各 domain 存取庫的工作目錄(底下要有 gitea 或 jsc-gitea)。' echo '已試過的候選:' printf '%s' "$_jpr_tried" } >&2 return 1 } # 直接執行時印出結果;被 source 時只提供函式。 case "${0##*/}" in plugins-root.sh) jsc_plugins_root || exit 1 ;; esac