claude-multiprofile

test npm License: MIT Node Platform

Run multiple Claude accounts side by side on macOS. Personal and work, multiple clients, separate test accounts. Each profile is fully isolated: its own login, chats, settings, MCP connectors, plugins, and skills. No more signing out of one account to use another.

Works for both Claude Desktop (the GUI app) and Claude Code (the terminal CLI), independently or together.

See it in action: examples/walkthrough.md, a full session showing every prompt and output.

Disclaimer. This is an unofficial community tool. It uses public Electron flags (--user-data-dir) and a stable but undocumented Claude Code environment variable (CLAUDE_CONFIG_DIR) to keep profiles isolated. Anthropic engineers have engaged on the open feature requests for native multi-account in both apps, so the approach is well known, but it is not officially supported. If a future Claude release changes how profiles work, this tool will need to catch up.

Why this exists

Claude Desktop and Claude Code both assume a single signed-in account. There's no built-in profile switcher today (open feature requests: Desktop, Desktop UI). The standard workaround is a manual setup:

  • For Desktop, launch with open -n -a "Claude" --args --user-data-dir=... against a custom data folder
  • For Code, set CLAUDE_CONFIG_DIR=... before running claude

Both work. Both are fiddly to set up and easy to mess up. This tool automates the whole thing, including the things people forget:

  • Generating a real macOS .app launcher you can drag to the Dock
  • Copying the Claude icon onto the launcher so it's visually distinct
  • Adding a properly quoted shell alias to the right rc file (zsh, bash, fish)
  • Seeding new Code profiles from your existing ~/.claude so plugins and MCP servers carry over (without leaking auth)
  • Tracking everything in a registry so you can list, status-check, and cleanly remove profiles

Install

npm install -g claude-multiprofile

To upgrade later, run claude-multiprofile upgrade (or re-run the install command above).

Install the latest unreleased commit

If you want changes that haven't been published to npm yet:

npm install -g github:jmdarre-v/claude-multiprofile

Requirements

Node 18 or newer. macOS is required for Claude Desktop profiles. Claude Code profiles work on macOS and Linux.

Quick start

claude-multiprofile add

The wizard walks you through every choice, explains what each step does, and lets you accept defaults if you don't care. A typical first run takes about 30 seconds.

After it finishes, you'll have a new launcher .app on your Dock (for Desktop) and a new shell alias like claude-work (for Code). Sign in once on each, and you're done.

What gets created

For a profile named work, with both Desktop and Code targets, the tool creates:

~/Library/Application Support/Claude-WORK/    ← Desktop data folder
~/Applications/Claude WORK.app                ← Desktop launcher (drag to Dock)
~/.claude-work/                               ← Code config folder
~/.zshrc                                      ← adds: alias claude-work='...'
~/.config/claude-multiprofile/profiles.json       ← registry entry

Nothing about your existing default Claude install changes. Your current login, chats, MCP servers, and skills stay exactly where they are.

How it works

Claude Desktop

Claude Desktop is built on Electron. Electron honors the --user-data-dir command-line flag, which moves the entire app state (auth tokens, chat list, settings, MCP connectors, projects, custom styles) to a directory of your choosing. Two .app launchers pointed at two different data folders give you two fully independent Desktop instances.

The launcher .app is a tiny AppleScript bundle generated by osacompile, a built-in macOS tool. The script is one line: do shell script "open -n -a 'Claude' --args --user-data-dir='/path/to/your/profile'". The -n flag forces a new instance even when Claude is already running, which is what makes side-by-side launches work.

Since v0.1.12, a profile that has both Desktop and Code targets also gets --env 'CLAUDE_CONFIG_DIR=...' in that launch line. --user-data-dir only isolates Desktop itself; the Claude Code sessions Desktop spawns would otherwise read ~/.claude in every profile, quietly merging CLAUDE.md, settings.json, and per-project memory across profiles that look isolated. Setting the variable on the launched app propagates it to the CLI processes Desktop starts.

