Named after Billy Milligan — the man of many identities.
Billy is a minimal CLI tool for managing multiple Claude Code accounts. It lets you pick which account to use at launch, keeping each account's credentials and settings fully isolated.
Each account gets its own configuration directory (~/.claude-<name>), passed to Claude Code via the CLAUDE_CONFIG_DIR environment variable. The first time you launch a new account, Claude Code will walk you through login. After that, credentials are persisted in that directory and login is never asked again. An account adopted from an existing ~/.claude keeps that directory and its login, so it never asks.
Account profiles are stored in ~/.config/billy/accounts.json.
- Node.js v18+
- Claude Code installed and available as
claudein your PATH
git clone https://github.com/francescocretti/billy.git
cd billy
npm installThen choose how to install the billy command globally:
Option A — npm link (recommended while developing)
npm linkCreates a symlink from the global npm bin to this project folder. Source changes take effect immediately without reinstalling. If you move or delete the project folder, the command breaks.
Option B — npm install -g . (recommended for stable use)
npm install -g .Installs billy as a proper global package, independent of the project folder's location. After any future source change, re-run this command to update.
billyWhat you see depends on whether you already use Claude Code.
If you already have a ~/.claude — the common case, since that setup is usually why you want a switcher — Billy finds it, shows what it found, and asks the one thing it cannot know:
◇ Existing Claude Code setup found ──────────────────╮
│ │
│ Directory /Users/you/.claude │
│ Logged in as you@example.com │
│ Projects 21 │
│ │
├─────────────────────────────────────────────────────╯
◆ What should Billy call this account?
│ personal
└
Nothing is moved and nothing is lost: ~/.claude stays where it is, keeps its history and credentials, and plain claude still reaches it. Billy just records it as an account so it shows up in the list. See Migrating from the default Claude Code setup for the details.
On a clean machine, there is nothing to adopt and you only get the option to add an account:
◆ Which account do you want to use?
│ ○ + Add account
│ ○ ⚙ Settings
└
Enter a name (e.g. work or personal). Billy creates an isolated config directory for it and launches Claude Code. Complete the login flow once — Billy remembers it from then on.
Your accounts appear in the list:
Billy — Claude Code Switch
◆ Which account do you want to use?
│ ● work ~/.claude-work · shared
│ ○ personal ~/.claude
│ ○ + Add account
│ ○ ⚙ Settings
└
Select one and Claude Code starts immediately, no login required.
Everything that isn't "launch an account" lives behind ⚙ Settings, so the main screen stays a list of accounts however many knobs Billy grows. Each entry shows its current state as a hint:
◆ Settings
│ ● Shared resources on for 1 of 4 accounts
│ ○ Delete account the profile and, if you want, its config dir
│ ○ Language English
│ ○ Where everything lives
│ ○ ← Back
└
← Back and Escape do the same thing, and returning from settings drops you back on the account list with the updated state — so you can change something and launch in the same run.
Where everything lives answers the question a tool like this keeps raising, since it juggles directories across two config trees:
◇ Where everything lives ────────────────────────────────────────╮
│ │
│ Accounts 4 │
│ Account list /Users/you/.config/billy/accounts.json │
│ Billy settings /Users/you/.config/billy/settings.json │
│ Shared resources /Users/you/.agents │
│ Shared plugins warp │
│ Shared MCP servers none │
│ │
├─────────────────────────────────────────────────────────────────╯
Delete account asks two separate questions, because they are two separate decisions:
- Remove the account from Billy's list — this only rewrites
accounts.json. - Delete its config directory — this erases the account's credentials, history and settings.
The second defaults to no, so answering through with Enter leaves the directory untouched and you can re-add the account later without logging in again. Billy refuses to delete any directory outside your home, and warns you explicitly if the directory happens to be ~/.claude, Claude Code's own default config dir.
Billy speaks English and Italian. Pick one from Language; the choice is saved in ~/.config/billy/settings.json and applies from the next screen onwards.
The language is never guessed from your system locale, so Billy stays in the language you chose on every machine you carry your dotfiles to. English is the default until you pick otherwise.
Adding a language means adding one entry to MESSAGES and one to LANGUAGES in i18n.mjs. Any key you leave out falls back to English rather than showing a raw key.
Some Claude Code resources are things you author and want identical across every identity — your skills, slash commands, subagents, and your global CLAUDE.md. Billy can keep these in sync via symlinks so you maintain a single source of truth instead of copying them into each account.
The source of truth is ~/.agents/:
| Source | Linked into each account as |
|---|---|
~/.agents/skills/* |
<config dir>/skills/* |
~/.agents/commands/* |
<config dir>/commands/* |
~/.agents/agents/* |
<config dir>/agents/* |
~/.agents/CLAUDE.md |
<config dir>/CLAUDE.md |
Turning it on. Billy asks when you add an account, and stores the answer per account (sharedResources in accounts.json). To change it later — or to enable it on an account created before the option existed — go to ⚙ Settings → Shared resources, pick the account, and answer the same question. Turning it off only stops future syncs: symlinks already in that config dir are left where they are.
When it runs. At every launch, for the account you are launching — not for all of them at once. Each account picks up a new skill the next time you open it.
What it does. Adds links for new resources, fixes outdated ones, and prunes broken links that point into the source, such as a skill you deleted from ~/.agents. Broken links pointing anywhere else are yours, and are left alone. A source folder that doesn't exist is skipped silently — if you have no ~/.agents/commands, nothing happens for commands. Real (non-symlink) files already in an account are never overwritten; they are reported as skipped.
The sync is best-effort: any error is reported and never blocks launching Claude Code. To relocate the source of truth, set the BILLY_AGENTS_DIR environment variable.
MCP servers can't be shared by symlinking: user-scoped servers live inside each account's .claude.json, which also holds per-account state (login, org, per-project toggles). Instead, Billy uses Claude Code's --mcp-config flag.
Put your shared servers in ~/.agents/mcp.json, using the same format as a project .mcp.json:
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["-y", "some-mcp-server"]
}
}
}For every account with sharedResources enabled, Billy launches Claude Code with --mcp-config ~/.agents/mcp.json. These servers are loaded in addition to any servers the account configured on its own. Notes:
- The shared servers only apply when launching through
billy— runningclaudedirectly won't load them. - OAuth-authenticated remote servers still require logging in once per account: tokens are stored per config dir and can't be shared.
- Project-scoped servers (
.mcp.jsonin a repo) already work across accounts with no help from Billy.
Plugins are installed per config dir (~/.claude/plugins/), so a plugin installed on your main account is invisible to every other identity. That's what breaks terminal integrations: Warp, for instance, only recognises a session as Claude Code because the warp plugin emits OSC notifications from its hooks. Launch another account and the plugin isn't there, so the terminal sees a plain process and none of its special features light up.
Billy fixes this with Claude Code's --plugin-dir flag: every directory in ~/.agents/plugins/ that contains a .claude-plugin/plugin.json is loaded at launch, for every account (unlike the resources above, this isn't gated on sharedResources — --plugin-dir is session-only and writes no state anywhere).
Symlinks are fine, so you can point at a marketplace checkout and keep getting updates:
mkdir -p ~/.agents/plugins
ln -s ~/.claude/plugins/marketplaces/claude-code-warp/plugins/warp ~/.agents/plugins/warpNotes:
- A plugin the account already installed itself is skipped rather than injected. Loading both copies would register every hook twice — for Warp, that means duplicate notifications on every event. Installed-but-disabled counts as installed: the account disabled it on purpose.
- The injected plugin appears as
<name>@inlineinstead of<name>@<marketplace>; check withclaude plugin list. - Link the marketplace checkout, not
plugins/cache/<marketplace>/<name>/<version>/— the cache path changes on every version bump. - The Warp plugin needs
jqon yourPATH.
Open two terminal windows and run billy in each. Select a different account in each window — they run fully independently.
| Path | Contents |
|---|---|
~/.config/billy/accounts.json |
Account list (name, config dir path, shared-resources flag) |
~/.config/billy/settings.json |
Billy's own settings — currently just the UI language |
~/.agents/skills,commands,agents,CLAUDE.md |
Shared resources, symlinked into opted-in accounts |
~/.agents/mcp.json |
Shared MCP servers, passed with --mcp-config |
~/.agents/plugins/* |
Plugins loaded into every account via --plugin-dir |
~/.claude-<name>/ |
Claude Code config, credentials, and settings for that account — ~/.claude for an adopted one |
If you used Claude Code before installing Billy, your history, projects, settings and credentials live in ~/.claude. The first time you run Billy with no accounts yet, it finds that setup, shows you what it found, and asks the one thing it cannot know — what to call it:
◇ Existing Claude Code setup found ──────────────────╮
│ │
│ Directory /Users/you/.claude │
│ Logged in as you@example.com │
│ Projects 21 │
│ │
├─────────────────────────────────────────────────────╯
◆ What should Billy call this account?
│ personal
└
Then the usual account list takes over, now with that account in it.
There is nothing else to decide, because adopting the directory costs nothing and takes nothing away: ~/.claude stays exactly where it is, and anything that runs claude without Billy — a script, an IDE extension, a claude -p in a pipeline — keeps reaching it. Billy simply records it as an account so it appears in the list alongside the ones you add later.
Cancelling the name prompt adopts nothing, and the offer comes back on the next launch since Billy still has no accounts.
If you would rather set this up by hand, add the account to ~/.config/billy/accounts.json before the first launch, with configDir pointing wherever you want.