Universal git worktree setup tool. Automatically provisions isolated development environments when creating git worktrees — copies env files, symlinks build directories, clones databases, and runs setup commands.
Designed to run unattended for AI coding agents, but works great for humans too.
curl -fsSL https://raw.githubusercontent.com/DefactoSoftware/werksfeer/main/install.sh | shOr manually:
curl -fsSL https://raw.githubusercontent.com/DefactoSoftware/werksfeer/main/werksfeer -o ~/.local/bin/werksfeer
chmod +x ~/.local/bin/werksfeer- Add a
.worktree.tomlto your project root (can be empty — defaults are auto-detected):
touch .worktree.toml-
Set up werksfeer for your workflow (see Setup below).
-
Create a worktree — werksfeer runs automatically:
[werksfeer] ==> Setting up worktree
[werksfeer] Copied .env
[werksfeer] Symlinked node_modules -> /path/to/main/node_modules
[werksfeer] Cloning myapp_development -> worktree_feature_branch_my_feature
[werksfeer] ==> Worktree setup complete!
When a new worktree is created, werksfeer:
- Copies env files (
.env,.envrc,.tool-versions) from the main worktree - Symlinks shared directories (
node_modules) to avoid redundant installs - Copies build directories (
_build,deps,.bundle, etc.) from the main worktree - Clones PostgreSQL databases using
CREATE DATABASE ... WITH TEMPLATEfor instant isolation - Allocates a unique port and Redis database per worktree, tracked in a registry
- Writes overrides (DB names, port, Redis URL) to
.env.local(Rails) or.envrc(Elixir) - Runs setup commands (
bin/setup,mix deps.get,pip install, etc.)
Everything is idempotent — safe to re-run.
The included post-checkout hook triggers werksfeer automatically on git worktree add. It only activates for worktree creation (not regular branch checkouts) and only when .worktree.toml exists.
# Global — all repos (note: replaces per-repo hooks):
git config --global core.hooksPath ~/.local/share/werksfeer/hooks
# Single repo:
cp ~/.local/share/werksfeer/hooks/post-checkout .git/hooks/Claude Code's WorktreeCreate hook replaces the default worktree creation, so the hook must create the worktree itself and print its path to stdout.
Add to .claude/settings.json (or .claude/settings.local.json):
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'set -e; NAME=$(cat | jq -r .name); DIR=\"$CLAUDE_PROJECT_DIR/.worktrees/$NAME\"; git worktree add \"$DIR\" --detach HEAD >&2; cd \"$DIR\" && werksfeer >&2; echo \"$DIR\"'"
}
]
}
],
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'cat | jq -r .worktree_path | xargs werksfeer --cleanup'"
}
]
}
]
}
}The WorktreeRemove hook drops the worktree's cloned databases when the session ends. Note that Claude Code does not always fire this hook reliably (e.g. when the worktree directory is already deleted before the hook runs). Use werksfeer --prune periodically to catch any missed cleanups.
Claude Desktop does not fire WorktreeCreate or WorktreeRemove hooks. Use the git hooks approach instead, and run werksfeer --prune to clean up after worktrees are removed.
Create .codex/setup.sh in your project:
#!/usr/bin/env bash
werksfeerThen select it as your Local Environment setup script in the Codex app settings.
Add to .cursor/worktrees.json:
{
"setup-worktree": [
"werksfeer"
]
}Run werksfeer after creating a worktree:
git worktree add ../my-feature feature-branch
cd ../my-feature
werksfeerIf your agent supports post-worktree hooks or setup scripts, point them at werksfeer. It is idempotent and runs fully unattended.
When a worktree is removed, --cleanup drops its cloned databases and releases its port/redis allocations. You can run this manually from inside a worktree before deleting it, or pass a path.
Most tools either lack removal hooks entirely (Cursor, Codex, Claude Desktop) or don't fire them reliably (Claude Code CLI). Use werksfeer --prune periodically to clean up orphaned databases and stale allocations from deleted worktrees — this is the most reliable approach.
# Clean up current worktree (drops DBs, releases port/redis)
werksfeer --cleanup
# Or specify a path
werksfeer --cleanup /path/to/worktree
# Prune current project — drops orphaned DBs and stale allocations
werksfeer --prune
# Prune all registered projects
werksfeer --prune-all| Type | Detected by | Symlinked | Copied | Setup command | DB pattern |
|---|---|---|---|---|---|
| Rails | Gemfile + config/database.yml |
node_modules |
.bundle, tmp/cache |
bin/setup |
{name}_development / {name}_test |
| Elixir | mix.exs |
node_modules |
_build, deps |
mix deps.get |
{name}_dev / {name}_test |
| Python | pyproject.toml / requirements.txt |
— | .venv, __pycache__ |
uv sync / pip install |
— |
| Node | package.json |
node_modules |
.next, .nuxt, etc. |
npm ci / yarn / pnpm / bun |
— |
Your project needs to read the environment variables werksfeer sets (PORT, DATABASE_NAME, TEST_DATABASE_NAME, REDIS_URL, REDIS_PORT). See docs/project-setup.md for a step-by-step guide with examples for Rails and Elixir/Phoenix.
Each worktree gets a unique port, Redis port, and Redis database number, so you can run multiple worktrees simultaneously without conflicts.
- Ports are allocated sequentially starting from the base port + 1 (e.g. 3001, 3002, ... for Rails). Ports are globally unique across all projects.
- Redis ports are allocated sequentially starting from 6380. Each worktree runs its own Redis instance.
- Redis databases are allocated per-project from 1–15 for extra isolation.
- Allocations are tracked in a registry at
~/.local/share/werksfeer/allocationsand released on--cleanupor--prune.
Your project needs to read the PORT environment variable for this to work. Examples:
Rails — Procfile.dev:
web: bin/rails server -p ${PORT:-3000}
redis: redis-server --port ${REDIS_PORT:-6379}
Elixir — config/dev.exs:
config :myapp, MyAppWeb.Endpoint,
http: [port: String.to_integer(System.get_env("PORT") || "4000")]Create .worktree.toml in your project root. All settings are optional:
[database]
# Override base database name (default: lowercase directory name)
base_name = "myapp"
# Override suffixes
dev_suffix = "_development"
test_suffix = "_test"
[port]
# Base port for the web server (default: 3000 for Rails/Node, 4000 for Elixir, 8000 for Python)
# Worktrees are allocated ports starting from base+1.
base = 3000
[redis]
# Base Redis URL (default: redis://localhost:6379 for Rails, empty for others)
# Each worktree gets a unique Redis port (starting from 6380) and database number.
url = "redis://localhost:6379"
[sync]
# Override directories to symlink (default: node_modules)
symlink = ["node_modules"]
# Override directories to copy (default: build dirs per project type)
copy_dirs = ["_build", "deps"]
# Override files to copy
copy = [".env", ".envrc"]
# Directories to skip
skip = ["tmp"]
[setup]
# Override setup command
command = "make setup"
[hooks]
# Run after setup completes
post_setup = "echo done"When worktrees are deleted, their cloned databases and port/redis allocations remain. Werksfeer can clean them up:
# Prune current project (drops orphaned DBs, releases stale allocations)
werksfeer --prune
# Prune all registered projects
werksfeer --prune-allSmart pruning compares worktree_* databases against git worktree list — only orphaned databases are dropped and only stale allocations are released.
The post-checkout hook fires on every git checkout and git worktree add. Werksfeer only activates when all three conditions are met:
- It's a branch checkout (not a file checkout)
- The previous HEAD is the null ref (new worktree, not a branch switch)
.gitis a file (we're in a worktree, not the main checkout or a fresh clone)
If .worktree.toml doesn't exist in the repo, the hook exits silently.
- bash 3.2+ (ships with macOS, Linux, WSL)
- git 2.5+ (worktree support)
- psql (optional — only needed for database cloning)
- curl (only for installation)
WERKSFEER_DEBUG=1 werksfeerMIT