diff --git a/.claude/hooks/worktree-guard.sh b/.claude/hooks/worktree-guard.sh new file mode 100755 index 0000000..7f32391 --- /dev/null +++ b/.claude/hooks/worktree-guard.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Worktree isolation guard. This repo is worked by MULTIPLE concurrent sessions; if +# they share the main checkout they clobber each other (branch switches, lost edits, +# commits landing on the wrong branch). Every session must work in its OWN git +# worktree OUTSIDE the main directory. +# +# worktree-guard.sh block PreToolUse(Edit|Write|NotebookEdit) — DENY edits while +# the session is in the main checkout (emit deny JSON). +# worktree-guard.sh notice SessionStart — tell the agent to enter a worktree. +# +# A session is "isolated" when it sits in a git worktree: there, `--git-dir` (…/.git/ +# worktrees/) differs from `--git-common-dir` (…/.git). Equal = the main checkout. +set -u +mode="${1:-notice}" + +gd=$(git rev-parse --absolute-git-dir 2>/dev/null) || exit 0 # not a repo → nothing to guard +gc=$(cd "$(git rev-parse --git-common-dir 2>/dev/null)" 2>/dev/null && pwd -P) || exit 0 +[ "$gd" = "$gc" ] || exit 0 # already in a worktree → allow + +root=$(git rev-parse --show-toplevel 2>/dev/null) +repo=$(basename "${root:-repo}") +guidance="You are in the SHARED main checkout ($root). Multiple sessions use it \ +concurrently and WILL clobber each other. Work in an isolated worktree OUTSIDE the \ +main directory: use the EnterWorktree tool (preferred — it relocates this session to \ +a fresh worktree branched from origin/main), or run \ +\`git worktree add -b ../${repo}-worktrees/ origin/main\` and cd there. \ +Do NOT edit files in the main checkout." + +esc() { printf '%s' "$1" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'; } + +if [ "$mode" = "block" ]; then + printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":%s}}\n' "$(esc "$guidance")" +else + printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":%s}}\n' "$(esc "⚠ WORKTREE ISOLATION REQUIRED. $guidance")" +fi diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..68a811e --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "worktree": { + "bgIsolation": "worktree", + "baseRef": "fresh" + }, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/worktree-guard.sh\" notice" + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Edit|Write|NotebookEdit", + "hooks": [ + { + "type": "command", + "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/worktree-guard.sh\" block" + } + ] + } + ] + } +}