Skip to content

feat(multi-account): new-session account picker + claude tab completion (Batch 2c-lite)#126

Open
grimmerk wants to merge 15 commits into
mainfrom
feat/new-session-account-picker
Open

feat(multi-account): new-session account picker + claude tab completion (Batch 2c-lite)#126
grimmerk wants to merge 15 commits into
mainfrom
feat/new-session-account-picker

Conversation

@grimmerk

@grimmerk grimmerk commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Batch 2c-lite (follows #125): pick the account when launching a new Claude session⌥⌘+Enter on a project opens a small account picker; plain ⌘+Enter stays exactly as before.

Design decision (per discussion): the picker is behind a modifier, never forced — quick-launch speed is preserved, and "which account does bare ⌘+Enter use" remains the global default (codev account default / Settings → Accounts), which shipped with 2e. Cross-account resume is deliberately NOT here — a session's transcript lives in its own account's projects/ dir, so "resume under another account" must be an explicit copy-fork (Batch 3), never a link.

Behavior

Gesture Result
⌘+Enter unchanged — instant launch under the global default (bare claude → dispatcher)
⇧+Enter unchanged — launch in the CodeV embedded terminal
⌥⌘+Enter account picker (↑↓ / Enter / Esc, click and hover work; default marker + email shown) → launches with explicit CLAUDE_CONFIG_DIR='…' command claude (dispatcher bypassed, mirroring resume)
⌥⌘+Enter, single account / no registry no picker — falls through to a normal launch

Implementation

  • launchNewClaudeSession(projectPath, terminalApp, terminalMode, accountLabel?) — with a label, builds the env-prefixed command claude (single-quote-escaped); without, bare claude as before. VS Code (Multi-account: VS Code (claude-vscode) sessions can't be pinned to a Claude account #121) and the embedded codev terminal ignore the label (logged).
  • IPC launch-new-claude-session + preload + types gain the optional label.
  • Renderer: launchClaudeRef gains 'external-pick'; the picker overlay reuses the data-settings-panel escape hatch so the global focus-stealing click handler leaves it alone; Esc closes only the picker (stopPropagation).
  • Hover tooltip on the "⌘+Enter: New Claude" hint advertises ⌥⌘+Enter.

Also in this PR (grew during review/testing)

  • Fix (critical, review-caught): runCommandInTerminal now receives claudeCmd as its 2nd arg — Ghostty/cmux build their launch scripts from it, so the picked account's env prefix reaches those terminals too (iTerm2/Terminal.app were unaffected; resume already did this).
  • Live hint: the search-bar hint shows · ⌥⌘+Enter: pick account only for multi-account setups, and refreshes after account add/remove (same-window codev-accounts-changed event + window-focus re-check) — no app restart (user-reported).
  • Picker error handling: if getAccounts() fails, the picker logs and aborts instead of silently launching the global default.
  • Tab completion for claude <name>: claude <TAB> completes account labels; registered only when nothing else completes claude (_comps guard — a future official completion wins automatically); other argument positions keep zsh's default file completion (_files fallback).
  • UI polish: removed react-select's default IndicatorSeparator (the stray | before the hint; user-reported).
  • Docs: README gains a full Multi-Account section (setup, shell-command table, codev account CLI table, UI map, session-probe gotcha); design doc marks 2e as shipped and drops 2d (folder→account auto-switch) — one folder legitimately hosts sessions from multiple accounts, so a folder default would guess wrong exactly where it matters; 2e global default + this picker cover the need.

How to test (needs yarn make)

  1. Projects tab → select a project → ⌥⌘+Enter → picker shows personal/work (+default marker, emails).
  2. Pick work → terminal opens a new claude in that project → !command claude auth status shows the work identity (!claude auth status would show the global default — see feat(multi-account): codev account CLI + global-default (Batch 2a + 2e) #123's gotcha).
  3. Pick personal → same check, personal identity.
  4. Esc closes the picker; clicking the dimmed backdrop closes it; ⌘+Enter still launches instantly.
  5. Settings → Accounts: add/remove an account → the search-bar hint switches between the short/long form without restarting the app; the stray | before the hint is gone.
  6. Shell: after codev account regenerate + a new shell, claude w<TAB> completes work; claude -p ./<TAB> still completes files.
  7. If you use Ghostty or cmux as the session terminal: pick an account via ⌥⌘+Enter and verify with !command claude auth status (this path carried the critical fix).

tsc --noEmit 0 errors; yarn test 18/18.

🤖 Generated with Claude Code


Summary by cubic

Adds an optional account picker for new Claude sessions, zsh completion for claude <TAB>, and a codev account rename command. Improves the multi-account UI hints, terminal launch correctness, and docs.

  • New Features

    • ⌥⌘+Enter shows an account picker; single-account skips; launch uses CLAUDE_CONFIG_DIR + command claude (dispatcher bypassed); VS Code and the codev terminal ignore the override.
    • Search-bar hint shows the picker shortcut only for multi-account and updates live on account changes and window focus.
    • zsh: claude <TAB> completes account labels; registers only if no existing claude completion; other args fall back to _files.
    • codev account rename <old> <new> renames a label without moving the folder and updates the default if needed; completion suggests labels.
    • UI/docs: show each account’s config dir (tooltip), clarify remove is non-destructive, footer examples use real labels and hide when none exist; README adds Multi-Account + “Common scenarios”; design doc drops 2d; not-yet-supported onboarding items link to Multi-account onboarding: detect existing config dirs, name-your-default UI, wrapper-collision warning #127.
  • Bug Fixes

    • Ghostty/cmux: pass the computed claudeCmd to ensure the account env prefix is preserved.
    • Picker aborts on getAccounts failure instead of launching the default.
    • Hide the stray IndicatorSeparator in the search bar.

Written for commit ff41698. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added a new account-picking flow when starting a new Claude session with ⌥⌘+Enter.
    • The picker supports both keyboard and mouse selection, and shows account status details.
    • New sessions can now launch using the selected account, while ⌘+Enter still uses the default account without prompting.
  • Bug Fixes

    • Improved multi-account launch behavior for new sessions, with clearer handling of account-specific session starts.

Alt+Cmd+Enter on a project opens a small account picker (arrow keys +
Enter, Esc cancels, click works too); the chosen account launches with
an explicit CLAUDE_CONFIG_DIR + `command claude`, bypassing the shell
dispatcher — mirroring buildResumeCommand.

- Plain Cmd+Enter is unchanged: instant launch under the global
  default (bare `claude` -> dispatcher). Single-account or
  registry-less setups never see the picker (falls through to a
  normal launch).
- launchNewClaudeSession gains an optional accountLabel; the codev
  embedded terminal and VS Code paths ignore it (logged) — the picker
  targets external terminals.
- The overlay reuses the data-settings-panel escape hatch so the
  focus-stealing click handler leaves it alone.
- Cross-account RESUME stays deferred to Batch 3 (copy-fork design).

tsc 0 errors; 17/17 tests. Bump 1.0.80 + CHANGELOG + design doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an optional accountLabel parameter to the new-session launch flow. The IPC contract, preload bridge, main process handler, and session-utility command construction propagate accountLabel to prefix CLAUDE_CONFIG_DIR when launching claude. The switcher UI adds an Option+Cmd+Enter account picker overlay. Docs and version are updated.

Changes

Multi-account new-session launch

Layer / File(s) Summary
IPC/preload contract for accountLabel
src/electron-api.d.ts, src/preload.ts, src/main.ts
Extends launchNewClaudeSession signature, preload IPC send, and main handler to accept and forward an optional accountLabel.
Session command construction with account override
src/claude-session-utility.ts
launchNewClaudeSession ignores accountLabel for the codev terminal, otherwise resolves the account via getAccountByLabel and prefixes CLAUDE_CONFIG_DIR with command claude when configDirEnv is set.
Account picker UI in switcher
src/switcher-ui.tsx
Adds external-pick launch mode for ⌥⌘+Enter, account-fetching state/functions, updated select tooltip, and a modal overlay for keyboard/mouse account selection.
Documentation and version bump
CHANGELOG.md, docs/multi-account-support-design.md, package.json
Adds a 1.0.80 changelog entry, updates the Batch 2c-lite design description, and bumps package version to 1.0.80.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SwitcherUI
  participant Preload
  participant MainProcess
  participant SessionUtility

  SwitcherUI->>SwitcherUI: user presses ⌥⌘+Enter, opens account picker
  SwitcherUI->>SwitcherUI: pickLaunchAccount(accountLabel)
  SwitcherUI->>Preload: launchNewClaudeSession(projectPath, accountLabel)
  Preload->>MainProcess: ipcRenderer.send(launch-new-claude-session, projectPath, accountLabel)
  MainProcess->>SessionUtility: launchNewClaudeSession(projectPath, terminalApp, terminalMode, accountLabel)
  SessionUtility->>SessionUtility: getAccountByLabel(accountLabel)
  alt configDirEnv present
    SessionUtility->>SessionUtility: build CLAUDE_CONFIG_DIR + command claude
  else no accountLabel or no configDirEnv
    SessionUtility->>SessionUtility: use default claude command
  end
  SessionUtility->>SessionUtility: runCommandInTerminal(claudeCmd)
Loading

Possibly related PRs

  • grimmerk/codev#102: Extends the same launchNewClaudeSession IPC/utility wiring by adding the accountLabel parameter and CLAUDE_CONFIG_DIR/command claude command construction.
  • grimmerk/codev#122: The ⌥⌘+Enter account-pick flow builds on this PR's multi-account CLAUDE_CONFIG_DIR injection logic for launching the correct account.
  • grimmerk/codev#109: Also modifies launchNewClaudeSession in src/claude-session-utility.ts, potentially intersecting with the new accountLabel-driven override.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: a new-session account picker for multi-account launches.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/new-session-account-picker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/claude-session-utility.ts (1)

11-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Named imports are not alphabetically ordered.

Per coding guidelines, imports should be organized alphabetically. The current order is getScannableAccounts, getProjectsDir, getAccountByLabel; it should be getAccountByLabel, getProjectsDir, getScannableAccounts.

♻️ Proposed fix
 import {
+  getAccountByLabel,
   getProjectsDir,
-  getScannableAccounts,
-  getAccountByLabel,
+  getScannableAccounts,
 } from './accounts';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/claude-session-utility.ts` around lines 11 - 15, The named imports in the
claude-session-utility module are not alphabetically ordered. Update the import
list from accounts so the symbols getAccountByLabel, getProjectsDir, and
getScannableAccounts are sorted alphabetically within the same import statement.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/claude-session-utility.ts`:
- Line 1442: The account override is being lost because `runCommandInTerminal`
is called with the literal `'claude'` instead of the constructed `claudeCmd`, so
terminals that use the second argument directly (notably the ghostty and cmux
paths) drop the `CLAUDE_CONFIG_DIR` prefix. Update the `runCommandInTerminal`
call in `claude-session-utility.ts` to pass the `claudeCmd` variable as the
command argument, matching the behavior expected by the ghostty/cmux branches
and preserving the active account override.

---

Nitpick comments:
In `@src/claude-session-utility.ts`:
- Around line 11-15: The named imports in the claude-session-utility module are
not alphabetically ordered. Update the import list from accounts so the symbols
getAccountByLabel, getProjectsDir, and getScannableAccounts are sorted
alphabetically within the same import statement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2a98555f-4cbb-4523-880b-1d32e0a904b5

📥 Commits

Reviewing files that changed from the base of the PR and between 4945f01 and 67eb8e4.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/multi-account-support-design.md
  • package.json
  • src/claude-session-utility.ts
  • src/electron-api.d.ts
  • src/main.ts
  • src/preload.ts
  • src/switcher-ui.tsx

Comment thread src/claude-session-utility.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 8 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/claude-session-utility.ts Outdated
Comment thread docs/multi-account-support-design.md
Comment thread src/switcher-ui.tsx Outdated
grimmerk and others added 2 commits July 10, 2026 02:32
Search-bar hint becomes '⌘+Enter: New Claude · ⌥⌘+Enter: pick account'
when more than one account is registered; single-account users keep the
unchanged short hint (no clutter — user's concern).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
One folder routinely hosts sessions from multiple accounts (the real
testing pattern), so a folder->account default would guess wrong where
the choice matters most; 2e global default + 2c-lite picker cover the
need. Feasible (a default, not a constraint) but dropped on value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/switcher-ui.tsx Outdated
grimmerk and others added 4 commits July 10, 2026 02:44
- CRITICAL: pass claudeCmd (not literal 'claude') as the 2nd arg to
  runCommandInTerminal — Ghostty/cmux build their launch scripts from
  it, so the picker's account env prefix was silently dropped there
  (iTerm2/Terminal.app were unaffected; resume already passed it)
- picker: on getAccounts failure, don't guess an identity — log and
  abort instead of silently launching the global default
- hint updates live: re-check the account count on a same-window
  codev-accounts-changed event (fired by the Settings popup after
  mutations) and on window focus (covers CLI-side changes) — no app
  restart needed (user-reported)
- hide react-select's default IndicatorSeparator (the stray '|' that
  looks like a cursor before the hint; user-reported)
- docs: mark 2e as shipped in §7 (review nit)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
claude-<label> function names already complete natively (command
position), but the dispatcher's first arg didn't — register a labels
completion for claude, guarded by _comps so an official Claude Code
completion (if one ever ships) wins automatically. Independent of
appPath. (user request) +1 test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
Registering our claude completion claimed the whole slot, which would
have killed zsh's default file completion at every other position
(e.g. claude -p ./path<TAB>) — fall back to _files there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
README gains the Multi-Account section: setup, shell commands table,
codev account CLI table, UI map, session-probe gotcha. Changelog covers
the full PR scope (picker, live hint, completions, separator, 2d drop).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
@grimmerk grimmerk changed the title feat(multi-account): new-session account picker via modifier (Batch 2c-lite) feat(multi-account): new-session account picker + claude tab completion (Batch 2c-lite) Jul 9, 2026
grimmerk and others added 7 commits July 10, 2026 02:58
Removing an account only unregisters it: folder, login, and session
history stay on disk, and re-adding the same name reattaches them
(verified during #124 testing). Noted in the README CLI table, the
Remove button tooltip, and the post-remove notice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
add <name> just registers name -> ~/.claude-<name> (the account's
CLAUDE_CONFIG_DIR); Claude Code fills the folder on first login. Noted
in the README callout; the UI shows the dir in the add notice and as a
hover tooltip on each account row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
'each account also has a function form, e.g. claude-work' read as if
only claude-work existed — now says claude work / claude-work are two
equivalent forms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
'work' in the footer read like a fixed keyword — derive the example
from the user's actual accounts (prefer a non-default one) and phrase
it as 'launch any account above by its name'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
The empty-registry fallback showed a hardcoded 'work' example whose
commands don't exist; now the example clause only renders when at
least one account is registered (default-only shows its real name).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
The anchor's auto-seeded 'personal' label was otherwise permanent (the
anchor can't be removed, so remove+add doesn't apply). rename changes
only the label — the folder is never moved (dir is stored in the
registry) — updates defaultAccount if it pointed at the old name, and
regenerates. Completion offers all labels for rename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp
Covers: existing-user first open (empty tab, auto-seeded personal),
renaming/naming the default up front (add --dir ~/.claude), add-then-
login bootstrap, attaching a hand-maintained account from any folder
(--dir; convention is only a default), and rename semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kCK4t5JSVynVZxGcrTGGp

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 9 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/multi-account-support-design.md">

<violation number="1" location="docs/multi-account-support-design.md:479">
P2: The 2e description overstates that all 'CodeV resume/launch' operations bypass the dispatcher. In fact, bare ⌘+Enter (no explicit account label) continues to use 'claude', routed through the accounts.sh dispatcher to respect the configurable global default. Only explicit account picks/resumes use 'command claude' to bypass the dispatcher. The wording should be narrowed to 'explicit account resume/launch' to avoid implying the default path also bypasses the dispatcher.</violation>
</file>

<file name="src/claude-session-utility.ts">

<violation number="1" location="src/claude-session-utility.ts:1445">
P1: When `launchNewClaudeSession` is called with an explicit `accountLabel`, a stale or missing label silently falls back to the default account. Because `getAccountByLabel` always returns an account (falling back to default), and `configDirEnv` is `null` for the default account, the picker can end up launching the wrong account with no error or log. Consider validating that the returned account actually matches the requested label and aborting/logging when it does not.

Additionally, the default-account branch uses bare `command claude` without `env -u CLAUDE_CONFIG_DIR`, so an inherited `CLAUDE_CONFIG_DIR` from the parent environment could still route the session to the wrong account. This contradicts the multi-account semantics documented in the design notes.</violation>
</file>

<file name="CHANGELOG.md">

<violation number="1" location="CHANGELOG.md:6">
P2: The changelog overstates the launch behavior for default-account selections. When the user picks the default/anchor account from the picker, the implementation intentionally omits `CLAUDE_CONFIG_DIR` (falling back to bare `command claude` because the default account's identity lives at `~/.claude.json`, not under a `CLAUDE_CONFIG_DIR`). The release note should clarify that only non-default accounts launch with explicit `CLAUDE_CONFIG_DIR`, or reword to avoid implying the default account behaves the same way.</violation>
</file>

<file name="src/popup.tsx">

<violation number="1" location="src/popup.tsx:892">
P2: The footer suggests users can launch accounts by name via shell commands, but it doesn't verify that shell integration is installed. The `accountsShellInstalled` state is already tracked in this component; gating the command example on it (or showing an alternative "install shell integration to use named launches" note) would prevent users from trying commands that don't yet exist.</violation>
</file>

<file name="src/cli/account-manager.ts">

<violation number="1" location="src/cli/account-manager.ts:360">
P2: The zsh completion for `claude` at the first argument position only calls `compadd` for account labels. When the user types a path prefix like `claude ./<TAB>`, no account label matches `./`, so `compadd` returns non-zero and no completions appear because `_files` is never reached. This removes file/path completion for the first argument even though the dispatcher legitimately passes non-label arguments through to Claude.

Use `compadd ${allLabelsForClaude} || _files` at `CURRENT == 2` so that file completion falls back automatically when the current word doesn't match any account label.</violation>

<violation number="2" location="src/cli/account-manager.ts:522">
P2: `renameAccount` changes the account label but leaves `dir` untouched. Because `addAccount` derives the default directory from the label without checking for collisions, renaming `work` → `office` and later adding `work` back silently creates two accounts sharing `~/.claude-work`. This violates the documented model of one config dir per account and can cause confusing shared identity/session behavior.

Consider adding a directory-collision check in `addAccount` (or guarding `renameAccount`) to prevent two labels from mapping to the same `dir`/`configDirEnv`.</violation>
</file>

<file name="src/cli/codev-account.ts">

<violation number="1" location="src/cli/codev-account.ts:108">
P3: The `rename` command reuses `reloadHint()`, which suggests `source ~/.zshrc` as an alternative to opening a new shell. After a rename, however, sourcing the regenerated `accounts.sh` defines the new `claude-<newLabel>` function but leaves the old `claude-<oldLabel>` function callable in the current shell session — shells do not automatically unset functions that disappear from a sourced file. This can mislead users into thinking the rename took full effect when the old label still works. Consider either warning users that a new shell is required for rename to fully take effect, or unsetting the old function during generation.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:64">
P2: The README should note that `claude <TAB>` account-name completion is skipped when another completion is already registered for `claude`, so users with an existing completion do not expect account labels that will not appear.</violation>

<violation number="2" location="README.md:81">
P2: The docs claim that `codev account add <name>` later reattaches everything after removal, but that only holds for accounts that used the default directory. If an account was originally registered with a custom `--dir`, removing it drops that directory mapping from the registry, and a plain `add <name>` (or UI add) will default to `~/.claude-<name>` instead of the original folder. To avoid silently orphaning the old data, the documentation should mention that reattaching a custom-directory account requires supplying `--dir <original-folder>` again.</violation>

<violation number="3" location="README.md:102">
P2: Account picking for new sessions (⌥⌘+Enter) and account-aware resume are not fully supported across all terminal types. The VS Code and CodeV embedded terminal launch paths currently ignore the account label: `launchNewClaudeSession` routes VS Code launches through a URI handler with no account injection, and explicitly skips account overrides for the embedded terminal. For resume, `openSession` does not thread session account info into the VS Code or embedded terminal paths either. Consider adding a caveat in the Sessions/Projects UI table rows noting that full account override is available only for external terminals (iTerm2, Terminal.app, Ghostty, cmux).</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

// Pass claudeCmd as the 2nd arg too — Ghostty/cmux build their launch
// scripts from it (iTerm2/Terminal.app use fullCommand), so the account
// env prefix must be present in both.
runCommandInTerminal(`cd "${projectPath}" && ${claudeCmd}`, claudeCmd, projectPath, terminalApp, terminalMode);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When launchNewClaudeSession is called with an explicit accountLabel, a stale or missing label silently falls back to the default account. Because getAccountByLabel always returns an account (falling back to default), and configDirEnv is null for the default account, the picker can end up launching the wrong account with no error or log. Consider validating that the returned account actually matches the requested label and aborting/logging when it does not.

Additionally, the default-account branch uses bare command claude without env -u CLAUDE_CONFIG_DIR, so an inherited CLAUDE_CONFIG_DIR from the parent environment could still route the session to the wrong account. This contradicts the multi-account semantics documented in the design notes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/claude-session-utility.ts, line 1445:

<comment>When `launchNewClaudeSession` is called with an explicit `accountLabel`, a stale or missing label silently falls back to the default account. Because `getAccountByLabel` always returns an account (falling back to default), and `configDirEnv` is `null` for the default account, the picker can end up launching the wrong account with no error or log. Consider validating that the returned account actually matches the requested label and aborting/logging when it does not.

Additionally, the default-account branch uses bare `command claude` without `env -u CLAUDE_CONFIG_DIR`, so an inherited `CLAUDE_CONFIG_DIR` from the parent environment could still route the session to the wrong account. This contradicts the multi-account semantics documented in the design notes.</comment>

<file context>
@@ -1439,7 +1439,10 @@ export const launchNewClaudeSession = (
+  // Pass claudeCmd as the 2nd arg too — Ghostty/cmux build their launch
+  // scripts from it (iTerm2/Terminal.app use fullCommand), so the account
+  // env prefix must be present in both.
+  runCommandInTerminal(`cd "${projectPath}" && ${claudeCmd}`, claudeCmd, projectPath, terminalApp, terminalMode);
 };
 
</file context>

need is covered by 2e (global default) + 2c-lite (explicit per-launch picker).
Technically it was feasible (a default, not a constraint — resume always follows
the session's own account); dropped on value, not feasibility.
- *2e — ✅ shipped with 2a (PR #123, v1.0.77):* configurable global-default —

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The 2e description overstates that all 'CodeV resume/launch' operations bypass the dispatcher. In fact, bare ⌘+Enter (no explicit account label) continues to use 'claude', routed through the accounts.sh dispatcher to respect the configurable global default. Only explicit account picks/resumes use 'command claude' to bypass the dispatcher. The wording should be narrowed to 'explicit account resume/launch' to avoid implying the default path also bypasses the dispatcher.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/multi-account-support-design.md, line 479:

<comment>The 2e description overstates that all 'CodeV resume/launch' operations bypass the dispatcher. In fact, bare ⌘+Enter (no explicit account label) continues to use 'claude', routed through the accounts.sh dispatcher to respect the configurable global default. Only explicit account picks/resumes use 'command claude' to bypass the dispatcher. The wording should be narrowed to 'explicit account resume/launch' to avoid implying the default path also bypasses the dispatcher.</comment>

<file context>
@@ -468,9 +468,18 @@ and **(d)** for CodeV, backed by the same `projectMap` in the registry so both a
+    need is covered by 2e (global default) + 2c-lite (explicit per-launch picker).
+    Technically it was feasible (a default, not a constraint — resume always follows
+    the session's own account); dropped on value, not feasibility.
+  - *2e — ✅ shipped with 2a (PR #123, v1.0.77):* configurable global-default —
+    `codev account default <label>` / Settings → Set default routes bare `claude`
+    through the dispatcher `*)` to the chosen account; CodeV resume/launch bypass the
</file context>

Comment thread CHANGELOG.md
## 1.0.80

- Feat: multi-account Batch 2c-lite — pick the account when launching a new session
- `⌥⌘+Enter` on a project opens a small account picker (↑↓ / Enter / Esc, or click); the chosen account launches via explicit `CLAUDE_CONFIG_DIR` + `command claude` (correct on iTerm2/Terminal.app/Ghostty/cmux)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The changelog overstates the launch behavior for default-account selections. When the user picks the default/anchor account from the picker, the implementation intentionally omits CLAUDE_CONFIG_DIR (falling back to bare command claude because the default account's identity lives at ~/.claude.json, not under a CLAUDE_CONFIG_DIR). The release note should clarify that only non-default accounts launch with explicit CLAUDE_CONFIG_DIR, or reword to avoid implying the default account behaves the same way.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CHANGELOG.md, line 6:

<comment>The changelog overstates the launch behavior for default-account selections. When the user picks the default/anchor account from the picker, the implementation intentionally omits `CLAUDE_CONFIG_DIR` (falling back to bare `command claude` because the default account's identity lives at `~/.claude.json`, not under a `CLAUDE_CONFIG_DIR`). The release note should clarify that only non-default accounts launch with explicit `CLAUDE_CONFIG_DIR`, or reword to avoid implying the default account behaves the same way.</comment>

<file context>
@@ -3,9 +3,13 @@
 
 - Feat: multi-account Batch 2c-lite — pick the account when launching a new session
-  - `⌥⌘+Enter` on a project opens a small account picker (↑↓ / Enter / Esc, or click); the chosen account launches via explicit `CLAUDE_CONFIG_DIR` + `command claude`
+  - `⌥⌘+Enter` on a project opens a small account picker (↑↓ / Enter / Esc, or click); the chosen account launches via explicit `CLAUDE_CONFIG_DIR` + `command claude` (correct on iTerm2/Terminal.app/Ghostty/cmux)
   - Plain `⌘+Enter` unchanged: opens instantly under the global default; single-account (or registry-less) setups never see the picker
-  - Cross-account *resume* stays deferred to Batch 3 (copy-fork design; a session's transcript lives in its own account)
</file context>
Suggested change
- `⌥⌘+Enter` on a project opens a small account picker (↑↓ / Enter / Esc, or click); the chosen account launches via explicit `CLAUDE_CONFIG_DIR` + `command claude` (correct on iTerm2/Terminal.app/Ghostty/cmux)
- `⌥⌘+Enter` on a project opens a small account picker (↑↓ / Enter / Esc, or click); the chosen non-default account launches via explicit `CLAUDE_CONFIG_DIR` + `command claude` (correct on iTerm2/Terminal.app/Ghostty/cmux); the default account launches via bare `command claude`

Comment thread src/popup.tsx
Registry: ~/.config/codev/accounts.json — each account also has a
function form, e.g. claude-work
Registry: ~/.config/codev/accounts.json
{accounts.length > 0 &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The footer suggests users can launch accounts by name via shell commands, but it doesn't verify that shell integration is installed. The accountsShellInstalled state is already tracked in this component; gating the command example on it (or showing an alternative "install shell integration to use named launches" note) would prevent users from trying commands that don't yet exist.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/popup.tsx, line 892:

<comment>The footer suggests users can launch accounts by name via shell commands, but it doesn't verify that shell integration is installed. The `accountsShellInstalled` state is already tracked in this component; gating the command example on it (or showing an alternative "install shell integration to use named launches" note) would prevent users from trying commands that don't yet exist.</comment>

<file context>
@@ -877,8 +888,9 @@ const PopupDefaultExample = ({
-              Registry: ~/.config/codev/accounts.json — each account also has a
-              function form, e.g. claude-work
+              Registry: ~/.config/codev/accounts.json
+              {accounts.length > 0 &&
+                ` — launch any account above by its name: claude ${exampleAccountLabel} and claude-${exampleAccountLabel} are equivalent`}
             </div>
</file context>

if (reg.accounts.some((a) => a.label === newLabel)) {
throw new Error(`Account "${newLabel}" already exists`);
}
acct.label = newLabel;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: renameAccount changes the account label but leaves dir untouched. Because addAccount derives the default directory from the label without checking for collisions, renaming workoffice and later adding work back silently creates two accounts sharing ~/.claude-work. This violates the documented model of one config dir per account and can cause confusing shared identity/session behavior.

Consider adding a directory-collision check in addAccount (or guarding renameAccount) to prevent two labels from mapping to the same dir/configDirEnv.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/account-manager.ts, line 522:

<comment>`renameAccount` changes the account label but leaves `dir` untouched. Because `addAccount` derives the default directory from the label without checking for collisions, renaming `work` → `office` and later adding `work` back silently creates two accounts sharing `~/.claude-work`. This violates the documented model of one config dir per account and can cause confusing shared identity/session behavior.

Consider adding a directory-collision check in `addAccount` (or guarding `renameAccount`) to prevent two labels from mapping to the same `dir`/`configDirEnv`.</comment>

<file context>
@@ -477,6 +500,31 @@ export function removeAccount(label: string): string | undefined {
+  if (reg.accounts.some((a) => a.label === newLabel)) {
+    throw new Error(`Account "${newLabel}" already exists`);
+  }
+  acct.label = newLabel;
+  if (reg.defaultAccount === oldLabel) reg.defaultAccount = newLabel;
+  writeRegistry(reg);
</file context>

L.push('# claude flags/subcommands still work — just not suggested).');
L.push('_claude_codev_accounts() {');
L.push(' if (( CURRENT == 2 )); then');
L.push(` compadd ${allLabelsForClaude}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The zsh completion for claude at the first argument position only calls compadd for account labels. When the user types a path prefix like claude ./<TAB>, no account label matches ./, so compadd returns non-zero and no completions appear because _files is never reached. This removes file/path completion for the first argument even though the dispatcher legitimately passes non-label arguments through to Claude.

Use compadd ${allLabelsForClaude} || _files at CURRENT == 2 so that file completion falls back automatically when the current word doesn't match any account label.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/account-manager.ts, line 360:

<comment>The zsh completion for `claude` at the first argument position only calls `compadd` for account labels. When the user types a path prefix like `claude ./<TAB>`, no account label matches `./`, so `compadd` returns non-zero and no completions appear because `_files` is never reached. This removes file/path completion for the first argument even though the dispatcher legitimately passes non-label arguments through to Claude.

Use `compadd ${allLabelsForClaude} || _files` at `CURRENT == 2` so that file completion falls back automatically when the current word doesn't match any account label.</comment>

<file context>
@@ -346,6 +346,29 @@ export function generateAccountsSh(reg: Registry): string {
+    L.push('# claude flags/subcommands still work — just not suggested).');
+    L.push('_claude_codev_accounts() {');
+    L.push('  if (( CURRENT == 2 )); then');
+    L.push(`    compadd ${allLabelsForClaude}`);
+    L.push('  else');
+    L.push('    # keep the default file completion for every other position');
</file context>
Suggested change
L.push(` compadd ${allLabelsForClaude}`);
L.push(` compadd ${allLabelsForClaude} || _files`);

Comment thread README.md

> **Add is just a mapping.** `add <name>` registers *name → config folder* (default `~/.claude-<name>` — that folder **is** the account's `CLAUDE_CONFIG_DIR`). Claude Code itself creates and fills the folder on first login (`claude <name>`).
>
> **Remove is non-destructive.** It only unregisters that mapping: the account's folder, login, and session history all stay on disk. `codev account add <name>` (or the UI) later reattaches everything — identity and sessions reappear immediately, no re-login needed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The docs claim that codev account add <name> later reattaches everything after removal, but that only holds for accounts that used the default directory. If an account was originally registered with a custom --dir, removing it drops that directory mapping from the registry, and a plain add <name> (or UI add) will default to ~/.claude-<name> instead of the original folder. To avoid silently orphaning the old data, the documentation should mention that reattaching a custom-directory account requires supplying --dir <original-folder> again.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 81:

<comment>The docs claim that `codev account add <name>` later reattaches everything after removal, but that only holds for accounts that used the default directory. If an account was originally registered with a custom `--dir`, removing it drops that directory mapping from the registry, and a plain `add <name>` (or UI add) will default to `~/.claude-<name>` instead of the original folder. To avoid silently orphaning the old data, the documentation should mention that reattaching a custom-directory account requires supplying `--dir <original-folder>` again.</comment>

<file context>
@@ -41,6 +41,69 @@ For the full same-cwd accuracy matrix (detection + switch by launch method and t
+
+> **Add is just a mapping.** `add <name>` registers *name → config folder* (default `~/.claude-<name>` — that folder **is** the account's `CLAUDE_CONFIG_DIR`). Claude Code itself creates and fills the folder on first login (`claude <name>`).
+>
+> **Remove is non-destructive.** It only unregisters that mapping: the account's folder, login, and session history all stay on disk. `codev account add <name>` (or the UI) later reattaches everything — identity and sessions reappear immediately, no re-login needed.
+
+**Common scenarios:**
</file context>

Comment thread README.md
| Where | What |
|-------|------|
| Settings → Accounts | List/add/remove accounts, set the global default, install shell integration |
| Sessions tab | Sessions from all accounts with account badges; resume uses each session's own account |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Account picking for new sessions (⌥⌘+Enter) and account-aware resume are not fully supported across all terminal types. The VS Code and CodeV embedded terminal launch paths currently ignore the account label: launchNewClaudeSession routes VS Code launches through a URI handler with no account injection, and explicitly skips account overrides for the embedded terminal. For resume, openSession does not thread session account info into the VS Code or embedded terminal paths either. Consider adding a caveat in the Sessions/Projects UI table rows noting that full account override is available only for external terminals (iTerm2, Terminal.app, Ghostty, cmux).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 102:

<comment>Account picking for new sessions (⌥⌘+Enter) and account-aware resume are not fully supported across all terminal types. The VS Code and CodeV embedded terminal launch paths currently ignore the account label: `launchNewClaudeSession` routes VS Code launches through a URI handler with no account injection, and explicitly skips account overrides for the embedded terminal. For resume, `openSession` does not thread session account info into the VS Code or embedded terminal paths either. Consider adding a caveat in the Sessions/Projects UI table rows noting that full account override is available only for external terminals (iTerm2, Terminal.app, Ghostty, cmux).</comment>

<file context>
@@ -41,6 +41,69 @@ For the full same-cwd accuracy matrix (detection + switch by launch method and t
+| Where | What |
+|-------|------|
+| Settings → Accounts | List/add/remove accounts, set the global default, install shell integration |
+| Sessions tab | Sessions from all accounts with account badges; resume uses each session's own account |
+| Projects tab: `⌥⌘+Enter` | Pick the account for a new session (`⌘+Enter` stays instant, under the global default) |
+
</file context>

Comment thread README.md
| `claude-whoami` | Which account bare `claude` resolves to here, plus its auth status |
| `claude-accounts` | Quick list of configured accounts |

Tab completion included (zsh): `claude <TAB>` completes account names; `codev account <TAB>` completes subcommands and labels.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The README should note that claude <TAB> account-name completion is skipped when another completion is already registered for claude, so users with an existing completion do not expect account labels that will not appear.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 64:

<comment>The README should note that `claude <TAB>` account-name completion is skipped when another completion is already registered for `claude`, so users with an existing completion do not expect account labels that will not appear.</comment>

<file context>
@@ -41,6 +41,69 @@ For the full same-cwd accuracy matrix (detection + switch by launch method and t
+| `claude-whoami` | Which account bare `claude` resolves to here, plus its auth status |
+| `claude-accounts` | Quick list of configured accounts |
+
+Tab completion included (zsh): `claude <TAB>` completes account names; `codev account <TAB>` completes subcommands and labels.
+
+**`codev account` CLI** (the account manager — same generator the Settings UI uses):
</file context>
Suggested change
Tab completion included (zsh): `claude <TAB>` completes account names; `codev account <TAB>` completes subcommands and labels.
Tab completion included (zsh): `claude <TAB>` completes account names when no other `claude` completion is registered; `codev account <TAB>` completes subcommands and labels.

Comment thread src/cli/codev-account.ts

case 'rename': {
const [oldLabel, newLabel] = rest;
manager.renameAccount(oldLabel, newLabel);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The rename command reuses reloadHint(), which suggests source ~/.zshrc as an alternative to opening a new shell. After a rename, however, sourcing the regenerated accounts.sh defines the new claude-<newLabel> function but leaves the old claude-<oldLabel> function callable in the current shell session — shells do not automatically unset functions that disappear from a sourced file. This can mislead users into thinking the rename took full effect when the old label still works. Consider either warning users that a new shell is required for rename to fully take effect, or unsetting the old function during generation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/cli/codev-account.ts, line 108:

<comment>The `rename` command reuses `reloadHint()`, which suggests `source ~/.zshrc` as an alternative to opening a new shell. After a rename, however, sourcing the regenerated `accounts.sh` defines the new `claude-<newLabel>` function but leaves the old `claude-<oldLabel>` function callable in the current shell session — shells do not automatically unset functions that disappear from a sourced file. This can mislead users into thinking the rename took full effect when the old label still works. Consider either warning users that a new shell is required for rename to fully take effect, or unsetting the old function during generation.</comment>

<file context>
@@ -102,6 +103,14 @@ function main(): number {
 
+    case 'rename': {
+      const [oldLabel, newLabel] = rest;
+      manager.renameAccount(oldLabel, newLabel);
+      console.log(`✓ Renamed "${oldLabel}" → "${newLabel}" (folder unchanged)`);
+      reloadHint();
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant