detect-clis.sh 與 list-models.sh 一律回零:找不到執行檔,或讀不到設定檔, 都只是少掉那一列,不算失敗。這個約定過去只存在讀過腳本的人腦袋裡。 呼叫端很容易拿結束碼去判斷「這台機器有沒有裝 CLI」,然後永遠判斷錯。 現在把它寫在檔頭,兩支腳本的行為都不動。 README 的腳本表與技能說明同步更新,讀 README 的人看到的流程,才跟技能 裡實際寫的一致。
87 lines
3.6 KiB
Bash
Executable File
87 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env sh
|
||
# list-models.sh — 讀各 AI CLI 的設定檔,列出模型與目前使用中的模型。
|
||
# 輸出(TSV,一行一個模型):cli<TAB>model<TAB>in-use(in-use 為 yes 或 no)
|
||
# 設定檔不存在或裡面沒寫模型,就不輸出該 CLI 的列;一律 exit 0。
|
||
# 設定檔位置只寫在這裡,SKILL.md 不再抄一份:
|
||
# claude ${CLAUDE_CONFIG_DIR:-~/.claude}/settings.json 的 model;ANTHROPIC_MODEL 可覆寫使用中的模型
|
||
# codex ${CODEX_HOME:-~/.codex}/config.toml:頂層 model 為使用中,[profiles.*] 的 model 併入清單
|
||
# copilot ~/.copilot/settings.json,退回 ~/.config/copilot/settings.json
|
||
# antigravity ~/.antigravity/settings.json,退回 ~/.config/antigravity/settings.json
|
||
# kiro ~/.kiro/settings/cli.json,退回 ~/.config/kiro/settings.json
|
||
# 五個 CLI 都沒有「列出可用模型」的指令,所以清單只到設定檔寫出來的模型。
|
||
# 沒有列的 CLI 由 skills/models 的 sub agent 補上預設模型並標註「預設推定」。
|
||
# 結束碼: 0=一律回 0。設定檔不存在、讀不到或裡面沒寫模型,都只是少掉那個 CLI 的列,
|
||
# 不算失敗,所以沒有其他碼。呼叫端要看輸出有沒有該 CLI 的列,
|
||
# 不要拿結束碼判斷讀不讀得到設定檔。
|
||
set -u
|
||
|
||
# 取第一個存在的檔案;都不存在就不印。
|
||
first_file() { # $@=候選路徑
|
||
for f in "$@"; do
|
||
[ -f "$f" ] && { printf '%s\n' "$f"; return 0; }
|
||
done
|
||
return 0
|
||
}
|
||
|
||
# 取 JSON 檔裡某個鍵的所有字串值,一行一個,順序照檔案。
|
||
json_val() { # $1=檔案 $2=鍵名
|
||
[ -n "$1" ] || return 0
|
||
[ -f "$1" ] || return 0
|
||
grep -oE "\"$2\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" "$1" 2>/dev/null \
|
||
| sed -e 's/^[^:]*:[[:space:]]*"//' -e 's/"$//'
|
||
return 0
|
||
}
|
||
|
||
# 取 TOML 檔裡所有 model 設定值,一行一個,順序照檔案。
|
||
# 只認行首的 model=,所以 model_reasoning_effort 這類鍵不會誤中。
|
||
toml_val() { # $1=檔案
|
||
[ -n "$1" ] || return 0
|
||
[ -f "$1" ] || return 0
|
||
grep -E '^[[:space:]]*model[[:space:]]*=' "$1" 2>/dev/null \
|
||
| sed -e 's/^[^=]*=[[:space:]]*//' -e 's/[[:space:]]*#.*$//' \
|
||
-e 's/^"//' -e 's/"[[:space:]]*$//' -e "s/^'//" -e "s/'[[:space:]]*$//"
|
||
return 0
|
||
}
|
||
|
||
# 印出某 CLI 的列:使用中的模型排第一,重複的只留一筆。
|
||
emit() { # $1=cli $2=使用中的模型(可空) $3=模型清單(換行分隔,可空)
|
||
printf '%s\n%s\n' "$2" "$3" | awk -v cli="$1" -v cur="$2" '
|
||
{ gsub(/^[ \t]+|[ \t]+$/, "") }
|
||
$0 == "" { next }
|
||
seen[$0]++ { next }
|
||
{ printf "%s\t%s\t%s\n", cli, $0, ($0 == cur ? "yes" : "no") }'
|
||
}
|
||
|
||
# claude
|
||
cfg=$(first_file "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json")
|
||
list=$(json_val "$cfg" model)
|
||
cur="${ANTHROPIC_MODEL:-}"
|
||
[ -n "$cur" ] || cur=$(printf '%s\n' "$list" | head -n1)
|
||
emit claude "$cur" "$list"
|
||
|
||
# codex
|
||
cfg=$(first_file "${CODEX_HOME:-$HOME/.codex}/config.toml")
|
||
list=$(toml_val "$cfg")
|
||
cur=$(printf '%s\n' "$list" | head -n1)
|
||
emit codex "$cur" "$list"
|
||
|
||
# copilot
|
||
cfg=$(first_file "$HOME/.copilot/settings.json" "$HOME/.config/copilot/settings.json")
|
||
list=$(json_val "$cfg" model)
|
||
cur=$(printf '%s\n' "$list" | head -n1)
|
||
emit copilot "$cur" "$list"
|
||
|
||
# antigravity
|
||
cfg=$(first_file "$HOME/.antigravity/settings.json" "$HOME/.config/antigravity/settings.json")
|
||
list=$(json_val "$cfg" model)
|
||
cur=$(printf '%s\n' "$list" | head -n1)
|
||
emit antigravity "$cur" "$list"
|
||
|
||
# kiro
|
||
cfg=$(first_file "$HOME/.kiro/settings/cli.json" "$HOME/.config/kiro/settings.json")
|
||
list=$(json_val "$cfg" model)
|
||
cur=$(printf '%s\n' "$list" | head -n1)
|
||
emit kiro "$cur" "$list"
|
||
|
||
exit 0
|