釋出 jsc-hooks 0.3.4:四支 CLI 的 pre-tool hook 接線修正 #59
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env sh
|
||||
# deny.sh — 產出各 CLI 認得的「擋下這一次呼叫」輸出。
|
||||
#
|
||||
# 用途:阻擋形態每支 CLI 都不一樣,判定卻是同一件事。形態留這一份,閘門只管判定:
|
||||
# 各自留一份輸出邏輯,改了一支忘了另一支,就會出現「判定擋下、CLI 卻沒收到拒絕」的無聲失效。
|
||||
#
|
||||
# 用法:deny.sh {cli} [訊息...]
|
||||
# 訊息從參數來,沒給參數就讀標準輸入。兩者都空就送一句預設訊息,不會產出空的拒絕。
|
||||
#
|
||||
# 各 CLI 的阻擋形態:
|
||||
# claude、codex、copilot 訊息寫 stderr,結束碼 2 就是拒絕
|
||||
# antigravity stdout 印 {"decision":"deny","reason":"..."}。
|
||||
# 結束碼語意兩邊文件都沒寫,**絕對不可靠**,所以固定回 0,
|
||||
# 拒絕整個靠那一行 JSON。也因此 stdout 只准有那一行,
|
||||
# 呼叫端要給人看的字一律走 stderr。
|
||||
# kiro 擋不下來。技能叫用是 agent 內部請求,preToolUse 攔不到,
|
||||
# userPromptSubmit 的非零結束碼也不會擋下那一輪。
|
||||
# 唯一可用的介入是 stdout 注入,所以改印警告並回 0。
|
||||
#
|
||||
# 結束碼:
|
||||
# 2 拒絕已經送出(claude、codex、copilot,以及認不得的 CLI 代號)。呼叫端直接把它當自己的結束碼。
|
||||
# 0 拒絕已經送出,但形態不靠結束碼(antigravity 的 stdout JSON),或這支 CLI 根本擋不下來(kiro)。
|
||||
# 本檔沒有其他結束碼。認不得的代號不另立一種:五支裡有三支是 stderr 加 2,未知代號走這個保守預設,
|
||||
# 比靜靜放行安全。
|
||||
set -u
|
||||
|
||||
CLI="${1:-}"
|
||||
shift 2>/dev/null || true
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
MSG="$*"
|
||||
else
|
||||
MSG=""
|
||||
[ -t 0 ] || MSG=$(cat 2>/dev/null || true)
|
||||
fi
|
||||
[ -n "$MSG" ] || MSG='[jsc]:這次呼叫已被 jsc 閘門擋下。'
|
||||
|
||||
# 把訊息壓成一個合法的 JSON 字串值:反斜線與雙引號先跳脫,換行與定位字元改成跳脫序列。
|
||||
# 直接把原文塞進 JSON 會做出解不開的負載,antigravity 收到壞 JSON 等於沒收到拒絕。
|
||||
json_reason() {
|
||||
printf '%s' "$MSG" \
|
||||
| sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' \
|
||||
| awk '{ printf "%s%s", sep, $0; sep = "\\n" } END { printf "\n" }'
|
||||
}
|
||||
|
||||
case "$CLI" in
|
||||
antigravity)
|
||||
printf '{"decision":"deny","reason":"%s"}\n' "$(json_reason)"
|
||||
exit 0 ;;
|
||||
kiro)
|
||||
printf '%s\n' "$MSG"
|
||||
printf '[jsc]:kiro 擋不下技能叫用(CLI 限制),上面這段只是警告,請自己先處理完再繼續。\n'
|
||||
exit 0 ;;
|
||||
*)
|
||||
printf '%s\n' "$MSG" >&2
|
||||
exit 2 ;;
|
||||
esac
|
||||
+32
-23
@@ -4,16 +4,26 @@
|
||||
# 技能組更新後,正在跑的 CLI 行程載入的還是舊版:SKILL.md、hook 腳本與 tools 都在啟動當下
|
||||
# 讀進記憶體。所以部署收尾要求重新啟動,這道閘門負責讓「還沒重啟就繼續用技能」擋在門外。
|
||||
#
|
||||
# 結束碼(hook 模式):0=放行 2=擋下該次技能呼叫,訊息走 stderr。
|
||||
# 安靜放行(exit 0)的情況:逃生門 JSC_RESTART_GATE=off、工具名取得到但不是 Skill、
|
||||
# 取不到技能名、技能名不是 jsc-{domain}:{name}、命中下方豁免清單那 10 支、取不到 CLI 代號、
|
||||
# 結束碼(hook 模式):0=放行 2=擋下該次技能呼叫。
|
||||
# 擋下時的輸出形態由 deny.sh 依當前 CLI 決定,本檔只負責判定與訊息內容:
|
||||
# claude、codex、copilot 走 stderr 加 exit 2;antigravity 走 stdout 的 deny JSON,結束碼
|
||||
# 固定 0(那支 CLI 的結束碼語意沒有文件,不可靠);kiro 擋不下來,改印警告後 exit 0。
|
||||
# 所以「exit 0」在這支腳本有兩種意思:放行,或已經以不靠結束碼的形態擋下。
|
||||
# 安靜放行(exit 0)的情況:逃生門 JSC_RESTART_GATE=off、負載裡解不出技能名、
|
||||
# 解出來的不是 jsc 技能、命中下方豁免清單那 10 支、取不到 CLI 代號、
|
||||
# 當前 CLI 那份狀態檔與舊格式狀態檔都不在。
|
||||
# 只有「當前 CLI 那份狀態檔存在」或「退回讀到的舊格式狀態檔存在」會 exit 2。
|
||||
# 只有「當前 CLI 那份狀態檔存在」或「退回讀到的舊格式狀態檔存在」會走 deny.sh。
|
||||
# 結束碼(require):0=閘門已掛上 2=取不到 CLI 代號或寫不進狀態檔,兩種都等於沒掛上。
|
||||
# 結束碼(clear、report):0=永遠成功。clear 檔案不存在也算成功,report 一份都沒有就不印。
|
||||
# 結束碼(不認得的子命令):0=安靜放行,不中斷宿主 CLI。
|
||||
# 註:本檔以 `. "$HERE/lib.sh"` 載入共用函式,沒有接 `|| true`。lib.sh 讀不到時 sh 會就地
|
||||
# 結束並回 2,接在 PreToolUse 上就是無聲擋下每一次技能呼叫,上面那些放行路徑一條都跑不到。
|
||||
# hooks/skill-name.sh 與 hooks/deny.sh 同理要一起裝上,但那兩支是以子行程呼叫,讀不到只會
|
||||
# 讓技能名解不出來而安靜放行,不會反過來擋人——所以那兩支刻意不用 source 載入。
|
||||
#
|
||||
# 輸入:技能名一律由 skill-name.sh 從當前 CLI 的負載解析,環境變數 JSC_SKILL、SKILL 優先,
|
||||
# 規則與 version-guard.sh 共用同一份。不再另外篩工具名:工具名每支 CLI 都不一樣
|
||||
# (Skill、Bash、skill、view_file),拿 Claude 的那一個當通用條件會把另外四支整批擋在判定之外。
|
||||
#
|
||||
# 用法:
|
||||
# restart-gate.sh hook 模式:當前 CLI 那份狀態檔存在就擋下該次技能
|
||||
@@ -184,19 +194,14 @@ read_stdin
|
||||
|
||||
[ "${JSC_RESTART_GATE:-}" = "off" ] && exit 0
|
||||
|
||||
# 輸入相容:stdin JSON(Claude 格式)與環境變數(其他四支 CLI 接線時設定)都要收,
|
||||
# 取法比照 version-guard.sh。工具名取不到就當成沒篩,繼續判技能名。
|
||||
tool="${JSC_TOOL_NAME:-${TOOL_NAME:-$(json_str tool_name)}}"
|
||||
[ -z "$tool" ] || [ "$tool" = "Skill" ] || exit 0
|
||||
|
||||
skill="${JSC_SKILL:-${SKILL:-$(json_str skill)}}"
|
||||
[ -n "$skill" ] || exit 0
|
||||
|
||||
# 只管本技能組(jsc-{domain}:{name})。別人的技能不受這道閘門影響。
|
||||
case "$skill" in
|
||||
jsc-*:*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
# 技能名解析:交給 skill-name.sh,規則與 version-guard.sh 共用同一份。輸出固定是
|
||||
# 「{domain}<TAB>{技能名}」;用 awk 判 NF==2 才取值,少一欄就當成解析不出來,免得沒有定位字元時
|
||||
# cut -f2 把整行當成技能名,拼出一個不存在的技能名去比對豁免清單。
|
||||
sn=$(printf '%s' "$STDIN_JSON" | sh "$HERE/skill-name.sh" "$(cli_name)" 2>/dev/null)
|
||||
sn_domain=$(printf '%s\n' "$sn" | awk -F'\t' 'NF == 2 { print $1; exit }')
|
||||
sn_name=$(printf '%s\n' "$sn" | awk -F'\t' 'NF == 2 { print $2; exit }')
|
||||
[ -n "$sn_domain" ] && [ -n "$sn_name" ] || exit 0
|
||||
skill="jsc-$sn_domain:$sn_name"
|
||||
|
||||
# 豁免清單(理由見檔頭)
|
||||
case "$skill" in
|
||||
@@ -236,9 +241,13 @@ info=""
|
||||
# 系統認定就是這一支剛部署過。
|
||||
[ "$legacy" = yes ] && info="${info}${info:+,}舊格式紀錄,分不出是哪一支 CLI 部署的"
|
||||
|
||||
printf '[jsc][重啟閘門][ERR]:技能組已更新%s,%s 還在跑舊版,新版要重新啟動才會載入。本次技能呼叫已擋下。\n' \
|
||||
"${info:+($info)}" "$bin" >&2
|
||||
printf '重新啟動:結束 %s 再重新開啟一次,狀態檔 %s 會在新工作階段開始時自動清除。\n' \
|
||||
"$bin" "$state" >&2
|
||||
printf '仍可使用:/jsc-cli:deploy、/jsc-hooks:hooks-install、/jsc-hooks:repair、/jsc-gitea:wiki、/jsc-log:worklog、/jsc-log:learn、/jsc-meta:*、/jsc-ask:ask、/jsc-git:pr、/jsc-git:commit(部署後的異動報告與工作日誌要寫得完,hook 壞掉也要修得回來) | 確定要略過閘門:JSC_RESTART_GATE=off\n' >&2
|
||||
exit 2
|
||||
# 擋人輸出交給 deny.sh:形態依 CLI 而定,本檔只組訊息。三段訊息整段走同一條管線送過去,
|
||||
# antigravity 那一支才有辦法把它們壓成同一個 reason 字串;分次呼叫會做出好幾份 deny JSON,
|
||||
# 那支 CLI 只認第一份,後面兩段使用者永遠看不到。
|
||||
{ printf '[jsc][重啟閘門][ERR]:技能組已更新%s,%s 還在跑舊版,新版要重新啟動才會載入。本次技能呼叫已擋下。\n' \
|
||||
"${info:+($info)}" "$bin"
|
||||
printf '重新啟動:結束 %s 再重新開啟一次,狀態檔 %s 會在新工作階段開始時自動清除。\n' \
|
||||
"$bin" "$state"
|
||||
printf '仍可使用:/jsc-cli:deploy、/jsc-hooks:hooks-install、/jsc-hooks:repair、/jsc-gitea:wiki、/jsc-log:worklog、/jsc-log:learn、/jsc-meta:*、/jsc-ask:ask、/jsc-git:pr、/jsc-git:commit(部署後的異動報告與工作日誌要寫得完,hook 壞掉也要修得回來) | 確定要略過閘門:JSC_RESTART_GATE=off\n'
|
||||
} | sh "$HERE/deny.sh" "$(cli_name)"
|
||||
exit $?
|
||||
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env sh
|
||||
# skill-name.sh — 從各 CLI 的 hook 負載解析出「這一次要用哪一支 jsc 技能」。
|
||||
#
|
||||
# 用途:五支 CLI 的負載形態各不相同,但「從負載取出 domain 與技能名」是同一件事。
|
||||
# 規則只留這一份:寫在每支閘門裡就會漂移,CLI 換了負載形態也只要改這一個地方。
|
||||
#
|
||||
# 用法:skill-name.sh {claude|codex|copilot|antigravity|kiro}
|
||||
# 從標準輸入讀該 CLI 的 hook 負載,印出一行「{domain}<TAB>{技能名}」,例如「sdlc<TAB>implement」。
|
||||
# 解析不出來就印空字串,由呼叫端安靜放行。
|
||||
#
|
||||
# 各 CLI 的取值來源:
|
||||
# claude stdin JSON 的 skill 欄位(PreToolUse matcher Skill 才會有)
|
||||
# codex stdin JSON 的 tool_input.command 裡那條 SKILL.md 路徑。Codex 沒有 Skill 工具,
|
||||
# 技能是模型自己用 Bash 讀 SKILL.md 載入的,所以要從指令字串裡認路徑
|
||||
# copilot stdin JSON 的 toolArgs。那個欄位是**字串化的 JSON**,要先剝一層跳脫才讀得到裡面的值
|
||||
# antigravity stdin JSON 的 toolCall.args.AbsolutePath;另外收 PreInvocation 那一輪的提示字串,
|
||||
# 因為斜線指令會把 SKILL.md 全文直接注入訊息,一個工具呼叫都不產生,PreToolUse 攔不到
|
||||
# kiro stdin JSON 的 prompt,取開頭那個「/{技能名}」
|
||||
# 五支都先看環境變數 JSC_SKILL、SKILL:接線時用環境變數餵資料的 CLI 要收得到,冒煙測試也走這條。
|
||||
#
|
||||
# 為什麼只認 jsc 技能:呼叫這支腳本的是 jsc 自己的閘門,別人的技能不歸它們管。解不出 jsc-{domain}
|
||||
# 這個形狀就等同「這一次不是 jsc 技能」,印空字串比印半個結果安全——呼叫端只要判空就好,不必再
|
||||
# 自己補一次「這是不是我們的技能」的判斷,那正是會漂移的那一段。
|
||||
#
|
||||
# 結束碼:
|
||||
# 0 永遠是 0,含「解析不出來」與「CLI 代號不認得」兩種。這支腳本只解析、不判定:
|
||||
# 閘門那一端一律 fail-open,解析失敗回非零只會讓呼叫端多一條沒必要的錯誤分支。
|
||||
# copilot 的 command hook 是 fail-closed 的(非零結束碼等於拒絕),更不能回非零。
|
||||
# 本檔沒有其他結束碼。
|
||||
set -u
|
||||
|
||||
CLI="${1:-}"
|
||||
|
||||
PAYLOAD=""
|
||||
[ -t 0 ] || PAYLOAD=$(cat 2>/dev/null || true)
|
||||
|
||||
# 把整份負載併成一行再取「某個欄位之後的內容」。不切逗號:命令字串裡本來就有逗號,
|
||||
# 切了會把路徑攔腰砍斷。貪婪比對取的是最後一次出現的那個欄位,巢狀負載也指得到裡層那一個。
|
||||
after_field() { # $1=欄位名
|
||||
printf '%s' "$PAYLOAD" | tr -d '\n' \
|
||||
| sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*//p"
|
||||
}
|
||||
|
||||
# 從一段文字取第一條 SKILL.md 路徑。刻意不去解那個 JSON 字串的值:值裡的引號是跳脫過的,
|
||||
# 照欄位邊界取會在第一個 \" 就被截斷,反而讀不到路徑。認路徑本身的形狀最穩。
|
||||
md_path() { # $1=文字
|
||||
printf '%s' "$1" | grep -o '/[A-Za-z0-9_./-]*SKILL\.md' | head -n1
|
||||
}
|
||||
|
||||
# 從一段文字取第一個 jsc-{domain}:{技能名} 字樣
|
||||
token_skill() { # $1=文字
|
||||
printf '%s' "$1" | grep -o 'jsc-[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*' | head -n1
|
||||
}
|
||||
|
||||
# jsc-{domain}:{技能名} → 兩欄輸出
|
||||
emit_token() { # $1=技能名字樣
|
||||
[ -n "$1" ] || return 0
|
||||
_d=${1#jsc-}; _d=${_d%%:*}
|
||||
_n=${1#*:}
|
||||
[ -n "$_d" ] && [ -n "$_n" ] || return 0
|
||||
printf '%s\t%s\n' "$_d" "$_n"
|
||||
}
|
||||
|
||||
# SKILL.md 路徑 → 兩欄輸出。domain 取路徑裡最後一段 jsc-{domain},技能名取 SKILL.md 的上一層目錄,
|
||||
# 所以 {前綴}/jsc-sdlc/skills/implement/SKILL.md 與 {前綴}/jsc-sdlc/implement/SKILL.md 都解得出來。
|
||||
emit_path() { # $1=路徑
|
||||
[ -n "$1" ] || return 0
|
||||
_d=$(printf '%s' "$1" | sed -n 's#.*/jsc-\([a-z0-9][a-z0-9-]*\)/.*#\1#p')
|
||||
_n=$(printf '%s' "$1" | sed -n 's#.*/\([^/][^/]*\)/SKILL\.md$#\1#p')
|
||||
[ -n "$_d" ] && [ -n "$_n" ] || return 0
|
||||
printf '%s\t%s\n' "$_d" "$_n"
|
||||
}
|
||||
|
||||
# 環境變數優先。接線時用環境變數餵資料的 CLI 只有這一條路,負載再怎麼解也解不出東西。
|
||||
env_skill="${JSC_SKILL:-${SKILL:-}}"
|
||||
if [ -n "$env_skill" ]; then
|
||||
emit_token "$(token_skill "$env_skill")"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$CLI" in
|
||||
claude)
|
||||
emit_token "$(token_skill "$(after_field skill)")" ;;
|
||||
codex)
|
||||
emit_path "$(md_path "$(after_field command)")" ;;
|
||||
copilot)
|
||||
# 剝一層字串化 JSON:把 \" 還原成 "、\\ 還原成 \,裡面的技能名才認得出來。
|
||||
_args=$(after_field toolArgs | sed 's/\\"/"/g; s/\\\\/\\/g')
|
||||
_tok=$(token_skill "$_args")
|
||||
if [ -n "$_tok" ]; then emit_token "$_tok"; else emit_path "$(md_path "$_args")"; fi ;;
|
||||
antigravity)
|
||||
_p=$(md_path "$(after_field AbsolutePath)")
|
||||
if [ -n "$_p" ]; then
|
||||
emit_path "$_p"
|
||||
else
|
||||
# PreInvocation 那一輪沒有工具呼叫,只有提示字串。斜線指令走的就是這條路。
|
||||
emit_token "$(token_skill "$(after_field prompt)")"
|
||||
fi ;;
|
||||
kiro)
|
||||
# 提示開頭那個斜線指令。kiro 的技能不走工具管線,userPromptSubmit 是唯一看得到技能名的時點。
|
||||
emit_token "$(token_skill "$(after_field prompt)")" ;;
|
||||
*)
|
||||
: ;; # 認不得的代號印空字串,理由見檔頭結束碼那一段
|
||||
esac
|
||||
|
||||
exit 0
|
||||
+39
-34
@@ -6,23 +6,32 @@
|
||||
# 二、技能所屬 plugin 宣告的相依 plugin 版本落後(manifest 的 jsc.requires)。
|
||||
# 兩種都會提示更新指令。
|
||||
#
|
||||
# 結束碼(hook 模式):0=放行 2=擋下該次技能呼叫,訊息走 stderr。
|
||||
# 結束碼(hook 模式):0=放行 2=擋下該次技能呼叫。
|
||||
# 擋下時的輸出形態由 deny.sh 依當前 CLI 決定,本檔只負責判定與訊息內容:
|
||||
# claude、codex、copilot 走 stderr 加 exit 2;antigravity 走 stdout 的 deny JSON,結束碼
|
||||
# 固定 0(那支 CLI 的結束碼語意沒有文件,不可靠);kiro 擋不下來,改印警告後 exit 0。
|
||||
# 所以「exit 0」在這支腳本有兩種意思:放行,或已經以不靠結束碼的形態擋下。
|
||||
# 安靜放行(exit 0)的情況要記清楚,這道閘門絕大多數時候走的是這幾條:逃生門
|
||||
# JSC_VERSION_GUARD=off、工具名取得到但不是 Skill、取不到技能名、技能名不是
|
||||
# jsc-{domain}:{name}、拆不出 domain、命中下方豁免清單那 7 支、解不出安裝路徑、
|
||||
# JSC_VERSION_GUARD=off、負載裡解不出技能名、解出來的不是 jsc 技能、
|
||||
# 命中下方豁免清單那 7 支、解不出安裝路徑、
|
||||
# 讀不到 manifest、manifest 沒有 jsc.requires、讀不到相依 plugin 的本機載入版本、
|
||||
# 讀不到自己的本機實際載入版本、推導不出遠端站台、查不到遠端版本、
|
||||
# 本機版本等於或超前遠端。
|
||||
# 只有「相依確定落後」與「本機落後遠端」這兩條會 exit 2。
|
||||
# 只有「相依確定落後」與「本機落後遠端」這兩條會走 deny.sh。
|
||||
# 結束碼(report、recommend):0=永遠成功,只讀不擋。結論看 stdout,不看結束碼。
|
||||
# 註:本檔以 `. "$HERE/lib.sh"` 載入共用函式,沒有接 `|| true`。lib.sh 讀不到時 sh 會就地
|
||||
# 結束並回 2,接在 PreToolUse 上就是無聲擋下每一次技能呼叫,上面那些放行路徑一條都跑不到
|
||||
# (write-guard.sh 踩過這個坑)。部署時要確認 hooks/lib.sh 跟這支腳本一起裝上。
|
||||
# hooks/skill-name.sh 與 hooks/deny.sh 同理要一起裝上,但那兩支是以子行程呼叫,讀不到只會
|
||||
# 讓技能名解不出來而安靜放行,不會反過來擋人——所以那兩支刻意不用 source 載入。
|
||||
#
|
||||
# 輸入:stdin JSON(Claude 格式)或環境變數,兩者都收。
|
||||
# 工具名 JSC_TOOL_NAME、TOOL_NAME、stdin 的 tool_name
|
||||
# 技能名 JSC_SKILL、SKILL、stdin 的 skill
|
||||
# 兩邊都拿不到就安靜降級 exit 0。
|
||||
# 輸入:技能名一律由 skill-name.sh 從當前 CLI 的負載解析,環境變數 JSC_SKILL、SKILL 優先。
|
||||
# 五支 CLI 的負載形態不同(claude 有 skill 欄位、codex 是 Bash 指令裡的 SKILL.md 路徑、
|
||||
# copilot 是字串化的 toolArgs、antigravity 是 AbsolutePath、kiro 是提示開頭的斜線指令),
|
||||
# 取值規則只留 skill-name.sh 那一份,本檔不重寫第二套。解不出來就安靜降級 exit 0。
|
||||
# 不再另外篩工具名:工具名每支 CLI 都不一樣(Skill、Bash、skill、view_file),
|
||||
# 拿 Claude 的那一個當通用條件,等於把另外四支整批擋在判定之外——這正是先前失效的原因。
|
||||
# 接線那一端已經用各自的 matcher 篩過一輪,解得出技能名就是該判的那一次。
|
||||
#
|
||||
# 判準與取值:
|
||||
# - 比對對象是「遠端發佈版本」與「本機**實際載入**的版本」。
|
||||
@@ -306,24 +315,14 @@ read_stdin
|
||||
|
||||
[ "${JSC_VERSION_GUARD:-}" = "off" ] && exit 0
|
||||
|
||||
# 輸入相容:stdin JSON(Claude 格式)與環境變數(其他四支 CLI 接線時設定)都要收。
|
||||
# 只讀 stdin 的話,用環境變數餵資料的 CLI 一律拿到空值,檢查會整支靜靜放行。
|
||||
# 兩者都缺才是真的沒資料,那時照舊安靜降級 exit 0。
|
||||
tool="${JSC_TOOL_NAME:-${TOOL_NAME:-$(json_str tool_name)}}"
|
||||
[ -z "$tool" ] || [ "$tool" = "Skill" ] || exit 0
|
||||
|
||||
skill="${JSC_SKILL:-${SKILL:-$(json_str skill)}}"
|
||||
[ -n "$skill" ] || exit 0
|
||||
|
||||
# 只管本技能組(jsc-{domain}:{name})
|
||||
case "$skill" in
|
||||
jsc-*:*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
domain=${skill#jsc-}
|
||||
domain=${domain%%:*}
|
||||
[ -n "$domain" ] || exit 0
|
||||
# 技能名解析:交給 skill-name.sh,它一支 CLI 一個子命令,規則只有那一份。
|
||||
# 輸出固定是「{domain}<TAB>{技能名}」;用 awk 判 NF==2 才取值,少一欄就當成解析不出來,
|
||||
# 免得沒有定位字元時 cut -f2 把整行當成技能名,拼出一個不存在的技能名去比對豁免清單。
|
||||
sn=$(printf '%s' "$STDIN_JSON" | sh "$HERE/skill-name.sh" "$(cli_name)" 2>/dev/null)
|
||||
domain=$(printf '%s\n' "$sn" | awk -F'\t' 'NF == 2 { print $1; exit }')
|
||||
name=$(printf '%s\n' "$sn" | awk -F'\t' 'NF == 2 { print $2; exit }')
|
||||
[ -n "$domain" ] && [ -n "$name" ] || exit 0
|
||||
skill="jsc-$domain:$name"
|
||||
|
||||
# 豁免清單
|
||||
case "$skill" in
|
||||
@@ -348,11 +347,15 @@ update_cmd() { # $1=domain
|
||||
esac
|
||||
}
|
||||
|
||||
# 擋人輸出交給 deny.sh:形態依 CLI 而定,本檔只組訊息。訊息整段走管線送過去,
|
||||
# antigravity 那一支才有辦法把多行訊息壓成同一個 reason 字串;分成好幾次呼叫會做出好幾份
|
||||
# deny JSON,那支 CLI 只認第一份,後面幾段訊息使用者永遠看不到。
|
||||
deny() { # $1=訊息
|
||||
printf '[jsc][版本檢查][ERR]:%s\n' "$1" >&2
|
||||
printf '更新指令:%s\n' "$(update_cmd "$domain")" >&2
|
||||
printf '更新整組:/jsc-cli:deploy | 確定要略過檢查:JSC_VERSION_GUARD=off\n' >&2
|
||||
exit 2
|
||||
{ printf '[jsc][版本檢查][ERR]:%s\n' "$1"
|
||||
printf '更新指令:%s\n' "$(update_cmd "$domain")"
|
||||
printf '更新整組:/jsc-cli:deploy | 確定要略過檢查:JSC_VERSION_GUARD=off\n'
|
||||
} | sh "$HERE/deny.sh" "$(cli_name)"
|
||||
exit $?
|
||||
}
|
||||
|
||||
# ── 相依版本檢查:讀技能所屬 plugin 的 manifest,逐項比對相依 plugin 的本機載入版本。
|
||||
@@ -374,10 +377,12 @@ if [ -n "$plugin_dir" ] && [ -f "$plugin_dir/plugin.json" ]; then
|
||||
printf ' - %s 需要 %s,目前 %s,更新指令:%s\n' "$dep" "$cond" "$cur" "$(update_cmd "$dep_domain")"
|
||||
done)
|
||||
if [ -n "$behind_list" ]; then
|
||||
printf '[jsc][版本檢查][ERR]:%s 宣告的相依 plugin 版本落後,本次技能呼叫已擋下\n' "$skill" >&2
|
||||
printf '%s\n' "$behind_list" >&2
|
||||
printf '更新整組:/jsc-cli:deploy | 確定要略過檢查:JSC_VERSION_GUARD=off\n' >&2
|
||||
exit 2
|
||||
# 逐項清單與結語一起送進 deny.sh,理由同上:一次呼叫、一份拒絕。
|
||||
{ printf '[jsc][版本檢查][ERR]:%s 宣告的相依 plugin 版本落後,本次技能呼叫已擋下\n' "$skill"
|
||||
printf '%s\n' "$behind_list"
|
||||
printf '更新整組:/jsc-cli:deploy | 確定要略過檢查:JSC_VERSION_GUARD=off\n'
|
||||
} | sh "$HERE/deny.sh" "$(cli_name)"
|
||||
exit $?
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user