#!/usr/bin/env bash # git-centralize-guard: PreToolUse hook that blocks .git-mutating commands for # agent-team teammates. Version control is centralized to the team lead # (see CLAUDE.md / memory: "Team lead commits, never agents"). # # Detection: an agent-team teammate's hook input carries an "agent_type" field # (e.g. "qatux"); the team lead's main session does NOT. So agent_type present # => a teammate (or subagent) => block git writes. Absent => the lead => allow. # (Empirically confirmed on Claude Code 2.1.148; docs field is "agent_type".) # # Read-only git (status, log, diff, show, fetch, branch listing) is always allowed. set -uo pipefail INPUT=$(cat) # Only act on Bash tool calls TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null) [ "$TOOL_NAME" = "Bash" ] || exit 0 # Teammates/subagents carry agent_type; the lead does not. AGENT_TYPE=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('agent_type','') or '')" 2>/dev/null) [ -n "$AGENT_TYPE" ] || exit 0 # lead -> allow everything COMMAND=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null) # Block git history/working-tree-mutating subcommands anywhere in the command # (catches chained forms like `cd x && git commit`). Read-only git is allowed. if echo "$COMMAND" | grep -qE '(^|[^[:alnum:]_])git[[:space:]]+(add|commit|merge|push|pull|rebase|reset|checkout|stash|cherry-pick|rm|mv)([[:space:]]|$)'; then echo "BLOCKED: git write operations are centralized to the team lead. Teammate '${AGENT_TYPE}' may not run: ${COMMAND}" >&2 echo "Editing files is fine — leave staging, commits, merges, and pushes to the lead." >&2 exit 2 fi exit 0