What:
- 新增 hooks/skill-name.sh。五支 CLI 各一個子命令,從各自的負載解析出這一次要用哪一支 jsc 技能,印一行「{domain}<TAB>{技能名}」。解不出來就印空字串並回 0。
- 新增 hooks/deny.sh。依當前 CLI 產出四種阻擋形態,訊息從參數或標準輸入進。
- version-guard.sh 與 restart-gate.sh 改用這兩支,各自那份工具名判定與技能名取值一併移除。
Why:
- 兩道閘門原本都拿 Claude 的工具名 Skill 當通用條件。另外四支 CLI 的工具名分別是 Bash、skill、view_file,一律被擋在判定之外,兩道閘門在那四支上長期完全失效,而且一聲都不吭。
- 阻擋形態每支 CLI 都不一樣,判定卻是同一件事。各自留一份輸出邏輯,改了一支忘了另一支,就會做出「判定擋下、CLI 照樣放行」的無聲失效。
How:
- 技能名取值規則只留 skill-name.sh 這一份,環境變數 JSC_SKILL、SKILL 優先。claude 讀 skill 欄位、codex 認 tool_input.command 裡那條 SKILL.md 路徑、copilot 先剝一層字串化 JSON 再讀 toolArgs、antigravity 讀 toolCall.args.AbsolutePath 並另收提示字串、kiro 取提示開頭那個斜線指令。
- 閘門端用 awk 判 NF == 2 才取值。少一欄就當成解析不出來,免得沒有定位字元時 cut -f2 把整行當成技能名,拼出一個不存在的技能名去比對豁免清單。
- deny.sh 定形態:claude、codex、copilot 走 stderr 加 exit 2;antigravity 印 stdout 的單行 deny JSON 並固定回 0,因為那支 CLI 的結束碼語意兩邊文件都沒寫、絕不可靠;kiro 擋不下技能叫用,改印警告後回 0;認不得的代號走 stderr 加 2 這個保守預設。
- 訊息整段走同一條管線送進 deny.sh。分次呼叫會做出好幾份 deny JSON,antigravity 只認第一份,後面幾段使用者永遠看不到。
- 豁免清單與 fail-open 原則不變。這兩支刻意以子行程呼叫、不用 source 載入:讀不到只會讓技能名解不出來而安靜放行,不會反過來擋掉每一次呼叫。
Who:codex、copilot、antigravity、kiro 四支 CLI 的 pre-tool hook 接線修正。
58 lines
2.6 KiB
Bash
Executable File
58 lines
2.6 KiB
Bash
Executable File
#!/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
|