Git Commit Prompt Hook
Here's a git hook that rejects commit messages if they’re coming from a coding agent and don’t have a "Prompt: " section. It works well.
#!/usr/bin/env python3
#
# Git commit-msg hook that requires a "Prompt:" section in agent-driven commits.
#
# Agent commits are detected by:
# 1. Commit message markers (Generated with [Claude Code], Co-Authored-By, etc.)
# 2. Parent process names (claude, cursor, aider, copilot, gemini, etc.)
import os
import re
import subprocess
import sys
AGENT_PROCESSES = [
"claude", "cursor", "aider", "copilot", "cody",
"codium", "windsurf", "gemini", "cline", "continue", "shelley"
]
AGENT_MARKERS_PATTERN = re.compile(
r"Generated with \[Claude|Co-Authored-By: Claude|🤖 Generated|"
r"Generated by (Claude|Cursor|Copilot|Aider|Gemini)",
re.IGNORECASE
)
def get_process_name(pid: int) -> str | None:
"""Get the process name for a given PID."""
try:
result = subprocess.run(
["ps", "-p", str(pid), "-o", "comm="],
capture_output=True,
text=True,
check=False
)
return result.stdout.strip() if result.returncode == 0 else None
except Exception:
return None
def get_parent_pid(pid: int) -> int | None:
"""Get the parent PID for a given PID."""
try:
result = subprocess.run(
["ps", "-p", str(pid), "-o", "ppid="],
capture_output=True,
text=True,
check=False
)
if result.returncode == 0:
ppid = result.stdout.strip()
return int(ppid) if ppid else None
return None
except Exception:
return None
def check_commit_message_markers(commit_msg: str) -> bool:
"""Check if commit message contains agent markers."""
return bool(AGENT_MARKERS_PATTERN.search(commit_msg))
def check_parent_processes() -> tuple[bool, str | None]:
"""Check parent process tree for known agent names."""
pid = os.getpid()
while pid and pid != 1:
proc_name = get_process_name(pid)
if proc_name:
proc_name_lower = proc_name.lower()
for agent in AGENT_PROCESSES:
if proc_name_lower == agent:
return True, proc_name
pid = get_parent_pid(pid)
return False, None
def main() -> int:
if len(sys.argv) < 2:
return 0
commit_msg_file = sys.argv[1]
try:
with open(commit_msg_file, "r") as f:
commit_msg = f.read()
except Exception:
return 0
# Check if this is an agent-driven commit
is_agent_commit = False
detected_by = ""
# Method 1: Check commit message for agent markers
if check_commit_message_markers(commit_msg):
is_agent_commit = True
detected_by = "commit message markers"
# Method 2: Check parent processes for known agent names
if not is_agent_commit:
found, proc_name = check_parent_processes()
if found:
is_agent_commit = True
detected_by = f"parent process '{proc_name}'"
# If it's an agent commit, require a "Prompt:" section
if is_agent_commit:
if not re.search(r"^Prompt:", commit_msg, re.MULTILINE):
print("ERROR: Agent-driven commits must include a 'Prompt:' section.")
print()
print("This commit appears to be driven by a coding agent")
print(f"(detected via {detected_by}).")
print()
print("Please add a line starting with 'Prompt:' with the exact prompt")
print("the user used that led to this change, including any follow up commands.")
print("Put it on the top of the commit message body.")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
Yes, it's vibe-coded.
Yes, I hate commit hooks too, but this one has been foisted on me by myself and runs instantaneously.