Run multiple Claude Code accounts from one machine — switch in a keystroke, and keep as many accounts signed in at once as you have terminals, each on its own account.
$ ccenv list
● default [email protected] max
work [email protected] team
client not signed in
● = active in this shell
- Concurrent, not just switchable — personal in one terminal, work in another, a client in a third… all live at once, no clobbering. No fixed limit.
- Secure — ccenv stores zero secrets; tokens stay in the OS credential store, managed by Claude Code.
- Your setup comes along — every profile inherits your plugins, skills, commands, and hooks.
- Nothing to migrate — your current login is already profile
default.
- Claude Code installed (
claudeon yourPATH) - macOS or Linux (native), or Windows via Git Bash or WSL
curl -fsSL https://ccenv.dev/install.sh | bash
# then activate in the current shell (new terminals do this automatically):
source ~/.zshrc # macOS (zsh)
# source ~/.bashrc # Linux / Git BashOne line, no clone, no build step — the script downloads the ccenv binary and
shell snippet from ccenv.dev and installs them per-user (no sudo). Needs
curl (or wget) and Claude Code on your
PATH. Re-run any time to update.
From a clone instead
git clone https://github.com/ViaViaSolutions/ccenv ccenv && cd ccenv
./install.shA clone install symlinks ccenv back to the repo (so git pull updates it);
the web install copies it. Same wiring otherwise.
install.sh is idempotent: it puts ccenv on your PATH (preferring a dir
already on it, e.g. ~/.local/bin, and only touching your rc if none is found),
adds one line for the use/unuse shell integration, and verifies the result.
Override the bin dir with CCENV_BIN_DIR=/usr/local/bin, or the download origin
with CCENV_BASE_URL=https://ccenv.dev.
ccenv list # your current account shows up as "default"
ccenv create work # new profile (inherits your plugins/skills), then sign inOne account per terminal, as many as you like — all running simultaneously:
# Terminal A
ccenv use work # this shell → work account
claude # …runs as work
# Terminal B (at the same time)
ccenv use default # this shell → your personal account
claude # …runs as personal
# Terminal C, D, … # ccenv use <profile> → and so onOr launch one without touching your shell:
ccenv run work # a claude session as 'work'
ccenv run work -- -p "summarize this repo" # pass args straight throughccenv use changes only the current shell; ccenv run only that one
session. Either way the accounts never interfere.
Pick a standing default and every new shell starts on it:
ccenv default work # new terminals auto-use 'work' (also switches this shell)
ccenv default # show the current default
ccenv default --clear # back to your personal account for new shellsAn explicit ccenv use <name> / ccenv unuse still overrides it in that one
shell, and subshells inherit whatever the parent picked. The choice is saved as
a plain profile name in ~/.ccenv/default (no secrets).
Attach a profile to a directory and any new terminal opened at or under it
comes up on that profile — no use needed:
cd ~/work && ccenv pin work # attach 'work' to ~/work (and its subdirs)
ccenv pins # list every pin
ccenv unpin ~/work # detach (defaults to the current dir)A terminal in ~/work/acme picks work; one in ~/personal falls back to your
standing default. The nearest pin wins over parent folders, a pin beats the
default, and an explicit ccenv use still overrides everything for that shell.
Pins are applied when a shell starts (not on cd) and are saved as
<path>\t<profile> lines in ~/.ccenv/pins (no secrets). Pinning lives in a
central registry — nothing is written into the repo, so a folder you clone can
never silently select one of your accounts.
Auto-applied profiles are re-resolved for every new terminal from its own
directory, so a pin never leaks out of its folder — even into a new tab, editor
terminal, or tmux pane that inherits the environment of a shell that was in a
pinned directory. Only an explicit ccenv use is sticky and carries into
subshells (until you unuse or open a fresh terminal).
| Command | What it does |
|---|---|
list / ls |
Accounts + sign-in status (● = active in this shell) |
use <name> |
Point the current shell at a profile |
unuse |
Revert this shell to default |
default [name] |
Auto-use <name> in every new shell (--clear to reset) |
pin <name> [dir] |
Pin <name> to a folder (auto-use there + subdirs; default: cwd) |
unpin [dir] / pins |
Remove a folder pin (default: cwd) / list all pins |
run <name> [-- args] |
Launch a claude session as <name> |
current |
Show this shell's active profile |
create <name> [opts] |
New profile, then login |
login <name> / logout <name> |
Sign a profile in / out |
status [name] |
Detailed auth status |
sync <name> [dir] |
Re-copy settings*.json / mcp_config.json from ~/.claude (or dir) |
remove <name> [-y] [--keep-creds] |
Delete a profile (signs it out first) |
rename <old> <new> |
Rename a profile (requires re-login afterward) |
doctor |
Health check |
update [--check] [--force] |
Fetch the latest build and swap it in (clone installs git pull) |
uninstall [--purge] [-y] |
Remove ccenv itself (--purge also deletes profiles) |
help / help full |
Short help / full reference |
create options: --isolated (don't share any config), --no-login,
--email <addr>, --share-from <dir>, and --console / --sso / --claudeai
(passed through to claude auth login).
A profile is an isolated Claude Code config directory (CLAUDE_CONFIG_DIR).
Claude Code stores each config dir's credentials in their own entry of the OS
credential store, so two shells pointed at two profiles are two accounts — at the
same time, with no clobbering. default is your existing ~/.claude.
What a new profile inherits from ~/.claude:
| Items | Why | |
|---|---|---|
| Symlinked (shared) | plugins, skills, commands, hooks, CLAUDE.md |
read-mostly; every account gets your full setup |
| Copied (independent) | settings.json, settings.local.json, mcp_config.json |
mutable at runtime — copying keeps concurrent accounts from racing; each can diverge. ccenv sync re-copies |
| Fresh per profile | sessions, projects, history, credentials | identity/write-heavy state stays isolated |
On platforms without symlink support (e.g. Git Bash without Developer Mode), the symlinked items are copied instead.
Custom hooks are shared. If you run accounts concurrently, keep them account-agnostic — read per-profile state via
$CLAUDE_CONFIG_DIR(set for the active profile), not hard-coded~/.claudepaths.
- ccenv stores no secrets. Tokens live in the OS credential store
(macOS Keychain, or a per-profile credentials file on Linux/Windows), managed
by Claude Code via
claude auth.~/.ccenvis0700; copied config files are0600. removeis guarded: signs the profile out first (--keep-credsskips), refuses symlinked profile dirs, refuses any path not under~/.ccenv/profiles, and refusesdefault.rm -rfguard:CCENV_HOMEmust be a real directory under$HOME, no.., not a symlink. Profile names are restricted to[A-Za-z0-9._-].- rc edits are injection-safe (
printf %q, atomic rewrite), and the shell snippet is copied to~/.ccenv/shell/so startup never runs code from the repo. - Web install fetches over HTTPS.
install.shpulls the binary and shell snippet fromccenv.devand refuses to install anything that doesn't look like the expected script (guards against truncated downloads / error pages). Pin the origin withCCENV_BASE_URLif you self-host. - Trust assumption: a clone install symlinks
ccenvback to the repo, so whoever can write the repo can run code as you. Keep it somewhere only you can write — the installer warns if the repo is group/other-writable. (A web install copies the binary instead, so there's no repo to protect.)
ccenv update # fetch the latest build and swap it in
ccenv update --check # just report the available versionupdate always fetches the newest ccenv from ccenv.dev, verifies it, and
atomically replaces the running binary (safe — rename swaps the directory entry,
not the inode the process is reading). For a clone install the PATH symlink is
replaced with the downloaded copy; your repo working tree is left untouched.
ccenv list and ccenv help show a one-line update available notice when a
newer build exists — checked in the background at most once a day (a tiny
ccenv.dev/version fetch, never on the shell-startup path). Opt out with
CCENV_NO_UPDATE_CHECK=1.
Override the origin with CCENV_BASE_URL (handy for testing a preview deploy).
ccenv uninstall # removes the CLI + rc lines; keeps your profiles
ccenv uninstall --purge # also signs out every profile and deletes ~/.ccenvWorks from anywhere — no repo needed. Add -y to skip the confirmation. A clone
is never deleted (symlink installs print its location). Equivalent forms:
./uninstall.sh [--purge] [-y] from a clone, or
curl -fsSL https://ccenv.dev/uninstall.sh | bash -s -- --purge.
MIT — see LICENSE.