Sub-100ms account switching for AI coding CLIs with fixed-cost subscription plans. When you hit usage limits on Claude Max, GPT Pro, or Gemini Ultra, don't wait 60 seconds for browser OAuthβjust swap to another account instantly.
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_account_manager/main/install.sh?$(date +%s)" | bashUsage:
caam backup claude [email protected] # Save current auth
caam activate claude [email protected] # Switch instantlyUse --json in agent contexts. stdout = data, stderr = diagnostics, exit 0 = success.
# List available profiles (machine-readable)
caam list --json
# Show current status for all tools
caam status --json
# Switch accounts
caam activate claude [email protected] --jsonYou're paying $200-275/month for fixed-cost AI coding subscriptions (Claude Max, GPT Pro, Gemini Ultra). These plans have usage limitsβnot billing caps, but rate limits that reset over time. When you hit them mid-flow, the official way to switch accounts:
/login β browser opens β sign out of Google β sign into different Google β
authorize app β wait for redirect β back to terminal
That's 30-60 seconds of friction. Multiply by 5+ switches per day across multiple tools.
Each AI CLI stores OAuth tokens in plain files. caam backs them up and restores them:
caam activate claude [email protected] # ~50ms, doneNo browser. No OAuth dance. No interruption to your flow state.
flowchart LR
subgraph System["Your System"]
A["~/.claude.json"]
B["~/.codex/auth.json"]
C["~/.gemini/settings.json"]
end
subgraph Vault["~/.local/share/caam/vault/"]
D["claude/[email protected]/"]
E["claude/[email protected]/"]
F["codex/[email protected]/"]
end
A <-->|"backup / activate"| D
A <-->|"backup / activate"| E
B <-->|"backup / activate"| F
style System fill:#1a1a2e,stroke:#4a4a6a,color:#fff
style Vault fill:#16213e,stroke:#4a4a6a,color:#fff
That's it. No external database servers (uses embedded SQLite), no required daemons (optional background service available). Just cp with extra steps.
OAuth tokens are bearer tokensβpossession equals access. The CLI tools don't fingerprint your machine beyond what's already in the token file. Swapping files is equivalent to "being" that authenticated session.
caam status uses content hashing to detect the active profile:
- SHA-256 hash current auth files
- Compare against all vault profiles
- Match = that's what's active
This means:
- Profiles are detected even if you switched manually
- No hidden state files that can desync
- Works correctly after reboots
Swap auth files in place. One account active at a time per tool. Instant switching.
caam backup claude [email protected]
caam activate claude [email protected]Use when: You want to switch between accounts sequentially (most common use case).
Run multiple accounts simultaneously with full directory isolation.
caam profile add codex [email protected]
caam profile add codex [email protected]
caam exec codex [email protected] -- "implement feature X"
caam exec codex [email protected] -- "review code"Each profile gets its own $HOME and $CODEX_HOME with symlinks to your real .ssh, .gitconfig, etc.
Use when: You need two accounts running at the same time in different terminals.
| Tool | Auth Location | Login Command |
|---|---|---|
| Claude Code | OAuth: ~/.claude.json + ~/.config/claude-code/auth.json β’ API key: ~/.claude/settings.json |
/login in CLI |
| Codex CLI | ~/.codex/auth.json (file store enforced) |
codex login (or --device-auth) |
| Gemini CLI | OAuth: ~/.gemini/settings.json (+ oauth_creds.json) β’ API key: ~/.gemini/.env |
gemini interactive |
Subscription: Claude Max ($200/month)
Auth Files:
~/.claude.jsonβ Main authentication token~/.config/claude-code/auth.jsonβ Secondary auth data~/.claude/settings.jsonβ API key mode viaapiKeyHelper
Login Command: Inside Claude Code, type /login
Notes: Claude Max has a 5-hour rolling usage window. When you hit it, you'll see rate limit messages. Switch accounts to continue.
Limitations:
- Email/Identity Detection: Claude's current auth format does not expose email or account ID. Profile names default to timestamp-based auto-names (
auto-YYYYMMDD-HHMMSS) unless you specify a name when backing up. - Automatic Token Refresh: Claude Code manages token refresh internally. CAAM cannot refresh Claude tokensβuse
/loginin Claude Code if tokens expire. - Usage API: Claude's usage API is undocumented and may not be reliable.
Subscription: GPT Pro ($200/month unlimited)
Auth Files:
~/.codex/auth.json(or$CODEX_HOME/auth.json)
Login Command: codex login (or codex login --device-auth for headless)
Notes: Respects CODEX_HOME. CAAM enforces file-based auth storage by writing cli_auth_credentials_store = "file" to ~/.codex/config.toml inside the profile.
Subscription: Gemini Ultra ($275/month)
Auth Files:
~/.gemini/settings.json~/.gemini/oauth_creds.json(OAuth cache)~/.gemini/.env(API key mode)
Login Command: Start gemini, select "Login with Google" or use /auth to switch modes
Notes: For CAAM, Gemini Ultra behaves like Claude Max and GPT Pro: OAuth tokens are stored locally and can be swapped instantly.
# After logging into Claude normally
caam backup claude [email protected]caam clear claude # Remove current auth
claude # Login as [email protected] via /login
caam backup claude [email protected] # Save itcaam activate claude [email protected] # Back to Alice
caam activate claude [email protected] # Back to Bob$ caam status
claude: [email protected] (active)
codex: [email protected] (active)
gemini: (no auth files)
$ caam ls claude
[email protected]
[email protected]
[email protected]| Command | Description |
|---|---|
caam backup <tool> <email> |
Save current auth files to vault |
caam activate <tool> <email> |
Restore auth files from vault (instant switch!) |
caam status [tool] |
Show which profile is currently active |
caam ls [tool] |
List all saved profiles in vault |
caam delete <tool> <email> |
Remove a saved profile |
caam paths [tool] |
Show auth file locations for each tool |
caam clear <tool> |
Remove auth files (logout state) |
caam alias <tool> <profile> <alias> |
Create a short alias for a profile |
caam rename <tool> <old> <new> |
Copy profile to a new name (non-destructive) |
caam uninstall |
Restore originals from _original and remove caam data/config |
Aliases: caam switch and caam use work like caam activate
Use caam pick when you want the fastest possible profile swap:
caam pick claude # fzf if installed; numbered prompt otherwise
caam pick # uses your default_provider if setSet a default provider so you can omit the tool name:
caam config set default_provider claudeAliases make long emails painless (works for pick and activate):
caam alias claude work-account-1 work
caam pick claude # type "work" at the prompt
caam activate claude work # alias resolution works here tooRename auto-generated profiles to friendly names (non-destructive copy):
caam rename claude auto-20260121-143022 work # Copy profile to "work"
caam rename claude old-name new-name # Original preserved by default
caam rename claude temp main --delete-old -y # Delete old after copyingSSH-safe fallback (no fzf, no TTY): use direct activation:
caam activate claude work-account-1fzf one-liner (if you prefer piping):
sel=$(caam ls claude | fzf --prompt 'claude> ') && [ -n "$sel" ] && caam activate claude "$sel"| Command | Description |
|---|---|
caam activate <tool> --auto |
Auto-select the best profile using rotation algorithm |
caam next <tool> |
Preview which profile rotation would select (dry-run) |
caam run <tool> [-- args] |
Wrap CLI execution with automatic failover on rate limits |
caam cooldown set <provider/profile> |
Mark profile as rate-limited (default: 60min cooldown) |
caam cooldown list |
List active cooldowns with remaining time |
caam cooldown clear <provider/profile> |
Clear cooldown for a specific profile |
caam cooldown clear --all |
Clear all active cooldowns |
caam project set <tool> <profile> |
Associate current directory with a profile |
caam project get [tool] |
Show project associations for current directory |
Options for caam run:
--max-retries Nβ Maximum retry attempts on rate limit (default: 1)--cooldown DURATIONβ Cooldown duration after rate limit (default: 60m)--algorithm NAMEβ Rotation algorithm: smart, round_robin, random--quietβ Suppress profile switch notifications
Options for caam activate:
--autoβ Use rotation algorithm to pick best profile--backup-currentβ Backup current auth before switching--forceβ Activate even if profile is in cooldown
When stealth.cooldown.enabled is true in config, caam activate warns if the target profile is in cooldown and prompts for confirmation. Use --force to bypass.
When stealth.rotation.enabled is true, caam activate <tool> automatically falls back to rotation if the default profile is in cooldown.
caam uninstall restores auth from any available _original backups first, then removes caamβs data/config. Useful flags:
--dry-runshows what would be restored/removed--keep-backupskeeps the vault after restoring originals--forceskips the confirmation prompt
| Command | Description |
|---|---|
caam profile add <tool> <email> |
Create isolated profile directory |
caam profile ls [tool] |
List isolated profiles |
caam profile delete <tool> <email> |
Delete isolated profile |
caam profile status <tool> <email> |
Show isolated profile status |
caam login <tool> <email> |
Run login flow for isolated profile |
caam exec <tool> <email> [-- args] |
Run CLI with isolated profile |
When you have multiple accounts across multiple providers, manually tracking which account has headroom, which one just hit a limit, and which one you used recently becomes tedious. Smart Profile Management automates this decision-making so you can focus on coding instead of account juggling.
Each profile displays a health indicator showing its current state at a glance:
| Icon | Status | Meaning |
|---|---|---|
| π’ | Healthy | Token valid for >1 hour, no recent errors |
| π‘ | Warning | Token expiring within 1 hour, or minor issues |
| π΄ | Critical | Token expired, or repeated errors in the last hour |
| βͺ | Unknown | No health data available yet |
Health scoring combines multiple factors:
- Token expiry: How long until the OAuth token expires
- Error history: Recent authentication or rate limit errors
- Penalty score: Accumulated issues with automatic decay over time
- Plan type: Enterprise/Pro plans get slight scoring boosts
The penalty system uses exponential decay (20% reduction every 5 minutes) so temporary issues don't permanently mark a profile as unhealthy. After about 30 minutes of no errors, a profile's penalty score returns to near zero.
When you run caam activate claude --auto, the rotation system picks the best profile for you. Three algorithms are available:
Smart (Default): Multi-factor scoring that considers:
- Cooldown state (profiles in cooldown are excluded)
- Health status (prefers healthy profiles)
- Recency (avoids profiles used in the last 30 minutes)
- Plan type (slight preference for higher-tier plans)
- Random jitter (breaks ties unpredictably)
Round Robin: Simple sequential rotation through profiles, skipping any in cooldown. Predictable and even distribution.
Random: Purely random selection among non-cooldown profiles. Least predictable but may cluster usage.
Configure the algorithm in ~/.caam/config.yaml:
stealth:
rotation:
enabled: true
algorithm: smart # smart | round_robin | randomWhen an account hits a rate limit, you can mark it as "in cooldown" so rotation algorithms skip it:
# Mark current Claude profile as rate-limited (default: 60 min cooldown)
caam cooldown set claude
# Or specify a profile and duration
caam cooldown set claude/[email protected] --minutes 120
# View active cooldowns
caam cooldown list
# Clear a cooldown early
caam cooldown clear claude/[email protected]When cooldown enforcement is enabled (stealth.cooldown.enabled: true), attempting to activate a profile in cooldown will warn you and prompt for confirmation. This prevents accidentally switching back to an account that just hit limits.
The caam run command wraps your AI CLI execution and automatically handles rate limits:
# Instead of running claude directly:
caam run claude -- "explain this code"
# If Claude hits a rate limit mid-session:
# 1. Current profile goes into cooldown
# 2. Next best profile is automatically selected
# 3. Command is re-executed with new accountFor seamless integration, add shell aliases:
alias claude='caam run claude --'
alias codex='caam run codex --'
alias gemini='caam run gemini --'Now you can use claude "explain this code" and rate limits are handled transparently.
Configuration options:
caam run claude --max-retries 2 --cooldown 90m --algorithm smart -- "your prompt"Link specific profiles to project directories so you don't have to remember which account to use where:
# In your work project directory
cd ~/projects/work-app
caam project set claude [email protected]
# Now whenever you're in this directory (or subdirectories)
caam activate claude # Automatically uses [email protected]
# The TUI also shows the project association
caam tui
# Status bar shows: Project: ~/projects/work-app β [email protected]Associations cascade: if you set an association on /home/user/projects, it applies to all subdirectories unless a more specific association exists.
In the TUI, press p to set the current profile as the default for your current directory.
Before committing to a rotation selection, preview what the algorithm would pick:
$ caam next claude
Recommended: [email protected]
+ Healthy token (expires in 4h 32m)
+ Not used recently (2h ago)
Alternatives:
[email protected] - Used recently (15m ago)
In cooldown:
[email protected] - In cooldown (45m remaining)This is useful for understanding why rotation is making certain choices, or for scripting conditional logic around account selection.
# Morning: Check what's active
caam status
# claude: [email protected] (active)
# codex: [email protected] (active)
# gemini: [email protected] (active)
# Afternoon: Hit Claude usage limit
caam activate claude [email protected]
# Activated claude profile '[email protected]'
claude # Continue working immediately with new account# 1. Login to first account using normal flow
claude
# Inside Claude: /login β authenticate with [email protected]
# 2. Backup the auth using the email as the profile name
caam backup claude [email protected]
# 3. Clear and login to second account
caam clear claude
claude
# Inside Claude: /login β authenticate with [email protected]
# 4. Backup that too
caam backup claude [email protected]
# 5. Now you can switch instantly forever!
caam activate claude [email protected] # < 100ms
caam activate claude [email protected] # < 100ms# Create isolated profiles
caam profile add codex [email protected]
caam profile add codex [email protected]
# Login to each (one-time, uses browser)
caam login codex [email protected] # Opens browser for work account
caam login codex [email protected] # Opens browser for personal account
# Run simultaneously in different terminals
caam exec codex [email protected] -- "implement auth system"
caam exec codex [email protected] -- "review PR #123"# Let rotation pick the best profile automatically
caam activate claude --auto
# Using rotation: claude/[email protected]
# Recommended: [email protected]
# + Healthy token (expires in 4h 32m)
# + Not used recently (2h ago)
# Hit a rate limit during your session? Mark it
caam cooldown set claude
# Recorded cooldown for claude/[email protected] until 14:30 (58m remaining)
# Next activation automatically picks another profile
caam activate claude --auto
# Using rotation: claude/[email protected]
# Recommended: [email protected]
# + Healthy status
# In cooldown:
# [email protected] - In cooldown (57m remaining)# Add aliases to your .bashrc/.zshrc
alias claude='caam run claude --'
alias codex='caam run codex --'
# Now just use the tool normally
claude "explain this authentication flow"
# If you hit a rate limit mid-session, caam automatically:
# 1. Marks current profile as in cooldown
# 2. Selects next best profile via rotation
# 3. Re-runs your command with the new profile
# All transparent - you just see the output~/.local/share/caam/
βββ vault/ # Saved auth profiles
β βββ claude/
β β βββ [email protected]/
β β β βββ .claude.json # Backed up auth
β β β βββ auth.json # From ~/.config/claude-code/
β β β βββ meta.json # Timestamp, original paths
β β βββ [email protected]/
β β βββ ...
β βββ codex/
β β βββ [email protected]/
β β βββ auth.json
β βββ gemini/
β βββ [email protected]/
β βββ settings.json
β
βββ profiles/ # Isolated profiles (advanced)
βββ codex/
βββ [email protected]/
βββ profile.json # Profile metadata
βββ codex_home/ # Isolated CODEX_HOME
β βββ auth.json
βββ home/ # Pseudo-HOME with symlinks
βββ .ssh -> ~/.ssh
βββ .gitconfig -> ~/.gitconfig
Customize the TUI appearance and behavior through ~/.caam/config.yaml:
tui:
theme: auto # auto | dark | light
high_contrast: false # Enable high-contrast colors for accessibility
reduced_motion: false # Disable animated UI effects (spinners)
toasts: true # Show transient notification messages
mouse: true # Enable mouse support
show_key_hints: true # Show keyboard shortcuts in status bar
density: cozy # cozy | compact
no_tui: false # Disable TUI, use CLI-only modeEnvironment variables take precedence over config file settings:
| Variable | Values | Description |
|---|---|---|
CAAM_TUI_THEME |
auto, dark, light |
Color scheme |
CAAM_TUI_CONTRAST |
high, hc, 1, true |
High contrast mode |
CAAM_TUI_REDUCED_MOTION |
true, false |
Disable animations |
REDUCED_MOTION |
1 |
Standard accessibility env var |
CAAM_TUI_TOASTS |
true, false |
Toast notifications |
CAAM_TUI_MOUSE |
true, false |
Mouse support |
CAAM_TUI_KEY_HINTS |
true, false |
Keyboard hints |
CAAM_TUI_DENSITY |
cozy, compact |
UI spacing |
CAAM_NO_TUI or NO_TUI |
true, 1 |
Disable TUI entirely |
# View all TUI settings
caam config tui
# View a specific setting
caam config tui theme
caam config tui density
# Change settings
caam config tui theme dark
caam config tui density compact
caam config tui high_contrast trueQ: Does this work with API keys / pay-per-token plans?
No. This tool is specifically designed for fixed-cost subscription plans like Claude Max ($200/month), GPT Pro ($200/month), and Gemini Ultra ($275/month). These plans authenticate via OAuth browser flows and store tokens locally. If you're using API keys with usage-based billing, you don't need account switchingβyou'd just use different API keys.
Q: Is this against terms of service?
No. You're using your own legitimately-purchased subscriptions. caam just manages local auth filesβit doesn't share accounts, bypass rate limits, or modify API traffic. Each account still respects its individual usage limits.
Q: What if the tool updates and changes auth file locations?
Run caam paths to see current locations. If they change in a tool update, we'll update caam. File an issue if you notice a discrepancy.
Q: Can I sync the vault across machines?
Don't. Auth tokens often contain machine-specific identifiers (device IDs, etc.). Backup and restore on each machine separately. Don't copy vault directories between machines.
Q: What's the difference between vault profiles and isolated profiles?
- Vault profiles (
backup/activate): Swap auth files in place. Simple, instant, one account active at a time per tool. - Isolated profiles (
profile add/exec): Full directory isolation with pseudo-HOME. Run multiple accounts simultaneously in parallel terminals.
Q: Will this break my existing sessions?
Switching profiles while a CLI is running may cause auth errors in the running session. Best practice: switch accounts before starting a new session, not during.
Q: How do I know which account I'm currently using?
Run caam status. It shows the active profile (email) for each tool based on content hash matching.
brew install dicklesworthstone/tap/caamThis method provides:
- Automatic updates via
brew upgrade - Dependency management
- Easy uninstall via
brew uninstall
scoop bucket add dicklesworthstone https://github.com/Dicklesworthstone/scoop-bucket
scoop install dicklesworthstone/caamDownload the latest release for your platform:
Each release ships with signed checksums:
cosign verify-blob \
--bundle SHA256SUMS.sig \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity "https://github.com/Dicklesworthstone/coding_agent_account_manager/.github/workflows/release.yml@refs/tags/vX.Y.Z" \
SHA256SUMS
sha256sum -c SHA256SUMS
# macOS fallback:
# shasum -a 256 -c SHA256SUMScurl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_account_manager/main/install.sh?$(date +%s)" | bashgit clone https://github.com/Dicklesworthstone/coding_agent_account_manager
cd coding_agent_account_manager
go build -o caam ./cmd/caam
sudo mv caam /usr/local/bin/go install github.com/Dicklesworthstone/coding_agent_account_manager/cmd/caam@latest- Use the actual email address as the profile name β it's self-documenting and you'll never forget which account is which
- Backup before clearing:
caam backup claude [email protected] && caam clear claude - Check status often:
caam statusshows what's active across all tools - Use --backup-current flag:
caam activate claude [email protected] --backup-currentauto-saves current state before switching
Special thanks to @darvell for inspiring this project and for the feature ideas behind Smart Profile Management. His work on codex-poolβa sophisticated proxy that load-balances requests across multiple AI accounts with automatic failoverβdemonstrated how much intelligence can be added to account management.
While codex-pool answers "which account should handle THIS request?" (real-time proxy), caam answers "which account should I USE for my work session?" (profile manager). The Smart Profile Management features adapt codex-pool's intelligence to caam's architecture:
- Proactive Token Refresh β Automatically refreshes OAuth tokens before they expire, preventing mid-session auth failures (not available for Claudeβuse
/loginto re-authenticate) - Profile Health Scoring β Visual indicators (π’π‘π΄) showing token status, error history, penalty decay, and plan type (Claude profiles may show limited identity info)
- Smart Rotation β Multi-factor algorithm picks the best available profile based on health, cooldown, recency, and usage patterns
- Cooldown Tracking β Database-backed tracking of rate limit hits with configurable cooldown windows
- Automatic Failover β The
caam runwrapper detects rate limits and seamlessly switches to another account - Usage Analytics β Track activation patterns and session durations across profiles
- Hot Reload β TUI auto-refreshes when profiles are added/modified in another terminal
- Project-Profile Associations β Remember which profile to use for each project directory
See docs/SMART_PROFILE_MANAGEMENT.md for the full design document.
About Contributions: Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via
ghand independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
MIT License (with OpenAI/Anthropic Rider). See LICENSE.
