Two Claude accounts, one config directory: the missing primitive

2026-04-26 · 8 min read textaccounts

The pain

If you use Claude Code with two accounts — say work and personal — you've already hit this. Claude stores everything under a single ~/.claude/ directory: sessions, memory files, settings, auth tokens, MCP server registrations. There's no profile flag, no --user, no concept of "switch accounts."

The result is mush. Open a personal session and your work memory loads. Run claude after a work claude logout and your personal sessions are still visible on disk. Custom slash commands you wrote for one workspace bleed into the other. The two contexts you want firewalled — for security, for sanity, for not-leaking-internal-stuff-into-personal-prompts — share a single home on disk.

The pattern, if you've been around long enough, is familiar. Browsers solved this with profiles. Git solved it with config-includes. Cloud CLIs solve it with --profile flags. Claude Code, today, hasn't.

I needed real isolation. So I wrote textaccounts — a small CLI that registers separate ~/.claude* directories as named profiles and switches between them by setting CLAUDE_CONFIG_DIR. No patches to Claude Code. No wrappers around claude itself. Just the missing primitive.

Why I built it

The story is mundane but maybe useful. I had a long-running personal project — a text-based multiplayer game I work on in the evenings — and I'd been using my personal Claude account on it. Day job runs on a separate account.

After a while, I started hitting on the personal side the same friction I'd been hitting at work: memory bleeding between contexts, settings drifting, sessions piling up indistinguishably. I made a list of the things that broke and grouped them. Most of the groups had their own fix — session navigation, MCP server lifecycle, agent context recovery, context-lensed reading. But underneath all of them was a shared problem: there was no notion of which Claude is this at the OS level. One config dir, two identities.

textaccounts is the base layer for the rest. Get profile isolation first; everything else gets easier on top of it. The other groups in that list became their own tools — sibling projects to this one.

The fix, if you read Claude Code's source, is half a fix that already exists: Claude reads CLAUDE_CONFIG_DIR natively. Set it to ~/.claude-work, Claude uses that directory for everything. Set it to ~/.claude-personal, same thing for personal. The config-dir variable is the seam. The missing piece was just: a tool to manage the seam.

textaccounts registers your existing config directories as named profiles, then switches CLAUDE_CONFIG_DIR in your shell when you ask it to. A Python subprocess can't change its parent's environment — that's a fundamental Unix constraint — so the tool ships a small fish function that evaluates the output of textaccounts show <name>. That fish function is the only "magic"; everything else is plain config.

Two design choices mattered most.

Adopt, don't copy. When you run textaccounts adopt work ~/.claude-work, nothing moves. The directory stays where it is. textaccounts just remembers it exists. If you decide tomorrow you don't like the tool, delete ~/.textaccounts/profiles.yaml and your real Claude directories are untouched.

Worker profiles. Sometimes I want two parallel work sessions — one for an experiment, one for the real branch — without copying the entire memory and session history. textaccounts create my-experiment --worker --from work makes an auth-only copy: same login, fresh session and memory state. Cheap to spin up, cheap to throw away. Useful when you're running parallel agents (which I now do daily).

That's the whole tool. A few hundred lines of Python plus a fish function, MIT licensed.

How to use it in five minutes

Install:

pip install textaccounts
# or with uv:
uv tool install textaccounts

textaccounts install   # writes the fish function + completions

Open a new shell to pick up the function, then register your existing directories:

textaccounts adopt work ~/.claude-work
textaccounts adopt personal ~/.claude-personal

Switch profiles:

textaccounts switch work
# Or with the fish wrapper installed:
ta switch work

Verify:

$ textaccounts status
Active profile: work
Path: /Users/you/.claude-work
Email: yo***@example.com
Sessions: 37
CLAUDE_CONFIG_DIR: /Users/you/.claude-work (in sync)

Now run Claude in that profile:

claude
# All sessions, memory, settings come from ~/.claude-work.

Switch to personal in another shell tab:

ta switch personal
claude
# Different sessions, different memory, different auth.

A few extras worth knowing:

textaccounts list                # all profiles + active marker
textaccounts status              # sync state, paths, current
textaccounts view                # interactive Textual TUI

# Worker profile for parallel experiments
textaccounts create scratch --worker --from work

# Aliases (type `ta switch p` instead of `personal`)
textaccounts alias personal p

The TUI auto-discovers any ~/.claude* directories you haven't registered yet and surfaces them as adoption suggestions. Press a, name the profile, done.

If you're on bash or zsh, the fish dependency is a fair complaint — bash/zsh integration is on the roadmap. The Python core is shell-agnostic; only switch needs the fish function, because that's the only command that has to mutate the parent shell's environment.

One requirement: Claude Code ≥ 2.1.56

One caveat that's worth knowing: CLAUDE_CONFIG_DIR alone doesn't isolate OAuth credentials. Older Claude Code versions stored auth in a single shared macOS keychain entry, so two profiles pointing at different config dirs would still share the underlying login (this is claude-code#20553).

Claude Code v2.1.56 fixed this by suffixing the keychain service name with a hash of CLAUDE_CONFIG_DIR. With current Claude Code, your work and personal logins really are isolated — different keychain entries, different tokens, no leak.

textaccounts checks this for you. Run:

textaccounts doctor

It reports your installed Claude Code version and tells you whether per-profile keychain isolation is supported on your machine. If you're below the floor, the fix is just to upgrade Claude Code — nothing in textaccounts changes.

The bigger picture

textaccounts is one of several tools in Paperworlds — small open-source fixes for missing primitives I kept hitting in Claude Code while running it daily. The other tools tackle different layers of the same problem; this one is the base layer.

If you've been merging your two Claude accounts into one directory and resigning yourself to it, try this instead. Issues, PRs, and "I want this for bash" requests welcome.

Repo: github.com/paperworlds/textaccounts