Launchers built before v0.1.12 keep their old launch line. Run claude-multiprofile doctor to find them and doctor --fix to rebuild them in place, which preserves the icon and bundle ID.

Claude Code

Claude Code (the terminal CLI) honors the CLAUDE_CONFIG_DIR environment variable. Set it to a folder, and Claude Code reads/writes all of its state (project memory, plugins, skills, MCP servers, slash commands) under that folder instead of the default ~/.claude.

Authentication is the interesting bit. Claude Code stores its OAuth token in macOS Keychain, keyed by a SHA-256 hash of the active CLAUDE_CONFIG_DIR. Different config dir, different keychain entry, completely separate session. You can copy a config folder around without leaking auth.

This means you can seed a new Code profile from your existing ~/.claude (carrying over skills, plugins, and MCP servers) and the new profile will still ask you to log in fresh. The wizard offers this by default.

Commands

claude-multiprofile add

Interactive wizard. Walks through:

  1. Whether to set up Desktop, Code, or both
  2. The profile name (e.g. work, personal, client-acme)
  3. Where the data/config folders should live
  4. Where to save the launcher .app (Desktop only)
  5. Whether to copy the Claude icon onto the launcher (Desktop only)
  6. The shell alias name (Code only)
  7. Whether to seed the new Code profile from your existing ~/.claude (Code only)

Then prints a plan, asks for confirmation, and applies.

The wizard refuses to claim a directory that belongs to something else. The names mem, profiles, multiprofile, code, and desktop are reserved outright, since each would collide with another tool's folder or this tool's own config. Beyond that, if the folder you pick already exists and is not already registered here, you get a warning describing what is in there and a chance to back out. This stops a profile named mem from quietly pointing at ~/.claude-mem and, later, a remove from offering to delete another tool's data.

claude-multiprofile list

Prints every configured profile with its paths and creation date, plus the claude binary currently winning on your PATH and its version. All profiles share that one binary, so it is worth seeing alongside them.

claude-multiprofile status

Walks every profile and verifies the directories, .app, and shell aliases still exist. Also reports the resolved claude binary and warns when more than one is on your PATH. Useful after a machine migration or after manually editing your .zshrc.

claude-multiprofile doctor [--fix]

Diagnoses the machine, not just the registry. Where status asks "is each profile's paperwork in order?", doctor asks "will these profiles actually behave?"

It checks:

  • Which claude wins on PATH, its version, and any shadowed copies. Every profile shares one binary, so a duplicate from another Node version is a common cause of "I upgraded and nothing changed."
  • Broken npm installs. A package directory left with node_modules/ but no package.json silently de-registers the command and lets PATH fall through to an older copy.
  • Directory collisions. A profile pointing at another tool's data folder (~/.claude-mem, ~/.claude-profiles), or two profiles sharing one directory.
  • Launcher bundle IDs. Launchers created before v0.1.9 still carry the default AppleScript bundle identifier; with two or more of them, macOS confuses the launchers and Dock double-clicks stop working.
  • Launchers that don't export CLAUDE_CONFIG_DIR. Launchers created before v0.1.12 let Claude Code sessions started from inside Desktop fall back to the shared ~/.claude. doctor reads the launcher's compiled script to find them, and --fix rebuilds them in place.
  • A corrupt registry file. A registry that exists but isn't valid JSON otherwise masquerades as "no profiles configured". Mutating commands refuse to run until it's fixed, and every write keeps a .bak of the last good version next to it.
  • Cross-profile read protection drift (see Profile isolation below).

--fix repairs what's safe to repair automatically: deny-rule drift, default bundle IDs, and launchers missing CLAUDE_CONFIG_DIR. Everything else is reported with the command to run.

claude-multiprofile rename [old] [new]

Renames a profile and moves everything that encodes its name: the Code config folder, the shell alias, the Desktop data folder, the launcher .app and its bundle ID, the registry entry, and every other profile's isolation rules.

