diff --git a/scripts/commit-check.sh b/scripts/commit-check.sh index 38e0f59..39e3b69 100755 --- a/scripts/commit-check.sh +++ b/scripts/commit-check.sh @@ -36,32 +36,58 @@ warn() { printf '%s\n' "commit-check: warning: $*" >&2; } check_attribution() { _msg=$1 + # One vendor alternation shared by every pattern below — extend it + # here, never in a single rule, or the rules drift apart. + _VENDOR='claude|cursor|copilot|chatgpt|codex|gemini|devin|openai|anthropic|codeium|windsurf|github[[:space:]]+copilot|claude[[:space:]]+code|(claude|gemini)[[:space:]]+(opus|sonnet|pro|ultra)|swe-[0-9a-z.-]*' # AI co-author / bot trailers. A human Co-authored-by is fine; the # ban is on AI vendors, bots, and *-ai-* / [bot] addresses. - if printf '%s\n' "$_msg" | grep -iE '^[[:space:]]*co-?authored-by:' \ - | grep -qiE 'claude|cursor|copilot|chatgpt|codex|gemini|devin-ai|cognition\.ai|devin\.ai[[:space:]>]|openai|anthropic|codeium|windsurf|swe-|\[bot\]|[a-z0-9-]*bot@|ai-integration'; then + _co=$(printf '%s\n' "$_msg" | grep -iE '^[[:space:]]*co-?authored-by:' || :) + # Unambiguous machine signals anywhere in the trailer: [bot] tags, + # bot@ addresses, *-ai-integration logins, AI-vendor domains (literal + # list of vendor-owned domains — never github.com, or humans on the + # privacy relay get flagged), and a vendor token inside a bare email + # (no ) — bot forms like + # "Co-authored-by: 175728472+Copilot@users.noreply.github.com". + if printf '%s\n' "$_co" | grep -qiE '\[bot\]|(^|[<.[:space:]_+-])bot@|ai-integration|noreply@(anthropic|openai)\.|@(anthropic|openai|cognition|devin|codeium|windsurf|claude-code|cursor|gemini|chatgpt|codex|copilot|google)\.(com|ai|dev|io)([[:space:]>]|$)|:[[:space:]]*[^<[:space:]]*('"$_VENDOR"')[^<[:space:]]*@'; then + err "AI Co-authored-by trailer" + # A bare vendor identity as the display name — "Co-authored-by: + # Claude " hits the domain rule above; this + # catches no-email and generic-mailbox forms. Human names that merely + # CONTAIN a vendor token ("Claude Martin ") stay + # legal per .cursor/rules/no-ai-attribution.mdc. + elif printf '%s\n' "$_co" | sed 's/^[^:]*:[[:space:]]*//; s/<.*//' \ + | grep -qiE '^\[?('"$_VENDOR"')\]?(\([^)]*\))?([[:space:]-]?ai)?[[:space:]]*$'; then err "AI Co-authored-by trailer" fi # generator footers: "Generated with ", "Made with: X", # "Authored via X" — the tool names that sign output, plus the # trailer forms regardless of vendor. - if printf '%s\n' "$_msg" | grep -iqE '(generated|made|created|authored|written|produced)[[:space:]]+(with|by|via)[[:space:]]*\[?[[:space:]]*(claude|cursor|copilot|chatgpt|codex|gemini|devin|openai|anthropic|codeium|windsurf|swe-)'; then + # connector may carry a colon ("Created with: Cursor"); the vendor + # token needs a non-alnum tail so "generated by cursors" stays clean. + if printf '%s\n' "$_msg" | grep -iqE '(generated|made|created|authored|written|produced)[[:space:]]+(with|by|via):?[[:space:]]*\[?[[:space:]]*('"$_VENDOR"')([^a-z0-9]|$)'; then err "AI generator footer" fi if printf '%s\n' "$_msg" | grep -iqE '^[[:space:]]*(generated|made|authored)[- ](with|by|via)[[:space:]]*:'; then err "generator trailer" fi - # bot-emoji generator signature - if printf '%s\n' "$_msg" | grep -q '🤖'; then + # standalone "via " / "by " / "with " lines — the + # footer regex above needs a leading verb, so a lone via-line slips. + if printf '%s\n' "$_msg" | grep -qiE '^[[:space:]]*(via|by|using|with)[[:space:]]+\[?('"$_VENDOR"')\]?(\([^)]*\))?([[:space:]-]?ai)?([^a-z0-9]|$)'; then + err "attribution via-line" + fi + # bot-emoji signature: a bare 🤖 line, or 🤖 sharing a line with + # attribution vocabulary — a mid-sentence robot mention stays clean. + _bot=$(printf '%s\n' "$_msg" | grep '🤖' || :) + if printf '%s\n' "$_bot" | grep -qiE '^[[:space:]]*🤖[[:space:]]*$|generated|made|created|authored|written|produced|built|co-?authored|via[[:space:]]|with[[:space:]]|by[[:space:]]'; then err "bot-emoji attribution marker" fi # AI named as the author/agent — not as the subject of the change. # (kept narrow on purpose: "add Copilot webhook" must stay legal) - if printf '%s\n' "$_msg" | grep -iqE '\b(as an ai|i generated this|authored by (claude|cursor|copilot|chatgpt|codex|gemini|devin|openai|swe-))\b'; then + if printf '%s\n' "$_msg" | grep -iqE '\b(as an ai|i generated this|authored by \[?('"$_VENDOR"'))\b'; then err "AI self-attribution" fi # vendor-stamped HTML comments smuggled into the message - if printf '%s\n' "$_msg" | grep -iqE '