Per-repository Claude account switching: keep personal and work subscriptions separate.
> /plugin marketplace add dcotelo/cprof> /plugin install cprof@dcotelo
What's inside
Log in for work, and your side project bills the company. Log in for yourself, and the work repo runs on a personal account. Switching means logging out, logging back in, and remembering which one you are on — every time you change directory.
cprof makes the directory decide.
Each profile is its own Claude config directory with its own credentials, so the accounts never touch. A default covers most of your work, a directory rule routes a whole tree, and a per-repository pin overrides both.
$ cprof list
PROFILE PLAN ACCOUNT 5H 7D FLAGS
work team you@acme.com ▓▓▓▓░░░░░░ 42% ▓▓░░░░░░░░ 18% native
personal max you@personal.dev ▓▓▓▓▓▓▓▓▓░ 91% ▓▓▓▓▓▓░░░░ 60% (default) (active)
$ cd ~/dev/acme/api && cprof which
work native (keychain) rule ~/dev/acme
brew install dcotelo/tap/cprof
Or without Homebrew — no sudo, installs to ~/.local, needs jq on PATH.
Download the installer, read it, then run it:
curl -fsSLO https://raw.githubusercontent.com/dcotelo/cprof/main/install.sh && less install.sh
then, once it reads right:
bash install.sh
Then one line in your shell config, and you are done:
claude() { eval "$(cprof env)"; command claude "$@"; }
The Claude Code plugin is optional and adds the ambient parts — a warning when
you walk into a directory expecting a different account, /profile, and the
statusline badge:
claude plugin marketplace add dcotelo/cprof
claude plugin install cprof@dcotelo
Quickstart walks the whole setup — profiles, rules, default —
in about two minutes. Requires macOS; Homebrew pulls in jq, and usage data
needs curl, which macOS ships. Install details covers
the plugin-only path and updating.
native profile. Adding cprof to a working setup
changes nothing about that setup.CLAUDE.md as well as credentials — so a naive profile
switch would silently switch away everything you have installed. cprof links
them, and never links the files that identify you.login snapshots the keychain first and
restores it if a profile login writes to the shared item. env never exits
non-zero, so a broken config degrades to stock Claude Code rather than a
broken shell.Contents · Quickstart · How it works · Install details · Resolution order · Commands · Usage headroom · Statusline · Fallback accounts · Safety · Development · Releasing
Five steps, about two minutes. Needs macOS.
# 1. install the CLI (jq comes with it), and the plugin for the ambient parts
brew install dcotelo/tap/cprof
claude plugin marketplace add dcotelo/cprof
claude plugin install cprof@dcotelo
# 2. route `claude` through it
cat >> ~/.zshrc <<'RC'
claude() { eval "$(cprof env)"; command claude "$@"; }
RC
exec zsh
# 3. keep the account you already use, then add a second one
cprof add work --native # adopts your current keychain login
cprof add personal # ~/.claude-profiles/personal, sharing
# your plugins, skills and settings
cprof login personal # interactive, opens a browser
# 4. choose the default profile, and route one tree to the other
cprof default personal
cprof rule add ~/dev/<company> work
# 5. confirm
cprof list
cprof which
$ cprof list
PROFILE PLAN ACCOUNT 5H 7D FLAGS
work team you@<company>.com ▓▓▓▓░░░░░░ 42% ▓▓░░░░░░░░ 18% native
personal max you@personal.dev ▓▓▓▓▓▓▓▓▓░ 91% ▓▓▓▓▓▓░░░░ 60% (default) (active)
$ cd ~/dev/<company>/api && cprof which
work native (keychain) rule ~/dev/<company>
That is the whole setup. From here claude picks the account for you; the only
rule to remember is that a change takes effect on the next claude launch,
never in a running session, because credentials are read at process start.
Nothing was moved or re-signed-in along the way: --native adopts your existing
login where it already lives, and step 3's login writes only inside the new
profile's own directory.
Step 3 assumes a profile that does not exist yet. If you have been switching accounts by hand — an alias along the lines of
alias claude-client='CLAUDE_CONFIG_DIR=~/.claude-client claude'
— then that directory is already a profile in all but name, and add adopts it
where it stands:
cprof add client --dir ~/.claude-client --isolated --note 'client account'
--isolated is the flag that matters here. Without it add links the shared
assets, which moves the directory's own settings.json, CLAUDE.md, plugins
and the rest aside as *.moved-<timestamp> and puts links to ~/.claude in
their place. Nothing is deleted and unshare
reverses it, but a directory you have already furnished usually wants to keep
what it has. Decide otherwise later with cprof share client.
The existing login carries over, with one caveat worth checking. Claude Code keys
credentials to the value of CLAUDE_CONFIG_DIR, and add stores the physical
path, so a symlink standing between the two leaves the stored path different from
the string your alias exported — and the login is then looked up under a name
nothing wrote:
[ "$(cd ~/.claude-client && pwd -P)" = "$HOME/.claude-client" ] && echo match || echo differs
differs costs one cprof login client. Either way cprof list reports the
account each profile actually resolves to, so it will tell you which happened.
Finish by giving the directory a rule, after which the alias has nothing left to do:
cprof rule add ~/dev/<client> client
Claude Code keys its credentials to CLAUDE_CONFIG_DIR. cprof uses this: each
profile is its own config directory with its own credentials, and a shell
function points CLAUDE_CONFIG_DIR at the right one before launching.
Where those credentials physically live depends on the Claude Code version, and
cprof deliberately does not care. Versions before 2.1 wrote
$CLAUDE_CONFIG_DIR/.credentials.json. Since 2.1 they go to the macOS keychain
under a service name derived from the directory —
Claude Code-credentials-<sha256(CLAUDE_CONFIG_DIR)[0:8]>, against the plain
Claude Code-credentials used when the variable is unset. Either way each
profile gets its own store, and cprof asks claude auth status whether a
profile is signed in rather than looking for a file.
Your existing setup stays exactly as it is, as a native profile — the launcher
exports nothing for it, so the keychain and ~/.claude.json are used unchanged.
No profile may point at ~/.claude; doing so would break authentication and
relocate .claude.json.
Credentials are fixed at process start, so switching accounts always means
relaunching claude. A SessionStart hook warns you when you have wandered into
a directory that expects a different account.
CLAUDE_CONFIG_DIR relocates the whole configuration directory, not only the
credentials in it — plugins, skills, agents, commands, hooks, settings.json and
CLAUDE.md all live there. Left alone, a profile would therefore start with none
of them, and switching account would silently mean switching away every
customisation.
So add links them, and cprof share <name> does it for a profile that
predates this behaviour:
$ cprof share personal
ASSET RESULT
settings.json linked (previous kept as settings.json.moved-20260729-103012)
CLAUDE.md linked
plugins linked
skills linked
hooks linked
They are symlinks, so installing a plugin or editing settings once applies to
every profile with nothing to re-sync. Anything the profile already had is moved
aside rather than deleted, and unshare removes only the links this created.
| Shared | Per-profile |
|---|---|
settings.json, keybindings.json | credentials (keychain item, or .credentials.json before Claude Code 2.1) |
CLAUDE.md | .claude.json |
plugins, skills, agents, commands, hooks | projects, sessions, history.jsonl, todos, caches |
The right-hand column is what keeps two accounts apart, so nothing there is ever
linked. Use add --isolated for a profile that should share nothing.
What Quickstart steps 1 and 2 are doing, and why.
Requires macOS and bash 3.2+ (the system shell). Homebrew pulls in jq; the
usage feature also needs curl, which every supported macOS ships.
Two pieces, and you can take either alone. brew installs the CLI on
PATH; the plugin installs the parts that only exist inside a Claude Code
session — the SessionStart warning, /profile, and the statusline segment.
The CLI is what the shell function needs, so brew alone is a working setup; the
plugin alone is not.
The curl installer is the CLI piece without Homebrew, in the same layout
the formula uses: the latest release's scripts, statusline, hooks and
commands land in ~/.local/share/cprof, with ~/.local/bin/cprof a symlink
into it. It refuses to run without jq and warns when ~/.local/bin is not
on PATH. Pin a version with CPROF_VERSION=cprof--v0.8.0 bash install.sh;
uninstall by deleting those two paths.
FAQ
cprof is a Claude Code plugin with hand-picked skills for development work, indexed on Flowy. Install it with the command on its page. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it