Configuration
Config file locations
Willow resolves its base directory with this precedence:
WILLOW_BASE_DIR- global config
baseDir - default
~/.willow
Other config merges two tiers (local wins):
| Priority | Path | Scope |
|---|---|---|
| 1 (highest) | <willow-base>/repos/<repo>.git/willow.json | Per-repo, local only |
| 2 | ~/.config/willow/config.json | Global 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 <path>.
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
| Field | Type | Description |
|---|---|---|
baseDir | string | Global-only willow base directory. Also overridable with WILLOW_BASE_DIR |
baseBranch | string | Default branch to fork new worktrees from |
branchPrefix | string | Prefix for new branch names (e.g. alice → alice/feature-auth) |
postCheckoutHook | string | Script to run after creating a worktree |
setup | string[] | Commands to run after creating a worktree (e.g. install deps) |
teardown | string[] | Commands to run before removing a worktree |
defaults.fetch | boolean | Whether to fetch before creating a worktree |
defaults.autoSetupRemote | boolean | Auto-configure remote tracking for new branches |
tmux.notification | boolean | Play sound on BUSY→DONE transitions (default: true) |
tmux.notifyCommand | string | Command to run for notifications (default: afplay Glass.aiff) |
tmux.layout | string[] | Raw tmux subcommands to run after session creation (e.g. ["split-window -h", "select-layout even-horizontal"]) |
tmux.panes | PaneConfig[] | Per-pane commands, indexed by pane order. Pane 0 is the initial pane, pane 1 is the first split, etc. Each entry: { "command": "..." } |
telemetry | boolean | Enable/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.