Paths you chose manually are left where they are; only folders still at their default location get moved.

Renaming a Code profile signs it out. Claude Code stores its login in the macOS Keychain under a key derived from the config folder path, so moving the folder orphans the token and you'll run /login once more. This tool deliberately does not try to move the Keychain entry: the key derivation isn't reproducible, and guessing risks clobbering a different account's credentials. Chats, skills, and MCP config all move normally. Desktop profiles are unaffected, since their auth lives inside the folder being moved.

claude-multiprofile extensions

Copy Claude Desktop extensions from one install into another, interactively. It prompts for the source (your default install or any profile) and then the target profile. There is no profile name to mistype, and cross-profile copying works in both directions.

When you create a profile, it starts empty by design: none of your default install's extensions, settings, or chats follow it over. That isolation is the point. But re-installing every extension on every profile by hand is tedious. This command is the relief valve.

The flow:

  1. Reads the extension list from your default Claude Desktop data folder (~/Library/Application Support/Claude/Claude Extensions/)
  2. Shows a multi-select prompt with each extension. Extensions already in the target profile are pre-deselected; new ones are pre-selected
  3. Copies the selected extension folders AND their matching settings files (Claude Extensions Settings/<id>.json) into the target profile
  4. If conflicts exist, asks once whether to overwrite (or use --force to skip the prompt)
claude-multiprofile extensions            # interactive: pick source, then target
claude-multiprofile extensions --force    # overwrite conflicts without asking

Restart Claude Desktop after running this for the new extensions to load.

This command does not apply to Code-only profiles (extensions are a Desktop concept).

claude-multiprofile repair [name]

Re-registers a profile's launcher .app with macOS LaunchServices. Use this if the Dock icon stops responding to double-clicks even though the .app bundle is intact and open path/to/Claude\ Work.app from the terminal still works.

The underlying cause is a stale LaunchServices cache, which can happen after macOS updates, app moves, or sometimes for no clear reason. The command runs lsregister -f on the launcher and touches the bundle to nudge the icon cache. Running it on a healthy profile is harmless.

