diff --git a/README.md b/README.md index f151eaac..862ff919 100644 --- a/README.md +++ b/README.md @@ -681,6 +681,9 @@ PROJECT_TYPE="typescript" CLAUDE_CODE_CMD="claude" # CLAUDE_CODE_CMD="npx @anthropic-ai/claude-code" # Alternative: use npx +# Optional Anthropic-compatible API base URL override +# CLAUDE_ANTHROPIC_BASE_URL="https://api.minimax.io/anthropic" + # Shell init file — source before running claude (useful for zsh/fish users # whose PATH or env vars are only set in their shell's init file) #RALPH_SHELL_INIT_FILE="~/.zshrc" diff --git a/ralph_loop.sh b/ralph_loop.sh index a7c2f1e0..260bdab2 100755 --- a/ralph_loop.sh +++ b/ralph_loop.sh @@ -78,6 +78,7 @@ _env_CLAUDE_CODE_CMD="${CLAUDE_CODE_CMD:-}" _env_CLAUDE_AUTO_UPDATE="${CLAUDE_AUTO_UPDATE:-}" _env_CLAUDE_MODEL="${CLAUDE_MODEL:-}" _env_CLAUDE_EFFORT="${CLAUDE_EFFORT:-}" +_env_CLAUDE_ANTHROPIC_BASE_URL="${CLAUDE_ANTHROPIC_BASE_URL:-}" _env_RALPH_SHELL_INIT_FILE="${RALPH_SHELL_INIT_FILE:-}" _env_ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-}" _env_ENABLE_BACKUP="${ENABLE_BACKUP:-}" @@ -113,6 +114,7 @@ CLAUDE_AUTO_UPDATE="${CLAUDE_AUTO_UPDATE:-true}" # Auto-update Claude CLI at st CLAUDE_CODE_CMD="${CLAUDE_CODE_CMD:-claude}" # Claude Code CLI command (default: global install) CLAUDE_MODEL="${CLAUDE_MODEL:-}" # Model override (e.g. claude-sonnet-4-6); empty = CLI default CLAUDE_EFFORT="${CLAUDE_EFFORT:-}" # Effort level override (e.g. high, low); empty = CLI default +CLAUDE_ANTHROPIC_BASE_URL="${CLAUDE_ANTHROPIC_BASE_URL:-}" # Anthropic-compatible API base URL override; empty = CLI default RALPH_SHELL_INIT_FILE="${RALPH_SHELL_INIT_FILE:-}" # Shell init file to source before running claude (e.g. ~/.zshrc) DRY_RUN="${DRY_RUN:-false}" # Simulate loop without making actual Claude API calls ENABLE_NOTIFICATIONS="${ENABLE_NOTIFICATIONS:-false}" # Enable desktop notifications; set true or use --notify flag @@ -328,6 +330,7 @@ load_ralphrc() { [[ -n "$_env_CLAUDE_AUTO_UPDATE" ]] && CLAUDE_AUTO_UPDATE="$_env_CLAUDE_AUTO_UPDATE" [[ -n "$_env_CLAUDE_MODEL" ]] && CLAUDE_MODEL="$_env_CLAUDE_MODEL" [[ -n "$_env_CLAUDE_EFFORT" ]] && CLAUDE_EFFORT="$_env_CLAUDE_EFFORT" + [[ -n "$_env_CLAUDE_ANTHROPIC_BASE_URL" ]] && CLAUDE_ANTHROPIC_BASE_URL="$_env_CLAUDE_ANTHROPIC_BASE_URL" [[ -n "$_env_RALPH_SHELL_INIT_FILE" ]] && RALPH_SHELL_INIT_FILE="$_env_RALPH_SHELL_INIT_FILE" [[ -n "$_env_ENABLE_NOTIFICATIONS" ]] && ENABLE_NOTIFICATIONS="$_env_ENABLE_NOTIFICATIONS" [[ -n "$_env_ENABLE_BACKUP" ]] && ENABLE_BACKUP="$_env_ENABLE_BACKUP" @@ -1559,6 +1562,11 @@ build_claude_command() { CLAUDE_CMD_ARGS+=("--effort" "$CLAUDE_EFFORT") fi + # Add Anthropic-compatible API base URL override (for custom providers) + if [[ -n "${CLAUDE_ANTHROPIC_BASE_URL:-}" ]]; then + CLAUDE_CMD_ARGS+=("--anthropic-base-url" "$CLAUDE_ANTHROPIC_BASE_URL") + fi + # Add output format flag if [[ "$CLAUDE_OUTPUT_FORMAT" == "json" ]]; then CLAUDE_CMD_ARGS+=("--output-format" "json") diff --git a/templates/ralphrc.template b/templates/ralphrc.template index 9f1baed1..baa66155 100644 --- a/templates/ralphrc.template +++ b/templates/ralphrc.template @@ -103,6 +103,10 @@ CLAUDE_AUTO_UPDATE=true # Minimum Claude CLI version required CLAUDE_MIN_VERSION="2.0.76" +# Optional Anthropic-compatible API base URL override for custom providers +# Example: MiniMax Claude-compatible endpoint +# CLAUDE_ANTHROPIC_BASE_URL="https://api.minimax.io/anthropic" + # Enable verbose logging RALPH_VERBOSE=false diff --git a/tests/unit/test_cli_modern.bats b/tests/unit/test_cli_modern.bats index d3228a3e..05fbeb63 100644 --- a/tests/unit/test_cli_modern.bats +++ b/tests/unit/test_cli_modern.bats @@ -192,6 +192,7 @@ setup() { _env_CLAUDE_CODE_CMD="${CLAUDE_CODE_CMD:-}" _env_CLAUDE_MODEL="${CLAUDE_MODEL:-}" _env_CLAUDE_EFFORT="${CLAUDE_EFFORT:-}" + _env_CLAUDE_ANTHROPIC_BASE_URL="${CLAUDE_ANTHROPIC_BASE_URL:-}" _env_RALPH_SHELL_INIT_FILE="${RALPH_SHELL_INIT_FILE:-}" load_ralphrc() { @@ -202,6 +203,7 @@ setup() { [[ -n "$_env_CLAUDE_CODE_CMD" ]] && CLAUDE_CODE_CMD="$_env_CLAUDE_CODE_CMD" [[ -n "$_env_CLAUDE_MODEL" ]] && CLAUDE_MODEL="$_env_CLAUDE_MODEL" [[ -n "$_env_CLAUDE_EFFORT" ]] && CLAUDE_EFFORT="$_env_CLAUDE_EFFORT" + [[ -n "$_env_CLAUDE_ANTHROPIC_BASE_URL" ]] && CLAUDE_ANTHROPIC_BASE_URL="$_env_CLAUDE_ANTHROPIC_BASE_URL" [[ -n "$_env_RALPH_SHELL_INIT_FILE" ]] && RALPH_SHELL_INIT_FILE="$_env_RALPH_SHELL_INIT_FILE" RALPHRC_LOADED=true return 0 @@ -548,6 +550,11 @@ build_claude_command() { CLAUDE_CMD_ARGS+=("--effort" "$CLAUDE_EFFORT") fi + # Add Anthropic-compatible API base URL override + if [[ -n "${CLAUDE_ANTHROPIC_BASE_URL:-}" ]]; then + CLAUDE_CMD_ARGS+=("--anthropic-base-url" "$CLAUDE_ANTHROPIC_BASE_URL") + fi + # Add output format flag if [[ "$CLAUDE_OUTPUT_FORMAT" == "json" ]]; then CLAUDE_CMD_ARGS+=("--output-format" "json") @@ -1937,6 +1944,17 @@ EOF assert_equal "$CLAUDE_EFFORT" "high" } +@test "CLAUDE_ANTHROPIC_BASE_URL from .ralphrc is loaded correctly" { + cat > "$TEST_DIR/.ralphrc" << 'EOF' +CLAUDE_ANTHROPIC_BASE_URL="https://api.minimax.io/anthropic" +EOF + _env_CLAUDE_ANTHROPIC_BASE_URL="" + CLAUDE_ANTHROPIC_BASE_URL="" + + load_ralphrc + assert_equal "$CLAUDE_ANTHROPIC_BASE_URL" "https://api.minimax.io/anthropic" +} + @test "CLAUDE_MODEL env var takes precedence over .ralphrc" { cat > "$TEST_DIR/.ralphrc" << 'EOF' CLAUDE_MODEL="claude-sonnet-4-6" @@ -1959,6 +1977,17 @@ EOF assert_equal "$CLAUDE_EFFORT" "high" } +@test "CLAUDE_ANTHROPIC_BASE_URL env var takes precedence over .ralphrc" { + cat > "$TEST_DIR/.ralphrc" << 'EOF' +CLAUDE_ANTHROPIC_BASE_URL="https://api.example.com/anthropic" +EOF + _env_CLAUDE_ANTHROPIC_BASE_URL="https://api.minimax.io/anthropic" + CLAUDE_ANTHROPIC_BASE_URL="https://api.minimax.io/anthropic" + + load_ralphrc + assert_equal "$CLAUDE_ANTHROPIC_BASE_URL" "https://api.minimax.io/anthropic" +} + @test "build_claude_command includes --model flag when CLAUDE_MODEL is set" { CLAUDE_MODEL="claude-sonnet-4-6" CLAUDE_EFFORT="" @@ -1981,15 +2010,29 @@ EOF [[ "${CLAUDE_CMD_ARGS[*]}" == *"high"* ]] } -@test "build_claude_command omits --model and --effort when not configured" { +@test "build_claude_command includes --anthropic-base-url flag when CLAUDE_ANTHROPIC_BASE_URL is set" { + CLAUDE_MODEL="" + CLAUDE_EFFORT="" + CLAUDE_ANTHROPIC_BASE_URL="https://api.minimax.io/anthropic" + echo "test prompt" > "$TEST_DIR/PROMPT.md" + + build_claude_command "$TEST_DIR/PROMPT.md" "" "" + + [[ "${CLAUDE_CMD_ARGS[*]}" == *"--anthropic-base-url"* ]] + [[ "${CLAUDE_CMD_ARGS[*]}" == *"https://api.minimax.io/anthropic"* ]] +} + +@test "build_claude_command omits --model, --effort, and --anthropic-base-url when not configured" { CLAUDE_MODEL="" CLAUDE_EFFORT="" + CLAUDE_ANTHROPIC_BASE_URL="" echo "test prompt" > "$TEST_DIR/PROMPT.md" build_claude_command "$TEST_DIR/PROMPT.md" "" "" [[ "${CLAUDE_CMD_ARGS[*]}" != *"--model"* ]] [[ "${CLAUDE_CMD_ARGS[*]}" != *"--effort"* ]] + [[ "${CLAUDE_CMD_ARGS[*]}" != *"--anthropic-base-url"* ]] } # ==============================================================================