feat(multi-account): codev account CLI + global-default (Batch 2a + 2e)

Merged
GR
4mo ago
+1373-24

Summary

Batch 2a + 2e of multi-account support (follows #122 / Batch 1).

  • 2a — codev account CLI: 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 bare claude resolve 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.yaml was a placeholder).

What's included

  • src/cli/account-manager.ts — the single shared generator: reads/writes accounts.json, generates accounts.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: buildResumeCommand emits command 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 → auth matches 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 ✅ → command bypasses shell functions → the real
    binary inherits the session process's own CLAUDE_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):

ItemShareable?MechanismStatus
Global CLAUDE.md✅file symlinkverified 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 symlinkverified live (a linked legacy command appeared in a work-session skills list)
statusLine✅it's a settings.json key → per-key copy, not a symlinkdone for work
plugins/⚠️payload dir may symlink, but enabledPlugins lives in each account's settings.json (+ plugin state)untested
.claude.json❌ neveridentity + org + per-project trustby design
Session data (history.jsonl, projects/, sessions/)❌ by designlive-written + identity-bound; CodeV's aggregation IS the union view; cross-account resume (2c) = copy-fork a transcriptby 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):

  1. CLI, read-only/safe: yarn account list, yarn account show.
  2. Generator equivalence: yarn account show output ≈ your current accounts.sh (functionally equal — verified via diff + zsh -n).
  3. 2e resume routing (the important one):
    • yarn account default work (sets bare claude → work), source ~/.zshrc.
    • In CodeV, resume a personal session → must still open under personal (not work). The command claude fix guarantees this.
    • Resume a work session → opens under work.
    • Restore with yarn account default personal when done.

Verification done

  • Generated accounts.sh is functionally equivalent to the hand-written Batch 1 file (diff) and passes zsh -n.
  • tsc --noEmit: 0 errors. yarn test: 13/13.
  • 2e command claude is 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/projects proves the account), work sessions resume under work.

Notes

  • This does not overwrite your live accounts.sh — run yarn account regenerate when 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.json and generates accounts.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 with yarn account <cmd>). Global default via codev account default <label>; dispatcher fallback honors it.
    • Vitest suite and a real CI workflow (yarn test) added.
  • Bug Fixes

    • Fixed add --dir parsing; validate labels and reject shell-unsafe or invalid paths in dir and configDirEnv at add time and during accounts.sh generation.
    • Default launches via env -u CLAUDE_CONFIG_DIR claude; others via env CLAUDE_CONFIG_DIR=… claude (bypasses the dispatcher).
    • Resume uses command claude and escapes single quotes in paths. Dropped hardcoded defaultAccount and only set it when valid. claude-whoami now reports the dispatcher default and runs auth status under that account’s env. Docs clarify that inside Claude Code sessions, bare claude routes to the dispatcher default; use command claude to target the session account.

Summary by CodeRabbit

  • New Features
    • Added codev account command 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.
  • 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.