The usage limit lands mid-week, and a second subscription sits unused on another email. Switching with /logout swaps everything else along with the bill: skills, servers, hooks, every session you've run. It doesn't have to: two aliases, a few symlinks, two short scripts, and nothing you already have gets touched.
Hand the whole thing to Claude Code instead
Set up a second Claude Code account on this machine, sharing everything except
billing and history. Work in order, and stop at any step whose preflight does
not match what you expect.
GOAL
~/.claude keeps working exactly as it does today. A new ~/.claude-personal
has its own login and its own transcripts, and reads the same skills,
settings, hooks, plugins and MCP servers through symlinks.
NEVER
- overwrite or move whatever `command -v claude` points at
- copy, merge or symlink oauthAccount between the two .claude.json files
- touch ~/.claude/projects, history.jsonl, or any transcript
- run `claude` or `claude-personal` yourself — I do the logins
- edit ~/.zshrc or PATH without showing me the diff and waiting
0 PREFLIGHT — report, change nothing
command -v claude
ls -la ~/.claude
jq '.mcpServers | keys' ~/.claude.json
Tell me the real binary path and how many MCP servers are defined.
Stop if the binary path is inside a directory you were going to
install the script into.
1 mkdir -p ~/.claude-personal
2 Append to ~/.zshrc, diff first:
alias claude-personal='CLAUDE_CONFIG_DIR=~/.claude-personal claude'
3 STOP. Tell me to run `source ~/.zshrc` then `claude-personal`, and wait.
That first run writes ~/.claude-personal/.claude.json with its own login.
Do not continue until I confirm it finished.
4 For each of skills, CLAUDE.md, settings.json, settings.local.json, plugins:
if ~/.claude/NAME exists and ~/.claude-personal/NAME does not,
ln -s ~/.claude/NAME ~/.claude-personal/NAME
Report anything you skipped and why.
5 Copy across the servers and nothing else. Back the file up first:
jq --slurpfile mcp <(jq '.mcpServers' ~/.claude.json) \
'.mcpServers = $mcp[0]' ~/.claude-personal/.claude.json \
> /tmp/merged.json && mv /tmp/merged.json ~/.claude-personal/.claude.json
Then confirm oauthAccount in that file is byte-identical to before.
6 Write the resume script into a folder the real binary is NOT in, set REAL
to the step 0 path, chmod +x it, and put that folder ahead of REAL's folder
in PATH. Diff ~/.zshrc first.
7 VERIFY without starting a session, and report all four:
command -v claude -> the script
REAL --version -> still runs
ls -l ~/.claude-personal -> five symlinks + its own .claude.json
jq -r '.oauthAccount.emailAddress' on both -> two different addresses
Anything already in place: say so and leave it alone rather than recreating it.
Give each account its own folder
My ~/.claude holds 28 skills, seven MCP servers, hooks on eight events, my memory files, and a transcript of every session I've ever run. The account's login sits right there with all of it, in ~/.claude.json — which is why logging out feels like it takes everything with it. It does.
So you don't swap the login inside one folder. You give the second account a folder of its own, and CLAUDE_CONFIG_DIR is how Claude Code decides which one to read. It defaults to ~/.claude, which is why the work account needs no alias at all — only the new one does.
# Claude Code second account alias claude-personal='CLAUDE_CONFIG_DIR=~/.claude-personal claude'
One alias, for the account that isn't the default. Bare claude goes on reading ~/.claude exactly as it always has, so nothing you already type changes.
Make the folder with mkdir ~/.claude-personal, and the first run of claude-personal prompts an OAuth login and writes the token there. That is the second account, finished — and completely useless.
An empty folder means no skills, no settings, no plugins. This is where the symlink does the work. A symlink is a file that points at another file — open it and you get whatever it points at. Make the personal folder's skills a symlink to ~/.claude/skills and there is still exactly one copy of your skills on disk — both accounts just walk to the same place to read it.
Five of those and the second account has your whole setup.
$ cd ~/.claude-personal $ ln -s ~/.claude/skills skills $ ln -s ~/.claude/CLAUDE.md CLAUDE.md $ ln -s ~/.claude/settings.json settings.json # hooks, permissions, env $ ln -s ~/.claude/settings.local.json settings.local.json $ ln -s ~/.claude/plugins plugins
A skill you add later shows up in both accounts on its own, and a hook you edit once takes effect in both. There is no second copy to keep in step.
Get your MCP servers into the personal account
Here's the surprise, and it turns up the first time you run claude-personal. Your MCP servers and your folder trust don't live in ~/.claude at all. They live in ~/.claude.json — a file sitting next to the folder, not inside it.
So claude-personal looks for that file inside its own folder, finds nothing, writes itself a blank one, and starts up with no servers at all. A symlink won't fix it: that same file holds oauthAccount, which is the account identity, so pointing it at the real one would sign both accounts into the same login. Copy over only the mcpServers part and the identity stays put.
$ jq --slurpfile mcp <(jq '.mcpServers' ~/.claude.json) \ '.mcpServers = $mcp[0]' ~/.claude-personal/.claude.json \ > /tmp/merged.json && mv /tmp/merged.json ~/.claude-personal/.claude.json
Make --resume find the right account
Each account keeps its own transcripts, at <folder>/projects/<project>/<session-id>.jsonl. Resume a personal session under your work account and Claude Code says it doesn't exist, because from where it's looking that's true.
A session id only ever exists in one of those two folders, so the right account can be worked out from the id alone. Doing that before Claude Code starts is the whole job of a shim, and nothing ever calls it by name. You keep typing claude exactly as you always did — what changes is which file the shell finds under that name.
Those four decisions are the whole script.
#!/bin/bash # routes --resume to the account that owns the session REAL=/opt/homebrew/bin/claude # wherever `command -v claude` pointed before if [[ -z "$CLAUDE_CONFIG_DIR" ]]; then id="" prev="" for arg in "$@"; do if [[ $prev == "--resume" || $prev == "-r" ]]; then id=$arg; break; fi if [[ $arg == --resume=* ]]; then id=${arg#--resume=}; break; fi prev=$arg done if [[ -n $id ]] && compgen -G "$HOME/.claude-personal/projects/*/$id.jsonl" >/dev/null; then export CLAUDE_CONFIG_DIR="$HOME/.claude-personal" fi fi exec "$REAL" "$@"
It only gets a look at the command because the shell finds it first, so it goes in a folder that sits ahead of the real binary in PATH. Read REAL off command -v claude before you install it — afterwards that command answers with the script.
Put the script somewhere the real binary is not. A Homebrew install answers /opt/homebrew/bin/claude; the native installer answers ~/.local/bin/claude, and writing the script over that one would replace the very binary it needs to exec.
$ command -v claude # do this FIRST — whatever it prints is REAL $ mkdir -p ~/bin # any folder REAL is not already sitting in $ $EDITOR ~/bin/claude # paste the script, set REAL to that path $ chmod +x ~/bin/claude $ export PATH="$HOME/bin:$PATH" # in ~/.zshrc, ahead of REAL's folder
After that you type the id and let the script work out which folder it lives in.
$ claude --resume 0b8e1d47-3ca9… # personal session — switches account for you $ claude --resume 4f2a9c3e-71bd… # work session — nothing to do, stays put $ claude-personal --resume 0b8e1d47-3ca9… # you said which — passes straight through
The aliases and the script are two separate controls, and they don't fight. A shell expands an alias before it ever searches PATH, so claude-personal becomes CLAUDE_CONFIG_DIR=~/.claude-personal claude — and that claude still lands on the script, which finds the variable already set and gets out of the way. Naming an account always wins. The script only decides when nobody did.
So the id is the only thing you have to remember, from either account.
Move a conversation to the other account
You hit the usage limit halfway through a conversation — that's the case that started all of this. A session is a transcript on disk, so continuing it on the other account is a copy and a resume. This one goes next to the shim in ~/bin, made executable the same way.
#!/bin/bash # claude-pickup <session-id> — continue a work session as personal set -euo pipefail id=${1:?usage: claude-pickup <session-id>} src=$(ls ~/.claude/projects/*/"$id".jsonl 2>/dev/null | head -1) [[ -n $src ]] || { echo "session $id not found" >&2; exit 1; } proj=$(basename "$(dirname "$src")") mkdir -p ~/.claude-personal/projects/"$proj" cp -n "$src" ~/.claude-personal/projects/"$proj"/ CLAUDE_CONFIG_DIR=~/.claude-personal exec claude --resume "$id"
The work account keeps its copy as history, and the shim routes later resumes to the personal one.
A few things stay separate on purpose: the logins, the transcripts, folder trust, and any server with its own OAuth token, which needs /mcp run once in the new account. Everything else is shared, and that is the whole surface of it.
$ claude # work (default, unchanged) $ claude-personal # second subscription, same brain $ claude --resume X # figures out the account itself $ claude-pickup X # mid-conversation wallet swap
Twenty minutes, once. Bare claude still opens the same account with the same skills, the same servers and the same history, so nothing about the way you already work changes. The only thing that changes is which subscription pays.