Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,199 changes: 1,199 additions & 0 deletions .agents/skills/git-worktree-ops/SKILL.md

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions .agents/skills/git-worktree-ops/cleanup-worktree.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# Clean up completed or abandoned worktrees
# Usage: cleanup-worktree.sh <worktree-path> [--delete-branch] [--force]

set -euo pipefail

usage() {
cat <<EOF
Usage: cleanup-worktree.sh <worktree-path> [options]

Options:
--delete-branch Delete the branch after removing worktree
--force Force removal even if worktree has uncommitted changes
--remote Also delete remote branch (requires --delete-branch)

Examples:
cleanup-worktree.sh ../myapp-worktrees/codex-fix-auth
cleanup-worktree.sh ../myapp-worktrees/codex-fix-auth --delete-branch
cleanup-worktree.sh ../myapp-worktrees/codex-fix-auth --delete-branch --remote --force
EOF
}

if [[ $# -lt 1 ]]; then
usage
exit 1
fi

WORKTREE_PATH="$1"
shift

DELETE_BRANCH=0
FORCE=0
DELETE_REMOTE=0

while [[ $# -gt 0 ]]; do
case "$1" in
--delete-branch)
DELETE_BRANCH=1
shift
;;
--force)
FORCE=1
shift
;;
--remote)
DELETE_REMOTE=1
shift
;;
*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
esac
done

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage text says --remote requires --delete-branch, but the script currently accepts --remote on its own and then silently does nothing. Add an explicit validation after option parsing to error out (or implicitly enable --delete-branch) when --remote is provided without --delete-branch.

Suggested change
# Validate option combinations
if [[ $DELETE_REMOTE -eq 1 && $DELETE_BRANCH -ne 1 ]]; then
echo "Error: --remote requires --delete-branch" >&2
usage
exit 1
fi

Copilot uses AI. Check for mistakes.
if [[ ! -d "$WORKTREE_PATH" ]]; then
echo "Error: Worktree path not found: $WORKTREE_PATH" >&2
exit 1
fi

# Get branch name from worktree
cd "$WORKTREE_PATH"
BRANCH="$(git branch --show-current)"
cd - >/dev/null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep cleanup git commands in the owning repository

The script captures the branch from the worktree and then cd - back to the caller’s previous directory before running git worktree remove and branch deletion commands. If the caller invokes this script from outside the main repository (using an absolute worktree path), those later git commands fail with not a git repository, so valid cleanup requests cannot complete.

Useful? React with 👍 / 👎.


echo "Cleaning up worktree:"
echo " Path: $WORKTREE_PATH"
echo " Branch: $BRANCH"
echo ""

# Remove worktree
echo "Removing worktree..."
if [[ $FORCE -eq 1 ]]; then
git worktree remove --force "$WORKTREE_PATH"
else
git worktree remove "$WORKTREE_PATH"
fi
echo " ✓ Worktree removed"

# Delete branch if requested
if [[ $DELETE_BRANCH -eq 1 ]]; then
echo "Deleting local branch..."
if git show-ref --verify --quiet "refs/heads/$BRANCH"; then
git branch -D "$BRANCH"
echo " ✓ Local branch deleted: $BRANCH"
else
echo " ⚠️ Branch not found: $BRANCH"
fi

# Delete remote branch if requested
if [[ $DELETE_REMOTE -eq 1 ]]; then
echo "Deleting remote branch..."
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
git push origin --delete "$BRANCH"
echo " ✓ Remote branch deleted: origin/$BRANCH"
else
echo " ⚠️ Remote branch not found: origin/$BRANCH"
fi
fi
fi

# Prune worktree metadata
echo "Pruning worktree metadata..."
git worktree prune
echo " ✓ Metadata pruned"

echo ""
echo "✓ Cleanup complete!"
echo ""
echo "Remaining worktrees:"
git worktree list
74 changes: 74 additions & 0 deletions .agents/skills/git-worktree-ops/quick-worktree.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Quick worktree setup with automatic guardrails configuration
# Usage: quick-worktree.sh <agent> <task> [base-branch]

set -euo pipefail

usage() {
cat <<EOF
Usage: quick-worktree.sh <agent> <task> [base-branch]

Examples:
quick-worktree.sh codex fix-auth
quick-worktree.sh claude add-metrics develop
quick-worktree.sh copilot docs main

Arguments:
agent Agent name (codex, claude, copilot)
task Task description (kebab-case)
base-branch Base branch to branch from (default: develop)
EOF
}

if [[ $# -lt 2 ]]; then
usage
exit 1
fi

AGENT="$1"
TASK="$2"
BASE="${3:-develop}"

BRANCH="feat/${AGENT}-${TASK}"
WORKTREE_ROOT="../$(basename "$(pwd)")-worktrees"
WORKTREE_PATH="${WORKTREE_ROOT}/${AGENT}-${TASK}"

echo "Creating worktree setup:"
echo " Agent: $AGENT"
echo " Task: $TASK"
echo " Branch: $BRANCH"
echo " Path: $WORKTREE_PATH"
echo " Base: $BASE"
echo ""

# Create worktrees directory if needed
mkdir -p "$WORKTREE_ROOT"

# Create worktree
echo "Creating worktree..."
git worktree add -b "$BRANCH" "$WORKTREE_PATH" "$BASE"

# Configure guardrails
echo "Configuring guardrails..."
cd "$WORKTREE_PATH"

GIT_DIR="$(git rev-parse --git-dir)"
printf '%s\n' "$BRANCH" > "$GIT_DIR/agent-expected-branch"
printf '%s\n' "$(pwd -P)" > "$GIT_DIR/agent-expected-worktree"

# Set safe defaults
git config --local push.default current
git config --local pull.ff only

echo ""
echo "✓ Worktree created and configured!"
echo ""
echo "Verification:"
echo " Current branch: $(git branch --show-current)"
echo " Worktree path: $(git rev-parse --show-toplevel)"
echo " Expected branch: $(cat "$GIT_DIR/agent-expected-branch")"
echo " Expected worktree: $(cat "$GIT_DIR/agent-expected-worktree")"
echo ""
echo "Next steps:"
echo " cd $WORKTREE_PATH"
echo " # Start your agent and begin work"
113 changes: 113 additions & 0 deletions .agents/skills/git-worktree-ops/verify-guardrails.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
# Verify guardrails are properly configured in current worktree
# Usage: verify-guardrails.sh [worktree-path]

set -euo pipefail

WORKTREE="${1:-.}"

cd "$WORKTREE"

echo "Verifying guardrails for: $(pwd)"
echo ""

ERRORS=0
WARNINGS=0

# Check if in a git repository
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "✗ Not a Git repository"
exit 1
fi

GIT_DIR="$(git rev-parse --git-dir)"
CURRENT_BRANCH="$(git branch --show-current)"
CURRENT_PATH="$(git rev-parse --show-toplevel)"

# Check hooks
echo "Hooks:"
for hook in pre-commit pre-push; do
HOOK_PATH="$(git rev-parse --git-path hooks/$hook)"
if [[ -x "$HOOK_PATH" ]]; then
echo " ✓ $hook is executable"
else
echo " ✗ $hook is missing or not executable"
((ERRORS++))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid post-increment counters under errexit

Because the script enables set -e, each ((ERRORS++))/((WARNINGS++)) can terminate execution when the prior value is 0 (the arithmetic command exits non-zero in that case). This means the verifier exits on the first missing hook or warning instead of completing checks and printing the summary/fix guidance; for example, running the script in this repo stops immediately after the first failed hook check.

Useful? React with 👍 / 👎.

fi
Comment on lines +33 to +36

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the script runs with set -e, using ((ERRORS++)) can cause an early exit the first time it runs (the arithmetic command returns status 1 when the pre-increment value is 0). Use an increment form that returns success (e.g., ERRORS+=1) or otherwise avoid errexit for these counters; make the same change for the other counter increments in this file as well.

Copilot uses AI. Check for mistakes.
done
echo ""

# Check metadata files
echo "Metadata files:"
if [[ -f "$GIT_DIR/agent-expected-branch" ]]; then
EXPECTED_BRANCH="$(cat "$GIT_DIR/agent-expected-branch")"
echo " ✓ agent-expected-branch exists: $EXPECTED_BRANCH"

if [[ "$CURRENT_BRANCH" != "$EXPECTED_BRANCH" ]]; then
echo " ⚠️ WARNING: Current branch ($CURRENT_BRANCH) != expected ($EXPECTED_BRANCH)"
((WARNINGS++))
fi
Comment on lines +46 to +49

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the ERRORS counter: with set -e, ((WARNINGS++)) may terminate the script unexpectedly when WARNINGS is 0. Prefer an increment form that returns success (e.g., WARNINGS+=1) for all WARNINGS increments in this script.

Copilot uses AI. Check for mistakes.
else
echo " ✗ agent-expected-branch is missing"
((ERRORS++))
fi

if [[ -f "$GIT_DIR/agent-expected-worktree" ]]; then
EXPECTED_PATH="$(cat "$GIT_DIR/agent-expected-worktree")"
echo " ✓ agent-expected-worktree exists: $EXPECTED_PATH"

if [[ "$CURRENT_PATH" != "$EXPECTED_PATH" ]]; then
echo " ⚠️ WARNING: Current path ($CURRENT_PATH) != expected ($EXPECTED_PATH)"
((WARNINGS++))
fi
else
echo " ✗ agent-expected-worktree is missing"
((ERRORS++))
fi
echo ""

# Check git config
echo "Git configuration:"
PUSH_DEFAULT="$(git config --local --get push.default 2>/dev/null || echo "not set")"
PULL_FF="$(git config --local --get pull.ff 2>/dev/null || echo "not set")"

if [[ "$PUSH_DEFAULT" == "current" ]]; then
echo " ✓ push.default = current"
else
echo " ⚠️ push.default = $PUSH_DEFAULT (recommended: current)"
((WARNINGS++))
fi

if [[ "$PULL_FF" == "only" ]]; then
echo " ✓ pull.ff = only"
else
echo " ⚠️ pull.ff = $PULL_FF (recommended: only)"
((WARNINGS++))
fi
echo ""

# Summary
echo "Summary:"
echo " Current branch: $CURRENT_BRANCH"
echo " Current worktree: $CURRENT_PATH"

if [[ $ERRORS -eq 0 && $WARNINGS -eq 0 ]]; then
echo ""
echo "✓ All guardrails properly configured!"
exit 0
elif [[ $ERRORS -eq 0 ]]; then
echo ""
echo "⚠️ $WARNINGS warning(s) found"
exit 0
else
echo ""
echo "✗ $ERRORS error(s) and $WARNINGS warning(s) found"
echo ""
echo "To fix, run:"
echo " GIT_DIR=\"\$(git rev-parse --git-dir)\""
echo " printf '%s\\n' \"$CURRENT_BRANCH\" > \"\$GIT_DIR/agent-expected-branch\""
echo " printf '%s\\n' \"$CURRENT_PATH\" > \"\$GIT_DIR/agent-expected-worktree\""
Comment on lines +108 to +109

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These echo lines construct shell commands that interpolate CURRENT_BRANCH and CURRENT_PATH directly, so if a branch name contains shell metacharacters (for example $(), backticks, semicolons, or quotes) and a user copy-pastes the suggested "To fix, run" commands, the shell will execute attacker-controlled payloads. An attacker who can create a branch with a malicious name (e.g., via a compromised or untrusted remote) could achieve arbitrary command execution on a developer machine when this script is run and its output is followed. To fix, avoid echoing fully-resolved commands that embed untrusted values (use placeholders or robust escaping), or apply these configuration changes directly in the script instead of asking users to run dynamically-generated shell commands.

Suggested change
echo " printf '%s\\n' \"$CURRENT_BRANCH\" > \"\$GIT_DIR/agent-expected-branch\""
echo " printf '%s\\n' \"$CURRENT_PATH\" > \"\$GIT_DIR/agent-expected-worktree\""
echo " printf '%s\\n' \"<CURRENT_BRANCH>\" > \"\$GIT_DIR/agent-expected-branch\""
echo " printf '%s\\n' \"<CURRENT_PATH>\" > \"\$GIT_DIR/agent-expected-worktree\""

Copilot uses AI. Check for mistakes.
echo " git config --local push.default current"
echo " git config --local pull.ff only"
exit 1
fi
Loading