claude-code-worktree-paths
claude --worktree foo puts worktrees in the wrong place. Template your way out.

Claude Code's default WorktreeCreate behavior drops a worktree at .claude/worktrees/<name>/ inside the repo, on a branch named worktree-<name>. If you want worktrees somewhere else — as siblings in ~/src/, under a different naming scheme, on a clean branch name — this plugin lets you template both the path and the branch via ~/.claude/settings.json.
Before / after
# Default: worktree buried inside the repo, branch gets a prefix
~/src/my-app/
.claude/worktrees/feature/ (branch: worktree-feature)
# With worktreeTemplate "~/src/{repo}@{owner}+{branch}" and branchTemplate "{input}":
~/src/my-app/ (main checkout)
~/src/my-app@you+feature/ (branch: feature)
No config, no change:
# Without worktreeTemplate or branchTemplate set, behavior is identical to vanilla Claude Code:
~/src/my-app/
.claude/worktrees/feature/ (branch: worktree-feature)
Installing without configuring is a no-op.
Config
Add a repoSettings block to any Claude Code settings file:
"repoSettings": {
"worktreeTemplate": "~/src/{repo}@{owner}+{branch}",
"branchTemplate": "{input}",
"cloneTemplate": "~/src/{repo}@{owner}"
}
All three keys are optional. Omit any you don't need.
worktreeTemplate — where the worktree directory lands. Read by this plugin.
branchTemplate — what the worktree's branch is named. Read by this plugin.
cloneTemplate — where clones land. Read by fnclaude, not this plugin — included here so the schema is centrally documented. Defining it without fnclaude installed is harmless (the plugin ignores it).
To disable this plugin in a specific project or on a specific machine, use Claude Code's enabledPlugins setting in the appropriate tier — e.g. "enabledPlugins": { "claude-code-worktree-paths@fnrhombus-plugins": false } in <repo>/.claude/settings.local.json. (docs)
Scopes
The plugin reads repoSettings from the same four tiers Claude Code itself uses, in the same precedence order (highest → lowest):
| Tier | Path | Use for |
|---|
| Managed | /etc/claude-code/managed-settings.json (Linux), /Library/Application Support/ClaudeCode/managed-settings.json (macOS), %ProgramData%\ClaudeCode\managed-settings.json (Windows) | IT-deployed overrides |
| Local | <repo>/.claude/settings.local.json | Your local-only project tweaks (gitignored) |
| Project | <repo>/.claude/settings.json | Team-shared per-project layout (committed) |
| User | ~/.claude/settings.json | Your default across all projects |
Tiers are shallow-merged per field, not whole-block-replaced. So a project that sets only branchTemplate keeps your user-level worktreeTemplate intact — set just the keys you want to override. (This is a small deviation from Claude Code's default whole-key override, picked because per-template independence is what people actually want here.)
Placeholders
Available in both worktreeTemplate and branchTemplate:
| Placeholder | Value |
|---|
{input} | What the user typed after --worktree |
{owner} | Owner from git remote get-url origin |
{repo} | Repo name from the same; falls back to the local directory basename if there is no remote |
{repo-dir} | Basename of the local repo root (often equal to {repo}, but captures local naming like myrepo@me) |
{clone-path} | Absolute path of the actual repo root. Lets worktreeTemplate sibling the clone without restating the prefix — e.g. worktreeTemplate: "{clone-path}+{branch}" paired with cloneTemplate: "~/src/{repo}@{owner}" produces ~/src/my-app@me/ for clones and ~/src/my-app@me+feature/ for worktrees. |
{cwd} | Basename of the directory Claude was invoked from |
{host} | Full hostname from the remote URL (e.g. github.com, gitlab.com, git.example.com) |
{host-plain} | TLD-stripped host (github for github.com, git for git.example.com). Always available, algorithmic. |
{host-short} | Short alias (gh, gl, etc.) per the host-aliases LUT. Errors with instructions if unconfigured for the current host. |
{branch} is also available in worktreeTemplate — it resolves to the branch name produced by branchTemplate.
Host-short aliases
{host-short} looks up the current host in a JSON file. Two locations are read, merged with user-level winning: