feat(claude-profile): add provider selection (model + endpoint + credentials)
Introduces provider files (`data/claude-profile/providers/*.yaml`) that bundle model ID, API base URL, API key source, and extra env vars. Phase 2.5 (provider selection) added between mode selection and launch, with a default derived from the mode's driver field so existing Anthropic usage is unchanged. Ships 5 providers: anthropic-haiku/sonnet/opus/opus-1m and minimax-sonnet. MiniMax token decrypted from agent-runtimes SOPS env, stored at ~/.claude-secrets/minimax-auth-token. Adding new providers = one YAML file, no script changes. 58 dryrun tests passing (up from 37). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,18 @@ assert_contains() {
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
local label=$1 needle=$2 haystack=$3
|
||||
if [[ "$haystack" != *"$needle"* ]]; then
|
||||
printf " ${GREEN}PASS${NC} %s\n" "$label"
|
||||
((PASS++))
|
||||
else
|
||||
printf " ${RED}FAIL${NC} %s\n" "$label"
|
||||
printf " expected NOT to contain: %s\n" "$needle"
|
||||
((FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
assert_exit_code() {
|
||||
local label=$1 expected=$2 actual=$3
|
||||
if [[ "$expected" == "$actual" ]]; then
|
||||
@@ -61,27 +73,31 @@ assert_exit_code "--help exits 0" 0 "$rc"
|
||||
assert_contains "--help shows usage" "claude-profile" "$out"
|
||||
assert_contains "--help shows --mode" "--mode" "$out"
|
||||
assert_contains "--help shows --preset" "--preset" "$out"
|
||||
assert_contains "--help shows --provider" "--provider" "$out"
|
||||
|
||||
# === Test 2: chat dryrun ===
|
||||
echo
|
||||
echo "Test 2: chat mode dryrun"
|
||||
out=$(run --dryrun oreillyit --mode chat)
|
||||
out=$(run --dryrun oreillyit --mode chat --provider anthropic-haiku)
|
||||
rc=$?
|
||||
assert_exit_code "chat dryrun exits 0" 0 "$rc"
|
||||
assert_contains "chat shows haiku driver" "Driver: haiku" "$out"
|
||||
assert_contains "chat shows full haiku model ID" "claude-haiku-4-5-20251001" "$out"
|
||||
assert_contains "chat shows provider" "Provider: anthropic-haiku" "$out"
|
||||
assert_contains "chat shows model ID" "Model ID: claude-haiku-4-5-20251001" "$out"
|
||||
assert_contains "chat shows mode tag" "Mode: chat" "$out"
|
||||
assert_contains "chat says no changes" "No changes made" "$out"
|
||||
assert_contains "chat does not ask time horizon" "<not asked in this mode>" "$out"
|
||||
assert_contains "chat shows model in exec command" "claude --model claude-haiku-4-5-20251001" "$out"
|
||||
|
||||
# === Test 3: deep dryrun with project flag ===
|
||||
echo
|
||||
echo "Test 3: deep mode dryrun"
|
||||
out=$(run --dryrun oreillyit --mode deep --project cluster-bootstrap)
|
||||
out=$(run --dryrun oreillyit --mode deep --project cluster-bootstrap --provider anthropic-sonnet)
|
||||
rc=$?
|
||||
assert_exit_code "deep dryrun exits 0" 0 "$rc"
|
||||
assert_contains "deep shows sonnet driver" "Driver: sonnet" "$out"
|
||||
assert_contains "deep shows full sonnet model ID" "claude-sonnet-4-6" "$out"
|
||||
assert_contains "deep shows provider" "Provider: anthropic-sonnet" "$out"
|
||||
assert_contains "deep shows model ID" "Model ID: claude-sonnet-4-6" "$out"
|
||||
assert_contains "deep shows --model in command" "claude --model claude-sonnet-4-6" "$out"
|
||||
assert_contains "deep records the project" "Project: cluster-bootstrap" "$out"
|
||||
assert_contains "deep defaults time horizon to hours" "Time horizon: hours" "$out"
|
||||
@@ -92,30 +108,33 @@ assert_contains "deep says spec_driven yes" "Spec driven: yes" "$out"
|
||||
# === Test 4: orch dryrun preserves autoloop ===
|
||||
echo
|
||||
echo "Test 4: orch mode dryrun (autoloop preserved)"
|
||||
out=$(run --dryrun oreillyit --mode orch --project ai-image-gen)
|
||||
out=$(run --dryrun oreillyit --mode orch --project ai-image-gen --provider anthropic-sonnet)
|
||||
rc=$?
|
||||
assert_exit_code "orch dryrun exits 0" 0 "$rc"
|
||||
assert_contains "orch preserves the slash command in autoloop" "/loop 2m /orchestrate" "$out"
|
||||
assert_contains "orch driver is sonnet" "Driver: sonnet" "$out"
|
||||
assert_contains "orch provider is anthropic-sonnet" "Provider: anthropic-sonnet" "$out"
|
||||
|
||||
# === Test 5: hybrid dryrun ===
|
||||
echo
|
||||
echo "Test 5: hybrid mode dryrun"
|
||||
out=$(run --dryrun oreillyit --mode hybrid)
|
||||
out=$(run --dryrun oreillyit --mode hybrid --provider anthropic-haiku)
|
||||
rc=$?
|
||||
assert_exit_code "hybrid dryrun exits 0" 0 "$rc"
|
||||
assert_contains "hybrid uses haiku driver" "Driver: haiku" "$out"
|
||||
assert_contains "hybrid allows async" "Async OK: yes" "$out"
|
||||
assert_contains "hybrid shows provider" "Provider: anthropic-haiku" "$out"
|
||||
|
||||
# === Test 6: quick dryrun (no time horizon question) ===
|
||||
echo
|
||||
echo "Test 6: quick mode dryrun"
|
||||
out=$(run --dryrun oreillyit --mode quick)
|
||||
out=$(run --dryrun oreillyit --mode quick --provider anthropic-sonnet)
|
||||
rc=$?
|
||||
assert_exit_code "quick dryrun exits 0" 0 "$rc"
|
||||
assert_contains "quick uses sonnet driver" "Driver: sonnet" "$out"
|
||||
assert_contains "quick disables async" "Async OK: no" "$out"
|
||||
assert_contains "quick does not ask time horizon" "<not asked in this mode>" "$out"
|
||||
assert_contains "quick shows provider" "Provider: anthropic-sonnet" "$out"
|
||||
|
||||
# === Test 7: error — unknown mode ===
|
||||
echo
|
||||
@@ -144,11 +163,49 @@ assert_contains "missing preset error message" "not found" "$out"
|
||||
# === Test 10: pass-through args via -- ===
|
||||
echo
|
||||
echo "Test 10: pass-through args"
|
||||
out=$(run --dryrun oreillyit --mode chat -- --resume some-session)
|
||||
out=$(run --dryrun oreillyit --mode chat --provider anthropic-haiku -- --resume some-session)
|
||||
rc=$?
|
||||
assert_exit_code "pass-through exits 0" 0 "$rc"
|
||||
assert_contains "pass-through preserves --resume" "--resume some-session" "$out"
|
||||
|
||||
# === Test 11: minimax provider dryrun ===
|
||||
echo
|
||||
echo "Test 11: minimax provider dryrun"
|
||||
out=$(run --dryrun oreillyit --mode quick --provider minimax-sonnet)
|
||||
rc=$?
|
||||
assert_exit_code "minimax dryrun exits 0" 0 "$rc"
|
||||
assert_contains "minimax shows provider" "Provider: minimax-sonnet" "$out"
|
||||
assert_contains "minimax shows model ID" "Model ID: claude-sonnet-4-20250514" "$out"
|
||||
assert_contains "minimax shows base URL" "Base URL: https://api.minimax.io/anthropic" "$out"
|
||||
assert_contains "minimax shows API key env" "API key env: ANTHROPIC_AUTH_TOKEN" "$out"
|
||||
assert_contains "minimax references key file" "minimax-auth-token" "$out"
|
||||
assert_contains "minimax shows model in exec command" "claude --model claude-sonnet-4-20250514" "$out"
|
||||
|
||||
# === Test 12: error — unknown provider ===
|
||||
echo
|
||||
echo "Test 12: error path — unknown provider"
|
||||
out=$(run --dryrun oreillyit --mode quick --provider bogus-provider)
|
||||
rc=$?
|
||||
assert_exit_code "unknown provider exits 1" 1 "$rc"
|
||||
assert_contains "unknown provider error message" "Provider not found" "$out"
|
||||
|
||||
# === Test 13: default provider from driver (no --provider flag) ===
|
||||
echo
|
||||
echo "Test 13: default provider selected from driver"
|
||||
out=$(run --dryrun oreillyit --mode chat)
|
||||
rc=$?
|
||||
assert_exit_code "default provider dryrun exits 0" 0 "$rc"
|
||||
assert_contains "chat default provider is anthropic-haiku" "Provider: anthropic-haiku" "$out"
|
||||
assert_contains "chat default model ID is haiku" "Model ID: claude-haiku-4-5-20251001" "$out"
|
||||
|
||||
# === Test 14: --preset + --provider mutual exclusion ===
|
||||
echo
|
||||
echo "Test 14: error path — --preset + --provider mutual exclusion"
|
||||
out=$(run --dryrun --preset whatever --provider minimax-sonnet)
|
||||
rc=$?
|
||||
assert_exit_code "preset+provider exits 1" 1 "$rc"
|
||||
assert_contains "preset+provider error message" "mutually exclusive" "$out"
|
||||
|
||||
# === Summary ===
|
||||
echo
|
||||
echo "================================"
|
||||
|
||||
Reference in New Issue
Block a user