claude-cli-switcher
v1.0.5
Published
Switch between multiple Claude Code (Anthropic Pro) accounts on one machine. Snapshot each native login as a profile, swap with one command, /status keeps showing the right Email/Organization. Interactive menu + CLI.
Maintainers
Readme
csw — Claude Account Switcher
Switch between multiple Claude Code (Anthropic) accounts on the same machine in one command. Native Claude Pro logins (refresh token + access token + organization metadata) are snapshotted per profile, so /status keeps showing the right Email and Organization no matter which account you're using.
$ csw
╭───────────────────────────────────────────╮
│ csw — Claude Account Switcher
├───────────────────────────────────────────┤
│ ⚡ Active: personal
│ 👤 Profiles: 3
╰───────────────────────────────────────────╯
What do you want to do?
❯ Switch account
Add account / save current login
Remove a profile
Run claude (with active account)
Show /status
Repair session index
Quit
[↑↓ navigate · Enter select · Esc/q back]Pure zsh with a small Python helper, zero npm runtime dependencies, works alongside the official Claude CLI without touching it.
Why
The official claude CLI stores one set of credentials at a time — in ~/.claude/.credentials.json and the macOS Keychain. Switching between, say, a personal Pro account and a work one means logging out and back in every single time. Tedious, and /status only shows the active login.
csw snapshots a full native login into a per-profile directory, then atomically swaps the live credentials file + Keychain entry + the oauthAccount field in ~/.claude.json when you say "switch". The Claude CLI thinks you logged in normally — your /status shows the right account, your usage counts against the right plan.
Features
- Interactive menu by default —
cswwith no args opens a polished arrow-key menu. - First-run account detection — if Claude Code is already logged in,
cswshows that current login even before it has been saved as a profile. - One-command switching between any number of accounts.
- Preserves native login state —
/statusshows Email/Organization per profile, not just an opaque "Auth token". - Completes Claude Code onboarding state after restoring a valid profile, so first-run login-method prompts do not reappear on fresh machines.
- Repairs Claude session visibility by rebuilding
~/.claude.json.projectsfrom raw transcripts in~/.claude/projects/, so old conversations remain resumable after account switches. - Secure by construction —
umask 077from line 3, credentials at0600, dirs at0700. - File-locking on mutating operations: atomic
mkdir-based mutex, PID-based stale-lock recovery, configurable timeout. - Schema validation with
CLAUDE_SKIP_VALIDATION=1escape hatch if Anthropic ever changes the credential format. - Auto-cleanup of live credentials (file + Keychain +
~/.claude.jsonoauthAccount) when you remove the active profile. - Zero npm deps beyond zsh, python3, and the real
claudeCLI.jqis optional but recommended.
Requirements
- macOS (Linux works too — you lose Keychain integration, credentials persist to
~/.claude/.credentials.jsononly) zsh(default shell on modern macOS)python3for the session repair helper- The official Claude Code CLI installed somewhere reasonable —
~/.local/bin/claude,~/.claude/local/claude,/opt/homebrew/bin/claude,/usr/local/bin/claude, or pointed at viaCLAUDE_REAL_BIN jq(optional) — enables credential schema validation and~/.claude.jsonpatching
Installation
Option 1 — npm (recommended)
npm install -g claude-cli-switcherThat puts csw on your PATH. Done.
Option 2 — git clone
git clone https://github.com/ntdung6868/claude-account-switcher.git
cd claude-account-switcher
chmod +x csw
# Put csw on your PATH (any one of these):
ln -s "$PWD/csw" ~/.local/bin/csw # if ~/.local/bin is on PATH
sudo ln -s "$PWD/csw" /usr/local/bin/csw # global
# …or add the repo dir to PATH in your shell rc.Reload your shell, and you're done
csw # opens the menu
csw help # full CLI referencecsw is completely independent of the official claude CLI. It doesn't wrap or shadow it — claude keeps doing exactly what it always did. All csw does is swap which credentials are sitting in ~/.claude/.credentials.json and the Keychain when you say "use this profile". The next claude call picks them up naturally.
The typical workflow is:
csw use work
claude # now logged in as the "work" profile
# ... later ...
csw use personal
claude # now logged in as "personal"Profiles and state live in ~/.claude-switcher/ regardless of where the script is installed. Override with CLAUDE_ACCOUNT_DIR=/some/path.
Quick start
First run on a machine already logged into Claude Code
Just run csw. If Claude Code is already logged in, the header shows that current login as unsaved. Choose Add account / save current login to save it as your first profile.
Or do it from the CLI:
csw save-native personal # snapshot under a nameAdd a second account
From the menu, choose Add account / save current login. If the current Claude login has not been saved yet, csw asks for a profile name and saves it first. Then it opens the Claude OAuth login flow for the new account, asks for the new profile name, saves it, and switches to it.
CLI equivalent:
csw save-native personal # only needed once if current login is not saved yet
csw use-native
csw run auth login --claudeai # log in as the other account
csw save-native workSwitch between them
csw use personal
claude # the official Claude CLI now uses 'personal'
# ... do stuff as personal ...
csw use work
claude # ... and now 'work'
# ... do stuff as work ...Check who's active
csw current # → personal
csw list
# * personal
# workCommands
| Command | What it does |
| --- | --- |
| csw | Open the interactive menu. |
| csw save-native <name> | Save the current Claude Code login into native-accounts/<name>/ and set it active. |
| csw use <name> | Restore a saved profile to the live credential locations. Only valid saved profiles are shown in the switch menu. |
| csw use-native | Switch to bare native mode without touching saved profiles. This is a utility mode for adding/logging in accounts; it is intentionally not shown as a selectable account in the switch menu. |
| csw remove-native <name> | Delete a saved profile. If it was active, also clears the live credentials (file + Keychain + oauthAccount). |
| csw list | List all profiles, marking the active one with *. |
| csw current | Print the active profile name (or native-login). |
| csw status | Run claude auth status against the active account. |
| csw session-status | Show local Claude transcript/project-index counts. |
| csw repair-sessions | Rebuild ~/.claude.json.projects from ~/.claude/projects/**/*.jsonl. |
| csw repair-sessions --dry-run | Show what session metadata would be repaired without writing. |
| csw run [args...] | Run the real claude CLI with the active account. Unsets any *_AUTH_TOKEN env vars first. |
| csw init | Create the data directories. Idempotent. |
| csw help | Print full help. |
Environment variables
| Variable | Default | Purpose |
| --- | --- | --- |
| CLAUDE_ACCOUNT_DIR | $HOME/.claude-switcher | Where profiles, lock, and state files live. |
| CLAUDE_HOME_DIR | $HOME/.claude | Real Claude config dir, where .credentials.json lives. |
| CLAUDE_JSON_FILE | $HOME/.claude.json | Real Claude top-level config (holds oauthAccount). |
| CLAUDE_REAL_BIN | (auto-detected) | Absolute path to the real claude CLI. The script searches $HOME/.local/bin/claude, $HOME/.claude/local/claude, /opt/homebrew/bin/claude, /usr/local/bin/claude in order; set this if your install is elsewhere. |
| CLAUDE_LOCK_TIMEOUT | 30 | Seconds to wait for the lock before failing. |
| CLAUDE_SESSION_SYNC | 1 | Set to 0 to skip automatic session repair after save-native, use, or use-native. |
| CLAUDE_SKIP_VALIDATION | (unset) | Set to 1 to bypass strict credential-schema validation. Use only if Anthropic changed the schema and the strict check is wrong. |
| PYTHON_BIN | python3 | Python interpreter for helper scripts. |
How it works
Native Claude Pro credentials live in three places on macOS:
~/.claude/.credentials.json— JSON withclaudeAiOauth.{refreshToken, accessToken, …}.- macOS Keychain — entry with service
Claude Code-credentials(suffixed with a hash ofCLAUDE_CONFIG_DIRif set), account$USER. ~/.claude.json— top-level config; theoauthAccountfield is what powers/statusshowing Email/Organization.
save-native <name> snapshots all three into native-accounts/<name>/.credentials.json and native-accounts/<name>/oauthAccount.json, then writes the saved credential back to the live file + Keychain so a browser login that only touched Keychain is immediately usable. use <name> writes them all back. After a valid OAuth profile is saved or restored, csw also marks Claude Code onboarding complete in ~/.claude.json, using the installed Claude Code version when available. The real claude CLI never knows it didn't log in normally.
When the active profile is removed, the script wipes all three live locations so the next claude run isn't tied to a "deleted" account.
csw run [args...] execs the real CLI after unsetting any CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_OAUTH_REFRESH_TOKEN, or ANTHROPIC_AUTH_TOKEN env vars that could shadow the native login.
Claude Code stores conversation transcripts under ~/.claude/projects/<project>/<session-id>.jsonl, but recent-session pointers live in ~/.claude.json.projects. csw repair-sessions scans the raw transcript files and merges missing or stale project pointers back into ~/.claude.json. It backs up ~/.claude.json under ~/.claude/backups/csw-sessions/ before writing and never modifies transcript contents.
Concurrency
Mutating commands (save-native, use, use-native, remove-native) take an exclusive lock at $BASE_DIR/.lock. Concurrent invocations wait up to CLAUDE_LOCK_TIMEOUT seconds (default 30) before failing. Read-only commands (list, current, status, run) are not locked. Stale locks from crashed processes are auto-detected via the recorded PID, so you never need to rm -rf .lock manually after a Ctrl-C.
Schema-change escape hatch
Credential validation checks for .claudeAiOauth.refreshToken and .claudeAiOauth.accessToken. If Anthropic changes the format, the strict check fails with a hint. Re-run with CLAUDE_SKIP_VALIDATION=1 to bypass — the script copies credentials raw and emits a warning rather than failing.
Security notes
- Profile credentials are at
0600under a0700parent. The script forcesumask 077from its third line, so even the brief window between file creation and an explicitchmodis safe. - The script never logs tokens.
csw rununsets known auth env vars (CLAUDE_CODE_OAUTH_TOKEN,CLAUDE_CODE_OAUTH_REFRESH_TOKEN,ANTHROPIC_AUTH_TOKEN) before exec'ing the real CLI, so a stray env var can't shadow your selected account.- Add the data directory to backups only if you understand that you're backing up active Claude Pro tokens.
Troubleshooting
Could not find the real Claude binary — your claude CLI is installed somewhere unusual. Set CLAUDE_REAL_BIN=/path/to/claude.
saved credentials for 'X' are not a full Claude Pro login — the snapshot is missing claudeAiOauth.refreshToken / accessToken. Either re-save with csw save-native X after logging in again, or, if you suspect Anthropic changed the schema, retry with CLAUDE_SKIP_VALIDATION=1.
could not acquire lock after 30s — another csw process is still running or stuck. The script auto-detects stale PIDs; if it still complains, remove $BASE_DIR/.lock manually.
/status still shows the old account after switching — most often means ~/.claude.json wasn't patched. Make sure jq is installed (brew install jq), then re-save the profile (csw save-native) while logged in as the desired account.
claude still asks you to select a login method after switching — run csw use <name> again with v1.0.2 or newer. This restores the profile and marks Claude Code onboarding complete.
Please run /login · API Error: 401 Invalid authentication credentials after resuming — the selected profile's OAuth token can no longer refresh. Log in again and re-save the same profile:
csw use-native
csw run auth login --claudeai
csw save-native <name>
csw use <name>Old conversations disappear from /resume after switching accounts — run csw repair-sessions or switch again with CLAUDE_SESSION_SYNC=1. The repair step rebuilds ~/.claude.json.projects from the raw transcript files that Claude keeps under ~/.claude/projects/.
interactive menu requires a terminal — csw was invoked from a non-TTY context (a script, a pipe, CI). Use the CLI subcommands directly: csw use <name>, csw run, etc.
License
MIT — see LICENSE.
