Files
hooks/tools/wire-cli.sh
T
jiantw83 69eee91f27 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 佈線行為。
2026-08-24 14:47:46 +08:00

150 lines
6.8 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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