Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

wtm

A small, standalone Git worktree manager for creating, preparing, browsing, removing, and merging isolated worktrees.

wtm demo screenshot

Note: I recommend worktrunk instead of this.

Why

Git worktrees are useful whenever you need multiple independent checkouts of the same repository:

  • working on several branches at once
  • testing a fix without disturbing your current checkout
  • reviewing another branch while keeping local work in place
  • running parallel automation or coding-agent sessions in isolated directories

The raw git worktree commands are powerful, but the everyday workflow is repetitive: choose a branch, create a safe directory name, add the worktree, run repo-specific setup, remember where it lives, clean it up later, and merge it back when ready.

wtm wraps that workflow in a focused CLI. It creates one worktree per branch under a predictable directory, runs optional setup steps, and provides an interactive terminal UI for managing active worktrees.

Usage

wtm feat/auth-refactor # create a worktree, run setup, print the path
wtm fix/login-race --base main # branch off a specific base branch
wtm feat/auth-refactor # already exists? reattach idempotently
wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now
wtm init # write .wtm.config.toml into the current repo
wtm manage # browse, remove, inspect, and merge worktrees

Worktrees are placed under .trees/ in the repo root by default:

your-repo/
├── .trees/
│ ├── feat-auth-refactor/ # isolated worktree, own branch
│ └── fix-login-race/
└── ...

Each worktree is a normal Git checkout. You can use any editor, terminal, automation, or agent inside it:

cd .trees/feat-auth-refactor

There is also a command runner shortcut:

wtm feat/auth-refactor --run codex
wtm feat/auth-refactor --run "pnpm dev"
wtm feat/auth-refactor --run codex --now

--run executes the provided command inside the worktree after setup. Quote commands that contain spaces so your shell passes them as a single option value. Add --now / -n with --run to start setup in the background and run the command immediately.

Install

Requires Node 18+ and Git. The wtm manage merge action requires a Git version with git merge-tree --write-tree support.

For local development from this repo:

pnpm install
pnpm link --global

To run the local checkout without linking it globally, use either:

pnpm wtm -- manage
node ./bin/wtm.js manage

To expose the wtm binary globally from your checked-out repo, use pnpm link --global. If you later publish this package, installation becomes the usual:

pnpm add -g @aspireone/wtm

Keep .trees/ in your repo's .gitignore:

echo".trees/">> .gitignore

Config

wtm looks for config in two places, merged in this order. Higher entries override lower entries.

FileScope
~/.config/wtm/config.tomlGlobal defaults for all repos
.wtm.config.toml in the repo rootPer-repo config, commit this

CLI flags override both.

To bootstrap a repo-local config file in the current directory, run:

wtm init

This creates .wtm.config.toml from the packaged example template and refuses to overwrite an existing file. wtm init keeps the file shape fixed and only auto-fills the setup steps when it can detect them confidently.

Minimal .wtm.config.toml:

baseBranch = "main"worktreeRoot = ".trees"# Add repo-specific [[setup]] steps here.

wtm init emits high-level setup steps such as copy and run, so common setup stays readable and portable.

init Detection

wtm init does not switch between multiple full templates. It always writes the same config file and only varies the generated setup steps:

  • if .env or .env.* files exist anywhere in the repo, it adds copy steps for each one
  • if .env is not present but .env.example exists, it also adds a fallback copy from .env.example to .env
  • if package.json exists and the package manager can be inferred from a lockfile or packageManager, it adds the matching run install step
  • if neither signal is present, it leaves a placeholder comment for future [[setup]] steps

Generated env copy steps do not overwrite files that already exist in the target worktree.

Current package-manager detection order:

  • pnpm-lock.yaml -> run = "pnpm install"
  • package-lock.json or npm-shrinkwrap.json -> run = "npm install"
  • yarn.lock -> run = "yarn install"
  • bun.lock or bun.lockb -> run = "bun install"
  • otherwise, package.json#packageManager

Setup Steps

Setup steps run once after the worktree is created. Three template variables are available:

VariableValue
{target}Absolute path to the new worktree
{root}Absolute path to the repo root
{branch}The branch name, such as feat/auth-refactor

Use [[setup]] tables to describe setup actions:

[[setup]]
copy = ".env"
[[setup]]
copy = "shared/.env.development"
[[setup]]
run = "pnpm install"

copy paths are resolved from the repo root and copied to the same relative path under the target worktree. Parent directories are created automatically and existing target files are not overwritten by default.

[[setup]]
copy = ".env.example"to = ".env"overwrite = true

run commands execute in {target} by default and use the configured shell. A custom working directory can be set with cwd.

[[setup]]
run = "pnpm exec prisma generate"cwd = "{target}"

If no setup is configured, the CLI just creates the worktree and exits.

All Config Keys

KeyDefaultDescription
baseBranch"main"Branch to fork from when creating a new branch
worktreeRoot".trees"Directory under repo root where worktrees are placed
shellsystem defaultShell used to run run setup steps, such as "bash" or "pwsh"
setup[]Ordered list of setup steps to run after worktree creation
themebuilt-in paletteOptional [theme] table for wtm manage colors

Manage UI Theme

You can override the interactive manage UI colors from either config file:

[theme]
accent = "#91a7ff"accentStrong = "#c1ccff"context = "#aebbd0"success = "#7fd38b"warning = "#f0b85a"danger = "#f07f7f"textPrimary = "#f4f7fb"textLabel = "#cbd7e3"textMuted = "#9aa8b7"textDim = "#657386"

These values map directly to Ink text colors. Named colors, hex colors, rgb(...), and ansi256(...) values are supported.

Behavior

  • Branch exists? Reused as-is.
  • Worktree exists? Reattached, setup skipped.
  • Worktree registered but directory missing? Stale entry pruned, worktree recreated.
  • Setup configured? Steps run once after a new worktree is created.
  • --run <command> / -r <command>? Runs the command inside the worktree after setup.
  • --run <command> --now / -r <command> -n? Starts setup in the background, then immediately runs the command inside the worktree.
  • No --run? Prints the worktree path, then exits.

Manage UI

Run wtm manage to open an interactive worktree navigator in the terminal.

  • search by branch, path, or HEAD with /
  • inspect the selected worktree in a dedicated details pane
  • refresh the inventory with r
  • delete the selected worktree with d
  • delete the selected worktree and its local branch with D
  • merge the selected worktree into the current checkout with M
  • quit with q

The main checkout is visible for context but cannot be removed from this screen. The details pane compares each selected worktree against the current checkout and reports commit distance, changed files, and whether Git's static merge check sees conflicts.

M is intentionally guarded. It only merges when:

  • the current checkout and selected worktree are both on local branches
  • the selected branch has commits that are not already in the current checkout
  • the current checkout is clean
  • no merge, rebase, cherry-pick, revert, or bisect operation is in progress
  • the static merge check reports no conflicts
  • both branch tips are unchanged between the static check and the merge

If those checks pass, wtm runs git merge --no-edit against the checked selected commit. If the selected branch has nothing new to merge, the UI reports that and leaves the repository unchanged.

Package Layout

bin/wtm.js npm-exposed executable
src/cli.mjs CLI entry point
src/ implementation
scripts/ release automation
package.json npm package metadata

Release

Create a new patch release, tag it, and publish it:

pnpm run release

For a different bump level:

pnpm run release -- minor

The preflight check uses pnpm pack in a temporary directory, so it validates the tarball contents locally without failing just because that version already exists on npm or leaving a tarball in the repo.

About

An advanced git worktree manager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages