From a50b5bdedd70d04a753699bd49de7dea4941e927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Fejfar?= Date: Thu, 5 Feb 2026 12:24:50 +0100 Subject: [PATCH 1/2] Add worktree skill with stale detection Add skill for creating git worktrees with: - create-worktree.sh: Creates worktrees with env file copying and yarn install - detect-stale-worktrees.sh: Finds worktrees without remote branches - purge-worktree.sh: Removes worktree and local branch Instructions guide Claude to check for stale worktrees after creating new ones and offer to clean them up. Co-Authored-By: Claude Opus 4.5 --- plugins/developer/skills/worktree/SKILL.md | 70 +++++++++++++++++++ .../worktree/scripts/create-worktree.sh | 56 +++++++++++++++ .../scripts/detect-stale-worktrees.sh | 67 ++++++++++++++++++ .../skills/worktree/scripts/purge-worktree.sh | 44 ++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 plugins/developer/skills/worktree/SKILL.md create mode 100755 plugins/developer/skills/worktree/scripts/create-worktree.sh create mode 100755 plugins/developer/skills/worktree/scripts/detect-stale-worktrees.sh create mode 100755 plugins/developer/skills/worktree/scripts/purge-worktree.sh diff --git a/plugins/developer/skills/worktree/SKILL.md b/plugins/developer/skills/worktree/SKILL.md new file mode 100644 index 0000000..ceda5d1 --- /dev/null +++ b/plugins/developer/skills/worktree/SKILL.md @@ -0,0 +1,70 @@ +--- +name: worktree +description: Create and manage git worktrees for parallel development. Use when user asks to create a worktree, work in parallel on multiple branches, set up isolated development environments, or clean up stale worktrees. Triggers on phrases like "create worktree", "new worktree", "worktree for branch", "/worktree", "clean worktrees", "stale worktrees". +--- + +# Git Worktree Management + +Create worktrees in `/worktrees//` with proper env file setup. + +`SKILL_DIR` = directory containing this SKILL.md + +## Creating a Worktree + +```bash +"$SKILL_DIR/scripts/create-worktree.sh" [base-branch] +``` + +- Creates worktree for existing branch, or auto-creates from `origin/main` if branch doesn't exist +- Copies env files from main repo +- Runs `yarn install` + +## After Creating a Worktree + +Always check for stale worktrees and offer to clean them up: + +```bash +"$SKILL_DIR/scripts/detect-stale-worktrees.sh" +``` + +If stale worktrees are found, ask user if they want to remove them. For each one they confirm: + +```bash +"$SKILL_DIR/scripts/purge-worktree.sh" "" +``` + +## Scripts + +### create-worktree.sh + +```bash +"$SKILL_DIR/scripts/create-worktree.sh" feature/my-branch # Existing or new branch from origin/main +"$SKILL_DIR/scripts/create-worktree.sh" feature/new-feature develop # New branch from specific base +``` + +### detect-stale-worktrees.sh + +Finds worktrees whose branches no longer exist on remote (merged/deleted PRs). + +```bash +"$SKILL_DIR/scripts/detect-stale-worktrees.sh" # Human-readable output +"$SKILL_DIR/scripts/detect-stale-worktrees.sh" --quiet # Just paths (for scripting) +``` + +Exit code 0 = stale worktrees found, 1 = none found. + +### purge-worktree.sh + +Removes worktree and deletes local branch. + +```bash +"$SKILL_DIR/scripts/purge-worktree.sh" "/path/to/worktree" +"$SKILL_DIR/scripts/purge-worktree.sh" "/path/to/worktree" --keep-branch # Keep local branch +``` + +## Other Commands + +```bash +git worktree list # List all worktrees +git worktree prune # Clean up stale worktree refs (different from stale branches) +``` diff --git a/plugins/developer/skills/worktree/scripts/create-worktree.sh b/plugins/developer/skills/worktree/scripts/create-worktree.sh new file mode 100755 index 0000000..d5a3c75 --- /dev/null +++ b/plugins/developer/skills/worktree/scripts/create-worktree.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Configuration - set these before running +BRANCH_NAME="${1:?Usage: $0 [base-branch]}" +BASE_BRANCH="${2:-}" # Optional: base branch for creating new branches + +# Compute paths +REPO_ROOT=$(git rev-parse --show-toplevel) +REPO_NAME=$(basename "$REPO_ROOT") +REPO_PARENT=$(dirname "$REPO_ROOT") +WORKTREE_BASE="$REPO_PARENT/worktrees/$REPO_NAME" +WORKTREE_PATH="$WORKTREE_BASE/$BRANCH_NAME" + +# Check if worktree already exists +if [ -d "$WORKTREE_PATH" ]; then + echo "Error: Worktree already exists at $WORKTREE_PATH" + exit 1 +fi + +# Create worktree base directory +mkdir -p "$WORKTREE_BASE" + +# Check if branch exists (locally or remotely) +branch_exists() { + git show-ref --verify --quiet "refs/heads/$1" 2>/dev/null || \ + git show-ref --verify --quiet "refs/remotes/origin/$1" 2>/dev/null +} + +# Create the worktree +if [ -n "$BASE_BRANCH" ]; then + echo "Creating new branch '$BRANCH_NAME' from '$BASE_BRANCH'..." + git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" "$BASE_BRANCH" +elif branch_exists "$BRANCH_NAME"; then + echo "Creating worktree for existing branch '$BRANCH_NAME'..." + git worktree add "$WORKTREE_PATH" "$BRANCH_NAME" +else + echo "Branch '$BRANCH_NAME' not found. Fetching origin/main and creating new branch..." + git fetch origin main + git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" origin/main +fi + +# Copy environment files if they exist +echo "Copying environment files..." +[ -f "$REPO_ROOT/apps/kai-assistant-backend/.env.local" ] && \ + cp "$REPO_ROOT/apps/kai-assistant-backend/.env.local" "$WORKTREE_PATH/apps/kai-assistant-backend/.env.local" +[ -f "$REPO_ROOT/apps/kbc-ui/.env" ] && \ + cp "$REPO_ROOT/apps/kbc-ui/.env" "$WORKTREE_PATH/apps/kbc-ui/.env" + +# Install dependencies +echo "Installing dependencies..." +cd "$WORKTREE_PATH" && yarn install + +echo "" +echo "Worktree created successfully at: $WORKTREE_PATH" +echo "To start working: cd $WORKTREE_PATH" diff --git a/plugins/developer/skills/worktree/scripts/detect-stale-worktrees.sh b/plugins/developer/skills/worktree/scripts/detect-stale-worktrees.sh new file mode 100755 index 0000000..329a5da --- /dev/null +++ b/plugins/developer/skills/worktree/scripts/detect-stale-worktrees.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Detect worktrees without corresponding remote branches +# Usage: ./detect-stale-worktrees.sh [--quiet] +# +# Returns exit code 0 if stale worktrees found, 1 if none found +# With --quiet, only outputs paths of stale worktrees (for scripting) + +QUIET="${1:-}" +REPO_ROOT=$(git rev-parse --show-toplevel) + +# Fetch latest remote refs +git fetch --prune origin 2>/dev/null || true + +stale_count=0 +stale_worktrees=() + +# Parse worktree list +while IFS= read -r line; do + # Extract path and branch info + path=$(echo "$line" | awk '{print $1}') + branch_info=$(echo "$line" | grep -oP '\[.*\]' || echo "") + + # Skip if no branch (detached HEAD) + if [[ -z "$branch_info" || "$branch_info" == *"detached"* ]]; then + continue + fi + + # Extract branch name from [branch] + branch=$(echo "$branch_info" | sed 's/\[\(.*\)\]/\1/') + + # Skip main repo directory + if [[ "$path" == "$REPO_ROOT" ]]; then + continue + fi + + # Check if remote branch exists + if ! git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then + stale_worktrees+=("$path|$branch") + stale_count=$((stale_count + 1)) + fi +done < <(git worktree list) + +# Output results +if [[ $stale_count -eq 0 ]]; then + [[ "$QUIET" != "--quiet" ]] && echo "No stale worktrees found." + exit 1 +fi + +if [[ "$QUIET" == "--quiet" ]]; then + for entry in "${stale_worktrees[@]}"; do + echo "${entry%%|*}" + done +else + echo "Found $stale_count stale worktree(s) without remote branches:" + echo "" + for entry in "${stale_worktrees[@]}"; do + path="${entry%%|*}" + branch="${entry##*|}" + echo " Path: $path" + echo " Branch: $branch" + echo "" + done +fi + +exit 0 diff --git a/plugins/developer/skills/worktree/scripts/purge-worktree.sh b/plugins/developer/skills/worktree/scripts/purge-worktree.sh new file mode 100755 index 0000000..2bb4063 --- /dev/null +++ b/plugins/developer/skills/worktree/scripts/purge-worktree.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Purge a worktree and optionally its local branch +# Usage: ./purge-worktree.sh [--keep-branch] +# +# Options: +# --keep-branch Keep the local branch after removing the worktree + +WORKTREE_PATH="${1:?Usage: $0 [--keep-branch]}" +KEEP_BRANCH="${2:-}" + +# Validate worktree exists +if ! git worktree list | grep -q "^$WORKTREE_PATH "; then + echo "Error: '$WORKTREE_PATH' is not a valid worktree" + exit 1 +fi + +# Get branch name from worktree +BRANCH_INFO=$(git worktree list | grep "^$WORKTREE_PATH " | grep -oP '\[.*\]' || echo "") +if [[ -z "$BRANCH_INFO" || "$BRANCH_INFO" == *"detached"* ]]; then + BRANCH="" +else + BRANCH=$(echo "$BRANCH_INFO" | sed 's/\[\(.*\)\]/\1/') +fi + +echo "Removing worktree: $WORKTREE_PATH" +if [[ -n "$BRANCH" ]]; then + echo "Associated branch: $BRANCH" +fi + +# Remove the worktree +git worktree remove "$WORKTREE_PATH" --force + +# Delete the local branch if requested and it exists +if [[ -n "$BRANCH" && "$KEEP_BRANCH" != "--keep-branch" ]]; then + if git show-ref --verify --quiet "refs/heads/$BRANCH" 2>/dev/null; then + echo "Deleting local branch: $BRANCH" + git branch -D "$BRANCH" + fi +fi + +echo "" +echo "Worktree purged successfully." From c732e81abe2dd0cb1969114652104182d6cd23d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Fejfar?= Date: Thu, 5 Feb 2026 13:02:04 +0100 Subject: [PATCH 2/2] Make worktree skill generic with optional init hook Replace hardcoded env file copying and yarn install with support for optional bin/worktree-init.sh script. The init script runs inside the new worktree with $1 set to the source repo root, allowing repos to customize their worktree setup. Co-Authored-By: Claude Opus 4.5 --- plugins/developer/skills/worktree/SKILL.md | 23 ++++++++++++++++--- .../worktree/scripts/create-worktree.sh | 20 ++++++++-------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/plugins/developer/skills/worktree/SKILL.md b/plugins/developer/skills/worktree/SKILL.md index ceda5d1..dc61165 100644 --- a/plugins/developer/skills/worktree/SKILL.md +++ b/plugins/developer/skills/worktree/SKILL.md @@ -5,7 +5,7 @@ description: Create and manage git worktrees for parallel development. Use when # Git Worktree Management -Create worktrees in `/worktrees//` with proper env file setup. +Create worktrees in `/worktrees//`. `SKILL_DIR` = directory containing this SKILL.md @@ -16,8 +16,25 @@ Create worktrees in `/worktrees//` with pro ``` - Creates worktree for existing branch, or auto-creates from `origin/main` if branch doesn't exist -- Copies env files from main repo -- Runs `yarn install` +- Runs `bin/worktree-init.sh` if present (see below) + +## Custom Initialization + +Create `bin/worktree-init.sh` in your repo to customize worktree setup: + +```bash +#!/usr/bin/env bash +# bin/worktree-init.sh - runs in new worktree, $1 = source repo root +SOURCE_REPO="$1" + +# Example: copy env files +cp "$SOURCE_REPO/.env" ./.env + +# Example: install dependencies +yarn install +``` + +The script runs inside the new worktree directory with `$1` set to the source repo root. ## After Creating a Worktree diff --git a/plugins/developer/skills/worktree/scripts/create-worktree.sh b/plugins/developer/skills/worktree/scripts/create-worktree.sh index d5a3c75..b56236b 100755 --- a/plugins/developer/skills/worktree/scripts/create-worktree.sh +++ b/plugins/developer/skills/worktree/scripts/create-worktree.sh @@ -40,16 +40,16 @@ else git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" origin/main fi -# Copy environment files if they exist -echo "Copying environment files..." -[ -f "$REPO_ROOT/apps/kai-assistant-backend/.env.local" ] && \ - cp "$REPO_ROOT/apps/kai-assistant-backend/.env.local" "$WORKTREE_PATH/apps/kai-assistant-backend/.env.local" -[ -f "$REPO_ROOT/apps/kbc-ui/.env" ] && \ - cp "$REPO_ROOT/apps/kbc-ui/.env" "$WORKTREE_PATH/apps/kbc-ui/.env" - -# Install dependencies -echo "Installing dependencies..." -cd "$WORKTREE_PATH" && yarn install +# Run repo-specific initialization if available +INIT_SCRIPT="$REPO_ROOT/bin/worktree-init.sh" +if [ -x "$INIT_SCRIPT" ]; then + echo "Running worktree initialization script..." + cd "$WORKTREE_PATH" && "$INIT_SCRIPT" "$REPO_ROOT" +else + echo "" + echo "Tip: Create bin/worktree-init.sh (chmod +x) to customize worktree setup." + echo " It runs in the new worktree with \$1=source_repo_root" +fi echo "" echo "Worktree created successfully at: $WORKTREE_PATH"