cc-account-manager
v0.0.1
Published
Local profile manager for running multiple isolated Claude Code accounts on one machine.
Maintainers
Readme
Claude Account Manager (ccam)
A local CLI for running multiple independent Claude Code accounts on one machine, built on a single rule: one account = one CLAUDE_CONFIG_DIR.
The tool does not implement OAuth, never reads or copies tokens, and stores no credentials in SQLite. Authentication is handled entirely by the official Claude Code CLI.
Commands
cm account add [name] [--login]
cm account list [--status]
cm account remove <name> [--delete-data] [--force]
cm login <account>
cm logout <account>
cm status <account> [--raw]
cm use <account>
cm use --clear
cm default [account|--clear]
cm run [account] [-- <claude args...>]
cm usage
cm usage <account> [--json]
cm usage setup [account|--all]
cm usage teardown [account|--all] [--force]
cm usage history <account> [--limit N]
cm session list [account] [--limit N]
cm session show <session-id> [--account NAME] [--json]
cm handoff <session-id> --to <account> [--from <account>] [--yes] [-- <claude args...>]
cm handoff --last --to <account> [--from <account>] [--yes] [-- <claude args...>]
cm handoff history [--limit N]
cm dashboard
cm dashboard --once
cm tui
cm doctorFeature summary
- Local session registry derived from the data Claude Code officially emits to the status line.
- Tracks
session_id, session name, transcript path, workspace, model, context usage and added dirs. cm session list/cm session showfor inspecting observed sessions.- Session handoff between accounts that preserves conversation history via
--resume+--fork-session. - The target session UUID is generated up front and passed as
--session-id, so handoff lineage is deterministic. - Transcripts are copied byte-for-byte as opaque JSONL; the manager never parses the internal transcript format.
- Observed
workspace.added_dirsare replayed onto the target launch. - Handoff is blocked when the target account has exhausted its active 5h/7d usage window, and warns at >=80%.
- Confirmation is required when a transcript crosses an identity/org boundary.
- Path hardening: rejects source transcripts outside the profile, target path escapes and symlink escapes.
- The transcript must be stable before copying, reducing the risk of snapshotting a file mid-write.
- The dashboard binds
Hto hand off the selected account's most recent session.
Requirements
- Node.js >= 22.5
- Claude Code CLI installed, with
claudeavailable onPATH
node --version
claude --versionInstallation
npm install -g cc-account-managerVerify:
ccam --version
ccam doctorRun without installing:
npx cc-account-manager dashboardThe package is published as cc-account-manager and exposes four equivalent commands: cc-account-manager, ccam, cm and claude-manager. The examples below use cm, which matches the CLI's own help output.
Install from source
npm install
npm run build
npm test
npm linkThe release ships a prebuilt dist/, so it can also be run directly:
npm link
cm --versionThe runtime depends on nothing beyond Node.js built-in APIs.
Data layout
~/.claude-manager/
├── manager.db
├── bin/
│ └── statusline-collector.mjs
└── accounts/
├── personal/
│ ├── settings.json
│ └── projects/
│ └── <project>/
│ └── <session-id>.jsonl
└── work/
├── settings.json
└── projects/Override the root when testing:
export CLAUDE_MANAGER_HOME=/tmp/claude-manager-test1. Create and log in accounts
cm account add personal
cm account add work
cm login personal
cm login workOr in one step:
cm account add personal --loginEach profile is equivalent to:
CLAUDE_CONFIG_DIR=~/.claude-manager/accounts/personal claude
CLAUDE_CONFIG_DIR=~/.claude-manager/accounts/work claudeVerify:
cm account list --status
cm status work2. Default account and launching
cm use work
cm defaultLaunch explicitly:
cm run personal
cm run workForward arguments to Claude after --:
cm run work -- --model opus
cm run personal -- -p "Explain this repository"In an interactive terminal, cm run without an account shows a selector. In a non-interactive shell it uses the default account.
3. Usage and session collector
Collection is opt-in:
cm usage setup work
cm usage setup --allSetup performs the following:
- Copies the local collector to
~/.claude-manager/bin/statusline-collector.mjs. - Backs up the current
statusLineconfiguration into the manager database. - Installs the collector wrapper into the Claude profile.
- Proxies the previous command if a custom status line already existed.
- Writes usage telemetry and the required session metadata to SQLite.
After setup, run Claude and complete at least one API response:
cm run workThen:
cm usage
cm session list work4. Usage
cm usageExample:
NAME PLAN COLLECTOR 5H 5H RESET 7D 7D RESET FRESH OBSERVED
personal pro on 72% in 2h 14m 31% in 4d 2h live 1m ago
work max on 18.5% in 4h 02m 54.2% in 2d 11h recent 9m agoUsage reflects the last observed local telemetry; it is not an independent poll of an API quota endpoint.
Freshness levels:
live: <= 5 minutes.recent: <= 30 minutes.stale: > 30 minutes.unknown: no snapshot yet.
5. Session registry
List every session the manager has observed:
cm session list
cm session list personal
cm session list work --limit 20Inspect one:
cm session show <session-id>
cm session show <session-id> --account personal
cm session show <session-id> --account personal --jsonThe registry stores:
account
session_id
session_name
transcript_path
workspace current/project dir
added_dirs
model
context usage
first_seen_at / last_seen_atThe manager does not parse JSONL contents. transcript_path is used only to locate the file Claude Code wrote and to perform a handoff.
If the registry is empty:
cm usage setup --allthen run Claude at least once under each profile you want tracked.
6. Session handoff A → B
Use case:
personal usage 95%
work usage 15%
personal:S1
↓ handoff
work:S2Hand off the most recent session:
cm handoff --last --from personal --to workOr an explicit session:
cm handoff <session-id> --to workForward safe launch options to the target Claude:
cm handoff <session-id> --to work -- --model opusInternal flow:
A / source session S1
│
├─ validate transcript belongs to profile A
├─ ensure transcript is stable
│
▼
copy opaque JSONL byte-for-byte
│
▼
B / same relative project path / S1.jsonl
│
▼
claude --resume S1 --fork-session --session-id S2
│
▼
B / new session S2The target session ID S2 is generated as a UUID by the manager before launch, so handoff history never has to guess which session was created.
Handoff preflight
The manager checks that:
- Source and target are two different accounts.
- The target is authenticated.
- The source transcript exists, is a regular
.jsonlfile, and genuinely lives inside the source profile. - The target's active usage is below 100% in both the 5h and 7d windows.
- A target at >=80% produces a warning.
- The target path does not escape the profile via
..or a symlink. - The transcript does not change during the stability check that precedes the copy.
If the email or org differs, the manager asks for confirmation before copying the transcript. In non-interactive mode this must be explicit:
cm handoff <session-id> --to work --yesHandoff lineage
cm handoff history
cm handoff history --limit 50Example:
ID SOURCE TARGET STATUS
1 personal:<source-id> work:<new-target-id> observedStatuses:
prepared: transcript staged, not launched yet.launched: the target Claude process was started.observed: the collector has seen the target session UUID.failed: Claude did not create the target session, or the spawn failed.
What handoff preserves and what it does not
Preserved:
- The conversation transcript/history.
- User and assistant messages Claude has written to the transcript.
- Tool calls and results already recorded in the transcript.
- The working directory, if the path still exists.
- Observed
workspace.added_dirsthat still exist. - The source session itself; the target is always a new fork.
Not copied, by design:
- A's OAuth tokens or account credentials.
- Account-scoped plugin/MCP OAuth credentials.
- In-flight background processes or subagents.
- A's prompt cache.
- User-level auto memory/config specific to account A.
Handoff is not a credential hot-swap inside one process. The manager stops at the process boundary: the target Claude is a new process using B's CLAUDE_CONFIG_DIR.
If the source context window is nearly full, switching accounts does not reduce context usage; use /compact when the constraint is context rather than a rate limit.
7. TUI dashboard
cm dashboard
# alias
cm tuiThe wide layout shows account, auth, plan, collector state, 5h/7d usage, freshness, model and the most recent session.
Keys:
↑ / k previous account
↓ / j next account
Enter launch Claude with the selected account
H hand off the latest session to another account
d set as default
a / r refresh auth metadata
l log in
u enable/refresh the usage + session collector
? / h help
q / Esc quitPressing H:
- Resolves the selected account's latest session.
- Shows a target selector with auth plus 5h/7d usage.
- Ranks targets with more remaining usage higher.
- Refreshes the target's auth.
- Asks for confirmation if the identity or org differs.
- Stages the transcript and launches the fork on the target.
- Returns to the normal view once the target Claude exits.
Static snapshot:
cm dashboard --once8. Usage detail and history
cm usage work
cm usage work --json
cm usage history work --limit 50Cost is the estimated session cost reported by Claude Code. On Pro/Max plans it should not be read as the account's actual billing.
9. Tearing down the collector
cm usage teardown work
cm usage teardown --allIf the profile previously had a custom status line, the original configuration is restored.
If the user changed statusLine after the collector was installed, the manager will not overwrite that change. Metadata can be detached while keeping the current config:
cm usage teardown work --forceAlready-observed session records remain in SQLite; teardown only stops further observation.
10. Database
accounts
settings
usage_collectors
usage_snapshots
sessions
session_handoffsAccount-scoped foreign keys use ON DELETE CASCADE for telemetry and session metadata owned by the manager.
The database is opened and migrated in place using CREATE TABLE IF NOT EXISTS plus additive column migrations, so profiles created by earlier builds are upgraded without having to be deleted.
11. Privacy and security
A usage snapshot stores:
rate limits
reset timestamps
session id
model
context percentage
token counters
estimated cost/durationThe session registry intentionally stores the metadata a handoff requires:
session id/name
transcript path
workspace path
added dirs
model/contextNever stored:
OAuth access/refresh tokens
API keys
cookies
prompt text
assistant responses
raw status-line JSON
raw transcript contents in SQLiteBy default the manager strips provider credential environment variables that could override a subscription profile:
ANTHROPIC_API_KEY
ANTHROPIC_AUTH_TOKEN
ANTHROPIC_PROFILE
CLAUDE_CODE_USE_BEDROCK
CLAUDE_CODE_USE_VERTEX
CLAUDE_CODE_USE_FOUNDRYTo inherit them deliberately:
cm run work --inherit-provider-env
cm status work --inherit-provider-envDo not commit or sync:
~/.claude-manager/
~/.claude-manager/accounts/*12. Doctor
cm doctorChecks Node, the manager home, SQLite, the Claude CLI, profile directories, collector configuration and per-account auth.
Related Claude Code documentation
- Sessions / resume / fork: https://code.claude.com/docs/en/sessions
- CLI reference: https://code.claude.com/docs/en/cli-reference
- Status line schema: https://code.claude.com/docs/en/statusline
CLAUDE_CONFIG_DIR: https://code.claude.com/docs/en/env-vars
