#!/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" "" "" "$(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" "" "" "$(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" "" "" "$(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 <