Tmux Integration

Willow includes native tmux support — a worktree picker popup, live Claude output preview, status bar widget, and automatic session management. If you use tmux, this replaces the need for any separate worktree-switching plugin.

Setup

ww tmux install

This prints the tmux.conf lines to add. Copy them into your ~/.tmux.conf:

bind w run-shell -b 'tmux display-popup -E -w 90% -h 80% "/path/to/willow tmux pick --session #S"'
set -g status-right '#(/path/to/willow tmux status-bar) %l:%M %a'
set -g status-interval 3

Then reload: tmux source ~/.tmux.conf

Picker (prefix + w)

Press prefix + w to open the worktree picker in a popup.

The right panel shows a live preview of the tmux pane content (Claude Code output).

Keybindings

KeyAction
EnterSwitch to worktree (creates tmux session if needed)
Ctrl-NCreate new worktree from typed query (also accepts PR URLs)
Ctrl-BCreate new worktree stacked on a base branch (opens branch picker)
Ctrl-EBrowse existing remote branches and create a worktree
Ctrl-PBrowse open PRs and create a worktree for the selected one
Ctrl-GDispatch: create worktree from query text as prompt, launch Claude Code
Ctrl-SSync stacked worktrees (selected branch's subtree, or all)
Ctrl-DDelete selected worktree and its tmux session
EscClose picker

Dispatch

Type a prompt in the query field and press Ctrl-G to dispatch a Claude Code agent. This creates a worktree (branch auto-named from the prompt, e.g. dispatch--fix-the-login-bug), opens a tmux session, and launches claude with your prompt. You're switched to the session immediately.

PR picker

Press Ctrl-P to list open PRs via gh pr list. Each line shows the PR number, title, author, and branch. Select one to create a worktree (or switch to it if one already exists). Requires the GitHub CLI.

You can also paste a PR URL into the query field and press Ctrl-N for quick one-off access.

Existing branch picker

Press Ctrl-E to open a secondary picker showing all remote branches that don't already have worktrees. Willow fetches from origin first (unless defaults.fetch is disabled) so freshly pushed branches show up. Select one to create a worktree and switch to it immediately.

Merged worktree indicator

Worktrees whose branches have been merged into the base branch show a dim [merged] tag. These sort to the bottom of the list, making it easy to spot stale worktrees. Clean them up with Ctrl-D or ww gc --prune.

Features

  • Auto-refresh — the list reloads every 2 seconds with fresh agent status
  • Auto-navigate — opens with the cursor on your current session
  • Status colors — BUSY (green), WAIT (red), DONE (blue), IDLE (yellow)
  • Unread indicator marks completed sessions you haven't viewed
  • Merged indicator[merged] marks worktrees whose branches are merged
  • Multi-Claude sub-rows — when a worktree has multiple active Claude sessions, each is shown as an indented sub-row with its own status and tool info
  • Embedded fzf — fzf is compiled into the willow binary, no external fzf dependency needed

Status bar widget

The status bar shows worktree and active agent counts:

🌳 5 🤖 3

It also tracks state transitions — when a Claude session goes from BUSY to any other state, it triggers an audio notification (macOS Glass.aiff by default).

Configuration

{
  "tmux": {
    "notification": true,           // enable/disable sound (default: true)
    "notifyCommand": "afplay /System/Library/Sounds/Glass.aiff"  // custom command
  }
}

Set "notification": false to disable sound.

Session layout

By default, willow tmux creates a single window with one pane for each worktree session. You can customize this with tmux.layout — a list of raw tmux subcommands that run after session creation. The -t (target session) and -c (working directory) flags are auto-injected when not present.

{
  "tmux": {
    "layout": [
      "split-window -h",
      "select-layout even-horizontal"
    ]
  }
}

Any tmux subcommand works: split-window, new-window, select-layout, resize-pane, etc.

Per-pane commands

Use tmux.panes to send different commands to specific panes after the layout is set up. The array index maps to the pane index — pane 0 is the initial pane created by new-session, pane 1 is created by the first split-window, and so on. Panes without a config entry (or with an empty command) receive no command.

{
  "tmux": {
    "layout": [
      "split-window -h",
      "select-layout even-horizontal"
    ],
    "panes": [
      { "command": "cd website" },
      { "command": "cd website" }
    ]
  }
}

This creates two side-by-side panes, each starting in the website/ subdirectory.

To run a command in only one pane, leave the others empty:

{
  "tmux": {
    "layout": ["split-window -h"],
    "panes": [
      {},
      { "command": "dev sync --only install_system_deps,install_python_deps" }
    ]
  }
}

The left pane (index 0) gets no startup command. The right pane (index 1) runs dev sync.

Outside tmux (during willow new), pane commands run sequentially in the foreground after setup hooks.

Shell integration

When inside tmux, ww sw automatically switches tmux sessions instead of just cd-ing. This works out of the box after running eval "$(willow shell-init)" — no additional setup needed.

Contextww sw behavior
Outside tmuxfzf picker → cd to worktree
Inside tmuxfzf picker → create/switch tmux session

Commands reference

ww tmux pick

Interactive worktree picker. Designed to run inside a tmux popup.

ww tmux pick              # all repos
ww tmux pick -r myrepo    # filter to one repo

ww tmux list

Print formatted picker lines. Used by fzf's reload binding for auto-refresh.

ww tmux status-bar

Output tmux status-right widget string. Called every status-interval seconds.

ww tmux install

Print the tmux.conf lines to add for willow integration.