diff --git a/.github/workflows/content-policy.yml b/.github/workflows/content-policy.yml new file mode 100644 index 0000000..ef6a53b --- /dev/null +++ b/.github/workflows/content-policy.yml @@ -0,0 +1,68 @@ +name: Content Policy + +on: + pull_request: + types: [opened, edited, reopened] + issues: + types: [opened, edited, reopened] + issue_comment: + types: [created, edited] + +permissions: + contents: read + pull-requests: read + issues: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Check content policy + uses: actions/github-script@v7 + with: + script: | + const FORBIDDEN_EMOJIS = ['🤖', '✅', '❌', '🚀', '🚫', '✨', '❤️']; + + // Get the content to check based on event type + let content = ''; + let contentType = ''; + + if (context.eventName === 'pull_request') { + content = context.payload.pull_request.body || ''; + contentType = 'PR description'; + } else if (context.eventName === 'issues') { + content = context.payload.issue.body || ''; + contentType = 'Issue description'; + } else if (context.eventName === 'issue_comment') { + content = context.payload.comment.body || ''; + contentType = 'Comment'; + } + + const errors = []; + + // Check for forbidden emojis + for (const emoji of FORBIDDEN_EMOJIS) { + if (content.includes(emoji)) { + errors.push(`Contains forbidden emoji: ${emoji}`); + } + } + + // Check for redundant Claude attributions (use full email to avoid false positives) + const hasCoAuthored = content.includes('Co-Authored-By: Claude '); + const hasGeneratedWith = content.includes('Generated with [Claude Code]'); + + if (hasCoAuthored && hasGeneratedWith) { + errors.push('Contains both "Co-Authored-By: Claude" and "Generated with [Claude Code]" - use one or the other, not both'); + } + + if (errors.length > 0) { + let message = `## Content Policy Violation\n\n${contentType} contains the following issues:\n\n${errors.map(e => `- ${e}`).join('\n')}\n\n`; + + // Add recommendation for redundant Claude attributions + if (hasCoAuthored && hasGeneratedWith) { + message += `**Recommended signature for Claude Code contributions:**\n\n> Generated with [Claude Code](https://claude.com/claude-code)\n> Steered and verified by @your-username\n\n`; + } + + message += 'Please edit to fix these issues.'; + core.setFailed(message); + } diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..f447977 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,44 @@ +#!/usr/bin/env sh +set -e +# commit-msg hook: Reject forbidden emojis in commit messages +# +# This is a global hook (committed to repo) that applies to all contributors. +# Rejects commits containing unprofessional emojis commonly added by AI tools. +# +# To bypass this check temporarily (for emergencies): +# git commit --no-verify + +commit_msg_file=$1 + +# Extract just the commit message (before any diff/patch content) +# This prevents false positives when commits remove emojis from code +commit_msg=$(sed '/^---$/q;/^diff /q' "$commit_msg_file" | sed '$d') + +# Forbidden emojis (commonly added by AI tools) +FORBIDDEN_EMOJIS="🤖 ✅ ❌ 🚀 🚫 ✨ ❤️" + +detected="" +for emoji in $FORBIDDEN_EMOJIS; do + if printf '%s' "$commit_msg" | grep -q "$emoji"; then + detected="$emoji" + break + fi +done + +if [ -n "$detected" ]; then + echo "" + echo "COMMIT REJECTED" + echo "" + echo "Commit message contains forbidden emoji: $detected" + echo "Please remove emojis from your commit message." + echo "" + echo "To bypass (emergencies only): git commit --no-verify" + echo "" + exit 1 +fi + +# Call local hook if it exists (for personal/machine-specific rules) +GIT_COMMON_DIR=$(git rev-parse --git-common-dir) +if [ -x "$GIT_COMMON_DIR/hooks/commit-msg" ]; then + "$GIT_COMMON_DIR/hooks/commit-msg" "$@" || exit $? +fi