chore(config): centralize git for teammates and enable tmux panes
Two agent-team tooling changes: - teammateMode: in-process -> auto. Teammates now spawn in tmux split panes when the lead runs inside tmux, with graceful in-process fallback. - New PreToolUse hook git-centralize-guard.sh blocks .git-mutating commands (add, commit, merge, push, pull, rebase, reset, checkout, stash, cherry-pick, rm, mv) for teammates, keeping version control centralized to the lead. Detection keys on the agent_type field, which a teammate's hook input carries and the lead's does not. Read-only git is allowed. Documented in .claude/rules/git-safety.md. Hooks load at lead startup, so a restart is required for the hook to reach teammates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+34
@@ -0,0 +1,34 @@
|
|||||||
|
#!/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
|
||||||
@@ -7,6 +7,21 @@
|
|||||||
- Skip files in `.gitignore`
|
- Skip files in `.gitignore`
|
||||||
- The `.claude/` directory IS tracked — skills and agents belong in the repo
|
- The `.claude/` directory IS tracked — skills and agents belong in the repo
|
||||||
|
|
||||||
|
## Centralized version control (agent teams)
|
||||||
|
|
||||||
|
Git is the **team lead's** job. Teammates edit files; the lead stages, commits,
|
||||||
|
merges, and pushes. This is enforced, not just convention:
|
||||||
|
|
||||||
|
- **Hook:** `.claude/hooks/git-centralize-guard.sh` (PreToolUse on Bash) blocks
|
||||||
|
`.git`-mutating commands — `add`, `commit`, `merge`, `push`, `pull`, `rebase`,
|
||||||
|
`reset`, `checkout`, `stash`, `cherry-pick`, `rm`, `mv` — for any agent-team
|
||||||
|
teammate. Read-only git (`status`, `log`, `diff`, `show`, `fetch`) is allowed.
|
||||||
|
- **Detection:** a teammate's PreToolUse hook input carries an `agent_type` field
|
||||||
|
(e.g. `"qatux"`); the lead's main session does not. Present → block; absent →
|
||||||
|
allow. (Confirmed on Claude Code 2.1.148.)
|
||||||
|
- **Activation:** hooks load at lead startup and teammates inherit that snapshot,
|
||||||
|
so changes to this hook require a Claude Code restart to reach teammates.
|
||||||
|
|
||||||
## Commit conventions
|
## Commit conventions
|
||||||
|
|
||||||
Use conventional commits: `<type>(<scope>): <summary>`
|
Use conventional commits: `<type>(<scope>): <summary>`
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
|
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
|
||||||
"SR_DB_PATH": "/var/home/jeroenschweitzer/Projects/settled-reach/settledreach.db"
|
"SR_DB_PATH": "/var/home/jeroenschweitzer/Projects/settled-reach/settledreach.db"
|
||||||
},
|
},
|
||||||
"teammateMode": "in-process",
|
"teammateMode": "auto",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(git add *)",
|
"Bash(git add *)",
|
||||||
@@ -87,6 +87,11 @@
|
|||||||
"type": "command",
|
"type": "command",
|
||||||
"command": ".claude/hooks/git-lock-guard.sh",
|
"command": ".claude/hooks/git-lock-guard.sh",
|
||||||
"timeout": 5
|
"timeout": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": ".claude/hooks/git-centralize-guard.sh",
|
||||||
|
"timeout": 5
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user