For Code-only profiles, this command has nothing to repair (there's no .app) and exits cleanly.

claude-multiprofile remove [name]

Tears down a profile. Unregisters and removes the launcher .app, removes the shell alias and the registry entry, and rewrites the remaining profiles' isolation rules. By default the data folders are kept (so you can recover your chats if you change your mind). The wizard asks separately about deleting the data folders.

One thing it deliberately leaves behind: the profile's saved login in your Keychain. Claude Code keys those entries by a hash of the config directory that this tool can't reproduce, and deleting the wrong one would take out another account's credentials. The orphan is inert. To avoid creating one, run /logout inside the profile before removing it; to clear it by hand, search Keychain Access for Claude Code-credentials.

claude-multiprofile help / --version

Self-explanatory.

First-launch checklist

This is the part most guides skip and most users get burned by.

Desktop: signing in for the first time

Claude Desktop's sign-in flow uses a claude:// deep link that macOS hands to whichever Claude instance is running. If two are open at once, the auth token can land on the wrong one and you'll end up with both profiles signed into the same account.

The fix is simple but important:

  1. Quit any other Claude window with Cmd+Q before doing the very first launch of the new profile.
  2. Double-click the new launcher (or run open ~/Applications/Claude\ WORK.app).
  3. Sign in with the account for this profile.
  4. Quit (Cmd+Q) once you've confirmed it logged in correctly.

From that point on, both profiles can run simultaneously. The auth token is stored in the per-profile data folder and won't get re-routed.

Code: signing in for the first time

source ~/.zshrc          # or open a new terminal tab
claude-work              # launches Claude Code with CLAUDE_CONFIG_DIR set

Inside the REPL, run /login. A browser tab opens. Sign in with the account for this profile. The OAuth token gets stored in Keychain under a key derived from the profile's config dir, so it's fully separate from your default account.

Common patterns

Per-project default profile

If you always want a specific profile active in a particular repo, drop a .envrc (with direnv) or a .env file:

export CLAUDE_CONFIG_DIR="$HOME/.claude-work"

When you cd into the repo, the variable is set automatically. Plain claude from inside that directory uses the right profile.

Visual disambiguation

By default, all profile launcher .apps share the Claude icon. If you want them visually distinct on the Dock:

  1. Right-click ~/Applications/Claude WORK.app → Get Info
  2. Drag any image (PNG, ICNS, JPG) onto the small icon in the top-left of the Info window
  3. The Dock and Cmd-Tab will pick up the new icon within a few seconds

For the terminal side, you can prefix with a colored slash command:

alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude -e "/color blue"'

/color makes the Claude Code REPL prompt visually distinct so you don't lose track of which account you're in.

Sharing skills between profiles

Profiles are independent, so installing a skill in one doesn't affect the others. If you want one set of skills available to all your profiles, symlink the skills directory:

rm -rf ~/.claude-work/skills
ln -s ~/.claude/skills ~/.claude-work/skills

Same trick works for plugins or any sub-folder you want to share. Be careful with projects/ if you want chat history to stay separate, that's the folder you do not want shared.

Troubleshooting

The new Desktop profile launched already signed into my other account.

You launched it while the other Claude instance was running. The claude:// auth deep link got routed to the wrong app. Fix:

rm -rf ~/Library/Application\ Support/Claude-WORK

Then quit ALL Claude windows (Cmd+Q) and launch the new profile again. It'll start fresh and you can sign in cleanly.

The launcher icon stopped responding to double-clicks but open from terminal still works.

This is a stale macOS LaunchServices cache, often combined with a stale Dock icon cache. Run:

claude-multiprofile repair <profile-name>

The command re-registers the .app with LaunchServices, refreshes the icon cache, and restarts the Dock so it picks up the new registration. The Dock will blink briefly (less than a second) and then come back. If clicking still doesn't work after that, log out and back in to force a full LaunchServices reset.

The shell alias isn't found.

Aliases live in your shell's rc file but they only get loaded when a new shell starts. Either open a new terminal tab or source ~/.zshrc (or whatever your shell's rc file is). Run claude-multiprofile status to confirm the alias is actually in the rc file.

The launcher .app shows the AppleScript icon, not Claude's.

You answered "no" to the icon copy step, or your Claude.app install is in a non-standard location. Either re-run claude-multiprofile add (and remove the old profile first) or copy the icon manually:

