Skip to content

Commit 1a7437d

Browse files
authored
chore(hooks): block AI attribution in commits and PR bodies (#1274)
* chore(hooks): block AI attribution in commits and PR bodies guard-git.sh now denies any git commit whose message contains AI authorship footers or AI tool references — both inline (-m) and via -F <file> / --file forms. Patterns are scoped to AI-specific forms to avoid blocking legitimate human co-authorship trailers. grep -oE + awk replaces grep -oP for macOS BSD grep portability. guard-pr-body.sh is expanded from the narrow 'generated with' check to the same full pattern set, covering authorship footers and AI tool URLs in both the inline --body arg and --body-file. * fix(hooks): handle --body-file=path equals-sign form in guard-pr-body.sh The --body-file extraction only handled the space-separated form. The equals-sign form (--body-file=<path>) now also extracted and checked, matching the three-form handling already in guard-git.sh for --file.
1 parent 2858d35 commit 1a7437d

2 files changed

Lines changed: 43 additions & 20 deletions

File tree

.claude/hooks/guard-git.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,27 @@ if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)gh[[:space:]]+pr[[
170170
validate_branch_name
171171
fi
172172

173+
# --- Block AI attribution in commit messages ---
174+
175+
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+commit'; then
176+
if echo "$COMMAND" | grep -qiE 'co-authored-by:.*claude|co-authored-by:.*anthropic|generated with claude|generated with \[claude|built with claude|claude\.ai'; then
177+
deny "BLOCKED: Remove AI attribution lines (Co-Authored-By with Claude/Anthropic, 'Generated with Claude', 'Built with Claude', claude.ai URLs) from the commit message."
178+
fi
179+
# Extract -F <file> or --file=<file> or --file <file> (all equivalent git commit forms)
180+
MSG_FILE=$(echo "$COMMAND" | grep -oE '\-F[[:space:]]+[^[:space:]]+' | awk '{print $2}' || true)
181+
if [ -z "$MSG_FILE" ]; then
182+
MSG_FILE=$(echo "$COMMAND" | grep -oE '\-\-file=[^[:space:]]+' | sed 's/--file=//' || true)
183+
fi
184+
if [ -z "$MSG_FILE" ]; then
185+
MSG_FILE=$(echo "$COMMAND" | grep -oE '\-\-file[[:space:]]+[^[:space:]]+' | awk '{print $2}' || true)
186+
fi
187+
if [ -n "$MSG_FILE" ] && [ -f "$MSG_FILE" ]; then
188+
if grep -qiE 'co-authored-by:.*claude|co-authored-by:.*anthropic|generated with claude|generated with \[claude|built with claude|claude\.ai' "$MSG_FILE"; then
189+
deny "BLOCKED: Remove AI attribution lines from the commit message file '$MSG_FILE'."
190+
fi
191+
fi
192+
fi
193+
173194
# --- Commit validation against edit log ---
174195

175196
if echo "$NCOMMAND" | grep -qE '(^|[[:space:]]|&&[[:space:]]*)git[[:space:]]+commit'; then

.claude/hooks/guard-pr-body.sh

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
2-
# Block PR creation if the body contains "generated with" (case-insensitive)
2+
# Block PR creation if the body contains AI attribution (case-insensitive)
33

44
set -euo pipefail
55

@@ -17,35 +17,37 @@ cmd=$(echo "$INPUT" | node -e "
1717

1818
echo "$cmd" | grep -qi 'gh pr create' || exit 0
1919

20-
# Block if body contains "generated with"
21-
if echo "$cmd" | grep -qi 'generated with'; then
20+
deny_pr() {
21+
local reason="$1"
2222
node -e "
2323
console.log(JSON.stringify({
2424
hookSpecificOutput: {
2525
hookEventName: 'PreToolUse',
2626
permissionDecision: 'deny',
27-
permissionDecisionReason: 'BLOCKED: Remove any \'Generated with ...\' line from the PR body.'
27+
permissionDecisionReason: process.argv[1]
2828
}
2929
}));
30-
"
30+
" "$reason"
3131
exit 0
32-
fi
32+
}
3333

34-
# Also check --body-file path
35-
BODY_FILE=$(echo "$cmd" | grep -oP '(?<=--body-file\s)\S+' || true)
36-
if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then
37-
if grep -qi 'generated with' "$BODY_FILE"; then
38-
node -e "
39-
console.log(JSON.stringify({
40-
hookSpecificOutput: {
41-
hookEventName: 'PreToolUse',
42-
permissionDecision: 'deny',
43-
permissionDecisionReason: 'BLOCKED: Remove any \'Generated with ...\' line from the PR body file.'
44-
}
45-
}));
46-
"
47-
exit 0
34+
check_attribution() {
35+
local text="$1"
36+
local source="$2"
37+
if echo "$text" | grep -qiE 'generated with claude|generated with \[claude|co-authored-by:.*claude|co-authored-by:.*anthropic|built with claude|claude\.ai'; then
38+
deny_pr "BLOCKED: Remove AI attribution lines (Co-Authored-By with Claude/Anthropic, 'Generated with Claude', 'Built with Claude', claude.ai URLs) from the PR ${source}."
4839
fi
40+
}
41+
42+
check_attribution "$cmd" "body"
43+
44+
# Also check --body-file path (handles both --body-file <path> and --body-file=<path>)
45+
BODY_FILE=$(echo "$cmd" | grep -oE '\-\-body-file[[:space:]]+[^[:space:]]+' | awk '{print $2}' || true)
46+
if [ -z "$BODY_FILE" ]; then
47+
BODY_FILE=$(echo "$cmd" | grep -oE '\-\-body-file=[^[:space:]]+' | sed 's/--body-file=//' || true)
48+
fi
49+
if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then
50+
check_attribution "$(cat "$BODY_FILE")" "body file"
4951
fi
5052

5153
exit 0

0 commit comments

Comments
 (0)