feat: launch Claude session as git worktree (⌘+Shift+Enter)

Draft
GR
4mo ago
+401-24

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 WT badge

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.

ApproachWorktree pathWorktree creationTrade-off
A. We own it (claude-control's choice)<parent>/<repo>-<branch> (sibling)git worktree add ourselvesFull 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 itMinimal 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+Enter keybinding, WT badge, hint text, modal event isolation, name validation
  • src/claude-session-utility.ts — parseWorktreePath(), isValidWorktreeName(), worktree fields in 3 session sources, -n in launch
  • src/main.ts, src/preload.ts, src/electron-api.d.ts — new launch-new-claude-session-worktree IPC with validation
  • docs/claude-session-integration-design.md — added "Git Worktree Sessions" section
  • README.md, CHANGELOG.md, package.json — feature doc + version 1.0.75

Test plan

  • ⌘+Shift+Enter opens 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 make build 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 start window + 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 / /rename to 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> branch lookup. Branch info for cleaned-up worktrees is a future enhancement.

  • Nested worktree visibility in IDE/git GUI. Because claude -w places 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 activate timing investigated, kept top-of-block. Moving activate into 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 supports claude --worktree <name> --resume <uuid> which preserves keep/remove prompts on next exit. Codev could detect isWorktree and use the worktree-aware form — future enhancement.

  • Drop -n once Ghostty exposes TTY (ghostty#11354).

🤖 On behalf of @grimmerk — generated with Claude Code