cp /Applications/Claude.app/Contents/Resources/*.icns \
   ~/Applications/Claude\ WORK.app/Contents/Resources/applet.icns
touch ~/Applications/Claude\ WORK.app

I want to see what's in the registry.

cat ~/.config/claude-multiprofile/profiles.json

It's a plain JSON file. You can inspect or hand-edit it, though running the CLI commands is safer.

I'm on Linux.

The Code half works fine on Linux, the Desktop half doesn't (Claude Desktop is macOS-only at the moment). The wizard skips Desktop questions on non-macOS automatically.

Comparison with similar tools

Tool Desktop Code Mac Linux Notes
claude-multiprofile (this) yes yes yes partial Single tool for both, interactive wizard
aimux no yes yes yes Code only, also handles symlink-sharing
aisw no yes yes yes Rust binary, also covers Codex CLI and Gemini CLI
Jean-Claude no yes yes yes Cross-machine sync, opinionated dotfiles
Manual setup yes yes yes yes Documented in several places

The pitch for this tool over the others: it's the only one that handles Claude Desktop alongside Claude Code in a single command, and the interactive wizard means you don't have to remember the right flags or know in advance where files should go.

Profile isolation

Profiles are isolated by configuration: each one points Claude at a different config/data directory (CLAUDE_CONFIG_DIR for Code, --user-data-dir for Desktop). That fully separates identity and storage: separate logins, chats, settings, and MCP connectors.

What it does not do on its own is restrict what a running profile can reach on disk. A broad filesystem search from $HOME could surface a sibling profile's CLAUDE.md, skills, or MCP config, and pull the wrong context into the conversation.

Since v0.1.10, Code profiles get an enforceable guard. Every profile's settings.json receives permissions.deny rules blocking reads of every other profile's directories:

{
  "permissions": {
    "deny": ["Read(//Users/you/.claude-work/**)"]
  }
}

These are hard denials (they don't prompt), and Claude Code applies Read rules to Grep and Glob as well, so directory walks are covered too. The rules are rewritten automatically on add, remove, and rename, and doctor --fix repairs them if they drift.

Three limits worth knowing:

  • Your own rules are preserved. The tool tracks only the rules it wrote and never removes deny rules you added yourself.
  • A symlinked settings.json is skipped. If a profile's settings.json is a symlink pointing outside that profile (a common way to share one config across profiles), the rules are not written. Writing them would push one profile's rules into a file the others also read, and each profile would then strip the others' rules on every change. doctor reports the profile as unprotected and names the link target; replace the link with a real file to opt back in.
  • Desktop profiles have no equivalent hook. Claude Desktop is Electron, not Claude Code, so there's no settings-level permission system to use. Desktop profiles remain isolated by data directory only. True filesystem confinement there would require sandbox-exec or a container.

Known limitations

Dock icons show the standard Claude icon

If you customize a launcher's icon, Finder and Get Info show your custom icon, but the Dock tile of the running window shows Claude's standard icon.

This is structural. The launcher is a small AppleScript bundle whose whole job is to run open -n -a Claude.app --args --user-data-dir=... and then exit. The window you end up with belongs to Claude.app's own process, not to the launcher, so the Dock tile uses Claude's icon. Changing the launcher's icon can't affect it, because the launcher isn't the running application.

Giving each profile a genuinely distinct Dock icon would mean shipping a separate re-bundled copy of Claude per profile, which breaks code signing and auto-update. Not a trade worth making. For telling windows apart at a glance, see Visual disambiguation.

Chats and Projects don't transfer between profiles

Profiles isolate accounts, so there's no way to move a conversation or a Desktop Project from one profile to another. Those live server-side, tied to the account that created them; this tool only ever touches local files and never talks to Claude's servers.

Your project files on disk are already shared, since profiles redirect Claude's own state, never your working directory. Any profile can open the same repository. What doesn't follow you is conversation history and account-bound Projects.

If you're switching profiles to spread usage across accounts, the workflow that works is to keep the context in the repository rather than in the chat: a CLAUDE.md (or a notes file) that any profile reads on start, so a fresh conversation in another account picks up where the last one left off.

Security notes

The tool reads and writes the following on your machine:

  • ~/Library/Application Support/Claude-{Name}/ (creates new folders only)
  • ~/Applications/ (creates new .app bundles only)
  • ~/.claude-{name}/ (creates new folders only)
  • ~/.claude-{name}/settings.json (adds cross-profile permissions.deny rules; tracks only the rules it wrote and never removes yours)
  • ~/.zshrc, ~/.bash_profile, or ~/.config/fish/config.fish (adds a delimited managed block; never touches lines outside the markers)
  • ~/.config/claude-multiprofile/profiles.json (the registry)

It does not touch:

  • Your default Claude data folder (~/Library/Application Support/Claude/)
  • Your default ~/.claude (except to read it for seeding, never to write)
  • macOS Keychain (Claude Code's auth lives there but the tool never reads or writes Keychain entries)
  • Anything else on your filesystem

The single npm dependency is @inquirer/prompts, the standard interactive-prompt library used by npm itself and most modern CLI tools.

Contributing

Issues and PRs welcome. The codebase is small and aggressively commented, so it should be easy to navigate. Run the tests with npm test.

License

MIT