@warnyin/claude-switcher
v0.3.0
Published
Switch Claude Code between config profiles - a personal subscription and an enterprise setup, whether Amazon Bedrock or a company API gateway - per shell, using CLAUDE_CONFIG_DIR.
Readme
claude-switcher (ccs)
Switch Claude Code between config profiles — your personal subscription and an
enterprise setup, whether that is Amazon Bedrock or a company API gateway — per
shell, without moving settings.json files around or touching a single OAuth
token.
ccs work # switch this shell to the company profile
ccs personal # ...and back
ccs run personal -- --resume # launch Claude Code under a profile, shell untouched
ccs status # backend, model pins, telemetry, AWS / gateway wiring
ccs doctor # diagnose conflicts (read-only, never signs in)Windows + PowerShell 7. The switching mechanism itself is portable, but the AWS credential-process and registry checks are Windows-specific.
Why not just edit settings.json?
Because that is machine-wide and instant: every terminal, the VS Code extension
and any running session change at once, and you have to remember to change it
back. ccs uses CLAUDE_CONFIG_DIR, which Claude Code uses to isolate its
entire config home — settings, credentials, plugins, history, MCP servers. That
buys three things:
- Two backends at the same time. One window on your subscription, another on the company backend, no interference.
- No credential surgery. Claude Code rotates OAuth tokens in place and guards
the refresh with lock directories (
.oauth_refresh.lock,~/.claude.lock). Tools that swap credential files can land inside that window and clobber a freshly refreshed token — leaving a saved refresh token that is already dead.ccsnever writes credentials at all;doctorjust reports when a lock is held. - Reversible. Nothing about your existing
~/.claudechanges when you install.
Install
Pick whichever fits your toolchain — they install the same module.
PowerShell Gallery (native, recommended)
Install-Module ClaudeSwitcher -Scope CurrentUser
Add-Content $PROFILE.CurrentUserAllHosts "`nImport-Module ClaudeSwitcher"npm
npx @warnyin/claude-switcher setup # hooks the module into your PowerShell profile
# or: npm i -g @warnyin/claude-switcher && claude-switcher setupFrom source
git clone https://github.com/warnyin/claude-switcher.git
cd claude-switcher
./Install.ps1 # -WhatIf to preview, -Uninstall to removeInstall.ps1 runs under both Windows PowerShell 5.1 and PowerShell 7, but always
targets the PowerShell 7 profile, since the two hosts read different files and
the module needs 7. Re-running it never duplicates the block, and -Uninstall
removes only what it added.
A note on npx and use
npx @warnyin/claude-switcher use work cannot work: an npm bin is a child
process, and a child cannot change its parent shell's environment. Every other
command works fine through npx because its effect is on disk. Either run setup
once and use the native ccs use, or evaluate the assignment yourself:
npx @warnyin/claude-switcher env work | Invoke-ExpressionGetting started
You have a subscription and just got a company package
ccs backup-personal # save your current settings as the personal baseline
# run your company's installer (it will overwrite ~/.claude/settings.json)
ccs adopt # move the company settings into their own profile, restore yours
ccs doctorCompany installers write to %USERPROFILE%\.claude\settings.json directly and
ignore CLAUDE_CONFIG_DIR, so running one always lands on the default profile
no matter which profile the shell was switched to. ccs adopt is the fix-up:
it reads what the installer left, sends it to the profile that matches its
backend — work for Bedrock, gateway for an API gateway — and puts your own
settings back. Pass a name (ccs adopt office) to override the destination.
You do not have to have run ccs backup-personal first: when there is no baseline,
adopt restores from the backup the installer itself wrote
(settings.json.bak.<timestamp>), and tells you which file it used. Backups that
turn out to hold company settings are skipped.
Left in place, that config outranks the account login of the profile it landed in. From inside Claude Code the symptom is:
⚠ claude.ai connectors are disabled because ANTHROPIC_API_KEY or another auth source is set and takes precedence over your claude.ai login
with /status reporting Auth token: apiKeyHelper. ccs doctor reports the same
thing as company-settings-in-default-home, with ccs adopt as the fix.
Your ~/.claude is already the company setup
ccs list will show one profile named personal whose backend is bedrock or
gateway — the name no longer describes what it holds, and there is no
subscription profile to switch to:
ccs rename personal work # call it what it is (~/.claude is not moved)
ccs new personal # create a profile for your personal account
ccs run personal # /login there with your subscriptionRenaming it is also how you tell doctor this is deliberate: it only reports
company-settings-in-default-home while the profile claiming ~/.claude is still
the seeded personal.
ccs new does not clone the existing login. It inherits your personal preferences
and drops every provider key — CLAUDE_CODE_USE_BEDROCK, AWS_*, OTEL_*,
ANTHROPIC_DEFAULT_*_MODEL, otelHeadersHelper, awsAuthRefresh,
modelOverrides, availableModels, plus the gateway set: apiKeyHelper,
ANTHROPIC_BASE_URL, CLAUDE_CODE_API_KEY_HELPER_TTL_MS,
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY, CLAUDE_CODE_AUTO_COMPACT_WINDOW.
Without that, the new profile would keep routing at the company backend — and
apiKeyHelper in particular would keep answering for it instead of the account
you just signed in — a profile that looks switched but isn't. Use -Blank to
start from an empty file.
Commands
| Command | What it does |
|---|---|
| ccs <profile> | Shorthand for ccs use <profile> |
| ccs list | Profiles, backend, model, and which one this shell uses |
| ccs use <p> | Point this shell at a profile; also clears auth-override env vars |
| ccs run <p> [-- args] | Launch claude under a profile, then restore the environment |
| ccs status [p] | Backend, model pins, telemetry endpoint, AWS or gateway wiring, login state |
| ccs doctor | Diagnose conflicts. Read-only; never triggers a sign-in |
| ccs new <p> | Create an empty profile to sign a second account into (-Blank) |
| ccs capture <p> | Snapshot existing settings into a profile (-FromSettingsFile, -LinkShared) |
| ccs rename <old> <new> | Rename a profile, moving its folder when it is managed |
| ccs remove <p> | Unregister a profile (-DeleteFiles to erase it) |
| ccs backup-personal | Save ~/.claude/settings.json as your personal baseline |
| ccs adopt [p] | After a vendor installer: move its settings out of ~/.claude into their own profile, restore yours |
| ccs aws [p] | aws sts get-caller-identity — may open a browser, hence separate |
| ccs sessions copy <from> <to> | Copy session state — transcripts, checkpoints, todos/plans, prompt history — between profiles (-Quiet) |
| ccs continue <p> [-From <p>] [-- args] | Copy sessions into <p>, then claude --continue there, shell untouched |
Every command emits real objects, so | Where-Object and | ConvertTo-Json work,
and the underlying cmdlets (Get-CcsStatus, Test-CcsHealth, Start-CcsClaude, …)
are exported for scripting.
ccs capture -LinkShared junctions commands/agents/skills and hard-links
CLAUDE.md back to the source profile, so both profiles share one set. Junctions
and hard links need no elevation; symlinks would.
Moving a session between profiles
ccs sessions copy personal work # copy session state from personal into work
ccs continue work # copy, then `claude --continue` under workccs sessions copy <from> <to> copies every project's transcripts (projects/,
including subagent transcripts and per-project memory/), file-history/
checkpoints (for /rewind), and todos//plans/ when present, then merges
history.jsonl — the union of both sides, duplicates removed, sorted by
timestamp; the previous target history survives as history.jsonl.bak. It
never copies credentials, settings.json, .claude.json, plugins, sessions/
or session-env/.
It is incremental: identical files are skipped, so re-running is cheap. A file
that differs on both sides is a conflict — the larger one wins (transcripts
only grow), ties go to the newer one — and the other is kept beside it as
<name>.bak. Nothing already in the target is ever deleted, and .bak files
are never copied onward. Expect a progress bar (files and MB) on the first
copy of a large ~/.claude; -Quiet hides it. The result is a summary
(Copied, Skipped, Conflicts, BytesCopied, Failed) — a locked file is
listed in Failed and the rest still copies, but the command then ends with
an error naming it.
ccs continue <p> [-From <p>] [-- args] is the "hit my usage limit, keep
going" command: it copies from the profile this shell uses (or -From) into
<p>, then launches claude --continue under <p> without changing the
shell. If the copy fails, claude never starts.
Do not have the same session open in two profiles at once: both write the same transcript independently, and the next
sessions copykeeps only the larger one — the other survives as.bak. The first launch in the target may also ask you to trust the folder again, and resuming a conversation started on a different backend is supported by the copy, but whether that backend accepts it is up to Claude Code.
What doctor catches
| Severity | Checks |
|---|---|
| Error | Company settings sitting in ~/.claude after a vendor installer · profile directory missing · unreadable settings · incomplete telemetry env · telemetry endpoint differs from the enforced one · AWS_PROFILE absent from ~/.aws/config · credential process missing · otelHeadersHelper points at a missing file · vendor config has no entry for the AWS profile · gateway apiKeyHelper runs a file that is not there · gateway profile with no ANTHROPIC_BASE_URL · ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY shadowing a gateway's apiKeyHelper |
| Warning | CLAUDE_CONFIG_DIR persisted in User/Machine scope · ANTHROPIC_API_KEY and friends set in this shell · provider keys left in a subscription profile · lock directory older than 60s · vendor package older than the one it ships now · gateway model discovery off · otelHeadersHelper left over from the Bedrock package · a helper path that lives on a network share, which doctor reports rather than probes |
| Info | Vendor client_id and credential-process build date · Claude Desktop pinned by machine policy · Claude Code is refreshing credentials right now |
Two of these deserve a word:
Settings beat environment variables. A key in a profile's env block wins over
$env: — so a stale CLAUDE_CODE_USE_BEDROCK in ~/.claude/settings.json cannot
be undone by unsetting the variable. status and doctor therefore read the
settings file, never the environment.
Diagnostics stay local, and never quote a credential. A finding names
ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY but never its value — doctor output is
the thing people paste into a ticket. And a helper path pointing at a UNC share is
reported unverified rather than probed, because Test-Path on one opens an SMB
session to a host that settings.json, not you, chose.
Backend order mirrors Claude Code's own resolver. The provider flags
(CLAUDE_CODE_USE_BEDROCK and friends) are presence-checked before
ANTHROPIC_BASE_URL and apiKeyHelper are consulted at all, so a settings file
carrying both reads as bedrock with dead gateway keys in it — which is what
doctor reports, rather than the reverse.
Enterprise Bedrock packages can enforce telemetry. Deployments built on
claude-code-with-bedrock
may verify the OTLP endpoint before issuing AWS credentials. A profile missing an
OTEL key does not merely lose telemetry — it can fail to authenticate. So ccs
copies such an env block verbatim and checks it for completeness rather than
trimming it, and doctor compares the endpoint against the vendor's expected one.
Claude Desktop / Cowork are not switched. They follow
HKCU\SOFTWARE\Policies\Claude, a machine-level policy, not CLAUDE_CONFIG_DIR.
doctor reports it as Info so you don't debug the wrong layer. Changing an
org-managed policy is a conversation with your IT team, not a ccs command.
Layout
~/.claude the 'personal' profile (untouched by install)
~/.claude-profiles/ccs.json the profile registry
~/.claude-profiles/<name>/ each additional profile's config homeThe registry stores only what cannot be derived: a directory and a note. Backend
is always read from the profile's own settings.json, so it can never drift.
Tests
./Tests/Run-Tests.ps148 tests, all against a sandbox home directory — they never touch your real
~/.claude, ~/.aws, or profile registry — and with no dependency on Pester, so
they run on a stock Windows box.
Prior art
Built after reading three existing switchers and keeping what fit:
- realiti4/claude-swap —
CLAUDE_CONFIG_DIRas the isolation mechanism, scrubbing auth-override env vars, and the lock protocol. Its multi-subscription vault and quota-driven auto-switching are out of scope here. - foreveryh/claude-code-switch —
the settings
envblock shape and backing up before overwrite. It rewrites~/.claude/settings.jsonmachine-wide and stores tokens base64-encoded; neither is repeated here. - jlcodes99/cockpit-tools — the central index and per-profile health checks. Its "vault is the source of truth" model is actively wrong for Claude Code, which rotates tokens inside the profile.
None of them handles Bedrock; that part is built from the Claude Code docs.
License
MIT
