feat: add Claude Code hook to block git commands
Enforces the jj-only rule at the tool level — bare git commands
are rejected with a pointer to the jj skill. Commands like gh,
gitea, and jj git pass through.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/ai/claude/hooks/no-git.sh b/ai/claude/hooks/no-git.sh
new file mode 100755
index 00000000..e22cd861
--- /dev/null
+++ b/ai/claude/hooks/no-git.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+# Block bare `git` commands — use jj instead.
+# Allows commands that happen to contain "git" (e.g., gh, gitea, git.kejadlen.dev).
+
+command=$(jq -r '.tool_input.command // ""')
+
+# Strip leading whitespace and env assignments, then check for `git` as the first word.
+bare_command=$(echo "$command" | sed 's/^[[:space:]]*//' | sed 's/^[A-Za-z_][A-Za-z_0-9]*=[^ ]* *//')
+
+if [[ "$bare_command" =~ ^git($|[[:space:]]) ]]; then
+ echo "Use jj, not git. See the jj skill for command reference." >&2
+ exit 2
+fi
diff --git a/ai/claude/settings.json b/ai/claude/settings.json
index e971d54f..401ff494 100644
--- a/ai/claude/settings.json
+++ b/ai/claude/settings.json
@@ -33,6 +33,19 @@
"type": "command",
"command": "~/.claude/statusline.sh"
},
+ "hooks": {
+ "PreToolUse": [
+ {
+ "matcher": "Bash",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "~/.claude/hooks/no-git.sh"
+ }
+ ]
+ }
+ ]
+ },
"enabledPlugins": {
"claude-md-management@claude-plugins-official": true,
"elements-of-style@superpowers-marketplace": true,