feat(multi-account): codev account CLI + global-default (Batch 2a + 2e)
Summary
Batch 2a + 2e of multi-account support (follows #122 / Batch 1).
- 2a —
codev accountCLI: manage the account registry + shell integration from the terminal, replacing Batch 1's hand-editing of~/.config/codev/accounts.json+accounts.sh. - 2e — configurable global-default:
codev account default <label>makes bareclauderesolve to a chosen account; CodeV resume now bypasses the shell dispatcher so it can't be misrouted. - Test infra: introduces Vitest + a generator test suite and a real CI workflow (the repo had none —
test_ci.yamlwas a placeholder).
What's included
src/cli/account-manager.ts— the single shared generator: reads/writesaccounts.json, generatesaccounts.sh(per-account launchers +claude <account>dispatcher +claude-whoami+claude-accounts). The Batch 2b Settings UI will reuse this same module (no second generator).src/cli/codev-account.ts— the CLI entry.src/cli/account-manager.test.ts— Vitest suite (13 tests).vitest.config.ts,.github/workflows/test_ci.yaml(real CI),package.json(yarn account,yarn test).src/claude-session-utility.ts— 2e:buildResumeCommandemitscommand claude.
How to use
codev account CLI (all commands)
Run in dev via yarn account <cmd> (a real codev binary on PATH lands in Batch 2b):
yarn account list # accounts + identity; '*' = bare-claude default (alias: ls)
yarn account add <label> # register account; dir defaults to ~/.claude-<label>
yarn account add <label> --dir D # …with a custom config dir (validated: no shell metachars)
yarn account default <label> # bare `claude` now resolves to <label>
yarn account remove <label> # unregister; leaves the dir on disk (alias: rm)
yarn account regenerate # rewrite ~/.config/codev/accounts.sh from the registry (alias: regen)
yarn account show # dry run: print the generated accounts.sh, writes nothing (alias: preview)
yarn account install # append the marker-guarded source block to ~/.zshrc (idempotent)
yarn account uninstall # remove the ~/.zshrc block (keeps registry + dirs)
yarn account help # usage
After add/default/remove/regenerate: open a new shell or source ~/.zshrc.
Generated shell commands (what accounts.sh gives you)
claude # → the configured default account
claude <label> […] # → that account, full passthrough (e.g. `claude work -r`)
claude-<label> […] # function form (e.g. `claude-work mcp list`)
claude-whoami # which account bare `claude` resolves to here + its auth status
claude-accounts # list configured accounts
Launchers use env so the default account runs with CLAUDE_CONFIG_DIR explicitly
unset (env -u) and extra accounts with it explicitly set — a stray exported
CLAUDE_CONFIG_DIR can't hijack either.
Gotcha: checking the account from inside a Claude Code session
Claude Code snapshots your interactive shell — functions included — and sources it
for Bash-tool / ! commands. So inside any session, bare claude is the accounts.sh
dispatcher function, not the binary:
!claude auth status→authmatches no account label → dispatcher*)→ the
global default account. It reports the default's identity no matter which account
the session itself runs under — a misleading probe.!command claude auth status✅ →commandbypasses shell functions → the real
binary inherits the session process's ownCLAUDE_CONFIG_DIR→ reports the session's
actual account.!echo ${CLAUDE_CONFIG_DIR:-unset}✅ → the session's own config dir (unset= the
anchor/default account).
Same rule for scripting: nested claude invocations from inside a session go through
the dispatcher to the global default — use command claude or an explicit
claude <label> when the account matters. (Documented in design doc §6.A.)
Cross-account sharing (Batch 3 preview — mechanism verified)
Verified while building this PR (full policy in design doc §5):
| Item | Shareable? | Mechanism | Status |
|---|---|---|---|
Global CLAUDE.md | ✅ | file symlink | verified live (work → personal) |
skills/ | ✅ | dir symlink — whole-dir (auto-shares future skills) or per-entry (selective, keeps private skills) | verified live |
commands/ (legacy custom slash commands — functionally unified with skills) | ✅ | same per-config-dir file scan; dir symlink | verified live (a linked legacy command appeared in a work-session skills list) |
statusLine | ✅ | it's a settings.json key → per-key copy, not a symlink | done for work |
plugins/ | ⚠️ | payload dir may symlink, but enabledPlugins lives in each account's settings.json (+ plugin state) | untested |
.claude.json | ❌ never | identity + org + per-project trust | by design |
Session data (history.jsonl, projects/, sessions/) | ❌ by design | live-written + identity-bound; CodeV's aggregation IS the union view; cross-account resume (2c) = copy-fork a transcript | by design |
Sharing never silently overwrites: existing target → .codev-bak backup or skip (§5 collision policy).
How to test
Automated: yarn test (13/13). CI runs it on PRs to main.
Manual smoke-test (needs yarn make — 2e is a main-process change):
- CLI, read-only/safe:
yarn account list,yarn account show. - Generator equivalence:
yarn account showoutput ≈ your currentaccounts.sh(functionally equal — verified via diff +zsh -n). - 2e resume routing (the important one):
yarn account default work(sets bareclaude→ work),source ~/.zshrc.- In CodeV, resume a personal session → must still open under personal (not work). The
command claudefix guarantees this. - Resume a work session → opens under work.
- Restore with
yarn account default personalwhen done.
Verification done
- Generated
accounts.shis functionally equivalent to the hand-written Batch 1 file (diff) and passeszsh -n. tsc --noEmit: 0 errors.yarn test: 13/13.- 2e
command claudeis behavior-unchanged when the default is the anchor, and a no-op for single-account users. - Smoke-tested with
default work: CLI add/list/show/remove round-trip; CodeV resume of a personal session stayed personal (transcript loaded from~/.claude/projectsproves the account), work sessions resume under work.
Notes
- This does not overwrite your live
accounts.sh— runyarn account regeneratewhen you want the generated version. - 2c (per-launch account picker) is coupled with Batch 3 (cross-account sharing) — see design doc §6.G.
🤖 Generated with Claude Code
Summary by cubic
Adds the codev account CLI with a single shared generator for multi-account setup, plus a configurable global default for bare claude. Shell integration is hardened, resume uses command claude, and docs now confirm symlink-based sharing of global files works for Batch 3.
-
New Features
- One shared generator reads/writes
~/.config/codev/accounts.jsonand generatesaccounts.sh(to be reused by the Settings UI). - New CLI:
list,add <label> [--dir D],default <label>,remove <label>,regenerate,show,install,uninstall(run in dev withyarn account <cmd>). Global default viacodev account default <label>; dispatcher fallback honors it. - Vitest suite and a real CI workflow (
yarn test) added.
- One shared generator reads/writes
-
Bug Fixes
- Fixed
add --dirparsing; validate labels and reject shell-unsafe or invalid paths indirandconfigDirEnvat add time and duringaccounts.shgeneration. - Default launches via
env -u CLAUDE_CONFIG_DIR claude; others viaenv CLAUDE_CONFIG_DIR=… claude(bypasses the dispatcher). - Resume uses
command claudeand escapes single quotes in paths. Dropped hardcodeddefaultAccountand only set it when valid.claude-whoaminow reports the dispatcher default and runsauth statusunder that account’s env. Docs clarify that inside Claude Code sessions, bareclauderoutes to the dispatcher default; usecommand claudeto target the session account.
- Fixed
Summary by CodeRabbit
- New Features
- Added
codev accountcommand to manage multi-account setup (list, add, set default, remove, regenerate, preview, install/uninstall shell hooks). - Introduced automatic generation of account launcher scripts for switching between accounts.
- Added
- Bug Fixes
- Improved session resume to use the correct configuration directory for non-default accounts.
- Tests / CI
- Added a Vitest suite for account script generation and default resolution, plus a real CI workflow to run tests.
- Documentation
- Updated the multi-account support design notes and added a new changelog entry.