feat: launch Claude session as git worktree (⌘+Shift+Enter)
Summary
Add ⌘+Shift+Enter on the Projects tab to launch a Claude Code session as a git worktree. Plus polish: worktree-aware naming.
What's new
Launch a worktree session — ⌘+Shift+Enter
A small dialog asks for an optional branch name:
- Empty → behaves like
⌘+Enter(normal session) - With a name → runs
claude -w "<name>" -n "<name>"in the configured terminal. Claude CLI creates a fresh worktree at<repo>/.claude/worktrees/<name>and starts the session inside it.
Esc cancels, click outside cancels, Enter launches. Modal traps Tab/click events so background shortcuts/inputs don't fire while the dialog is open.
Worktree session display
Sessions whose path is <repo>/.claude/worktrees/<name> now display:
- The parent repo name (e.g.,
codev) — not the worktree folder name (e.g.,test-worktree-4) - A small purple
WTbadge
New parseWorktreePath() helper + isWorktree / parentRepo fields on ClaudeSession. The project field still points at the worktree path (resume / cwd matching unchanged). Detection is path-based: also recognizes worktree sessions launched manually via claude -w or by other tools.
Why -n <name> is required
Without -n, Ghostty session switching breaks for worktree sessions:
- Worktree terminal cwd = parent repo (shell stays in parent; only the claude process internally cd's into the worktree)
- Main terminal cwd = parent repo too
- Ghostty has no per-tab TTY exposure → only cwd / title match available
- Same cwd → AppleScript first-match-wins → wrong tab
Passing -n <name> makes Claude CLI write a custom-title JSONL entry. Codev's existing Layer-1 title match finds the correct tab. iTerm2 / Terminal.app / cmux were already unaffected (they use TTY match).
Future: once Ghostty exposes per-tab TTY (ghostty-org/ghostty#11354), the -n flag becomes optional.
Shell-injection guard
worktreeName is interpolated into a shell command that ends up in AppleScript. Validated at three layers (dialog input, IPC handler, launchNewClaudeSession) via isValidWorktreeName() — whitelist of branch-name-safe characters; rejects shell metacharacters, leading dash/dot/slash, double slash, ...
Why we use claude -w instead of git worktree add ourselves
Two approaches were considered. We picked B but A remains a viable future direction.
| Approach | Worktree path | Worktree creation | Trade-off |
|---|---|---|---|
| A. We own it (claude-control's choice) | <parent>/<repo>-<branch> (sibling) | git worktree add ourselves | Full control, sibling visibility, no IDE/git-GUI clutter inside the parent repo. But codev owns lifecycle: cleanup UI, branch conflict handling, error paths. |
B. claude -w <name> -n <name> (this PR) | <repo>/.claude/worktrees/<name> (nested) | Claude CLI does it | Minimal code, leverages Claude CLI features (auto-cleanup, --tmux). Downside: the nested worktree folder is visible to VS Code / git GUI, so users may see worktree files in their workspace, accidentally commit them, or include them in cross-repo searches. |
Why B for now: codev is Claude-Code-focused (claude-control supports multiple AI CLIs, so they need their own implementation). Auto-cleanup is the right default for codev's typical use case. Detection of <repo>/.claude/worktrees/ paths is path-based, so even if we ever add A as an option, existing nested worktrees keep showing the WT badge.
See the design doc update for the full discussion.
Files
src/switcher-ui.tsx— new dialog component,⌘+Shift+Enterkeybinding, WT badge, hint text, modal event isolation, name validationsrc/claude-session-utility.ts—parseWorktreePath(),isValidWorktreeName(), worktree fields in 3 session sources,-nin launchsrc/main.ts,src/preload.ts,src/electron-api.d.ts— newlaunch-new-claude-session-worktreeIPC with validationdocs/claude-session-integration-design.md— added "Git Worktree Sessions" sectionREADME.md,CHANGELOG.md,package.json— feature doc + version 1.0.75
Test plan
-
⌘+Shift+Enteropens dialog - Empty branch name → normal session (same as
⌘+Enter) - With branch name →
claude -w … -n …launches in external terminal -
Esc/ click-outside cancel - Worktree session shows parent repo name + WT badge in Sessions tab
- Switching back to a worktree session focuses the correct Ghostty tab (when no same-cwd collision)
- Resume worktree session works
- Invalid worktree names rejected (shell metachars, leading dash, etc.)
- Tab/click inside dialog don't fire background shortcuts
- Non-Ghostty terminals (iTerm2/Terminal.app/cmux) still work as before
-
yarn makebuild OK (no debug logs in production)
Known limitations / future work
These are pre-existing issues that became more visible with worktree sessions but are not specific to this PR. Documented for follow-up:
-
Same-cwd switch ambiguity (Ghostty). When multiple Ghostty windows report the same cwd (e.g., a
yarn startwindow + the active conversation window + an old worktree window all at~/git/codev), Layer-2 cwd match returns the first window in iteration order — often the top-most. Workaround: use-n//renameto disambiguate via title match. Real fix waits on Ghostty TTY exposure. -
Status indicator occasionally orange when expected green. Worktree sessions that should be idle sometimes show the working/needs-attention orange dot. Cause TBD — may be hook timing or status-file race. Tracked separately.
-
Old worktree sessions launched without
-n(before this PR) won't have a custom title and will still hit the cwd-collision issue when switching. Workaround: exit + re-launch. -
Non-alive worktree sessions don't show a branch name. Claude CLI may auto-cleanup the worktree directory on clean exit, breaking the
git -C <worktree-path> branchlookup. Branch info for cleaned-up worktrees is a future enhancement. -
Nested worktree visibility in IDE/git GUI. Because
claude -wplaces worktrees at<repo>/.claude/worktrees/<name>, VS Code's file explorer and git GUI tools see those folders inside the workspace. Mitigation: add.claude/worktrees/to.gitignore, or revisit Approach A in a future PR. -
AppleScript
activatetiming investigated, kept top-of-block. Movingactivateinto match-success branches showed a perceptible slowdown in switch latency in local testing. Reverted; the visual glitch on match-miss is documented as a manifestation of the same-cwd ambiguity above, not a separate bug. -
Resume with worktree-aware context. Codev resumes worktree sessions via
cd <path> && claude --resume <uuid>. Claude CLI also supportsclaude --worktree <name> --resume <uuid>which preserves keep/remove prompts on next exit. Codev could detectisWorktreeand use the worktree-aware form — future enhancement. -
Drop
-nonce Ghostty exposes TTY (ghostty#11354).
🤖 On behalf of @grimmerk — generated with Claude Code