#!/usr/bin/env sh # model-config.sh — 解析 SDLC 各階段的指定模型鏈(角色式路由)。 # 設定格式(一行一階段):stage=model[,fallback1[,fallback2...]] # 「#」開頭為註解,空白行忽略。階段限定:plan、analyze、implement、maintain。 # 解析順序: # 1. 目前工作目錄的 ./.jsc/models(專案覆寫) # 2. $JSC_HOME/models.conf(JSC_HOME 預設 ~/.jsc) # 專案檔有設定的階段完全覆寫全域設定;其餘階段仍讀全域檔。 # 用法: # model-config.sh get {stage} 印出該階段解析後的模型鏈(逗號分隔);未設定不印。兩種情況都 exit 0。 # model-config.sh list 每階段一行:stagechainsource(project 或 global);未設定的 chain 與 source 印「-」。 # model-config.sh resolve {stage} 印出該階段「目前 CLI 可用的第一個模型」單一名稱;鏈為空或無法判定時不印,exit 0。 set -u JSC_HOME="${JSC_HOME:-$HOME/.jsc}" PROJECT_FILE=./.jsc/models GLOBAL_FILE="$JSC_HOME/models.conf" STAGES="plan analyze implement maintain" # 取出單一設定檔裡某階段的模型鏈。 # 同階段出現多行時取最後一行,結果才有確定性。 read_chain() { # $1=設定檔 $2=階段 [ -f "$1" ] || return 0 awk -F= -v stage="$2" ' { sub(/\r$/, "") } # 去掉 CR /^[ \t]*#/ { next } # 註解行 /^[ \t]*$/ { next } # 空白行 index($0, "=") == 0 { next } { key = $1 gsub(/[ \t]/, "", key) if (key != stage) next val = substr($0, index($0, "=") + 1) gsub(/[ \t]/, "", val) if (val != "") chain = val } END { if (chain != "") print chain } ' "$1" } # 解析單一階段:專案檔優先,該階段沒設定才退回全域檔。 # 輸出:chainsource;兩檔都沒設定時不輸出。 resolve() { # $1=階段 chain=$(read_chain "$PROJECT_FILE" "$1") if [ -n "$chain" ]; then printf '%s\tproject\n' "$chain" return 0 fi chain=$(read_chain "$GLOBAL_FILE" "$1") if [ -n "$chain" ]; then printf '%s\tglobal\n' "$chain" fi return 0 } # 判斷目前是哪個 CLI 在跑(盡量而為,非必要)。 # 依 CLI 官方常見環境變數判斷;偵測不到就回傳 unknown。 current_cli() { [ -n "${CLAUDECODE:-}" ] && { echo claude; return 0; } [ -n "${CODEX_SANDBOX:-}${CODEX_HOME:-}" ] && { echo codex; return 0; } [ -n "${COPILOT_AGENT:-}" ] && { echo copilot; return 0; } [ -n "${ANTIGRAVITY_SESSION:-}" ] && { echo antigravity; return 0; } [ -n "${KIRO_SESSION:-}" ] && { echo kiro; return 0; } echo unknown } # resolve 子指令:印出「目前 CLI 可用的第一個模型」單一名稱。 # 簡化說明:可用性一律簡化為「直接取模型鏈的第一個模型」,不逐一檢查該模型是否真的能用。 # tools/list-models.sh 已經能讀各 CLI 設定檔列出模型,但五個 CLI 都沒有 # 「列出可用模型」的指令,設定檔沒寫出來的模型仍然查不到;拿這份清單當白名單過濾, # 會把合法但沒寫進設定檔的模型誤判成不可用,比不過濾更糟。 # 因此這裡只用 current_cli() 抓到的「目前 CLI 名稱」這個既有訊號做記錄。 # 之後若要依 CLI 實際可用模型過濾,等各 CLI 提供列出模型的指令,再比對 # list-models.sh 的輸出,逐一嘗試鏈上模型直到找到可用的。 resolve_first_usable() { # $1=階段 chain=$(resolve "$1" | cut -f1) [ -n "$chain" ] || return 0 cli=$(current_cli) : "目前偵測到的 CLI:$cli(本版本尚未用來篩選模型,見上方簡化說明)" printf '%s\n' "$chain" | cut -d, -f1 return 0 } usage() { echo "用法:model-config.sh get {plan|analyze|implement|maintain} | list | resolve {plan|analyze|implement|maintain}" >&2 exit 1 } case "${1:-}" in get) stage="${2:-}" case " $STAGES " in *" $stage "*) ;; *) usage ;; esac resolve "$stage" | cut -f1 exit 0 ;; resolve) stage="${2:-}" case " $STAGES " in *" $stage "*) ;; *) usage ;; esac resolve_first_usable "$stage" exit 0 ;; list) for stage in $STAGES; do line=$(resolve "$stage") if [ -n "$line" ]; then printf '%s\t%s\n' "$stage" "$line" else printf '%s\t-\t-\n' "$stage" fi done exit 0 ;; *) usage ;; esac