Configuration

Config file locations

Willow resolves its base directory with this precedence:

  1. WILLOW_BASE_DIR
  2. global config baseDir
  3. default ~/.willow

Other config merges two tiers (local wins):

PriorityPathScope
1 (highest)<willow-base>/repos/<repo>.git/willow.jsonPer-repo, local only
2~/.config/willow/config.jsonGlobal defaults

The local config lives inside the bare repo directory, so it's private to your machine. Global config provides machine-wide defaults for all repos. baseDir is global-only so willow can resolve <willow-base> before it tries to load repo-local config.

<willow-base> defaults to ~/.willow. Move an existing setup with ww migrate-base &lt;path&gt;.

Config schema

{
  "baseDir": "~/code/willow",
  "baseBranch": "main",
  "branchPrefix": "alice",
  "postCheckoutHook": ".husky/post-checkout",
  "setup": ["npm install", "cp .env.example .env"],
  "teardown": [],
  "defaults": {
    "fetch": true,
    "autoSetupRemote": true
  },
  "tmux": {
    "notification": true,
    "notifyCommand": "afplay /System/Library/Sounds/Glass.aiff",
    "layout": ["split-window -h"],
    "panes": [
      {},
      { "command": "dev sync --only install_system_deps" }
    ]
  },
  "telemetry": true
}

Fields

FieldTypeDescription
baseDirstringGlobal-only willow base directory. Also overridable with WILLOW_BASE_DIR
baseBranchstringDefault branch to fork new worktrees from
branchPrefixstringPrefix for new branch names (e.g. alicealice/feature-auth)
postCheckoutHookstringScript to run after creating a worktree
setupstring[]Commands to run after creating a worktree (e.g. install deps)
teardownstring[]Commands to run before removing a worktree
defaults.fetchbooleanWhether to fetch before creating a worktree
defaults.autoSetupRemotebooleanAuto-configure remote tracking for new branches
tmux.notificationbooleanPlay sound on BUSY→DONE transitions (default: true)
tmux.notifyCommandstringCommand to run for notifications (default: afplay Glass.aiff)
tmux.layoutstring[]Raw tmux subcommands to run after session creation (e.g. ["split-window -h", "select-layout even-horizontal"])
tmux.panesPaneConfig[]Per-pane commands, indexed by pane order. Pane 0 is the initial pane, pane 1 is the first split, etc. Each entry: { "command": "..." }
telemetrybooleanEnable/disable anonymous usage telemetry (default: true). Also controllable via WILLOW_TELEMETRY=off env var

Directory structure

<willow-base>/
├── repos/                       # Bare clones
│   └── myrepo.git/
│       └── willow.json          # Per-repo config
├── worktrees/                   # All worktrees, grouped by repo
│   └── myrepo/
│       ├── main/
│       ├── auth-refactor/
│       └── payments/
└── status/                      # Claude Code agent status
    └── myrepo/
        ├── main.json
        └── auth-refactor.json

<willow-base>/repos/

Contains bare git clones. Each repo is cloned once with ww clone and shared across all worktrees. Per-repo config (willow.json) lives here.

<willow-base>/worktrees/

Each worktree is a fully isolated directory with its own working copy. Grouped by repo name — <willow-base>/worktrees/<repo>/<branch>/.

<willow-base>/status/

Created by ww cc-setup. Contains JSON files with Claude Code agent status for each worktree.

// <willow-base>/status/myrepo/auth-refactor.json
{
  "status": "BUSY",
  "timestamp": "2024-01-15T10:30:00Z",
  "worktree": "auth-refactor"
}

Claude Code hooks

ww cc-setup registers the hidden willow hook subcommand in ~/.claude/settings.json. When Claude Code fires a hook event, it invokes the willow binary directly — there is no intermediate shell script or background daemon.