From ac-git
Creates git worktree with symlinked/copied assets, independent per-project environments (venv/npm), and direnv .envrc. Uses .worktree.yml or infers config.
npx claudepluginhub waterplanai/agentic-config --plugin ac-gitThis skill is limited to using the following tools:
Creates a new git worktree with:
Creates isolated git worktrees with smart directory selection, .gitignore safety verification, auto-dependency installs, and baseline tests for parallel feature development.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Creates a new git worktree with:
.envrc configuration for direnvworktree_name (required): Name for the worktree and branchassets_path (optional): Path to external assets directory to symlink fromSetup guide: cookbook/setup.md -- guided walkthrough for creating .worktree.yml (read when no config exists or user requests setup help)
CRITICAL: Use Task tool with model: haiku for parallel execution when:
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.
Verify git repo root:
git rev-parse --show-toplevel
Validate worktree name:
- and _)Check for existing worktree/branch:
Check for .worktree.yml:
REPO_ROOT=$(git rev-parse --show-toplevel)
find "$REPO_ROOT" -maxdepth 1 -name ".worktree.yml" -type f | head -1
.worktree.yml exists: continue to Asset Discovery (Step 4) normally.worktree.yml does NOT exist: prompt the user via AskUserQuestion:
No
.worktree.ymlfound 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.ymlnow? I can guide you through the setup. (You can skip this and the skill will use inference instead.)
cookbook/setup.md and follow the guided creation flow. After creating the config, continue to Step 4 (Asset Discovery) using the new config.assets_path is providedUse the provided path directly. Mirror its structure into the worktree via symlinks.
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:
# Find gitignored directories/files that exist locally
git ls-files --others --ignored --exclude-standard --directory
Filter for:
inputs/, data/, fixtures/).env, .env.local, *.env)config.local.*)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 |
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:
Skill(skill="branch", args="...") -> when it completes, CONTINUE IMMEDIATELYUUID_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.
Invoke the branch skill with the full name:
Skill(skill="branch", args="$FULL_NAME")
Creates AND COMMITS (CRITICAL):
$FULL_NAME (e.g., a1b2c3-feature-name)specs/<YYYY>/<MM>/$FULL_NAME/000-backlog.mdWhy 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.
git worktree add "trees/$FULL_NAME" "$FULL_NAME"
Branch and worktree directory now have the same name.
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")
# Read source from config (use yq or grep+sed)
SOURCE_VALUE=$(yq '.assets.source' "$CONFIG_FILE")
# CRITICAL: Resolve source relative to CONFIG_DIR, not CWD
if [[ -n "$SOURCE_VALUE" && "$SOURCE_VALUE" != "null" ]]; then
ASSETS_SOURCE=$(cd "$CONFIG_DIR" && realpath "$SOURCE_VALUE")
else
# Fallback to inference
ASSETS_SOURCE="$INFERRED_ASSETS_PATH/$CONFIG_RELATIVE"
fi
# Process symlinks
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
# Process copies
for ASSET in $(yq '.assets.copy[]' "$CONFIG_FILE"); do
SOURCE="$ASSETS_SOURCE/$ASSET"
TARGET="$CONFIG_DIR/$ASSET"
[[ -f "$SOURCE" ]] && cp "$SOURCE" "$TARGET"
done
# Process root_symlinks (gitignored dirs symlinked from repo root)
REPO_ROOT=$(git -C "$WORKTREE_PATH" rev-parse --show-toplevel 2>/dev/null)
# For worktrees, resolve to the main repo root
MAIN_WORKTREE=$(git -C "$WORKTREE_PATH" worktree list --porcelain | head -1 | sed 's/^worktree //')
[[ -n "$MAIN_WORKTREE" ]] && REPO_ROOT="$MAIN_WORKTREE"
for ASSET in $(yq '.root_symlinks[]' "$CONFIG_FILE" 2>/dev/null); 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:
# Remove placeholder if exists (may show as deleted in git status - expected)
rm -rf "$WORKTREE_PATH/$RELATIVE_PATH"
# Create symlink with absolute path to source
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.
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")
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 .
Create .envrc:
dotenv_if_exists .env
PATH_add node_modules/.bin
Install dependencies:
npm install # or yarn/pnpm based on lockfile
Detect and configure accordingly. Prioritize:
For each configured directory:
direnv allow
If direnv not installed, warn but continue.
CRITICAL: Commit all worktree setup changes to leave a clean state.
cd "$WORKTREE_PATH"
git add -A
git status --short
# Only commit if there are changes
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:
.envrc, copied assetsWhat stays untracked (gitignored):
.venv/, node_modules/, .env (secrets)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)
| 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 |
Projects can define .worktree.yml at repo root or in subdirectories:
# .worktree.yml
assets:
source: ../my-project-assets # Override default discovery
symlink:
- data/inputs
- models/
copy:
- .env
- config.local.yml
environments:
- path: backend
type: python
- path: frontend
type: node
skip_branch_command: false # Set true to skip /branch integration
# Symlink gitignored directories from repo root into worktrees
# NOT resolved from assets.source — uses main worktree root as base
root_symlinks:
- .specs # External specs git repo (shared across worktrees)
When .worktree.yml exists, use its configuration instead of inference.
All paths in .worktree.yml are resolved relative to the config file's directory, NOT the current working directory or repo root.
.worktree.yml:Determine config directory:
CONFIG_DIR=$(dirname "$CONFIG_FILE")
Resolve assets.source:
# If source is relative path
ASSETS_SOURCE=$(cd "$CONFIG_DIR" && realpath "$SOURCE_VALUE")
Resolve symlink/copy paths:
$ASSETS_SOURCE/<path>$WORKTREE_PATH/$CONFIG_RELATIVE_PATH/<path>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"
# source resolved from CONFIG_DIR:
ASSETS_SOURCE=$(cd "services/api" && realpath "../../../repo-assets/services/api")
# -> /abs/path/repo-assets/services/api
# .env source: $ASSETS_SOURCE/.env
# .env target: $WORKTREE_PATH/services/api/.env
WRONG:
# Resolves source relative to repo root - INCORRECT
ASSETS_SOURCE=$(realpath "$SOURCE_VALUE")
CORRECT:
# Resolves source relative to where .worktree.yml is located
ASSETS_SOURCE=$(cd "$CONFIG_DIR" && realpath "$SOURCE_VALUE")