Skip to content

Junnyyy/wt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

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 present
export 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" ]; then
    local dir
    dir=$(command wt cd "$2" 2>/dev/null)
    if [ -n "$dir" ]; then
      cd "$dir"
    else
      command wt cd "$2"
    fi
  elif [ $# -eq 0 ]; then
    local dir
    dir=$(command wt)
    if [ -n "$dir" ] && [ -d "$dir" ]; then
      cd "$dir"
    fi
  else
    command 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 worktrees
patterns = ["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 root
dir = ".worktrees"

[hooks]
# Shell command to run after creating a worktree
post_create = "bun install"

# Shell command to run before removing a worktree
pre_remove = ""

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

Defaults (no config needed)

Setting Default
Symlinked patterns node_modules, .env, .env.local
Excluded patterns none
Worktree directory .worktrees/
Post-create hook none
Pre-remove hook none

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

Packages

 
 
 

Contributors