Switch LLM providers — and Claude accounts — with one command.
Two Pro subscriptions, no /logout. No mess. No restarts.
curl -fsSL https://raw.githubusercontent.com/lizzyman04/claude-code-switcher/main/install.sh | bashirm https://raw.githubusercontent.com/lizzyman04/claude-code-switcher/main/install.ps1 | iexThe installer writes both the ccs alias and the claude function into your
$PROFILE, so there is nothing to add by hand. Directory sharing uses junctions
and hard links rather than symlinks, so no elevation and no Developer Mode are
required.
The PowerShell implementation mirrors the shell one command for command, but it has had far less real-world use. If something looks wrong, please open an issue.
Open a new terminal, then:
ccsDone. Your providers are listed. The installer handles profiles, the active symlink, and the shell integration — zero manual setup.
Hit the limit on one Pro subscription? Add the second one once:
ccs login anthropic@secondThen, forever after:
ccs nextThat is the whole feature. ccs next rotates to the next account of the
current provider — no /logout, no browser, no re-authentication. Both
accounts stay logged in simultaneously, and you can run a session on each at the
same time in different terminals.
$ ccs accounts
ACCOUNT IDENTITY BG AGENTS
* anthropic@main you@example.com yes
anthropic@second you+work@example.com no
deepseek@main api key yesYour session history is shared, so claude --resume finds the session you were
just in even if you rotated accounts mid-task.
ccs next never crosses provider boundaries — switching provider changes model
and billing, so that stays explicit.
<p> is a provider, <a> an account. Every command taking a provider also
accepts <provider>@<account>; a bare provider means its last-used account.
| Command | What it does |
|---|---|
ccs |
List providers and accounts, marking the active one |
ccs <p>[@<a>] |
Switch (shorthand for switch) |
ccs next |
Rotate to the next account of the current provider |
ccs login <p>[@<a>] |
Sign in to an account — creates it if needed |
ccs logout <p>@<a> |
Clear one account's credentials |
ccs accounts [<p>] |
List accounts with identity and daemon availability |
ccs current |
Active provider, account, email (API key hidden) |
ccs switch <p>[@<a>] |
Set the active account |
ccs add <p>[@<a>] |
Add a new profile interactively |
ccs key <p>[@<a>] |
Update an API key in seconds |
ccs edit <p>[@<a>] |
Open a profile in $EDITOR |
ccs remove <p>[@<a>] |
Delete an account, or a whole provider |
ccs test [<p>[@<a>]] |
Real API call, or login status for OAuth accounts |
ccs run <p>[@<a>] [args] |
Run claude with a specific account |
ccs run --provider <p> --model <m> |
Run without a saved profile (ephemeral) |
ccs clean [<p>[@<a>]] |
Launch Claude Code with no custom agents/skills |
ccs doctor |
Check the shell integration, links and credential modes |
ccs --help |
Show help |
The installer writes this to your .bashrc and .zshrc, between
# >>> ccs … markers:
claude() { ...; CLAUDE_CONFIG_DIR="$target" command claude --settings "$active" "$@"; }
alias deepseek='ccs run deepseek'After ccs deepseek, claude talks to DeepSeek. After ccs next, it talks to
your other account. No flags, no env vars.
It has to be a function, not an alias. Claude Code reads
CLAUDE_CONFIG_DIR from the environment before it loads any settings file, so
that variable must be exported by the shell — and an alias cannot export
anything. Earlier versions of ccs shipped
alias claude='claude --settings …'; the installer detects and removes it,
backing up your rc file first.
If type claude does not say claude is a function, run:
ccs shell-install # then open a new terminalThe function reads active-home to decide whether to export
CLAUDE_CONFIG_DIR, and it cannot report a broken one — if that link is missing
or points at a directory that is gone, it falls back to the default account
without saying so. ccs doctor fails loudly on exactly that, and any ccs <p>
rewrites the link.
ccs switch and ccs next refuse outright rather than silently send you to the
wrong account when the integration is missing. ccs run <p>@<a> always works
regardless, because it sets the config dir in its own process.
Need a session without your custom agents and skills eating context tokens?
ccs clean # active account
ccs clean deepseek # a specific provider
ccs clean anthropic@second # a specific account
ccs clean --restore # recover after a crashed sessionEverything is restored when you exit. Nothing is deleted.
For an isolated account this only removes that account's own links, so another
account's live session is unaffected. For anthropic@main — whose config dir
is ~/.claude — the directories are real rather than links, so a concurrent
session on another account loses them until this one exits. ccs clean says
which case applies.
Each account owns a settings file. A symlink at
~/.config/claude-profiles/active points at the current one, and a second
symlink, active-home, points at that account's config directory.
~/.config/claude-profiles/
├── profiles/
│ ├── anthropic/
│ │ ├── profile.json auth kind, default/last/native account
│ │ └── accounts/
│ │ ├── main.json ← {"env": {}} — OAuth has no API key
│ │ └── second.json
│ └── deepseek/
│ ├── profile.json
│ └── accounts/main.json ← {"env": {"ANTHROPIC_AUTH_TOKEN": "…"}}
├── homes/
│ └── anthropic-second/ ← this account's CLAUDE_CONFIG_DIR
├── shared/ ← links into ~/.claude
├── backups/
│ └── displaced/ ← content a repair moved aside, never deleted
├── active -> profiles/anthropic/accounts/main.json
└── active-home -> ~/.claude
Switching a provider updates active. Switching an account also updates
active-home, and the shell function exports it as CLAUDE_CONFIG_DIR.
Every switch also rebuilds shared/, from any account including
anthropic@main, so a link that went missing is repaired by the next ccs <p>.
A link there can also be replaced by a real file — an external writer that
resolves one symlink level and renames its temp file onto shared/<item> leaves
a copy where the link was, and sharing silently stops. That matters most for
settings.json: while it is degraded, isolated accounts run with none of your
hooks, security guards included, and nothing in the session says so.
So ccs restores sharing on the next switch, and never destroys what it found:
what it finds at shared/<item> |
what ccs does |
|---|---|
a file byte-identical to ~/.claude/<item> |
relinks it — nothing can be lost |
| a file whose content differs | moves it to backups/displaced/<item>.<timestamp>, relinks, and prints where it went |
| a directory | leaves it alone — merging two directories is not a choice ccs can make for you |
Displaced files are never deleted. ccs doctor keeps listing them, because
restoring the link also reverts whatever the displaced version held — if that
version was the one you wanted, diff it back yourself.
For DeepSeek or OpenRouter an account is just a different API key, and that fits
in the settings file. For a Claude Pro login there is no API key — that is
why anthropic/accounts/main.json is {"env": {}}. The credentials live in
<config-dir>/.credentials.json, or on macOS in a Keychain entry whose name is
derived from the config directory path. Nothing in a settings file can point at
them. Giving each account its own CLAUDE_CONFIG_DIR is the only way to keep
two logins side by side.
Earlier versions of this README claimed ~/.claude/ is never touched. That
was never quite true and is now plainly not, so here is what actually happens:
~/.claude/is theanthropic@mainaccount's live config directory. It is not a copy and not a sandbox. That account runs withCLAUDE_CONFIG_DIRunset, exactly as Claude Code does withoutccs.ccsdoes not restructure it. The links live inshared/and point into~/.claude/; those are what get linked into the sibling homes underhomes/. Your files stay exactly where they are.- Claude Code itself writes to
~/.claude/while that account is active — as it always did. Becausesettings.json,agents/,skills/,commands/,plugins/,projects/,history.jsonland your*.mdmemory files are shared, an edit made from any account lands in~/.claude/. That is the point: one source of truth, not per-account copies. ccs cleanonanthropic@mainmoves~/.claude/agentsand~/.claude/skillsaside for the life of that session, and restores them on exit. For any other account it only removes that account's own symlinks, so nothing shared is at risk.ccs cleantells you which case you are in.- Additional accounts under
homes/are fully isolated.ccsnever creates or deletes real files in~/.claude/.
It would be tidier to give every account its own directory. It also costs a
feature: a custom CLAUDE_CONFIG_DIR disables Claude Code's background-agent
daemon, and claude daemon service install refuses to run at all. So one
account per OAuth provider — native_account in profile.json — deliberately
stays on the default directory and keeps working normally. ccs accounts shows
this per account in its BG AGENTS column, and switching into an isolated
account says so.
| Shared across accounts | Per-account |
|---|---|
agents/ skills/ commands/ plugins/ |
.credentials.json |
settings.json, all top-level *.md |
.claude.json (identity, trust flags) |
projects/ (so --resume works) |
sessions/ (live-PID registry) |
history.jsonl, todos/, session-env/ |
shell-snapshots/, file-history/ |
Two consequences worth knowing, both from .claude.json. It holds the account
identity, so it cannot be shared — and it also stores:
- per-project trust flags, so a second account asks you to trust each folder once;
- MCP servers added with
claude mcp add, which therefore do not follow an account switch. An account you just created starts with none.
claude mcp list also shows a second kind of entry, prefixed claude.ai —
account-managed connectors. Those are not in any file: Claude Code fetches
them from your claude.ai org, so they follow the account, not the config
directory. ccs cannot see or copy them; they are managed at
claude.ai/customize/connectors per
account.
The gap is silent during a session — a server is simply missing — so
ccs doctor reports both, and says plainly which part it cannot see:
MCP servers
note anthropic@main: 0 added with 'claude mcp add', 4 claude.ai connector(s) ever connected
note anthropic@second: 0 added with 'claude mcp add', 0 claude.ai connector(s) ever connected
warn anthropic: accounts have connected different claude.ai connectors
Connectors follow the claude.ai account, not the config dir, so ccs
cannot copy them. Align them at https://claude.ai/customize/connectors
note ccs cannot see account-managed claude.ai connectors — these counts
are incomplete. Compare with: claude mcp list
The connector figure comes from claudeAiMcpEverConnected, a record of what each
account has ever connected, so treat it as a divergence hint rather than a live
count. The incompleteness note always prints: a count of zero must never be read
as "the accounts match".
ccs deliberately copies nothing across. Connectors cannot be copied at all, and
for claude mcp add servers, merging definitions between config dirs can
duplicate or clobber them — so that stays your call.
Nothing to do. On first run ccs migrates profiles/<name>.json to the new
layout, after copying the whole tree to
~/.config/claude-profiles/backups/pre-multiaccount-<timestamp>/. It is
idempotent, and your existing Anthropic login is untouched — it becomes
anthropic@main and stays on ~/.claude.
docs/TESTING-multi-account.md is a
step-by-step run that proves two Pro accounts coexist without a logout,
including cross-account --resume.
- Claude Code — in
PATH - jq — for
switch,current,keyandtest.ccs,ccs accountsand the automatic migration deliberately work without it, so a box missingjqcan still list and migrate. - curl — for
testand the installer - PowerShell 5.1+ — Windows only
- bash or zsh — the shell integration is a function, so a POSIX-only
shwill not do
MIT License - see LICENSE file for details.
Issues and PRs welcome — open one here