Project context for Claude Code working in this repo.
A single bash script (claude-switch) plus installer and docs. The whole
tool lives in one file. Do not split it into multiple files — the
single-file shape is a feature (drop-in install, easy to audit).
- macOS only. Anything Linux/Windows is out of scope. The
securityCLI and macOS Keychain are non-negotiable dependencies. - Bash 3.2 compatible. The system
/bin/bashon macOS is 3.2 and we target it. Avoid: associative arrays (declare -A),mapfile/readarray,${var^^}/${var,,},&>redirect shorthand,wait -n.[[ ]],local,printf '%q'are fine. jqis optional. Allaccount_*functions must be no-ops whenjqis missing (has_jq). Never make jq a hard dependency.
kc_save uses security add-generic-password -A (trust any app, no
prompts). This is the documented "strategy B" trade-off in the README —
do not "fix" it by switching to -T <path> without discussing first.
Per-binary ACLs would prompt repeatedly because the claude binary path
varies across installs (Homebrew x86/arm, npm, manual).
- Active token: macOS Keychain service
Claude Code-credentials(read by Claude Code itself, never inside~/.claude/). - Per-profile backup tokens:
Claude Code-credentials-<name>. - Active
~/.claude.json: top-level cache Claude Code reads on launch foroauthAccount,/status,/usage, etc. It is NOT a shared file across profiles —claude-switch useswaps it. If you forget to swap it,/statusand/usagekeep showing the previous account until an API refresh fires. - Per-profile
~/.claude.jsonsnapshot:~/.claude-profiles/<name>/.claude-root.json(written byroot_stash, restored byroot_restore). - Per-profile account-info snapshot:
<name>/.account.json— a smalleroauthAccount-only copy used bylist/statusto print the email. Decorative; the root snapshot is the load-bearing one. - Per-profile dir:
~/.claude-profiles/<name>/(target of the~/.claudesymlink).
- User-facing strings are English only. Emojis allowed as status icons
(✅
⚠️ ❌ ⭐️ 🔑 ℹ️ 👤 🏢 📁) — don't add new emojis without reason. - The
legacyprofile name is reserved and cannot be deleted. - Every command function should exit 0 on success. A common pitfall: a
trailing
[[ ... ]] && echo ...returns non-zero when the test is false. Wrap inif ... then ... fito preserve exit status.
bash -n claude-switch # syntax check
bash -n install.shThen exercise the behavioral changes manually — there's no test suite.
Especially verify: claude-switch help, list, status all exit 0.
- Linux/Windows ports (would need a different secret backend).
- Token decoding (JWT parse). Email comes from
~/.claude.json, not from the token itself. - Mid-session migration. A
--forceswitch never migrates a running Claude Code session — only future launches see the new profile.