diff --git a/.github/workflows/no-ai-attribution.yml b/.github/workflows/no-ai-attribution.yml new file mode 100644 index 0000000..ab4186c --- /dev/null +++ b/.github/workflows/no-ai-attribution.yml @@ -0,0 +1,41 @@ +name: Block AI Attribution + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + scan: + name: Scan commits for AI authorship markers + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Compute commit range + id: range + env: + EVENT_NAME: ${{ github.event_name }} + PR_BASE: ${{ github.event.pull_request.base.sha }} + PR_HEAD: ${{ github.event.pull_request.head.sha }} + PUSH_BEFORE: ${{ github.event.before }} + PUSH_AFTER: ${{ github.event.after }} + run: | + set -euo pipefail + if [ "$EVENT_NAME" = "pull_request" ]; then + range="${PR_BASE}..${PR_HEAD}" + else + if [ -z "${PUSH_BEFORE:-}" ] || [ "$PUSH_BEFORE" = "0000000000000000000000000000000000000000" ]; then + range="${PUSH_AFTER}~1..${PUSH_AFTER}" + else + range="${PUSH_BEFORE}..${PUSH_AFTER}" + fi + fi + echo "range=$range" >> "$GITHUB_OUTPUT" + echo "Scanning range: $range" + + - name: Run scanner + run: bash scripts/check-no-ai-attribution.sh '${{ steps.range.outputs.range }}' diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3e83351 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# 项目规范 + +| 项目 | 规则 | +| --- | --- | +| Commit 身份 | 仅以用户身份提交,**禁止**添加 `Co-Authored-By`、AI 署名或任何 AI 辅助标记 | diff --git a/scripts/ai-attribution-patterns.txt b/scripts/ai-attribution-patterns.txt new file mode 100644 index 0000000..567a851 --- /dev/null +++ b/scripts/ai-attribution-patterns.txt @@ -0,0 +1,70 @@ +# AI 贡献痕迹关键字清单(不区分大小写,作为子串/词条匹配)。 +# 任意一行命中即视为 AI 痕迹。新增工具/厂商时直接加一行;以 # 开头为注释。 +# +# 使用位置: +# 1) commit author/committer 的 name 或 email +# 2) commit message 中的 Co-Authored-By: trailer +# 3) commit message 中的 "Generated/Authored/Written/Created/Powered (with|by|using) " 声明 +# +# 行内字面字符串即可,无需写正则;脚本会自动转义。 + +# Anthropic +claude +anthropic.com + +# OpenAI +chatgpt +openai.com +gpt-3 +gpt-4 +gpt-5 +gpt-6 +codex + +# Google +gemini +bard +google ai +googleai +palm 2 + +# GitHub / Microsoft +copilot +github copilot + +# Cursor / Windsurf / Codeium +cursor.sh +cursor.com +cursor ai +windsurf +codeium + +# Coding agents +aider +devin +cognition labs +replit agent +ghostwriter +tabnine +cline +roo code +continue.dev +amazon q +codewhisperer + +# 其他模型 / 产品 +mistral +llama +phind +deepseek +qwen +tongyi +kimi +doubao +文心一言 +baichuan +perplexity +v0.dev +zhipu +glm-4 +glm-5 diff --git a/scripts/check-no-ai-attribution.sh b/scripts/check-no-ai-attribution.sh new file mode 100755 index 0000000..19636ca --- /dev/null +++ b/scripts/check-no-ai-attribution.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# 在指定 git 范围内拦截 AI 贡献痕迹。 +# +# 检测三类位置(使用 ai-attribution-patterns.txt 中的关键字): +# 1) commit author / committer 的 name 或 email +# 2) commit message 中的 Co-Authored-By: trailer +# 3) commit message 中的 "Generated/Authored/Written/Created/Powered (with|by|using) " 声明 +# 此外单独检测: +# 4) 🤖 emoji(极少在合法 commit 中出现,常用于 AI 自动生成签名) +set -euo pipefail + +range="${1:-}" +if [ -z "$range" ]; then + echo "Usage: $0 " >&2 + exit 2 +fi + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +patterns_file="${script_dir}/ai-attribution-patterns.txt" + +if [ ! -f "$patterns_file" ]; then + echo "Patterns file missing: $patterns_file" >&2 + exit 2 +fi + +# 把 patterns 文件转成 ERE 交替式:去除注释/空行/前后空白,转义正则元字符,再用 | 拼接。 +regex=$(sed -E ' + /^[[:space:]]*#/d + /^[[:space:]]*$/d + s/^[[:space:]]+// + s/[[:space:]]+$// + s/[][\\.^$|*+?{}()]/\\&/g +' "$patterns_file" | paste -sd'|' -) + +if [ -z "$regex" ]; then + echo "Patterns file has no active entries: $patterns_file" >&2 + exit 2 +fi + +tmp="$(mktemp)" +trap 'rm -f "$tmp"' EXIT + +git log --pretty=format:'COMMIT %H%nAUTH %an <%ae>%nCOMM %cn <%ce>%nMSG-BEGIN%n%B%nMSG-END%n' "$range" > "$tmp" + +fail=0 + +# 1. author / committer 身份 +if grep -nEi "^(AUTH|COMM) .*(${regex})" "$tmp"; then + echo "::error::Detected AI-related identity in commit author/committer" + fail=1 +fi + +# 提取消息体,复用给 trailer / 声明 / emoji 检测 +msgs="$(awk '/^MSG-BEGIN$/{flag=1;next}/^MSG-END$/{flag=0}flag' "$tmp")" + +# 2. Co-Authored-By trailer +if printf '%s\n' "$msgs" | grep -nEi "^[[:space:]]*co-authored-by:.*(${regex})"; then + echo "::error::Detected Co-Authored-By trailer referencing AI" + fail=1 +fi + +# 3. AI 生成 / 编写 / 驱动声明 +if printf '%s\n' "$msgs" \ + | grep -nEi "(generated|authored|written|created|produced|powered)[[:space:]]+(with|by|using)[[:space:]].*(${regex})"; then + echo "::error::Detected AI generation/authoring statement" + fail=1 +fi + +# 4. 🤖 emoji +if printf '%s\n' "$msgs" | grep -nF '🤖'; then + echo "::error::Detected 🤖 emoji in commit message (commonly used as AI signature)" + fail=1 +fi + +if [ "$fail" -ne 0 ]; then + echo + echo "Range scanned: $range" + echo "Patterns file: $patterns_file" + echo "Adjust the offending commits (e.g., 'git rebase -i' to drop the trailer/footer) and re-push." +fi + +exit "$fail"