以 plugin 形式安裝時,腳本會落在 CLI 的 plugin 快取目錄。 六支腳本原本一律取「腳本位置的上兩層」當技能組根目錄,這時一定推錯。 這一輪例行稽核的第一步就實際踩到:不先手動設環境變數,腳本根本跑不動, 而叫用它們的技能說明也沒提要設,等於留了一個必炸的預設值。 把推導規則抽成共用腳本,依序試環境變數、從目前目錄往上找、 腳本位置的上兩層、家目錄底下的 plugins,並以 gitea.sh 在不在當判準。 marketplace 每個 domain 存取庫都帶一份,單看它會把 domain 誤判成根。 推不出來就結束並指名要設環境變數,同時列出試過的每一個候選, 呼叫端一眼看得出要設什麼。只 clone 單一存取庫的環境留了逃生門: 環境變數有設且是目錄就照用,並在錯誤輸出提醒。 順手補上語言檢查與 manifest 同步兩支腳本的結束碼宣告與環境變數說明。 沒有宣告,呼叫端只能猜;猜錯就把失敗當成功。
96 lines
4.2 KiB
Bash
Executable File
96 lines
4.2 KiB
Bash
Executable File
#!/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
|