wktree is a deterministic git worktree manager. It creates, finds, prepares, finishes, and removes worktrees using one consistent contract for humans, shell/tmux wrappers, scripts, and agents.
It is useful when you want predictable worktree paths, safe cleanup, JSON output, optional fixed worktree pools, and repeatable setup for local files such as .env.
Requirements: git and Bun.
Tap this repository directly, then install the latest tagged release:
brew tap codethread/wktree https://github.com/codethread/wktree
brew install codethread/wktree/wktreeOr as one shell command:
brew tap codethread/wktree https://github.com/codethread/wktree && brew install codethread/wktree/wktreeThis installs a small wktree launcher that runs the tagged TypeScript CLI with Homebrew's Bun. Because the CLI is installed from source instead of distributed as a downloaded macOS executable, it avoids Developer ID signing/notarization friction.
To upgrade to the newest tagged release after the formula is updated:
brew update
brew upgrade wktreemakemake runs bun install and bun run build. The build writes:
~/.local/bin/wktree
Make sure ~/.local/bin is on your PATH.
After installing, use the built-in help for the full command reference:
wktree --help
wktree add --help
wktree config explain --helpwktree resolves one checkout as the canonical root for a repository. That root is protected and anchors config lookup, default-branch policy, generated paths, and safety checks.
Normal worktrees are created beside the root:
<canonical-root>__<branch-name-with-/encoded-as-->
Pooled repositories reuse fixed slots instead:
<canonical-root>__feat1
<canonical-root>__feat2
...
There is no app database. Current state comes from git worktree metadata, filesystem paths, and config. Tmux integration consumes emitted path/session data; it is not the source of truth.
From any path inside a repository worktree set:
wktree root
wktree add --branch feature/example
wktree list
wktree path --branch feature/example
wktree remove --branch feature/exampleFor scripts and agents, prefer JSON where available and branch on the payload kind:
wktree add --branch feature/example --json
wktree list --json
wktree finish --jsonIf add --json returns post_create_script_path, run that script with bash before treating the worktree as ready.
Use this to see what config applies to the current repository:
wktree config explain --jsonConfig is optional. Without it, wktree still creates deterministic sibling worktrees.
Config is read from:
${XDG_CONFIG_HOME:-~/.config}/ct-worktrees/trees.toml
Resolution order:
- built-in defaults;
- matching
[[rule]]entries in file order; - exact
[[project]]entry for the canonical root.
Later layers override earlier ones.
[defaults.add]
policy = "origin_default" # "origin_default" | "fresh_canonical"
[defaults.finish]
enabled = true
strategy = "ff_only" # "ff_only" | "rebase_ff" | "squash" | "merge_commit"
push = false
remove_worktree = false
delete_branch = false
[[rule]]
root_glob = "~/dev/projects/**" # required for rules; leading ~/ supported
command = "bun install" # optional bash snippet
pre_remote_check = "test -f .env" # optional bash snippet
[rule.add]
policy = "fresh_canonical"
[rule.finish]
enabled = true
strategy = "squash"
push = true
remove_worktree = true
delete_branch = true
[[project]]
root = "~/dev/projects/example" # required for projects
name = "example" # optional; defaults to basename(root)
command = "bun install" # required for pools/copy unless inherited from a rule
pre_remote_check = "test -f .env" # optional
pool_size = 3 # optional; enables fixed slots
copy_mode_default = "copy" # optional: "copy" | "symlink"; default "copy"
copy = [ # optional
".env",
{ from = "~/shared/tooling", to = ".tooling", mode = "symlink" },
{ from = ".claude", to = [".claude", ".pi/claude"], mode = "copy" },
]
[project.add]
policy = "origin_default"
[project.finish]
enabled = true
strategy = "ff_only"
push = false
remove_worktree = false
delete_branch = falseNotes:
commandandpre_remote_checkrun under bash.commandreceivesWK_ROOTandWK_CREATED.origin_defaultstarts default-base work fromorigin/<default>without mutating the canonical root.fresh_canonicalfetches, requires a clean canonical root on the default branch, fast-forwards it, then starts work from that fresh local branch.copydestinations are always worktree-relative. String entries copy from the canonical root to the same relative path.delete_branch = truerequiresremove_worktree = truein the same effective finish policy.
[defaults.add]
policy = "origin_default"
[[rule]]
root_glob = "~/dev/projects/**"
command = '''
if [[ -f bun.lock ]]; then
bun install
elif [[ -f package-lock.json ]]; then
npm install
else
echo "wktree: no known install step"
fi
'''[[rule]]
root_glob = "~/work/**"
pre_remote_check = "test -f .envrc || { echo 'missing .envrc' >&2; exit 1; }"
[rule.add]
policy = "fresh_canonical"[[project]]
name = "big-app"
root = "~/dev/projects/big-app"
pool_size = 4
command = "bun install"
copy = [".env"]wktree ensure --cwd ~/dev/projects/big-app
wktree status --cwd ~/dev/projects/big-app[[project]]
name = "library"
root = "~/dev/projects/library"
[project.finish]
strategy = "squash"
push = true
remove_worktree = true
delete_branch = trueFrom a non-canonical worktree:
wktree finish --jsonThe TypeScript CLI is the source of truth. nu/wktree/ provides human-friendly wk commands and tmux switching around the same engine.
bun test tests
bun run typecheck
bun run checkThe durable design contract lives in devflow/specs/git-worktrees.md.