feat(hooks-install): 將 hooks-install 的 CLI 佈線邏輯抽離為腳本
為什麼:
`skills/hooks-install/SKILL.md` 原本直接內嵌各 CLI(codex/copilot/antigravity/kiro)的詳細佈線步驟,包含逐步設定檔修改、wrapper alias 安裝、以及 `<!-- jsc-hooks -->` 標記區塊的冪等替換邏輯,違反 jsc-meta:skill-check 稽核準則中「確定性程序邏輯應由腳本執行,不應留給子代理每次自行解讀 prose」的要求。
做了什麼:
新增腳本 `tools/wire-cli.sh {cli}`,將原本內嵌於 SKILL.md 的佈線邏輯全數抽出,並支援冪等執行(標記區塊替換、可安全重跑),輸出機器可讀的 `status=wired|degraded|skipped reason=...` 首行訊息與對應結束碼(0=wired、1=degraded、2=bad usage、3=skipped)。改寫 `skills/hooks-install/SKILL.md`,改為依偵測到的 CLI 呼叫此腳本,並讀取其 status/結束碼判斷結果,不再自行內含佈線步驟。同步更新 `README.md`:在工具表新增 `tools/wire-cli.sh` 說明列,並改寫 hooks-install skill 的描述段落,反映新的腳本驅動流程與 `status=` 回報慣例。三份外掛清單 `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 版本號自 0.0.3 升級為 0.0.4,對應此行為變更。
怎麼做:
建立獨立腳本承載原本的 CLI 佈線程序邏輯,改造 SKILL.md 為呼叫腳本並解讀其標準化輸出,同步更新文件與版本號。
影響範圍:
`skills/hooks-install/SKILL.md`、`README.md`、`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json`、新增檔案 `tools/wire-cli.sh`;影響 hooks-install skill 的執行流程與所有安裝 jsc-hooks 外掛的 CLI 佈線行為。
This commit is contained in:
Executable
+149
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env sh
|
||||
# wire-cli.sh — 把 jsc 四支 hook 接線到單一 CLI(供 hooks-install 技能呼叫)。
|
||||
# 用法: wire-cli.sh {claude|codex|copilot|antigravity|kiro}
|
||||
# 行為(依 CLI 而定,皆為冪等:重跑只取代既有的 jsc-hooks 標記段落,不會重複疊加):
|
||||
# claude — 什麼都不用寫,hooks.json 已自動接線四支 hook
|
||||
# codex — 在 config.toml 設 notify(呼叫 session-timer.sh mark,JSC_CLI=codex);
|
||||
# 在 AGENTS.md 附加 STE100 規則段落(prompt 降級)
|
||||
# copilot — 在 shell rc 檔加上 copilot 別名,轉呼叫 tools/jsc-wrap.sh copilot;
|
||||
# 在 copilot-instructions.md 附加 STE100 規則段落
|
||||
# antigravity — 在 shell rc 檔加上 agy 別名,轉呼叫 tools/jsc-wrap.sh antigravity;
|
||||
# 在全域規則檔附加 STE100 規則段落
|
||||
# kiro — 在工作區 .kiro/hooks/ 下建立 jsc-hooks.json(JSC_CLI=kiro)
|
||||
#
|
||||
# 輸出: 第一行固定為 `status={wired|degraded|skipped} reason=...`(可供程式判讀),
|
||||
# 其後為人類可讀的繁中說明。
|
||||
# 結束碼: 0=wired(已完整接線) 1=degraded(降級為 prompt/技能步驟檢查)
|
||||
# 2=用法錯誤 3=skipped(該 CLI 未偵測到執行檔,略過)
|
||||
set -u
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
ROOT=$(cd "$HERE/.." && pwd)
|
||||
HOOKS="$ROOT/hooks"
|
||||
|
||||
cli="${1:-}"
|
||||
case "$cli" in
|
||||
claude|codex|copilot|antigravity|kiro) ;;
|
||||
*)
|
||||
echo "用法:wire-cli.sh {claude|codex|copilot|antigravity|kiro}" >&2
|
||||
exit 2 ;;
|
||||
esac
|
||||
|
||||
# 每個 CLI 對應的實際執行檔名稱(antigravity 的執行檔是 agy,其餘與 CLI 代號同名)
|
||||
cli_bin() {
|
||||
case "$1" in
|
||||
antigravity) printf 'agy' ;;
|
||||
kiro) printf 'kiro-cli' ;;
|
||||
*) printf '%s' "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# STE100 規則段落的唯一來源:ste100-guard.sh 的實際輸出
|
||||
ste100_text() { sh "$HOOKS/ste100-guard.sh" 2>/dev/null | sed '/^exit /d'; }
|
||||
|
||||
# 以標記整段取代(冪等);標記不存在就在檔尾新增;檔案不存在就建立。
|
||||
# $1=檔案 $2=開頭標記行 $3=結尾標記行 $4=標記之間要寫入的內容
|
||||
replace_block() {
|
||||
file="$1"; bopen="$2"; bshut="$3"; content="$4"
|
||||
dir=$(dirname "$file")
|
||||
mkdir -p "$dir" 2>/dev/null || return 1
|
||||
touch "$file" 2>/dev/null || return 1
|
||||
block=$(printf '%s\n%s\n%s' "$bopen" "$content" "$bshut")
|
||||
if grep -qF "$bopen" "$file" 2>/dev/null; then
|
||||
awk -v bopen="$bopen" -v bshut="$bshut" -v block="$block" '
|
||||
$0==bopen { print block; skip=1; next }
|
||||
$0==bshut { skip=0; next }
|
||||
skip { next }
|
||||
{ print }
|
||||
' "$file" > "$file.jsc-tmp" 2>/dev/null && mv "$file.jsc-tmp" "$file"
|
||||
else
|
||||
printf '\n%s\n' "$block" >> "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
# 找出可寫入別名的 shell rc 檔;都不存在就以 ~/.bashrc 為預設(自動建立)。
|
||||
# 印出找到/建立的 rc 檔路徑,一行一個。
|
||||
rc_files() {
|
||||
found=""
|
||||
for f in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/fish/config.fish"; do
|
||||
[ -f "$f" ] && { printf '%s\n' "$f"; found=1; }
|
||||
done
|
||||
[ -n "$found" ] || printf '%s\n' "$HOME/.bashrc"
|
||||
}
|
||||
|
||||
skip() { # $1=reason
|
||||
printf 'status=skipped reason=%s\n' "$1"
|
||||
echo "[jsc] 略過:$1"
|
||||
exit 3
|
||||
}
|
||||
|
||||
case "$cli" in
|
||||
claude)
|
||||
printf 'status=wired reason=%s\n' "hooks.json 自動接線"
|
||||
echo "[jsc] claude:由 hooks/hooks.json 自動接線全部四支 hook,無需寫入設定。"
|
||||
exit 0 ;;
|
||||
|
||||
codex)
|
||||
command -v "$(cli_bin codex)" >/dev/null 2>&1 || skip "未偵測到 codex 執行檔"
|
||||
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
||||
config="$CODEX_HOME/config.toml"
|
||||
agents="$CODEX_HOME/AGENTS.md"
|
||||
notify_line="notify = [\"env\", \"JSC_CLI=codex\", \"sh\", \"$HOOKS/session-timer.sh\", \"mark\"]"
|
||||
replace_block "$config" "# jsc-hooks" "# /jsc-hooks" "$notify_line" \
|
||||
|| skip "無法寫入 $config"
|
||||
replace_block "$agents" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(ste100_text)" \
|
||||
|| skip "無法寫入 $agents"
|
||||
printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查"
|
||||
echo "[jsc] codex:已設定 $config 的 notify 呼叫 session-timer.sh mark。"
|
||||
echo "[jsc] codex:已在 $agents 寫入 STE100 規則段落(prompt 降級)。"
|
||||
echo "[jsc] codex:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。"
|
||||
exit 1 ;;
|
||||
|
||||
copilot)
|
||||
bin=$(cli_bin copilot)
|
||||
command -v "$bin" >/dev/null 2>&1 || skip "未偵測到 copilot 執行檔"
|
||||
instr="${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}"
|
||||
alias_line="alias $bin='sh \"$HERE/jsc-wrap.sh\" copilot'"
|
||||
rc_files | while IFS= read -r rc; do
|
||||
replace_block "$rc" "# jsc-hooks:copilot" "# /jsc-hooks:copilot" "$alias_line"
|
||||
done
|
||||
replace_block "$instr" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(ste100_text)" \
|
||||
|| skip "無法寫入 $instr"
|
||||
printf 'status=wired reason=%s\n' "wrapper 別名 + 規則檔已接線"
|
||||
echo "[jsc] copilot:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh copilot。"
|
||||
echo "[jsc] copilot:已在 $instr 寫入 STE100 規則段落。"
|
||||
exit 0 ;;
|
||||
|
||||
antigravity)
|
||||
bin=$(cli_bin antigravity)
|
||||
command -v "$bin" >/dev/null 2>&1 || skip "未偵測到 antigravity(agy)執行檔"
|
||||
rules="${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}"
|
||||
alias_line="alias $bin='sh \"$HERE/jsc-wrap.sh\" antigravity'"
|
||||
rc_files | while IFS= read -r rc; do
|
||||
replace_block "$rc" "# jsc-hooks:antigravity" "# /jsc-hooks:antigravity" "$alias_line"
|
||||
done
|
||||
replace_block "$rules" "<!-- jsc-hooks -->" "<!-- /jsc-hooks -->" "$(ste100_text)" \
|
||||
|| skip "無法寫入 $rules"
|
||||
printf 'status=wired reason=%s\n' "wrapper 別名 + 規則檔已接線"
|
||||
echo "[jsc] antigravity:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh antigravity。"
|
||||
echo "[jsc] antigravity:已在 $rules 寫入 STE100 規則段落。"
|
||||
exit 0 ;;
|
||||
|
||||
kiro)
|
||||
command -v "$(cli_bin kiro)" >/dev/null 2>&1 || skip "未偵測到 kiro-cli 執行檔"
|
||||
hookdir="./.kiro/hooks"
|
||||
hookfile="$hookdir/jsc-hooks.json"
|
||||
mkdir -p "$hookdir" 2>/dev/null || skip "無法建立 $hookdir"
|
||||
cat > "$hookfile" 2>/dev/null <<EOF || skip "無法寫入 $hookfile"
|
||||
{
|
||||
"name": "jsc-hooks",
|
||||
"description": "jsc session timer + STE100 guard bridge (auto-generated by jsc-hooks:hooks-install, do not edit by hand)",
|
||||
"on": ["sessionStart", "sessionEnd", "userPromptSubmit"],
|
||||
"env": { "JSC_CLI": "kiro" },
|
||||
"run": "sh \\"$HOOKS/session-timer.sh\\" mark; sh \\"$HOOKS/ste100-guard.sh\\""
|
||||
}
|
||||
EOF
|
||||
printf 'status=degraded reason=%s\n' "SDLC 模型鎖降級為技能步驟檢查"
|
||||
echo "[jsc] kiro:已建立 $hookfile(JSC_CLI=kiro)。"
|
||||
echo "[jsc] kiro:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。"
|
||||
exit 1 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user