@hasna/accounts
v0.1.28
Published
Manage and switch between multiple Claude Code (and other AI coding tool) profiles/accounts locally — isolated config dirs, per-account email, one-command switching.
Downloads
3,862
Maintainers
Readme
@hasna/accounts
Manage and switch between multiple AI coding tool profiles/accounts on one machine — Claude Code, Takumi, Codex CLI, Codex App, Gemini CLI, opencode, Cursor Agent, Pi Coding Agent, Hermes, Kimi Code, Grok Build, and custom tools.
accounts is a local-first CLI. Each profile is an isolated config directory.
Switch in the terminal with CLAUDE_CONFIG_DIR, or in Cursor / VS Code with
accounts apply (syncs auth to live ~/.claude paths).
- Isolated profiles — separate config dirs (skills, settings, sessions). Nothing leaks.
- Apply mode — sync OAuth / credentials to live paths for IDEs (Claude-only today).
- Remembers the email — auto-detected from
.claude.jsonwhen possible. - Multi-tool — first-class built-ins for Claude, Takumi, Codex CLI, Codex
App, Gemini, opencode, Cursor Agent, Pi, Hermes, Kimi Code, and Grok Build;
custom tools via
accounts tools add. - Per-tool names —
workcan exist for Claude, Codex, Cursor, etc.; pass--toolwhen a bare profile name is ambiguous. - Local-first — registry at
~/.hasna/accounts/. No network, no telemetry.
Install
bun install -g @hasna/accounts
accounts --helpRequires Node ≥ 18 (or Bun ≥ 1.0).
Quick start (two Claude subscriptions)
# 1. Import your current install (optional)
accounts import main --dir ~/.claude
# 2. Create profiles
accounts add work --email [email protected]
accounts add personal --email [email protected]
# 3. Log in once per profile (isolated dir)
accounts login work # run /login inside Claude, then /exit; accounts auto-applies it
accounts login personal # same: login, exit; it becomes the live/default account
# 4. Switch
accounts apply work # Cursor / VS Code — live ~/.claude auth
accounts apply personal
# Or terminal-only (parallel sessions OK):
accounts launch work
eval "$(accounts env personal)" # other terminal
# Or supervised: lets MCP switch/restart this Claude process automatically
accounts use work
accounts run claude --resume
accounts switch personal --supervisor # from another terminalAfter accounts login <name>, accounts snapshots the auth Claude wrote,
updates the detected email, and applies that profile to live ~/.claude paths
automatically. accounts apply still refuses profiles without auth so live OAuth
is not wiped.
Codex App profiles on macOS
Codex CLI profiles use --tool codex. The macOS desktop app needs its own tool
because it also needs an isolated Electron user data directory:
# Create/sign into a desktop app profile. Quit Codex.app after login finishes.
accounts login personal --tool codex-app
accounts login work --tool codex-app
# Switch by launching the desired app profile.
accounts launch personal --tool codex-app
accounts launch work --tool codex-app
# Or print/launch the exact handoff command.
accounts switch work --tool codex-app
accounts switch work --tool codex-app --launch
# Or run a native macOS menu-bar switcher.
accounts codex-app menubarEach codex-app profile gets its own CODEX_HOME and
--user-data-dir=<profile>/electron-user-data. Before login, launch, switch, or
shell commands, accounts ensures the profile root config.toml has
cli_auth_credentials_store = "file" so ChatGPT auth stays in that profile
directory instead of sharing one macOS Keychain credential.
The menu-bar switcher lists codex-app profiles, marks the active profile, and
switches with a button click. A switch marks the selected profile active, asks a
running Codex.app to quit, waits briefly, and relaunches Codex.app with the
selected profile's isolated CODEX_HOME and Electron user data directory.
Three pointers (active, applied, isolated)
| Pointer / mode | Set by | Meaning |
|----------------|--------|---------|
| Active | accounts use, launch, pick | Registry current — which profile you intend (terminal + hook) |
| Applied | accounts apply, pick (default) | Registry applied — auth on live ~/.claude (what Cursor sees) |
| Isolated | env, launch, shell | Per-process CLAUDE_CONFIG_DIR; does not change live disk |
| Mode | Command | Best for |
|------|---------|----------|
| Isolated | accounts launch, accounts env, accounts shell | Terminal, two accounts at once |
| Apply | accounts apply <name> | Cursor, VS Code, single global auth |
| Picker | accounts pick | Interactive choose; default applies to live paths |
accounts use alone does not change Cursor — run accounts apply for IDE auth.
A child process cannot change your parent shell — use eval "$(accounts env …)" or the
shell hook (terminal claude only, not IDE extensions).
Implementation details: docs/IMPLEMENT.md.
Switching modes (summary)
accounts active— prints active profile (store.current); scripting.accounts applied— prints applied profile (store.applied); scripting.accounts current— human-readable active (+ applied hint) per tool.accounts list—●active,◉applied,●◉when both are the same profile.
Commands
| Command | Description |
|---------|-------------|
| accounts add <name> | Create a profile. --tool, --email, --display-name, --identity, --card-last4, --metadata key=value, --dir, --description. |
| accounts import [name] | Import existing config dir (default ~/.claude). --copy for managed copy. |
| accounts login <name> | Launch the profile's tool login flow in an isolated profile dir. Use --tool only for new or ambiguous profiles. |
| accounts apply <name> | Apply profile auth to live Claude paths (requires snapshot; Claude-only). |
| accounts pick | Interactive picker; default applies. --env, --no-act. |
| accounts switch <name> | Switch profile and print a restart/resume command. Add --resume, --launch, or --permissions <preset>. Use --tool only when ambiguous. |
| accounts switch <name> --supervisor | Ask a running accounts run <tool> supervisor to restart under that profile. Supports --permissions <preset>. |
| accounts use <name> | Mark profile active; prints apply/env hints. |
| accounts list (ls) | List profiles (● active, ◉ applied, ●◉ both). |
| accounts show <name> --tool <tool> | Profile details including active/applied flags. |
| accounts current | Active profile per tool (with applied hint). |
| accounts active [tool] | Print active profile name (scripting). |
| accounts applied [tool] | Print applied profile name (scripting). |
| accounts env [name] | Print one or more export ... lines for the profile. Use --tool only when ambiguous or when no name is passed. |
| accounts launch <name> | Launch tool once with profile env. Supports --permissions <preset>. |
| accounts run <tool> [args...] | Run a tool under the supervisor so MCP/CLI can switch and restart it. Supports --permissions <preset>. |
| accounts supervisor status [tool] | Show running supervisors. |
| accounts supervisor switch <name> | Switch a running supervisor to another profile. Use --tool only when ambiguous. |
| accounts supervisor stop <tool> | Stop a running supervisor and its child process. |
| accounts shell <name> | Subshell with profile env. |
| accounts hook install | Install claude() wrapper — see docs/hook.md. |
| accounts hook uninstall | Remove hook script. |
| accounts hook path | Print hook script path. |
| accounts agents | List Claude agent sessions across all profiles, the default ~/.claude dir, and untracked processes (claude agents only shows the current account). --background, --profile <name>, --json. |
| accounts detect <name> | Re-detect email from config dir. |
| accounts doctor | Check registry and dirs (exits 1 on errors). |
See accounts --help for set, rename, remove, tools, etc.
Account Metadata
Profiles can carry non-secret ownership metadata alongside their isolated config directory:
accounts add account001 \
--email [email protected] \
--display-name "Owner Name" \
--identity agent:owner-name \
--card-last4 4242 \
--metadata machine=spark02
accounts set account001 --identity identity_abc123 --metadata source=spark01
accounts show account001 --jsoncardLast4 is validated as exactly four digits. metadata accepts repeated
key=value pairs with string, finite number, boolean, or null values. Metadata
keys may use letters, digits, _, ., :, and -; object prototype keys such
as __proto__, prototype, and constructor are rejected. Do not store
secrets, tokens, full card numbers, or billing addresses in profile metadata.
Agent / MCP Switching
accounts ships a stdio MCP server:
accounts-mcpAdd it to Claude/Codex/opencode/Cursor MCP config as a command server named
accounts. It exposes:
list_toolslist_profilescurrent_profileswitch_profile
For automatic agent restarts, start the agent through accounts run:
accounts use account001
accounts run claude --resumeWhen switch_profile is called from that Claude session, accounts-mcp contacts
the supervisor. The supervisor applies/switches the profile, closes the current
Claude process, and restarts it with the selected profile. Claude uses
claude --continue; Codex uses codex resume --last; opencode uses
opencode --continue; custom tools can define resumeArgs.
If the agent was not started through accounts run, MCP falls back to the safe
handoff behavior and returns a command such as:
CLAUDE_CONFIG_DIR=... claude --continue.
Human equivalent:
accounts switch account001 --resume
accounts switch account001 --resume --launch
accounts switch account001 --resume --permissions dangerous
accounts switch account001 --supervisor
accounts switch codex-work --tool codex --resume
accounts switch ops --tool opencode --resume--permissions <preset> maps a permission mode to the tool's own flags. For
example, --permissions dangerous launches Claude/Takumi with
--dangerously-skip-permissions, Codex with
--dangerously-bypass-approvals-and-sandbox, and Gemini/Hermes/Kimi with their
YOLO mode flags. Unsupported tools fail with a list of configured presets.
Shell hook (optional)
accounts hook install
# Add to ~/.zshrc or ~/.bashrc:
source "$(accounts hook path)"The wrapper runs accounts apply when the active profile differs from applied,
then invokes the real claude binary. Full behavior and footguns: docs/hook.md.
Storage layout
~/.hasna/accounts/
accounts.json # registry: profiles, current, applied (mode 600)
claude-hook.sh # optional shell wrapper
supervisors/
claude.sock # local control socket for `accounts run claude`
claude.json # supervisor pid/profile/command metadata
profiles/
claude/<name>/ # managed config dir
claude/<name>/.accounts-auth/ # auth snapshots for apply mode
oauth-account.json
credentials.json # Linux / file-based auth
keychain.json # macOS keychain payloadOverrides: ACCOUNTS_HOME, ACCOUNTS_STORE_PATH.
The native storage API is available from @hasna/accounts/storage. It exposes
the local registry paths, snapshot helpers, and optional S3 registry sync for
internal cross-machine use:
import { getAccountsStorageStatus, storagePush } from "@hasna/accounts/storage";
console.log(getAccountsStorageStatus().local.storePath);
await storagePush();Remote sync uses service-owned S3 env names and only syncs the accounts registry JSON by default; auth snapshots stay local.
HASNA_ACCOUNTS_STORAGE_MODE=local|remote|hybridHASNA_ACCOUNTS_S3_BUCKET=hasna-xyz-opensource-accounts-prodHASNA_ACCOUNTS_S3_PREFIX=accounts/HASNA_ACCOUNTS_AWS_REGION=us-east-1
Supported tools
| Tool | id | Env var | Default dir |
|------|----|---------|-------------|
| Claude Code | claude | CLAUDE_CONFIG_DIR, TELEGRAM_STATE_DIR | ~/.claude |
| Takumi | takumi | TAKUMI_CONFIG_DIR | ~/.takumi |
| Codex CLI | codex | CODEX_HOME | ~/.codex |
| Codex App | codex-app | CODEX_HOME + --user-data-dir | ~/.codex |
| Gemini CLI | gemini | GEMINI_CONFIG_DIR | ~/.gemini |
| opencode | opencode | OPENCODE_CONFIG_DIR, XDG_CONFIG_HOME, XDG_DATA_HOME | ~/.config/opencode |
| Cursor Agent | cursor | CURSOR_CONFIG_DIR | ~/.cursor |
| Pi Coding Agent | pi | PI_CODING_AGENT_HOME | ~/.pi |
| Hermes | hermes | HERMES_HOME | ~/.hermes |
| Kimi Code | kimi | KIMI_CODE_HOME | ~/.kimi-code |
| Grok Build | grok | HOME (process-scoped) | ~/.grok |
apply is Claude-only today. Use launch / env for other tools.
For Grok Build, prefer accounts launch or accounts shell; exporting HOME
globally is intentionally not recommended.
Custom tools can join supervised resume switching with accounts tools add ... --resume-arg <arg>.
They can also define permission presets with --permission-arg preset=--flag.
Use --launch-arg for app-level arguments that should be prepended to every
login/launch/run command; templates support {profileDir}, {profileName}, and
{toolId}.
Library
import { addProfile, applyProfile, importProfile } from "@hasna/accounts";License
Apache-2.0 © Andrei Hasna
