A git worktree manager built for AI agent workflows.
Spin up isolated worktrees for Claude Code sessions.
Switch between them instantly with fzf.
See which agents are busy, waiting, or idle.
Documentation
Running multiple Claude Code sessions on the same repo means constant context-switching, stashing, and branch juggling. Willow fixes this by giving every task its own isolated directory via git worktrees, then adding fzf-based switching and live agent status tracking on top.
<willow-base>/
βββ repos/
β βββ myrepo.git/ # bare clone (shared git database)
βββ worktrees/
β βββ myrepo/
β βββ main/ # each branch = isolated directory
β βββ auth-refactor/ # Claude Code running here
β βββ payments/ # another agent running here
βββ status/
βββ myrepo/
βββ auth-refactor/
β βββ <session_id>.json # {"status": "BUSY", ...}
βββ payments/
βββ <session_id>.json # {"status": "WAIT", ...}
<willow-base> defaults to ~/.willow. You can override it with WILLOW_BASE_DIR or the global baseDir config, and move an existing setup with ww migrate-base <path>.
brew install iamrajjoshi/tap/willowgo install github.com/iamrajjoshi/willow/cmd/willow@latest- git
- tmux β optional, for the
ww tmuxpicker popup - gh β optional, required for
ww new --pr,ww stack status,ww pr create, and GitHub-aware merged worktree detection
# Add to .bashrc / .zshrc
eval "$(willow shell-init)"
# fish
willow shell-init | sourceThis gives you:
| Command | Description |
|---|---|
ww <cmd> |
Alias for willow |
ww sw |
fzf worktree switcher (cd's into selection) |
ww new <branch> |
Create worktree + cd into it (tmux-aware) |
ww checkout <branch> |
Smart checkout + cd (switch or create, tmux-aware) |
wwn <branch> |
Shorthand for ww new |
wwc <branch> |
Shorthand for ww checkout |
www |
cd to <willow-base>/worktrees/ |
Optional: Set terminal tab title to the current worktree name:
eval "$(willow shell-init --tab-title)"Teach Claude Code how to use willow automatically. The repo ships a skill at skills/willow/SKILL.md that the skills CLI can install:
# Global (available across all projects)
npx skills add iamrajjoshi/willow --skill willow -g -a claude-code
# Project-local (only inside the current project)
npx skills add iamrajjoshi/willow --skill willow -a claude-code
# Or clone directly
git clone https://github.com/iamrajjoshi/willow ~/.claude/skills/willow-g installs to ~/.claude/skills/, -a claude-code targets Claude Code specifically, and --skill willow avoids installing everything in the repo. Once installed, Claude Code will reach for ww checkout, ww sync, ww dispatch, and other willow commands automatically when you ask it to work on branches, PRs, or parallel tasks.
ww cc-setupInstalls hooks into ~/.claude/settings.json that write per-session agent status (BUSY / DONE / WAIT / IDLE) to <willow-base>/status/. Supports multiple Claude sessions per worktree. This powers the status column in ww ls, ww sw, ww status, and ww dashboard.
# Clone a repo (one-time)
ww clone git@github.com:org/myrepo.git
# Create a worktree and cd into it
ww new auth-refactor
# Start Claude Code
claude
# In another terminal β create a second worktree
ww new payments-fix
claude
# Switch between worktrees (fzf picker with agent status)
ww sw
# Check on all agents
ww status
# Clean up when done
ww rm auth-refactorBare-clone a repo and create an initial worktree on the default branch. Required entry point.
ww clone git@github.com:org/repo.git
ww clone git@github.com:org/repo.git myrepo # custom name
ww clone git@github.com:org/repo.git --force # re-clone from scratchCreate a new worktree with a new branch, an existing branch, or a GitHub PR.
ww new feature/auth # create worktree
ww new feature/auth -b develop # fork from specific branch
ww new -e existing-branch # use existing branch
ww new -e # pick from remote branches (fzf)
ww new --pr 123 # checkout PR #123
ww new https://github.com/org/repo/pull/123 # checkout a PR by URL
ww new feature/auth -r myrepo # target a specific repo
ww new feature/auth # auto-cd via shell integration (tmux-aware)From the tmux picker, Ctrl-E opens the same existing-branch flow from cached refs first, then refreshes remote branches in the background so large repos feel instant to open.
| Flag | Description |
|---|---|
-b, --base |
Base branch to fork from |
-r, --repo |
Target repo by name |
-e, --existing |
Use an existing branch (or pick from fzf if no branch given) |
--pr |
GitHub PR number or URL |
--no-fetch |
Skip fetching from remote |
--cd |
Print only the path (for scripting) |
Smart switch-or-create. If a worktree exists for the branch, switch to it. If the branch exists on the remote, create a worktree for it. Otherwise, create a new branch and worktree. Merged worktrees show a [merged] indicator in ww ls and the tmux picker. When gh is installed, willow also marks branches whose latest PR was merged on GitHub via squash/rebase, even if the branch tip is not an ancestor of the base branch. The tmux picker uses cached GitHub merge results on open so slow PR lookups don't block rendering.
ww checkout auth-refactor # switch if exists, create if not
ww checkout --pr 123 # checkout PR #123
ww checkout https://github.com/org/repo/pull/123 # checkout a PR by URL
ww checkout brand-new-feature # creates new branch + worktree
ww checkout brand-new -b develop # new branch from develop
ww checkout auth-refactor # auto-cd via shell integration (tmux-aware)| Flag | Description |
|---|---|
-r, --repo |
Target repo by name |
-b, --base |
Base branch (only when creating a new branch) |
--pr |
GitHub PR number or URL |
--no-fetch |
Skip fetching from remote |
--cd |
Print only the path (for scripting) |
Create stacked branches with --base:
ww new feature-a -b main # start a stack
ww new feature-b -b feature-a # stack on top
ww new feature-c -b feature-b # third layerStacked branches are shown as a tree in ww ls and the tmux picker. Parent relationships are tracked in branches.json per repo.
Show CI, review, and merge status for every PR in a stack at a glance. Fetches all PR data in a single gh pr list call.
ww stack status # current repo
ww stack status -r myrepo # target a specific repo
ww stack status --json # JSON output feature-a #42 β CI β Review MERGEABLE +100 -20
ββ feature-b #43 β CI β― Review CONFLICTING +50 -10
ββ feature-c (no PR)
| Flag | Description |
|---|---|
-r, --repo |
Target repo by name |
--json |
JSON output |
Requires the GitHub CLI (gh).
Rebase stacked worktrees onto their parents in topological order.
ww sync # sync all stacks in current repo
ww sync feature-b # sync feature-b and its descendants only
ww sync --abort # abort any in-progress rebases| Flag | Description |
|---|---|
-r, --repo |
Target repo by name |
--no-fetch |
Skip fetching from remote |
--abort |
Abort in-progress rebases |
Create a GitHub PR for the current worktree. Willow derives the PR base from the stack parent when the branch is stacked, or from the repo's default base branch otherwise. It pushes the branch if needed, skips creation if an open PR already exists, and can publish the current branch's ancestor stack in order.
ww pr create # create PR for the current branch
ww pr create --draft # open a draft PR
ww pr create --stack # create missing PRs from root β current branch| Flag | Description |
|---|---|
--draft |
Create draft pull requests |
--stack |
Create missing PRs for the current branch's ancestor stack |
Requires the GitHub CLI (gh) and must be run from inside a willow-managed worktree with a clean working tree.
Switch worktrees via fzf. Shows Claude Code agent status per worktree, sorted by urgency: WAIT, unread DONE, BUSY, read DONE, IDLE, then offline.
β³ WAIT payments <willow-base>/worktrees/repo/payments
β
DONEβ api-cleanup <willow-base>/worktrees/repo/api-cleanup
π€ BUSY auth-refactor <willow-base>/worktrees/repo/auth-refactor
π‘ IDLE main <willow-base>/worktrees/repo/main
-- old-feature <willow-base>/worktrees/repo/old-feature
Remove a worktree. Without arguments, opens fzf picker with multi-select (TAB to toggle, Ctrl-A to select all).
From the tmux picker, Ctrl-D removes the selected worktree and Ctrl-X bulk-removes safe merged worktrees currently shown in the picker, skipping the active tmux session plus any merged worktrees with local changes, unpushed commits, or stacked children.
ww rm auth-refactor # direct removal
ww rm # fzf picker
ww rm auth-refactor --force # skip safety checks
ww rm auth-refactor --prune # also run git worktree prune| Flag | Description |
|---|---|
-f, --force |
Skip safety checks |
--keep-branch |
Keep the local branch |
--prune |
Run git worktree prune after |
List worktrees with status. Uses the same urgency ordering as ww sw, while keeping stacked branches together and merged worktrees at the bottom.
| Flag | Description |
|---|---|
--json |
JSON output |
--path-only |
Paths only (one per line) |
Rich view of Claude Code agent status. Shows per-session rows when multiple agents run in the same worktree, with a short session ID on each session row and unread indicators (β) for completed sessions you haven't reviewed.
| Flag | Description |
|---|---|
--json |
JSON output |
Live-refreshing TUI showing active Claude Code sessions across all repos. Includes short session IDs, diff stats, unread counts, per-session activity, and a timeline sparkline showing agent status transitions over the last 60 minutes.
ww dashboard # default 2s refresh
ww dash -i 5 # 5s refresh interval
ww dash --no-timeline # hide the timeline column| Key | Action |
|---|---|
j/k |
Navigate rows |
Enter |
Switch to tmux session |
t |
Toggle timeline column |
r |
Refresh |
q |
Quit |
Show activity log of worktree events (creates, removes, syncs).
ww log # last 20 events
ww log --branch auth-refactor # filter by branch
ww log --repo myrepo # filter by repo
ww log --since 7d # events from last 7 days
ww log -n 50 # last 50 events
ww log --json # raw JSON output| Flag | Description |
|---|---|
--branch |
Filter by branch name |
-r, --repo |
Filter by repo name |
--since |
Show events after duration (e.g. 7d, 24h) |
-n, --limit |
Max events to show (default 20) |
--json |
JSON output |
Desktop notifications fire directly from Claude Code's hook system β no daemon, no polling. Run ww cc-setup once; whenever an agent transitions from BUSY to DONE or WAIT, a macOS Notification Center alert appears within ~200ms.
Enable with "notify": {"desktop": true} in config. Set "notify": {"command": "..."} to run a custom shell command instead (it receives WILLOW_NOTIFY_TITLE and WILLOW_NOTIFY_BODY env vars). The tmux status bar widget uses a separate sound-only channel and is unaffected.
Create a worktree and launch Claude Code with a prompt. From the terminal, Claude runs interactively in the foreground. From the tmux picker (Ctrl-G), it launches in a background session.
ww dispatch "Fix the login validation bug" # auto-name branch
ww dispatch "Add retry logic" --name add-retries # explicit branch name
ww dispatch "Write tests for auth" --base feature/auth # stacked on a branch
ww dispatch "Refactor payments" --repo myrepo # target specific repo| Flag | Description |
|---|---|
--name |
Worktree/branch name (default: auto-generated from prompt) |
-r, --repo |
Target repo by name |
-b, --base |
Base branch to fork from |
--no-fetch |
Skip fetching from remote |
--yolo |
Run Claude with --dangerously-skip-permissions |
One-time hook installation for Claude Code status tracking.
Check your willow setup for common issues. Verifies git version, optional tools (gh, tmux), Claude Code hooks, willow directories, stale sessions, and config validity. Flags unmarked legacy willow hooks left over from older releases.
ww doctor # report issues only
ww doctor --fix # prompt to remove legacy willow hooks from ~/.claude/settings.jsonView, edit, and initialize willow configuration.
ww config show # show merged config with sources
ww config show --json # raw JSON output
ww config edit # open global config in $EDITOR
ww config edit --local # open local (per-repo) config
ww config init # create default global config
ww config init --local # create default local configThe global baseDir setting controls <willow-base> for the whole machine. It is global-only, resolved before repo-local config, and can be overridden at runtime with WILLOW_BASE_DIR.
Move willow's base directory to a new path, repair Git worktree metadata, and persist the new global baseDir.
ww migrate-base ~/code/evergreen/worktrees/willow
ww migrate-base ~/code/evergreen/worktrees/willow --dry-run
ww migrate-base ~/code/evergreen/worktrees/willow --yesPrint shell integration script.
| Flag | Description |
|---|---|
--tab-title |
Include terminal tab title hook (sets tab to repo/branch) |
After running ww cc-setup, Claude Code automatically reports its state:
| Icon | Status | Meaning |
|---|---|---|
| π€ | BUSY |
Agent is actively working |
| β | DONE |
Agent finished its turn |
| β³ | WAIT |
Agent is waiting for user input |
| π‘ | IDLE |
Agent session ended |
-- |
No activity detected |
Status appears in ww ls, ww sw, ww status, and ww dashboard. Stale BUSY/WAIT status (>2 min) automatically degrades to IDLE. Completed sessions stay DONE until the session ends. Completed sessions show a β unread indicator until you switch to that worktree via ww sw.
Config merges two tiers (local wins):
| Priority | Path | Scope |
|---|---|---|
| 1 | ~/.config/willow/config.json |
Global defaults |
| 2 | <willow-base>/repos/<repo>.git/willow.json |
Per-repo |
Willow can collect anonymous error telemetry via Sentry after you opt in. This includes system errors and panic reports, plus basic context like the failing command and elapsed time. No repo contents, branch names, file paths, or personally identifiable information is sent. Each machine is identified by a hashed hostname only.
Opt out:
# Environment variable
export WILLOW_TELEMETRY=off
# Or in config (persistent)
# ~/.config/willow/config.json
{ "telemetry": false }See the configuration docs for all options.
Use --tab-title to automatically set your terminal tab to the worktree name:
eval "$(willow shell-init --tab-title)"Each tab shows repo/branch (e.g. myrepo/auth-refactor) when inside a willow worktree.
Recommended Ghostty layout per worktree:
βββββββββββββββββββββββββββββββββββββββ
β Tab: myrepo/auth-refactor β
ββββββββββββββββββββ¬βββββββββββββββββββ€
β claude β claude β
β (agent 1) β (agent 2) β
ββββββββββββββββββββ΄βββββββββββββββββββ€
β shell (git diff, tests, etc.) β
βββββββββββββββββββββββββββββββββββββββ
# Build
go build -o bin/willow ./cmd/willow
# Test
go test ./...Requires Go 1.26+. fzf is bundled into the binary β no external fzf install needed.
The docs site is built with Next.js using MDX.
cd website
pnpm install
pnpm dev # localhost:3000
pnpm build # production buildDeployed automatically to GitHub Pages on push to main when website/ changes.
Releases are automated via GoReleaser and GitHub Actions.







{ "baseDir": "~/code/willow", "baseBranch": "main", "branchPrefix": "alice", "postCheckoutHook": ".husky/post-checkout", "setup": ["npm install"], "teardown": [], "defaults": { "fetch": true, "autoSetupRemote": true }, "tmux": { "layout": ["split-window -h", "select-layout even-horizontal"], "panes": [ { "command": "cd website" }, { "command": "cd website" } ] }, "telemetry": true }