Files
cli/tools/model-config.sh
jiantw83 e8bf4ddaaf fix(cli): 分開「模型不合格」與「腳本被叫錯」的結束碼
model-tags.sh 的 gate 原本用同一個結束碼表示兩件事:模型缺能力標籤,
以及這支腳本被叫錯。delegate 讀到用法錯誤時,會當成模型沒通過檢查,
默默把一個能用的模型丟掉。真正的錯在哪,永遠不會浮出來。現在用法錯誤
改走另一個碼,兩種「無法判定」也歸到同一個碼,呼叫端只要認碼就分得出
三種結果。model-config.sh 照同一套規則調整,兩支腳本的契約才一致。

check-requires.sh 在沒有 python3 的機器上,會直接讓 shell 回一個沒宣告過
的碼,呼叫端讀不到原因。現在先確認 python3 在不在,並印出擋下的理由,
manifest 相依檢查才是真的做得到的事。

delegate 的七個步驟原本沒有任何完成條件,引用了不存在的 plugin,也把
CLI 偵測與標籤篩選寫成文字敘述,可是這兩件事早就有腳本負責。挑模型那
一步需要模型 id,全篇卻沒有任何步驟產得出來。現在每一步都寫出完成條件,
資料一律取自腳本,模型夠不夠格由結束碼判定,不讓模型自評標籤。

setup 的環境變數重驗原本去讀目前這個 shell。可是寫進 rc 檔的值,要等新的
shell 起來才存在,所以重驗永遠回報「沒設到」,把修好的項目誤判成失敗。
現在只驗 rc 段落裡確實有那一行,環境層面交給下一次體檢。
2026-08-31 11:09:59 +08:00

125 lines
4.5 KiB
Bash
Executable File
Raw Permalink 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
# 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 每階段一行:stage<TAB>chain<TAB>source(project 或 global);未設定的 chain 與 source 印「-」。
# model-config.sh resolve {stage} 印出該階段「目前 CLI 可用的第一個模型」單一名稱;鏈為空或無法判定時不印,exit 0。
# 結束碼:0=查詢完成(查得到、查不到都算完成,判斷交給呼叫端)
# 2=用法錯誤(子命令不認得,或階段不在 plan、analyze、implement、maintain 之內)
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"
}
# 解析單一階段:專案檔優先,該階段沒設定才退回全域檔。
# 輸出:chain<TAB>source;兩檔都沒設定時不輸出。
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 2
}
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