| name | worktree |
| description | Creates new git worktree with symlinked/copied assets and independent environment setup. Triggers on keywords: worktree, create worktree, new worktree, git worktree |
| project-agnostic | false |
| allowed-tools | ["Bash","Read","Write","Glob","Grep","Task","Skill"] |
Create New Worktree
Creates a new git worktree with:
- Shared assets (symlinked or copied)
- Independent environment per project type
- Proper
.envrc configuration for direnv
Arguments
worktree_name (required): Name for the worktree and branch
assets_path (optional): Path to external assets directory to symlink from
Cookbook
Setup guide: cookbook/setup.md -- guided walkthrough for creating .worktree.yml (read when no config exists or user requests setup help)
Parallelization
CRITICAL: Use Task tool with model: haiku for parallel execution when:
- Setting up assets in multiple directories
- Installing dependencies in multiple environments
- Any operation that can run independently
Spawn separate haiku agents for each independent task to maximize parallelization.
Example - parallel environment setup:
Task(model: haiku, prompt: "Setup Python venv in /path/to/backend")
Task(model: haiku, prompt: "Run npm install in /path/to/frontend")
Task(model: haiku, prompt: "Create symlinks for assets in /path/to/data")
Send all independent Task calls in a SINGLE message to run them concurrently.
Pre-Flight Checks
-
Verify git repo root:
git rev-parse --show-toplevel
-
Validate worktree name:
- Must be provided
- No spaces or special chars (except
- and _)
-
Check for existing worktree/branch:
- If worktree exists: STOP and report
- If branch exists: Ask user to confirm reuse
-
Check for .worktree.yml:
REPO_ROOT=$(git rev-parse --show-toplevel)
find "$REPO_ROOT" -maxdepth 1 -name ".worktree.yml" -type f | head -1
- If
.worktree.yml exists: continue to Asset Discovery (Step 4) normally
- If
.worktree.yml does NOT exist: prompt the user via AskUserQuestion:
No .worktree.yml found in your project. This file configures how worktrees set up assets (symlinks, copies) and environments. Without it, the skill infers configuration from directory structure and .gitignore.
Would you like to create a .worktree.yml now? I can guide you through the setup. (You can skip this and the skill will use inference instead.)
- If user says yes: read
cookbook/setup.md and follow the guided creation flow. After creating the config, continue to Step 4 (Asset Discovery) using the new config.
- If user says no or skips: continue to Step 4 (Asset Discovery) with inference mode.
Asset Discovery
When assets_path is provided
Use the provided path directly. Mirror its structure into the worktree via symlinks.
When assets_path is NOT provided (inference mode)
Step 1: Check for sibling assets directory
Look for directories matching <repo-name>-assets pattern:
REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
REPO_PARENT=$(dirname "$(git rev-parse --show-toplevel)")
ASSETS_CANDIDATES=(
"$REPO_PARENT/${REPO_NAME}-assets"
"$REPO_PARENT/assets"
"$REPO_PARENT/.${REPO_NAME}-assets"
)
If found, use it as assets_path.
Step 2: Analyze .gitignore for local assets
If no sibling assets directory found, parse .gitignore to identify:
git ls-files --others --ignored --exclude-standard --directory
Filter for:
- Directories containing data files (
inputs/, data/, fixtures/)
- Environment files (
.env, .env.local, *.env)
- Config files that vary per environment (
config.local.*)
- Large binary directories (
models/, weights/, cache/)
Step 3: Classify assets
For each discovered asset, classify:
| Asset Type | Action | Reason |
|---|
.env files | Copy | Secrets, may need per-worktree customization |
inputs/, data/ dirs | Symlink | Large, read-only shared data |
config.*.local | Copy | May need per-worktree customization |
| Binary/model dirs | Symlink | Large, read-only |
Execution Flow
CRITICAL: AUTONOMOUS EXECUTION REQUIRED
This command MUST execute ALL steps (1-8) in sequence WITHOUT stopping for user input. After the branch skill invocation completes in Step 2, IMMEDIATELY continue to Step 3. Do NOT wait for user confirmation between steps.
Execution pattern:
- Generate unique name
- Invoke
Skill(skill="branch", args="...") -> when it completes, CONTINUE IMMEDIATELY
- Create worktree -> CONTINUE
- Setup assets -> CONTINUE
- Setup environment -> CONTINUE
- Enable direnv -> CONTINUE
- Commit changes -> CONTINUE
- Display summary -> DONE
Execution Steps
Step 1: Generate unique name
UUID_PREFIX=$(uuidgen | cut -c1-6 | tr 'A-Z' 'a-z')
FULL_NAME="$UUID_PREFIX-<worktree_name>"
This ensures branch name and worktree directory match exactly.
Step 2: Create branch with spec directory
Invoke the branch skill with the full name:
Skill(skill="branch", args="$FULL_NAME")
Creates AND COMMITS (CRITICAL):
- Branch:
$FULL_NAME (e.g., a1b2c3-feature-name)
- Spec dir:
specs/<YYYY>/<MM>/$FULL_NAME/
- Backlog:
000-backlog.md
Why commit is critical: If spec dir is not committed before git worktree add, the files won't exist in the new worktree (worktree is created from the branch's committed state).
AFTER branch COMPLETES: Do NOT stop. Do NOT show todos. IMMEDIATELY proceed to Step 3.
Step 3: Create worktree
git worktree add "trees/$FULL_NAME" "$FULL_NAME"
Branch and worktree directory now have the same name.
Step 4: Setup assets based on .worktree.yml
4a. Discover all .worktree.yml files:
find "$WORKTREE_PATH" -name ".worktree.yml" -type f
4b. For each config file, process assets with correct path resolution:
for CONFIG_FILE in $(find "$WORKTREE_PATH" -name ".worktree.yml"); do
CONFIG_DIR=$(dirname "$CONFIG_FILE")
CONFIG_RELATIVE=$(realpath --relative-to="$WORKTREE_PATH" "$CONFIG_DIR")
SOURCE_VALUE=$(yq '.assets.source' "$CONFIG_FILE")
if [[ -n "$SOURCE_VALUE" && "$SOURCE_VALUE" != "null" ]]; then
ASSETS_SOURCE=$(cd "$CONFIG_DIR" && realpath "$SOURCE_VALUE")
else
ASSETS_SOURCE="$INFERRED_ASSETS_PATH/$CONFIG_RELATIVE"
fi
for ASSET in $(yq '.assets.symlink[]' "$CONFIG_FILE"); do
SOURCE="$ASSETS_SOURCE/$ASSET"
TARGET="$CONFIG_DIR/$ASSET"
rm -rf "$TARGET"
ln -sf "$SOURCE" "$TARGET"
done
for ASSET in $(yq '.assets.copy[]' "$CONFIG_FILE"); do
SOURCE="$ASSETS_SOURCE/$ASSET"
TARGET="$CONFIG_DIR/$ASSET"
[[ -f "$SOURCE" ]] && cp "$SOURCE" "$TARGET"
done
if ! REPO_ROOT=$(git -C "$WORKTREE_PATH" rev-parse --show-toplevel); then
REPO_ROOT="$WORKTREE_PATH"
fi
MAIN_WORKTREE=$(git -C "$WORKTREE_PATH" worktree list --porcelain | head -1 | sed 's/^worktree //')
[[ -n "$MAIN_WORKTREE" ]] && REPO_ROOT="$MAIN_WORKTREE"
ROOT_SYMLINKS=$(yq '.root_symlinks[]' "$CONFIG_FILE" || true)
for ASSET in $ROOT_SYMLINKS; do
[[ -z "$ASSET" || "$ASSET" == "null" ]] && continue
SOURCE="$REPO_ROOT/$ASSET"
TARGET="$WORKTREE_PATH/$ASSET"
if [[ -e "$SOURCE" ]]; then
rm -rf "$TARGET"
ln -sf "$SOURCE" "$TARGET"
else
echo "WARN: root_symlink source not found: $SOURCE"
fi
done
done
If action = COPY:
cp -r "$SOURCE_PATH" "$WORKTREE_PATH/$RELATIVE_PATH"
If action = SYMLINK:
rm -rf "$WORKTREE_PATH/$RELATIVE_PATH"
ln -sf "$ABSOLUTE_SOURCE_PATH" "$WORKTREE_PATH/$RELATIVE_PATH"
Important: Symlinked paths that replace tracked placeholder files will show as deleted in git status. This is expected and should NOT be committed.
COMMON BUG: Do NOT resolve assets.source from repo root or CWD. Always cd to the config file's directory first.
Step 5: Detect and setup environment
PARALLELIZE: When multiple environments are configured, spawn separate haiku agents for each.
Detect project type by presence of:
| File | Project Type |
|---|
pyproject.toml, requirements.txt | Python |
package.json | Node.js |
Cargo.toml | Rust |
go.mod | Go |
Gemfile | Ruby |
For each project root (may be multiple in monorepo) - RUN IN PARALLEL:
# Send ALL environment setup tasks in a SINGLE message:
Task(model: haiku, prompt: "Setup Python venv at <path1>: create .venv, install deps")
Task(model: haiku, prompt: "Setup Node at <path2>: npm install")
Task(model: haiku, prompt: "Setup Python venv at <path3>: create .venv, install deps")
Python projects
-
Create .envrc:
dotenv_if_exists .env
source .venv/bin/activate
-
Symlink .env to worktree root:
ln -sf <relative_path_to_root>/.env .env
-
Create venv and install:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
[ -f requirements.dev.txt ] && pip install -r requirements.dev.txt
[ -f pyproject.toml ] && pip install -e .
Node.js projects
-
Create .envrc:
dotenv_if_exists .env
PATH_add node_modules/.bin
-
Install dependencies:
npm install
Other project types
Detect and configure accordingly. Prioritize:
- Linking environment files
- Installing dependencies
- Setting up direnv
Step 6: Enable direnv
For each configured directory:
direnv allow
If direnv not installed, warn but continue.
Step 7: Commit setup changes
CRITICAL: Commit all worktree setup changes to leave a clean state.
cd "$WORKTREE_PATH"
git add -A
git status --short
if ! git diff --cached --quiet; then
git commit -m "chore(worktree): setup $WORKTREE_NAME
- Configure asset symlinks
- Setup development environment
- Create .envrc for direnv"
fi
What gets committed:
- New files:
.envrc, copied assets
- Deleted placeholders: files replaced by symlinks show as deleted (expected)
What stays untracked (gitignored):
.venv/, node_modules/, .env (secrets)
Step 8: Summary
Display:
Worktree created:
Path: trees/<UUID>-<worktree_name>
Branch: <worktree_name>
Spec: specs/<YYYY>/<MM>/<worktree_name>/
Assets configured:
[COPY] .env
[SYMLINK] data/inputs -> /abs/path/to/assets/data/inputs
[SYMLINK] models/ -> /abs/path/to/assets/models/
Environments setup:
- ./app (Python, venv created)
- ./frontend (Node.js, npm installed)
Error Handling
| Condition | Action |
|---|
| Worktree exists | STOP, report path |
| Branch exists | Ask to reuse or abort |
| Assets path not found | STOP if explicit, warn if inferred |
| venv creation fails | Warn, continue with other dirs |
| Dependency install fails | Warn, continue |
| direnv not installed | Warn, continue |
| Symlink target missing | Warn, skip that asset |
Project Configuration (Optional)
Projects can define .worktree.yml at repo root or in subdirectories:
assets:
source: ../my-project-assets
symlink:
- data/inputs
- models/
copy:
- .env
- config.local.yml
environments:
- path: backend
type: python
- path: frontend
type: node
skip_branch_command: false
root_symlinks:
- .specs
When .worktree.yml exists, use its configuration instead of inference.
Path Resolution Rules (CRITICAL)
All paths in .worktree.yml are resolved relative to the config file's directory, NOT the current working directory or repo root.
When processing .worktree.yml:
-
Determine config directory:
CONFIG_DIR=$(dirname "$CONFIG_FILE")
-
Resolve assets.source:
ASSETS_SOURCE=$(cd "$CONFIG_DIR" && realpath "$SOURCE_VALUE")
-
Resolve symlink/copy paths:
- Source:
$ASSETS_SOURCE/<path>
- Target:
$WORKTREE_PATH/$CONFIG_RELATIVE_PATH/<path>
Example - Monorepo with subproject config
repo/
|-- .worktree.yml # Root config (if any)
`-- services/
`-- api/
`-- .worktree.yml # Subproject config
If services/api/.worktree.yml has:
assets:
source: ../../../repo-assets/services/api
copy:
- .env
Resolution:
CONFIG_DIR="services/api"
ASSETS_SOURCE=$(cd "services/api" && realpath "../../../repo-assets/services/api")
Common Bug: Using CWD instead of CONFIG_DIR
WRONG:
ASSETS_SOURCE=$(realpath "$SOURCE_VALUE")
CORRECT:
ASSETS_SOURCE=$(cd "$CONFIG_DIR" && realpath "$SOURCE_VALUE")