Skip to content

Instantly share code, notes, and snippets.

@yurukusa
Last active April 12, 2026 17:21
Show Gist options
  • Select an option

  • Save yurukusa/98bd43c5d0d8a6ebbf2cf21bfc1e2907 to your computer and use it in GitHub Desktop.

Select an option

Save yurukusa/98bd43c5d0d8a6ebbf2cf21bfc1e2907 to your computer and use it in GitHub Desktop.
Claude Code Worktree Safety Hooks — 3 hooks to protect against worktree deletion, cross-tree destruction, and unmerged commit loss

Scheduled tasks can lose context and create the same PR repeatedly. One user reported 38 duplicate PRs created in a single session.

COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
echo "$COMMAND" | grep -qE '\bgh\s+pr\s+create\b' || exit 0
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
[ -z "$BRANCH" ] && exit 0
EXISTING=$(gh pr list --head "$BRANCH" --state open --json number,title --jq '.[0].number' 2>/dev/null)
if [ -n "$EXISTING" ]; then
    TITLE=$(gh pr list --head "$BRANCH" --state open --json title --jq '.[0].title' 2>/dev/null)
    echo "BLOCKED: An open PR already exists for branch '$BRANCH'." >&2
    echo "  PR #$EXISTING: $TITLE" >&2
    exit 2
fi
exit 0

Claude Code Worktree Safety Hooks — 3 hooks to protect against worktree deletion, cross-tree destruction, and unmerged commit loss

gist-addition.md

Scheduled tasks can lose context and create the same PR repeatedly. One user reported 38 duplicate PRs created in a single session.

COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
echo "$COMMAND" | grep -qE '\bgh\s+pr\s+create\b' || exit 0
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
[ -z "$BRANCH" ] && exit 0
EXISTING=$(gh pr list --head "$BRANCH" --state open --json number,title --jq '.[0].number' 2>/dev/null)
if [ -n "$EXISTING" ]; then
    TITLE=$(gh pr list --head "$BRANCH" --state open --json title --jq '.[0].title' 2>/dev/null)
    echo "BLOCKED: An open PR already exists for branch '$BRANCH'." >&2
    echo "  PR #$EXISTING: $TITLE" >&2
    exit 2
fi
exit 0

worktree-safety-hooks.md

Worktree issues are increasing rapidly in Claude Code (April 2026). The main risks: one session deleting another's worktree, destructive git operations crossing worktree boundaries, and unmerged commits being silently lost. These 3 hooks protect against the most common worktree incidents. Prevents one session from deleting a worktree that another session is using.

COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
if echo "$COMMAND" | grep -qE 'git\s+worktree\s+(remove|prune)'; then
    echo "BLOCKED: Cannot remove git worktrees — other sessions may depend on them." >&2
    echo "List worktrees first: git worktree list" >&2
    exit 2
fi
if git rev-parse --git-dir &>/dev/null; then
    if echo "$COMMAND" | grep -qE "(rm|rmdir)\s+.*worktrees"; then
        echo "BLOCKED: Cannot delete worktree storage directory." >&2
        exit 2
    fi
fi
exit 0

Git worktrees share the same .git directory. git clean, git reset --hard in one worktree can affect the main tree.

COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
echo "$COMMAND" | grep -qE '\bgit\s+(clean|reset|checkout\s+--|stash\s+drop)' || exit 0
GITDIR=$(git rev-parse --git-dir 2>/dev/null)
if echo "$GITDIR" | grep -q "worktrees"; then
    MAIN_DIR=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null | sed 's|/.git$||')
    echo "WARNING: You are in a git worktree." >&2
    echo "Main working tree: $MAIN_DIR" >&2
    echo "Destructive git operations may affect the main tree." >&2
fi
exit 0
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
echo "$COMMAND" | grep -qE 'git\s+worktree\s+(remove|prune)' || exit 0
for branch in $(git branch --list 'worktree-*' 2>/dev/null); do
    UNMERGED=$(git log --oneline main..$branch 2>/dev/null | wc -l)
    if [ "$UNMERGED" -gt 0 ]; then
        echo "WARNING: $branch has $UNMERGED unmerged commit(s)." >&2
        echo "Push first: git push origin $branch" >&2
    fi
done
exit 0

Add to ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/worktree-delete-guard.sh" },
          { "type": "command", "command": "bash ~/.claude/hooks/worktree-guard.sh" },
          { "type": "command", "command": "bash ~/.claude/hooks/worktree-cleanup-guard.sh" },
          { "type": "command", "command": "bash ~/.claude/hooks/pr-duplicate-guard.sh" }
        ]
      }
    ]
  }
}

Or install all 654+ safety hooks at once: npx cc-safe-setup These worktree issues often cause retry loops that burn tokens fast. For a systematic approach to reducing token consumption (CLAUDE.md optimization, context management, 9 token-saving hooks): → Token Book — Claude Codeのトークン消費を半分にする (¥2,500 / 10 chapters / 800h real data)

Part of cc-safe-setup — 655 safety hooks for Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment