diff --git a/internal/claude/claude.go b/internal/claude/claude.go index 8d972e5..195810b 100644 --- a/internal/claude/claude.go +++ b/internal/claude/claude.go @@ -55,6 +55,18 @@ func BuildEnv(configDir string) []string { return env } +// UnsetEnv removes a named environment variable from an env slice. If the key +// is not present, the slice is returned unchanged. +func UnsetEnv(env []string, key string) []string { + prefix := key + "=" + for i, e := range env { + if len(e) >= len(prefix) && e[:len(prefix)] == prefix { + return append(env[:i], env[i+1:]...) + } + } + return env +} + // RunDirect executes the claude binary as a child process without a PTY // wrapper, connecting stdin/stdout/stderr directly. It returns the child's exit // code and any non-exit error. This is used by subcommands like "auth login" diff --git a/internal/claude/claude_test.go b/internal/claude/claude_test.go index 8e75bd2..3fdd324 100644 --- a/internal/claude/claude_test.go +++ b/internal/claude/claude_test.go @@ -102,6 +102,30 @@ func TestRunDirect_BinaryNotFound(t *testing.T) { assert.Error(t, err) } +func TestUnsetEnv_RemovesKey(t *testing.T) { + env := []string{"FOO=bar", "CLAUDE_CODE_USE_BEDROCK=1", "BAZ=qux"} + result := UnsetEnv(env, "CLAUDE_CODE_USE_BEDROCK") + assert.Equal(t, []string{"FOO=bar", "BAZ=qux"}, result) +} + +func TestUnsetEnv_KeyNotPresent(t *testing.T) { + env := []string{"FOO=bar", "BAZ=qux"} + result := UnsetEnv(env, "CLAUDE_CODE_USE_BEDROCK") + assert.Equal(t, []string{"FOO=bar", "BAZ=qux"}, result) +} + +func TestUnsetEnv_EmptyEnv(t *testing.T) { + result := UnsetEnv([]string{}, "FOO") + assert.Empty(t, result) +} + +func TestUnsetEnv_DoesNotMatchPrefix(t *testing.T) { + env := []string{"CLAUDE_CODE_USE_BEDROCK_EXTRA=1", "FOO=bar"} + result := UnsetEnv(env, "CLAUDE_CODE_USE_BEDROCK") + // Should not remove CLAUDE_CODE_USE_BEDROCK_EXTRA since it's a different key + assert.Equal(t, []string{"CLAUDE_CODE_USE_BEDROCK_EXTRA=1", "FOO=bar"}, result) +} + func TestBuildEnv_PreservesOtherVars(t *testing.T) { t.Setenv("MY_TEST_VAR_12345", "hello") diff --git a/internal/cli/passthrough.go b/internal/cli/passthrough.go index d7b9e72..048f45f 100644 --- a/internal/cli/passthrough.go +++ b/internal/cli/passthrough.go @@ -56,6 +56,14 @@ func runPassthrough(cmd *cobra.Command, _ []string) error { cfg := p.LoadConfig() env := claude.BuildEnv(p.ConfigDir) + + // When the profile has SSO credentials, strip provider flags that would + // override the auth method. The user's intent is to use the Anthropic API. + if authStatus == "keychain" || authStatus == "file" { + env = claude.UnsetEnv(env, "CLAUDE_CODE_USE_BEDROCK") + env = claude.UnsetEnv(env, "CLAUDE_CODE_USE_VERTEX") + } + env = setEnv(env, "CLAUDE_PROFILE_NAME", name) env = setEnv(env, "CLAUDE_PROFILE_AUTH", authStatus) env = setEnv(env, "CLAUDE_PROFILE_SUB", subType) @@ -113,7 +121,13 @@ func extractClaudeArgs() []string { // Version is set at build time via -ldflags (e.g., -ldflags "-X ...cli.Version=1.2.3"). // Falls back to VCS info embedded by Go, or "dev" if neither is available. -var Version = buildVersion() +var Version string + +func init() { + if Version == "" { + Version = buildVersion() + } +} func buildVersion() string { info, ok := debug.ReadBuildInfo()