Switch between multiple AI CLI accounts and profiles in a single shell. Today: Codex and Claude Code. Built so more providers slot in without rewriting the CLI surface.
- Quick start
- Why
- Requirements
- Key features
- Install
- Shell setup
- How it works
- Usage
- Configuration
- Roadmap
- License
# Install via Homebrew (macOS)
brew install baedonguri/tap/aiwitch
# Enable in-shell switching (add to ~/.zshrc; bash/fish similar — see Shell setup)
eval "$(aiwitch shell init zsh)"
# Add profiles (registers and runs the provider's login flow)
aiwitch add codex personal # ChatGPT login by default
aiwitch add codex work
aiwitch add claude personal # spawns `claude`; run `/login` inside the REPL
# Switch the current shell to a profile
aiwitch use personal
# Then run the provider's CLI with that profile's account
codex # or `claude`
aiwitch add <provider> <profile>triggers the provider's login flow for the new profile. Codex supports--auth apifor API-key login (see Login); Claude is interactive-only — log in via/logininside the spawnedclaudeTUI.
Running multiple AI CLI accounts — personal, work, an API-key-only sandbox — means juggling CODEX_HOME / CLAUDE_CONFIG_DIR and shuffling auth files by hand. aiwitch keeps each account in its own home directory and switches the active one in your current shell with a single command.
Inspired by nvm/pyenv-style version switchers, applied to AI CLI accounts.
- macOS or Linux with Rust 1.85+ (Windows is not supported in v0.1.0)
- Homebrew on macOS, if you want the
brew installpath - Codex CLI and/or Claude Code installed and on
PATH(only the providers you intend to use) - For Codex API-key login: an OpenAI API key on stdin (
$OPENAI_API_KEYor piped)
- Per-profile provider home — every profile has an isolated directory (
CODEX_HOMEfor Codex,CLAUDE_CONFIG_DIRfor Claude); switching is a single env-var swap. See How it works. - In-shell switching —
aiwitch use <profile>andaiwitch add <provider> <profile>mutate the current shell via a small snippet for zsh, bash, and fish. - Two Codex auth modes — ChatGPT login and API-key login; the latter reads the key from stdin and pipes it straight to
codexwithoutaiwitchpersisting it. - Claude interactive login —
aiwitch add claude <profile>provisions an isolatedCLAUDE_CONFIG_DIRand spawns theclaudeTUI; users log in via/logininside the REPL. - TOML config — a single
~/.config/aiwitch/profiles.tomlwith strict name validation and duplicate detection. - Backend trait — adding a new provider is a
Backendimpl, not a CLI rewrite.
brew install baedonguri/tap/aiwitchAfter tapping once:
brew tap baedonguri/tap
brew install aiwitchRequires Rust 1.85+.
cargo install --git https://github.com/baedonguri/aiwitch --tag v0.2.0aiwitch switches profiles by exporting environment variables in the current shell, so it needs a small shell function.
# zsh — add to ~/.zshrc
eval "$(aiwitch shell init zsh)"
# bash — add to ~/.bashrc
eval "$(aiwitch shell init bash)"
# fish — add to ~/.config/fish/config.fish
aiwitch shell init fish | sourceThis wires up two extra subcommands:
| Command | What it does |
|---|---|
aiwitch use <profile> |
Switch the current shell to an existing profile. |
aiwitch add <provider> <profile> |
Add a profile, run its login flow, and activate it in the current shell. |
Without shell init you can still drive everything manually via eval "$(aiwitch env <profile>)".
Each provider picks up its account from a directory pointed to by an environment variable — CODEX_HOME for Codex, CLAUDE_CONFIG_DIR for Claude Code. aiwitch keeps one such directory per profile and exposes them as named entries in ~/.config/aiwitch/profiles.toml.
~/.codex-work/ ← CODEX_HOME for codex/"work"
└── auth.json
~/.codex-personal/ ← CODEX_HOME for codex/"personal"
└── auth.json
~/.claude-personal/ ← CLAUDE_CONFIG_DIR for claude/"personal"
└── (managed by Claude Code; on Linux a `.credentials.json` lives here)
aiwitch use <profile> evaluates to export <PROVIDER_VAR>=... and export AIWITCH_CURRENT=... (or set -gx on fish). The next codex / claude invocation picks up the right account; aiwitch current reads AIWITCH_CURRENT. No background daemon, no symlink swapping, no global state to corrupt.
macOS note for Claude profiles: Claude Code stores OAuth credentials in the system Keychain on macOS, not inside
CLAUDE_CONFIG_DIR.aiwitch listwill showemail=-,plan=-,expires=-for Claude profiles even after a successful login — that's expected, not a bug. On Linux,CLAUDE_CONFIG_DIR/.credentials.jsonis parsed best-effort.
aiwitch add <provider> <profile> registers the profile and immediately runs the provider's login flow. The provider is required; supported values are codex and claude.
aiwitch add codex personal # Codex ChatGPT login
echo "$OPENAI_API_KEY" | aiwitch add codex work --auth api # Codex API-key login (stdin)
aiwitch add claude personal # Claude — spawns `claude` TUI; run `/login`| Flag | Meaning |
|---|---|
--home <PATH> |
Directory used as the provider home. Optional; defaults to ~/.codex-<profile> (codex) or ~/.claude-<profile> (claude). |
--auth <chatgpt|api> |
Codex only. Login mode; defaults to chatgpt. Rejected with an explicit error for claude (Claude is interactive-only). |
If login fails or is canceled, the profile is still registered. Retry with
aiwitch login <profile>(add--api-keyfor API-key mode).
aiwitch listShows each profile's name, provider, email, plan, and token expiry. The active profile (per provider) is marked.
Requires shell setup — aiwitch use is a shell function, not a binary subcommand.
aiwitch use work
aiwitch current # → workaiwitch run <profile> -- <cmd> spawns <cmd> with the profile's provider env vars set, without mutating the current shell. The -- separator is recommended so flags are passed to the child instead of consumed by aiwitch.
# Current shell stays on `personal`; just this codex invocation runs as `work`.
aiwitch run work -- codex exec "fix this bug"
# Useful in CI / scripts where `eval "$(aiwitch env ...)"` is awkward.
aiwitch run ci-bot -- codex --versionThe child inherits the parent environment and aiwitch overlays only CODEX_HOME / CLAUDE_CONFIG_DIR and AIWITCH_CURRENT. Other variables are not stripped — if OPENAI_API_KEY or ANTHROPIC_API_KEY are exported in your shell, the provider CLI may use them instead of the profile's stored credentials. To run with a cleaner environment, use standard tools:
env -u OPENAI_API_KEY aiwitch run work -- codex # drop one variable
env -i HOME="$HOME" PATH="$PATH" aiwitch run work -- codex # nuke everything elseThe exit code of <cmd> is propagated; on Unix, signal-terminated children are reported as 128 + signal.
aiwitch doctorReports per-profile status (home dir, login, token expiry) and global checks (provider CLI on PATH, AIWITCH_CURRENT). Exits non-zero on any [err]; warnings don't affect the exit code.
# Codex — ChatGPT login (delegates to codex)
aiwitch login work
# Codex — API key login: aiwitch reads the key from stdin and pipes it to codex.
# aiwitch itself never persists the key.
echo "$OPENAI_API_KEY" | aiwitch login work --api-key
# Claude — interactive only: spawns `claude`; log in via `/login` inside the REPL.
aiwitch login claude-personal
aiwitch login <claude-profile> --api-keyis rejected with an explicit error before stdin is read; Claude does not yet have an API-key login flow.
aiwitch env work # POSIX: export K='v'
aiwitch env work --shell fish # fish: set -gx K 'v'Profiles are stored in ~/.config/aiwitch/profiles.toml:
[[profiles]]
name = "work"
backend = "codex"
home_dir = "~/.codex-work"
[[profiles]]
name = "personal"
backend = "codex"
home_dir = "~/.codex-personal"
[[profiles]]
name = "claude-personal"
backend = "claude"
home_dir = "~/.claude-personal"The file is validated on every load: names must be [A-Za-z0-9_-]+ (no leading dash), and duplicate names are rejected.
- More providers (Gemini, …) behind the existing
Backendtrait. - Prebuilt Homebrew bottles to skip the Rust build step.
- Optional encrypted secret storage (today
aiwitchdoes not store secrets — they live in the provider's own home dir).