[Feature Request] Configurable worktree directory location with sibling directory support
Status Open
Reported on v2.1.50
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 13 comments · opened Feb 21, 2026
Bug Description
Please provide a way to configure where worktrees go, specifically a sibling directory. I asked Claude what is the recommended location for worktrees, and it said: "The widely recommended convention is to put worktrees in a sibling directory to the main repository, not inside it." Using a subdirectory like you do now causes problems.
Environment Info
- Platform: darwin
- Terminal: iTerm.app
- Version: 2.1.50
- Feedback ID: f0086445-61db-4342-b4ea-df9fff886599
Errors
[]
13 Comments
Yup, plus one to this! If you are using webpack, then it will go through the node_modules in the worktree and take ages, or sometimes stall.
Being able to choose the directory where the tree is built would be awesome!
Now I know I can use the hooks, but then some of the nice things don't come in that come with workt ree management by
--worktree@ChiragThesia for now you can achieve this with a hook.
https://code.claude.com/docs/en/hooks#worktreecreate
Or you can use a claude code/worktree script I wrote: https://github.com/aparajita/claude-code/tree/main/commands/worktree
@aparajita yup, I actually tried the hooks first, but I liked the auto cleanup and all.
Haha, same, I did the same and built a script. Thank you for sharing this. I can improve mine by comparing with yours. Thank you!
This would also solve monorepo workspace symlink resolution issues — worktrees inside the repo share
node_moduleswhich points back to the main checkout'spackages/, breaking module resolution. Related: #27744Claude 1.1.7714 (3bd6f6) 2026-03-19T14:50:53.000Z still defaults to within-repo which breaks tooling.
<img width="749" height="86" alt="Image" src="https://github.com/user-attachments/assets/f5dcfe4e-1e1d-44a1-a95d-fca220b65b83" />
Please consider making sibling directory the default. https://github.com/max-sixty/worktrunk puts them at
$repopath.$branchnamewhich works well.@turadg Thanks for the worktrunk link. I create my own worktree script which does much the same thing, but will check out worktrunk.
A PreToolUse hook on
EnterWorktreecan redirect worktree creation to a sibling directory:Create
.claude/worktree-config:The hook detects
git worktree addtargeting a path inside the repo and redirects it to a sibling directory.@yurukusa Does the PreToolUse hook allow user interaction? If not, it's a blocker for me.
+1 — and a concrete tooling breakage I don't see called out yet: because Claude Code creates worktrees under
.claude/worktrees/, and that path is (per the docs' own recommendation) added to.gitignore, any tool that respects.gitignoreskips files inside the worktree. In our case CSharpier and other linters/formatters silently skip the worktree's source, so formatting/lint simply don't run there — which undercuts the whole point of working in the worktree.Current workarounds are a
WorktreeCreatehook, orworktree.bgIsolation: "none"+ manualgit worktree add ../worktrees/<name>— both more setup than this should need. Aworktree.baseDirectory/worktree.locationsetting to point worktrees at a sibling directory outside the repo (as this issue requests) would fix it cleanly and keep worktrees out of.gitignore's way.(Posting here after #28242 was closed as a duplicate of this and locked.)
The custom path support is broken under Windows-driven WSL sessions. Clicking
Customopens a file picker, this generates a Windows-style path, and then that path is ignored and the worktree is created nested under the repository (as per default).Edited to add: I'm fixing my build system to work with nested directories, so the fix to this is won't be needed for me anymore.
+1 - Here's failure mode plus a concrete proposal.
The hook path isn't a _real_ _workaround_. It worked in one repo and not in another, and I'm still tracking down why (likely a conflict on my end, and I'll find it). But...that's rather the point. A hook is imperative, so it has to win against whatever else is loaded in that repo. A setting is declarative, so there's nothing for it to lose to. One of those I debug on a Friday night; the other just...works.
Like many developers, I work across dozens of repos and a few monorepos under an enforced directory taxonomy, and a lot of tooling keys off those paths: backup exclusions, local indexing, ignore-file scope, and scripts that discover repos by location. A worktree that lands outside that tree is invisible to all of it, and relocating it after creation defeats the purpose.
Similarly - having two different harnesses working in one repository is commonplace - but having significantly differing worktree management complicates that delicate orchestration further.
The ask:
{ "worktreeDirectory": ".worktrees" }Resolve relative values against the repo root, so the above yields
/path/to/repo/.worktrees/<branch>. Absolute paths and~keep working for anyone who wants a central store, so one setting covers both camps.I'd argue for repo-relative as the default because the value is portable. No machine-specific path means it can be committed to a shared settings file and resolve correctly for every developer on the team (an absolute default can never do that).
One objection I'd expect: a worktree nested inside the working tree shows up as untracked content. Writing
.worktrees/ to.git/info/excludeat creation handles that without touching anyone's.gitignore.Unset falls back to current behavior, so nothing breaks. Happy to test a prerelease against my setup if that's useful.
Worth flagging for anyone landing here: the setting this issue asks for already exists in the settings schema — the CLI just doesn't read it.
In the shipped bundle for v2.1.267 there is a
worktreesettings object with these keys:| key | purpose |
|---|---|
|
symlinkDirectories| dirs to symlink from the main repo into the worktree (node_modules,.cache, …) ||
sparsePaths| dirs to include viagit sparse-checkout(cone mode) ||
baseRef|fresh(branch fromorigin/<default>) \|head||
bgIsolation|worktree\|none||
location| the worktree base directory |location's owndescribe()text, verbatim:And the group description:
So the key exists, the semantics are already specified (absolute or
~/-relative, replaces<project>/.claude/worktrees), and the desktop app honors it — but only for SSH sessions, sourced from the SSH host's user settings.That makes this request considerably narrower than the thread has been treating it. It is not "design a new config surface"; it is:
--worktree,EnterWorktree,Agentwithisolation: "worktree") readworktree.locationfrom the normal settings chain, and@Qythyx guessed the name exactly right in the comment above —
worktree.locationis what it's called.Two things this would fix that the hook workaround doesn't, both already raised in this thread:
.gitignore-respecting tooling silently skips the worktree. Because worktrees live under.claude/worktrees/and that path is gitignored per the docs' own recommendation, formatters and linters that honor.gitignore(CSharpier, and others) skip the worktree's sources entirely. A location outside the repo removes the conflict rather than papering over it.WorktreeCreatehook is imperative and takes over the whole lifecycle. Configuring it also disables Claude Code's own worktree removal, so you have to reimplementWorktreeRemoveas well, and you lose auto-cleanup and the resume/reset-to-fresh-base handling. A declarative setting has nothing to lose against, as @bobbypierce42 put it above.Since the schema entry and its documented semantics are already in place, honoring it in the CLI looks like a much smaller change than this issue's age suggests.
Some findings on how the desktop app interacts with this, since the thread so far is mostly about the CLI. Tested 2026-09-22, Claude desktop 2.2553.13 on macOS, with a logging
WorktreeCreatehook in<repo>/.claude/settings.local.jsonthat records its input and then creates the worktree at the default location.WorktreeCreate. A chip ("start in a fresh worktree with one click") ran the hook and started its session at the path the hook printed. The input wassession_id, an emptytranscript_path,cwd= the repo root, andname= a generated slug such aswizardly-bartik-f35b31. So a hook can relocate desktop chip worktrees — the docs only list--worktree, subagent isolation and background sessions.git worktree add ../feature-x), the hook still receivedcwd= the main repo root, and the hook in the main checkout's settings ran — a hook in../feature-x/.claude/settings.local.jsondid not. A hook therefore cannot tell which existing worktree the session was meant for..claude/worktrees/(a detached one from an earlier session, switched to a newclaude/<name>branch) and never calledWorktreeCreate. A hook-based relocation is therefore not reliable for desktop sessions: some sessions land in.claude/worktrees/regardless.WorktreeRemovehook), an unchanged worktree created for anisolation: "worktree"subagent was left on disk after the subagent finished.Taken together, a hook cannot give desktop sessions a sibling-directory layout today. Honouring
worktree.locationfor local desktop sessions (as @risperdal described above), or letting a desktop session start in an existing linked worktree, would.