Skip to content

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - Junnyyy/wt: Simple worktree manager · GitHub
Skip to content

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

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

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

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

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

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

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

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

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

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

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

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

Repository files navigation

wt

Fast git worktree manager for humans and AI agents.

Create, navigate, and clean up worktrees without the ceremony. wt handles path resolution, node_modules symlinking, and lifecycle hooks so you can get to work in seconds.

$ wt list
Branch Path Status
* main /home/you/project clean
feat-auth /home/you/project/.worktrees/feat-auth dirty (3 files)
fix-typo /home/you/project/.worktrees/fix-typo clean

Install

Quick install (precompiled binary, macOS only)

curl -fsSL https://raw.githubusercontent.com/Junnyyy/wt/main/install.sh | bash

Run the same command to update to the latest version. It will skip the download if you're already up to date.

From source

Requires Bun.

git clone https://github.com/Junnyyy/wt.git
cd wt
bun install

For development (source-linked, changes take effect immediately)

bun run link

For daily use (compiled binary, faster startup)

bun run install:global

Both methods install wt to ~/.local/bin/. Make sure it's in your PATH:

# Add to ~/.zshrc or ~/.bashrc if not already presentexport PATH="$HOME/.local/bin:$PATH"

Uninstall

bun run uninstall

Or manually: rm ~/.local/bin/wt

Shell setup (recommended)

wt cd prints a path but can't change your shell's directory directly. Add the shell integration to fix this:

wt shell-init >>~/.zshrc &&source~/.zshrc

Or inspect the function first and add it manually:

wt() {
if [ "$1"="cd" ];thenlocal dir
dir=$(command wt cd"$2"2>/dev/null)if [ -n"$dir" ];thencd"$dir"elsecommand wt cd"$2"fielif [ $#-eq 0 ];thenlocal dir
dir=$(WT_TUI=1 command wt)if [ -n"$dir" ] && [ -d"$dir" ];thencd"$dir"fielsecommand wt "$@"fi
}

With shell integration, wt cd feat-auth changes your working directory, and bare wt lets you navigate interactively.

Usage

Create a worktree

wt add feat-auth # new branch off HEAD
wt add feat-auth --base main # new branch off main

Creates the worktree at .worktrees/feat-auth, symlinks node_modules and .env files from the main worktree, and runs any configured post-create hook.

List worktrees

wt list # or: wt ls

Shows all worktrees with branch name, path, and clean/dirty status.

Navigate to a worktree

cd$(wt cd feat-auth)

With shell integration, just run wt cd feat-auth directly.

wt cd outputs only the path -- designed for shell integration and scripts.

Remove a worktree

wt rm feat-auth # fails if worktree has uncommitted changes
wt rm feat-auth -f # force remove

Prune orphaned references

wt prune

Cleans up metadata for worktrees whose directories were deleted manually.

Interactive mode

wt

When run with no arguments in a terminal, launches an interactive TUI:

  • j/k or arrow keys to navigate
  • Enter to navigate to the selected worktree
  • d to remove a worktree (with confirmation)
  • q to quit

When piped or in a non-TTY context, falls back to wt list.

Configuration

Create a .wt.toml in your repo root to customize behavior. All settings are optional -- sensible defaults apply without it.

[symlinks]
# Files/directories to symlink from main worktree into new worktreespatterns = ["node_modules", ".env", ".env.local"]
# Remove specific patterns from the list above (or from defaults)# exclude = ["node_modules"]
[worktree]
# Directory for worktrees, relative to repo rootdir = ".worktrees"
[hooks]
# Shell command to run after creating a worktreepost_create = "bun install"# Shell command to run before removing a worktreepre_remove = ""

See .wt.toml.example for a copy-paste starting point.

Defaults (no config needed)

SettingDefault
Symlinked patternsnode_modules, .env, .env.local
Excluded patternsnone
Worktree directory.worktrees/
Post-create hooknone
Pre-remove hooknone

To keep the defaults but skip node_modules symlinking:

[symlinks]
exclude = ["node_modules"]

For AI agents

Every command is non-interactive and outputs clean text suitable for scripting:

# Get worktree path for a branch
path=$(wt cd feat-auth)# Create a worktree
wt add my-task
# Force remove without prompts
wt rm my-task -f
  • Non-TTY mode automatically uses plain list output (no TUI)
  • All destructive operations use --force flag instead of interactive prompts
  • Exit codes: 0 success, non-zero on error

Architecture

Built with Bun, Effect TS, @effect/cli, and Ink.

ProcessService Bun.spawn wrapper
|
+-- GitWorktreeService git worktree list/add/remove/prune
+-- SymlinkService symlink creation and cleanup
+-- ConfigService .wt.toml loading with defaults

Services use Effect's dependency injection (Effect.Service + Layer). Errors are type-safe tagged unions (Data.TaggedError). The CLI is declarative via @effect/cli with auto-generated help and shell completions (wt --completions zsh).

Development

bun install
bun run link # install as source symlink (edits take effect immediately)
bun run dev -- --help # run directly without installing
bun run build # compile standalone binary
bun run typecheck # type check

Releasing

Requires the GitHub CLI (gh).

bun run release patch # 0.1.0 -> 0.1.1
bun run release minor # 0.1.0 -> 0.2.0
bun run release major # 0.1.0 -> 1.0.0

This bumps package.json version, cross-compiles darwin arm64 and x64 binaries, commits, tags, pushes, and creates a GitHub Release with both binaries attached.

License

MIT

About

Simple worktree manager

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages