Worktree management CLI that keeps all worktrees as siblings in one folder.
| Status | Discovery |
| Created | 2025-12-29 |
| Name | wts = Worktree Siblings + TypeScript |
| Repository | github.com/j2h4u/wts (planned) |
Current repository structure uses bare clone + worktrees, causing compatibility issues with tools expecting a standard .git/ directory.
- Vercel CLI fails with error
ENOTDIR: not a directory, lstat '.git/config' - IDE plugins and CI tools may incorrectly detect Git repository
- Tools don't understand
.gitpointer file
In a worktree, .git is a text file with a reference to bare repository:
gitdir: /path/to/.bare/worktrees/<name>
Tools expect .git/ directory with config, HEAD, objects/, etc.
This infrastructure is needed to simplify AI agent work with feature branches:
- Creating feature branches — agent runs
wts new feature/xyzand gets isolated working directory - Parallel work — multiple agents (or human + agent) can work on different features simultaneously
- Pull Request workflow — from feature worktree do
git pushand create PR - Cleanup after merge —
wts doneremoves worktree and local branch
- Isolation — each feature in its own folder, no conflicts with
node_modules/.env - Simplicity — single CLI (
wts) with obvious commands - Standardization — all repositories have the same structure
Multiple CLI tools exist for git worktree management:
| Tool | Description | Link |
|---|---|---|
| wtm | Bun + TypeScript, for bare repos, zero deps | github.com/your-tools/wtm |
| wtp | Auto-copy .env, post-create hooks |
github.com/satococoa/wtp |
| worktree-cli | AI assistant focus, isolated contexts | github.com/fnebenfuehr/worktree-cli |
| worktree-cli | GitHub Issues + Claude Code integration | npmjs.com/@jlawman/worktree-cli |
| Treekanga | YAML config, zoxide/tmux/VSCode integration | github.com/treekanga/treekanga |
| newt | Minimal CLI for quick operations | github.com/cdzombak/newt |
| git-worktree-toolbox | MCP server + CLI (gwtree) | github.com/ben-rogerson/git-worktree-toolbox |
| git-worktree-wrapper | API for checkout/branch commands | github.com/lu0/git-worktree-wrapper |
None of the tools solve our main problem:
- Sibling layout — all worktrees in one folder, side by side
- Regular clone for main — not bare, so Vercel CLI works
- Global installation — single CLI for all repositories
Most tools work on top of standard git worktree without changing clone structure.
github_repo_name.worktree/ ← Worktree home
├── .bare/ ← Bare repository (core git data)
│ ├── config
│ ├── objects/
│ ├── refs/
│ └── worktrees/
│ └── master/ ← Worktree metadata
│ ├── HEAD
│ └── gitdir → ...master/.git
├── master/ ← Worktree for master branch
│ └── .git (file!) ← Points to ../.bare/worktrees/master
└── feature-xyz/ ← Other worktrees
└── .git (file!)
Problem: All worktrees have .git file, including main branch.
| File | Dependency |
|---|---|
scripts/wt-helpers/wt-common-lib.sh |
findWorktreeHome() searches for .bare/ directory |
scripts/wt-helpers/wt-clone.sh |
git clone --bare + hardcoded .bare path |
scripts/wt-helpers/wt-new.sh |
Requires running from main/ worktree |
scripts/wt-helpers/wt-done.sh |
Depends on detect_layout() from common-lib |
scripts/wt-helpers/CONTRIBUTING.md |
Documentation describes bare layout |
github_repo_name.worktree/ ← Worktree home (preserved!)
├── master/ ← Main branch = REGULAR CLONE
│ └── .git/ ← DIRECTORY (full git repo)
│ ├── config
│ ├── objects/
│ ├── refs/
│ └── worktrees/
│ └── feature-xyz/ ← Sibling worktree metadata
└── feature-xyz/ ← Worktree (sibling of main branch)
└── .git (file) ← Points to master/.git/worktrees/...
| Aspect | Before | After |
|---|---|---|
| Main branch | worktree from bare | regular git clone |
.git in main |
file | directory |
.git in feature |
file | file (unchanged) |
| Object storage | .bare/objects/ |
master/.git/objects/ |
| Worktree home detection | search for .bare/ |
search for .git/ directory |
- Vercel CLI works — main worktree has real
.git/directory - Standard git workflow — main worktree indistinguishable from regular clone
- Folder structure preserved — visual hierarchy remains
- Feature worktrees work as before — created as siblings
- Feature worktrees still have
.gitfile (git worktree limitation) - Vercel CLI from feature worktree still won't work
- Deploy expected only from main/master
Building a global CLI in TypeScript. Separate repository, installed via bun link or npm.
| Principle | Application |
|---|---|
| DRY | Common functions (paths, colors, git) at file start, reused by commands |
| KISS | Minimal abstractions, direct bun shell calls |
| YAGNI | No configs or options that "might be useful" |
wts/
├── src/
│ └── wts.ts # CLI entry point + all logic
├── package.json # bin: { "wts": "src/wts.ts" }
└── README.md
| Command | Description |
|---|---|
wts clone <url> [dir] |
Clone repo with all worktrees as siblings |
wts new <branch> [dir] |
Create feature worktree |
wts done <dir> |
Remove worktree and branch |
wts list |
Show all worktrees |
# Clone and install globally
git clone git@github.com:j2h4u/wts.git ~/.local/share/wts
cd ~/.local/share/wts
bun link
# Usage (from anywhere)
wts clone git@github.com:user/repo.git
wts new feature/xyz
wts done feature__xyzModules:
- CLI Router — command parsing, handler dispatch
- Clone Handler —
git clone+ worktree home structure creation - New Handler —
git worktree add+ setup (deps, env) - Done Handler — checks +
git worktree remove+ cleanup - List Handler — wrapper over
git worktree list - Helpers — colors, paths, git utils
Key Functions:
| Function | Responsibility |
|---|---|
findWorktreeHome() |
Find worktree home by .git/ directory |
findMainWorktree() |
Find main worktree (where .git is directory) |
getDefaultBranch() |
Auto-detect from git ls-remote |
branchToDir() |
Convert feature/xyz → feature__xyz |
hasUncommittedChanges() |
Check git status --porcelain |
-
wts clonecreates correct structure (main = regular clone) -
wts newcreates sibling worktrees -
wts donecorrectly removes worktrees and branches - Vercel CLI works in main worktree
-
git push/pull/fetchwork from all worktrees
- Publish to GitHub:
github.com/j2h4u/wts - Add README with examples
- (optional) Publish to npm:
npm install -g wts
-
Main branch name: ✅ Auto-detection
- Use
git ls-remote --symref origin HEADto determine default branch
- Use
-
Backward compatibility: ✅ Not required
- Old bare layout not supported
- Existing repos simply re-clone in new format
-
Behavior on uncommitted changes: ✅ Abort
- Scripts should check
git status --porcelainand abort if changes exist
- Scripts should check
-
Package manager: ✅ Configurable
- Create
.wt-configor use environment variables - Default:
bun
- Create
-
CLI format: ✅ Global CLI
- Commands:
wts clone,wts new,wts done,wts list - Installation:
bun linkor npm global - Separate repository:
github.com/j2h4u/wts
- Commands:
-
Git libraries: ✅ Not used
- None support
git worktree - Using
bun shell— no dependencies, easy debugging
- None support
In-place migration not required.
Since local repositories are synced with remote, simply:
- Create and install
wts - Use
wts cloneto deploy repositories in new location - Delete old bare worktree homes after verification
~/projects/
├── my-repo/ ← Main clone
│ └── .git/ ← Full directory
├── my-repo-feature-xyz/ ← Worktree (somewhere nearby)
│ └── .git (file)
└── other-place/ ← Worktree can be anywhere
└── .git (file)
Problem: Worktrees scattered, no single location.
~/projects/
└── my-repo.worktree/ ← WORKTREE HOME
├── main/ ← Main clone
│ └── .git/ ← Directory
└── feature-xyz/ ← Sibling worktree
└── .git (file)
Benefit: All worktrees in one place, convenient hierarchy.
# From master/ (main worktree)
git worktree add ../feature-xyz feature/xyzGit creates:
../feature-xyz/— working directory with.gitfile.git/worktrees/feature-xyz/— worktree metadata (HEAD, index, etc.)
gitdir: /absolute/path/to/master/.git/worktrees/feature-xyz
master/.git/worktrees/feature-xyz/gitdir → ../../../feature-xyz/.git
feature-xyz/.git → master/.git/worktrees/feature-xyz