Add AI indicator pre-commit guard

This commit is contained in:
Jennifer Wang committed 2026-01-08 14:26:03 -08:00
1 parent 3cc5c5371e
commit 6f7cb19373
5 files changed
+479

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
# AI-Blocking Git Hooks
Git hooks that prevent AI indicators from being committed to your repository.
## What These Hooks Block
The hooks scan for AI references including:
- AI tool names (Claude, Cursor, Copilot, ChatGPT, Gemini, etc.)
- AI-related phrases ("ai-generated", "ai-assisted", "generated with", etc.)
- Model names (GPT-4, Claude Sonnet, Claude Code, etc.)
- Company names (OpenAI, Anthropic, etc.)
- Co-authorship markers (Co-Authored-By: Claude, noreply@anthropic.com, etc.)
## Prerequisites
Install [pre-commit](https://pre-commit.com/):
```bash
pip install pre-commit
```
## Installation
### Single Repository
1. Navigate to your repository:
```bash
cd /path/to/your/repo
```
2. Create or update `.pre-commit-config.yaml`:
```yaml
repos:
- repo: local
hooks:
- id: ai-pre-commit
name: Block AI indicators in staged files
entry: python3 .config/ai-hooks/ai_guard.py pre-commit
language: system
pass_filenames: false
stages: [pre-commit]
- id: ai-commit-msg
name: Block AI indicators in commit messages
entry: python3 .config/ai-hooks/ai_guard.py commit-msg
language: system
stages: [commit-msg]
```
3. Install the hooks:
```bash
pre-commit install
```
## How It Works
### commit-msg Hook
- Runs when you create a commit
- Scans the commit message for AI indicators
- Blocks the commit if any indicators are found
### pre-commit Hook
- Runs before the commit is created
- Scans all staged files for AI indicators
- Blocks the commit if any indicators are found in the code
## Uninstallation
To remove the hooks from a repository:
```bash
cd /path/to/your/repo
rm .git/hooks/commit-msg .git/hooks/pre-commit .git/hooks/ai_guard.py
```
## Testing
To verify the hooks are working:
1. Create a test file with AI indicators:
```bash
echo "Generated with Claude Code" > test.txt
git add test.txt
git commit -m "test"
```
2. You should see an error message blocking the commit
3. Clean up:
```bash
git reset HEAD test.txt
rm test.txt
```
## Bypassing Hooks When Needed
In rare cases where you need to commit content with AI indicators, you can bypass the hooks using the `--no-verify` flag:
```bash
git commit --no-verify -m "your commit message"
```
## Notes
- These hooks are **repository-specific** and must be installed in each repo where you want them active
- Git hooks are not tracked by git itself (they live in `.git/hooks/`)
- If you have existing hooks, the installer will create `.backup` copies
- All pattern matching is case-insensitive
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""
Python wrapper for AI indicator guard hooks.
Calls the appropriate bash script based on the hook type.
"""
import subprocess
import sys
from pathlib import Path
def main():
if len(sys.argv) < 2:
print("Usage: ai_guard.py <pre-commit|commit-msg>", file=sys.stderr)
sys.exit(1)
hook_type = sys.argv[1]
# Get the directory where this script is located
script_dir = Path(__file__).parent
# Determine which hook script to call
if hook_type == "pre-commit":
hook_script = script_dir / "pre-commit"
elif hook_type == "commit-msg":
hook_script = script_dir / "commit-msg"
# For commit-msg, we need to pass the commit message file
if len(sys.argv) < 3:
print("Error: commit-msg hook requires message file argument", file=sys.stderr)
sys.exit(1)
commit_msg_file = sys.argv[2]
else:
print(f"Unknown hook type: {hook_type}", file=sys.stderr)
sys.exit(1)
if not hook_script.exists():
print(f"Hook script not found: {hook_script}", file=sys.stderr)
sys.exit(1)
# Call the hook script
try:
if hook_type == "commit-msg":
result = subprocess.run(
[str(hook_script), commit_msg_file],
check=False
)
else:
result = subprocess.run(
[str(hook_script)],
check=False
)
sys.exit(result.returncode)
except Exception as e:
print(f"Error running hook: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# Guard commit messages against explicit AI usage indicators.
# The script rejects messages containing phrases that suggest the text
# was generated by an AI assistant or tool (as defined in onboarding docs).
set -euo pipefail
MSG_FILE=${1:?}
# Patterns to block; all matches are case-insensitive (grep -i).
declare -a PATTERNS=(
"generated by cursor"
"cursor ai"
"cursor wrote this"
"cursor suggestion"
"generated by claude"
"claude wrote this"
"claude ai"
"claude opus"
"claude sonnet"
"claude haiku"
"claude code"
"generated with claude code"
"generated with \\[claude code\\]"
"co-authored-by: claude"
"noreply@anthropic\\.com"
"generated by gemini"
"gemini wrote this"
"google gemini"
"gemini ultra"
"gemini pro"
"gemini nano"
"generated by bard"
"bard wrote this"
"google bard"
"as an ai assistant"
"as an ai"
"ai assistant"
"ai suggestion"
"ai suggestions"
"ai-generated"
"ai generated"
"ai-assisted"
"ai assisted"
"ai-powered"
"ai powered"
"powered by ai"
"with ai assistance"
"with the help of ai"
"assisted by ai"
"written with ai"
"written by ai"
"generated with ai"
"generated via ai"
"created with ai"
"created by ai"
"authored by ai"
"co-authored by ai"
"ai help"
"ai helped"
"ai copilot"
"ai co-pilot"
"chatgpt"
"gpt-4"
"gpt4"
"gpt-3"
"gpt3"
"openai"
"anthropic"
"google ai"
"google deepmind"
"deepmind"
"perplexity"
"perplexity ai"
"tabnine"
"tabnine ai"
"replit ghostwriter"
"ghostwriter ai"
"aws codewhisperer"
"amazon codewhisperer"
"code whisperer"
"codewhisperer"
"github copilot"
"copilot"
"amazon q"
"amazon q developer"
"meta ai"
"deepseek"
"phind"
"blackbox ai"
"magic ai"
"ai model"
"llm"
"large language model"
"language model"
"\\[ai\\]"
"\\[ai-generated\\]"
"\\[cursor\\]"
"\\[claude\\]"
"\\[gemini\\]"
"\\[copilot\\]"
"\\[chatgpt\\]"
"\\[llm\\]"
)
for pattern in "${PATTERNS[@]}"; do
if LC_ALL=C grep -qiE "$pattern" "$MSG_FILE"; then
cat >&2 <<EOF
✖ Commit message blocked: found AI indicator "$pattern".
Please rewrite the message without explicitly referencing AI tools or prompts.
EOF
exit 1
fi
done
exit 0
+171
View File
@@ -0,0 +1,171 @@
#!/usr/bin/env bash
# Block commits when staged files contain explicit AI usage indicators.
# Mirrors the onboarding guidance so reviewers remain blinded to AI tooling.
# Markdown (.md) files are explicitly scanned to prevent AI flag text in docs.
set -euo pipefail
declare -a PATTERNS=(
"generated by cursor"
"cursor ai"
"cursor wrote this"
"cursor suggestion"
"generated by claude"
"claude wrote this"
"claude ai"
"claude opus"
"claude sonnet"
"claude haiku"
"claude code"
"generated with claude code"
"generated with \\[claude code\\]"
"co-authored-by: claude"
"noreply@anthropic\\.com"
"generated by gemini"
"gemini wrote this"
"google gemini"
"gemini ultra"
"gemini pro"
"gemini nano"
"generated by bard"
"bard wrote this"
"google bard"
"as an ai assistant"
"as an ai"
"ai assistant"
"ai suggestion"
"ai suggestions"
"ai-generated"
"ai generated"
"ai-assisted"
"ai assisted"
"ai-powered"
"ai powered"
"powered by ai"
"with ai assistance"
"with the help of ai"
"assisted by ai"
"written with ai"
"written by ai"
"generated with ai"
"generated via ai"
"created with ai"
"created by ai"
"authored by ai"
"co-authored by ai"
"ai help"
"ai helped"
"ai copilot"
"ai co-pilot"
"chatgpt"
"gpt-4"
"gpt4"
"gpt-3"
"gpt3"
"openai"
"anthropic"
"google ai"
"google deepmind"
"deepmind"
"perplexity"
"perplexity ai"
"tabnine"
"tabnine ai"
"replit ghostwriter"
"ghostwriter ai"
"aws codewhisperer"
"amazon codewhisperer"
"code whisperer"
"codewhisperer"
"github copilot"
"copilot"
"amazon q"
"amazon q developer"
"meta ai"
"deepseek"
"phind"
"blackbox ai"
"magic ai"
"ai model"
"llm"
"large language model"
"language model"
"\\[ai\\]"
"\\[ai-generated\\]"
"\\[cursor\\]"
"\\[claude\\]"
"\\[gemini\\]"
"\\[copilot\\]"
"\\[chatgpt\\]"
"\\[llm\\]"
)
PATTERN_REGEX=$(printf '%s|' "${PATTERNS[@]}")
PATTERN_REGEX=${PATTERN_REGEX%|}
# Build a newline-separated list of binary file paths (bash 3.2 compatible)
BINARY_PATHS=""
while IFS=$'\t' read -r added deleted path; do
[[ -z "${path:-}" ]] && continue
if [[ "$path" == *" -> "* ]]; then
path=${path##* -> }
fi
if [[ "$added" == "-" || "$deleted" == "-" ]]; then
BINARY_PATHS="${BINARY_PATHS}${path}"$'\n'
fi
done < <(git diff --cached --numstat --diff-filter=ACMRT || true)
report=""
current_file=""
last_report_file=""
violations=0
while IFS= read -r line; do
case "$line" in
"diff --git "*)
current_file=""
;;
"+++ "*)
current_file=${line#+++ }
if [[ "$current_file" == b/* ]]; then
current_file=${current_file#b/}
elif [[ "$current_file" == a/* ]]; then
current_file=${current_file#a/}
fi
if [[ "$current_file" == "/dev/null" ]]; then
current_file=""
fi
;;
"Binary files "*)
current_file=""
;;
*)
if [[ "$line" == +* && "$line" != "+++"* ]]; then
[[ -z "$current_file" ]] && continue
if [[ -n "$BINARY_PATHS" ]] && echo "$BINARY_PATHS" | grep -qxF "$current_file"; then
continue
fi
content=${line:1}
if [[ -n "$content" ]] && LC_ALL=C grep -qiE "$PATTERN_REGEX" <<<"$content"; then
if [[ "$current_file" != "$last_report_file" ]]; then
report+=$'\n'"• $current_file"
last_report_file="$current_file"
fi
report+=$'\n'" ${line}"
violations=1
fi
fi
;;
esac
done < <(git diff --cached --diff-filter=ACMRT --unified=0 --no-color)
if [[ $violations -eq 1 ]]; then
cat >&2 <<EOF
✖ Commit blocked: staged changes contain AI usage indicators in the current diff.
Please remove or rephrase the highlighted additions before committing:$report
EOF
exit 1
fi
exit 0
+17
View File
@@ -0,0 +1,17 @@
# Configure pre-commit to reuse the AI indicator guard scripts.
# Both hooks run locally from .config/ai-hooks.
repos:
- repo: local
hooks:
- id: ai-pre-commit
name: Block AI indicators in staged files
entry: python3 .config/git-hooks/ai_guard.py pre-commit
language: system
pass_filenames: false
stages: [pre-commit]
- id: ai-commit-msg
name: Block AI indicators in commit messages
entry: python3 .config/git-hooks/ai_guard.py commit-msg
language: system
stages: [commit-msg]