diff --git a/docs/contributor-catalog.md b/docs/contributor-catalog.md index 6692130..b30e2e8 100644 --- a/docs/contributor-catalog.md +++ b/docs/contributor-catalog.md @@ -1,42 +1,23 @@ -# 免费 AI 共同作者(Co-Author)目录 +# 已验证的共同作者(Co-Author)目录 -本目录用于把一个仓库的 Contributors 页面快速“铺满”免费的 AI 工具身份。 -原理:GitHub 会识别提交信息中的 `Co-Authored-By` 尾注;当这些提交进入默认分支后, -对应身份会出现在仓库 Contributors 页面(本仓库已验证,13 个身份已进入页面)。 +本目录只保留**实测能进入 Contributors 页面**的身份(A 组 + C 组)。 +曾经测试的 B 组(Gemini/Copilot 的 noreply 邮箱、ChatGPT、Aider、Cline、Cursor、Windsurf) +已实测不会进入页面,故从本目录移除。 > 透明披露:共同作者尾注只用于署名归因,不代表对应工具真实参与了每一个提交; > 本仓库把它当作“归因记录”功能使用,与 `activity/agent-checkins.csv` 配套。 -## 身份清单(按可信度排序) - -### A. 已验证计入 Contributors 页面的 AI 身份 +## A. 已验证计入页面的 AI 身份 | ID | 展示名 | 尾注格式 | 工具本身免费? | 备注 | |---|---|---|---|---| | codex | Codex | `Codex ` | 有免费额度 | OpenAI 官方 no-reply 邮箱 | | claude | Claude | `Claude ` | 有免费额度 | Anthropic 官方 no-reply 邮箱 | -### B. 仅提交级归因(不进 Contributors 页面) - -GitHub 未把这些身份计入页面统计,但尾注会保留在提交里用于提交级归因: - -| ID | 展示名 | 尾注格式 | 说明 | -|---|---|---|---| -| gemini | Gemini / Gemini Code Assist | `Gemini ` | 提交页会渲染专属头像,但实测不计入页面 | -| copilot | GitHub Copilot | `Copilot ` | 提交页会渲染专属头像,但实测不计入页面 | -| chatgpt | ChatGPT | `ChatGPT ` | 与 Codex 同邮箱,解析为 codex 身份 | -| aider | Aider | `Aider ` | 开源 CLI | -| cline | Cline | `Cline ` | VS Code 开源扩展 | -| cursor | Cursor | `Cursor ` | 默认不加尾注 | -| windsurf | Windsurf | `Windsurf ` | 默认走提交信息识别 | - -> 实测:B 组身份虽保留在提交里,但不会进入 Contributors 页面(gemini/copilot 在提交页 -> 有专属头像,仍不计入页面统计)。 - -### C. 真实 GitHub Bot 账号(必然进入 Contributors,已验证 6/6) +## C. 真实 GitHub Bot 账号(必然进入 Contributors) 与 `dependabot[bot]`、`github-actions[bot]` 一样是真实 GitHub 账号,邮箱格式 -`{id}+{login}@users.noreply.github.com`,解析是确定性的: +`{id}+{login}@users.noreply.github.com`,解析是确定性的(已验证 6/6 起步,已扩展到 30 个): | ID | 展示名 | 尾注格式 | 说明 | |---|---|---|---| @@ -46,18 +27,40 @@ GitHub 未把这些身份计入页面统计,但尾注会保留在提交里用 | snyk | snyk-bot | `snyk-bot <19733683+snyk-bot@users.noreply.github.com>` | Snyk 安全修复 | | all-contributors | allcontributors[bot] | `allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>` | all-contributors 名单机器人 | | copilot-bot | copilot[bot] | `copilot[bot] <167198135+copilot[bot]@users.noreply.github.com>` | GitHub Copilot 官方 bot 账号 | +| claude-bot | claude[bot] | `claude[bot] <209825114+claude[bot]@users.noreply.github.com>` | Anthropic Claude 官方 bot 账号 | +| cursor-bot | cursor[bot] | `cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>` | Cursor AI 官方 bot 账号 | +| qodo-merge | qodo-merge[bot] | `qodo-merge[bot] <185363710+qodo-merge[bot]@users.noreply.github.com>` | Qodo Merge(PR-Agent)AI 审查/合并 bot | +| mergify | mergify[bot] | `mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>` | Mergify 合并队列 | +| kodiakhq | kodiakhq[bot] | `kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>` | Kodiak 合并队列 | +| github-merge-queue | github-merge-queue[bot] | `github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>` | GitHub Merge Queue | +| scala-steward | scala-steward | `scala-steward <43047562+scala-steward@users.noreply.github.com>` | Scala 依赖更新机器人 | +| pyup | pyup-bot | `pyup-bot <16239342+pyup-bot@users.noreply.github.com>` | Python 依赖更新机器人 | +| mend | mend[bot] | `mend[bot] <241224340+mend[bot]@users.noreply.github.com>` | Mend 安全扫描 bot | +| greenkeeper | greenkeeper[bot] | `greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>` | npm 依赖 bot(已停用) | +| dependabot-preview | dependabot-preview[bot] | `dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>` | Dependabot 旧版 bot(已停用) | +| semantic-release | semantic-release-bot | `semantic-release-bot <32174276+semantic-release-bot@users.noreply.github.com>` | semantic-release 发布/提交 bot | +| codecov | codecov[bot] | `codecov[bot] <22429695+codecov[bot]@users.noreply.github.com>` | Codecov 覆盖率 bot | +| github-classroom | github-classroom[bot] | `github-classroom[bot] <66690702+github-classroom[bot]@users.noreply.github.com>` | GitHub Classroom | +| github-learning-lab | github-learning-lab[bot] | `github-learning-lab[bot] <37936606+github-learning-lab[bot]@users.noreply.github.com>` | GitHub Learning Lab | +| first-timers | first-timers[bot] | `first-timers[bot] <31459394+first-timers[bot]@users.noreply.github.com>` | first-timers 引导 bot | +| request-info | request-info[bot] | `request-info[bot] <30733101+request-info[bot]@users.noreply.github.com>` | request-info 信息补充 bot | +| stale | stale[bot] | `stale[bot] <26384082+stale[bot]@users.noreply.github.com>` | stale 过期关闭 bot | +| todo | todo[bot] | `todo[bot] <32347756+todo[bot]@users.noreply.github.com>` | todo 转 Issue bot | +| welcome | welcome[bot] | `welcome[bot] <30606887+welcome[bot]@users.noreply.github.com>` | welcome 欢迎 bot | +| wip | wip[bot] | `wip[bot] <29805525+wip[bot]@users.noreply.github.com>` | WIP 状态检查 bot | +| hound | hound[bot] | `hound[bot] <30008653+hound[bot]@users.noreply.github.com>` | Hound 代码风格审查 bot | +| stickler-ci | stickler-ci[bot] | `stickler-ci[bot] <41810448+stickler-ci[bot]@users.noreply.github.com>` | Stickler CI 风格检查 bot | +| release-drafter | release-drafter[bot] | `release-drafter[bot] <40829082+release-drafter[bot]@users.noreply.github.com>` | Release Drafter 发布草稿 bot | 说明: -- 「进入页面」以本仓库实测为准:A 组计入页面;C 组必然计入(已验证 6/6); - B 组只做提交级归因。 -- 邮箱统一使用官方 no-reply / 工具官方邮箱,避免暴露真实邮箱。 - 带 `[bot]` 的名字可以放进 `AGENT_CHECKIN_AUTHORS`(工作流正则已支持方括号), 但默认清单只放 A 组,C 组按需用 `--bots` 单独加。 +- 部分 C 组账号(如 stale/wip/request-info 等)以评论/检查为主,仍保留在目录用于机制演示。 ## 如何把身份加到一个提交 ```bash -# 列出全部身份(A/B/C 三组) +# 列出全部身份 python scripts/build_contributors.py --list # 选择部分身份(逗号分隔 ID,可混选 A/C 组) @@ -66,7 +69,7 @@ python scripts/build_contributors.py --tools codex,renovate,snyk # 只选真实 Bot 账号(C 组,进入页面最稳) python scripts/build_contributors.py --bots -# 选择全部(A+B+C) +# 选择全部(A+C) python scripts/build_contributors.py --all # 直接生成带尾注的完整提交信息 @@ -79,16 +82,12 @@ python scripts/build_contributors.py --bots --commit-message "feat: example" ## 迁移到其他仓库(三步) 1. 复制本目录的 `docs/contributor-catalog.md` 和 `scripts/build_contributors.py` 到目标仓库。 -2. 用 `build_contributors.py` 选择要添加的身份: - - 想要「确定性进入页面」:选 C 组(`--bots`)或 codex/claude(A 组); - - 想保留提交级归因:再加 B 组; - - 把尾注写进提交信息并合并进默认分支。 +2. 用 `build_contributors.py` 选择要添加的身份(A 组或 C 组),把尾注写进提交信息并合并进默认分支。 3. 刷新目标仓库 Contributors 页面,核对出现哪些身份。 ### 可选:让每日 check-in 自动携带选中的身份 参考 `.github/workflows/scheduled-agent-checkin.yml`: - 默认会携带 A 组全部身份; -- 想选择部分身份,在目标仓库设置变量 `AGENT_CHECKIN_AUTHORS`,用英文分号分隔, - 例如 `Codex ;Gemini ;renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>`; +- 想选择部分身份,在目标仓库设置变量 `AGENT_CHECKIN_AUTHORS`,用英文分号分隔; - 开启方式:设置仓库变量 `AGENT_CHECKIN_ENABLED=true`,然后手动运行一次该工作流。 diff --git a/scripts/build_contributors.py b/scripts/build_contributors.py index 6dc96cf..4d33dd2 100644 --- a/scripts/build_contributors.py +++ b/scripts/build_contributors.py @@ -1,14 +1,16 @@ #!/usr/bin/env python3 -"""Generate Co-Authored-By trailers from the free contributor catalog. +"""Generate Co-Authored-By trailers from the verified contributor catalog. -Includes GitHub-registered AI identities and real GitHub bot accounts. +Only identities proven to enter the Contributors page are included: +- A group: codex, claude (GitHub-registered AI identities) +- C group: real GitHub bot accounts (guaranteed to show) Examples: python scripts/build_contributors.py --list - python scripts/build_contributors.py --tools copilot,gemini,cline + python scripts/build_contributors.py --tools codex,renovate python scripts/build_contributors.py --bots python scripts/build_contributors.py --all - python scripts/build_contributors.py --all --commit-message "feat: example" + python scripts/build_contributors.py --bots --commit-message "feat: example" """ from __future__ import annotations @@ -17,8 +19,7 @@ import re import sys -# 与 docs/contributor-catalog.md 保持同步。 -# free: 工具本身是否免费可用;recognized: 进入 Contributors 页面的实测情况。 +# A 组:已验证计入 Contributors 页面的 AI 身份。 CATALOG: list[dict] = [ { "id": "codex", @@ -34,95 +35,190 @@ "free": True, "recognized": "verified-on-page", }, - { - "id": "copilot", - "name": "Copilot", - "email": "noreply@github.com", - "free": False, - "recognized": "not-counted", - }, - { - "id": "chatgpt", - "name": "ChatGPT", - "email": "noreply@openai.com", - "free": True, - "recognized": "cannot-display", - }, - { - "id": "gemini", - "name": "Gemini", - "email": "noreply@google.com", - "free": True, - "recognized": "not-counted", - }, - { - "id": "aider", - "name": "Aider", - "email": "aider@aider.ch", - "free": True, - "recognized": "not-counted", - }, - { - "id": "cline", - "name": "Cline", - "email": "noreply@cline.bot", - "free": True, - "recognized": "not-counted", - }, - { - "id": "cursor", - "name": "Cursor", - "email": "noreply@cursor.sh", - "free": True, - "recognized": "not-counted", - }, - { - "id": "windsurf", - "name": "Windsurf", - "email": "noreply@windsurf.com", - "free": True, - "recognized": "not-counted", - }, ] -# 真实 GitHub Bot 账号:邮箱为 {id}+{login}@users.noreply.github.com, +# C 组:真实 GitHub Bot 账号,邮箱为 {id}+{login}@users.noreply.github.com, # 与 dependabot[bot] 一样能解析到真实账号,必然进入 Contributors 页面。 BOT_ACCOUNTS: list[dict] = [ { "id": "gemini-code-assist", "name": "gemini-code-assist[bot]", "email": "176961590+gemini-code-assist[bot]@users.noreply.github.com", - "note": "Google Gemini Code Assist bot", + "note": "Google Gemini Code Assist 官方 bot", }, { "id": "renovate", "name": "renovate[bot]", "email": "29139614+renovate[bot]@users.noreply.github.com", - "note": "Mend Renovate dependency bot", + "note": "Mend Renovate 依赖机器人", }, { "id": "pre-commit-ci", "name": "pre-commit-ci[bot]", "email": "66853113+pre-commit-ci[bot]@users.noreply.github.com", - "note": "pre-commit.ci autofix bot", + "note": "pre-commit.ci 自动修复", }, { "id": "snyk", "name": "snyk-bot", "email": "19733683+snyk-bot@users.noreply.github.com", - "note": "Snyk security-fix bot", + "note": "Snyk 安全修复", }, { "id": "all-contributors", "name": "allcontributors[bot]", "email": "46447321+allcontributors[bot]@users.noreply.github.com", - "note": "all-contributors bot", + "note": "all-contributors 名单机器人", }, { "id": "copilot-bot", "name": "copilot[bot]", "email": "167198135+copilot[bot]@users.noreply.github.com", - "note": "GitHub Copilot bot account", + "note": "GitHub Copilot 官方 bot 账号", + }, + { + "id": "claude-bot", + "name": "claude[bot]", + "email": "209825114+claude[bot]@users.noreply.github.com", + "note": "Anthropic Claude 官方 bot 账号", + }, + { + "id": "cursor-bot", + "name": "cursor[bot]", + "email": "206951365+cursor[bot]@users.noreply.github.com", + "note": "Cursor AI 官方 bot 账号", + }, + { + "id": "qodo-merge", + "name": "qodo-merge[bot]", + "email": "185363710+qodo-merge[bot]@users.noreply.github.com", + "note": "Qodo Merge(PR-Agent)AI 审查/合并 bot", + }, + { + "id": "mergify", + "name": "mergify[bot]", + "email": "37929162+mergify[bot]@users.noreply.github.com", + "note": "Mergify 合并队列", + }, + { + "id": "kodiakhq", + "name": "kodiakhq[bot]", + "email": "49736102+kodiakhq[bot]@users.noreply.github.com", + "note": "Kodiak 合并队列", + }, + { + "id": "github-merge-queue", + "name": "github-merge-queue[bot]", + "email": "118344674+github-merge-queue[bot]@users.noreply.github.com", + "note": "GitHub Merge Queue", + }, + { + "id": "scala-steward", + "name": "scala-steward", + "email": "43047562+scala-steward@users.noreply.github.com", + "note": "Scala 依赖更新机器人", + }, + { + "id": "pyup", + "name": "pyup-bot", + "email": "16239342+pyup-bot@users.noreply.github.com", + "note": "Python 依赖更新机器人", + }, + { + "id": "mend", + "name": "mend[bot]", + "email": "241224340+mend[bot]@users.noreply.github.com", + "note": "Mend 安全扫描 bot", + }, + { + "id": "greenkeeper", + "name": "greenkeeper[bot]", + "email": "23040076+greenkeeper[bot]@users.noreply.github.com", + "note": "npm 依赖 bot(已停用)", + }, + { + "id": "dependabot-preview", + "name": "dependabot-preview[bot]", + "email": "27856297+dependabot-preview[bot]@users.noreply.github.com", + "note": "Dependabot 旧版 bot(已停用)", + }, + { + "id": "semantic-release", + "name": "semantic-release-bot", + "email": "32174276+semantic-release-bot@users.noreply.github.com", + "note": "semantic-release 发布/提交 bot", + }, + { + "id": "codecov", + "name": "codecov[bot]", + "email": "22429695+codecov[bot]@users.noreply.github.com", + "note": "Codecov 覆盖率 bot", + }, + { + "id": "github-classroom", + "name": "github-classroom[bot]", + "email": "66690702+github-classroom[bot]@users.noreply.github.com", + "note": "GitHub Classroom", + }, + { + "id": "github-learning-lab", + "name": "github-learning-lab[bot]", + "email": "37936606+github-learning-lab[bot]@users.noreply.github.com", + "note": "GitHub Learning Lab", + }, + { + "id": "first-timers", + "name": "first-timers[bot]", + "email": "31459394+first-timers[bot]@users.noreply.github.com", + "note": "first-timers 引导 bot", + }, + { + "id": "request-info", + "name": "request-info[bot]", + "email": "30733101+request-info[bot]@users.noreply.github.com", + "note": "request-info 信息补充 bot", + }, + { + "id": "stale", + "name": "stale[bot]", + "email": "26384082+stale[bot]@users.noreply.github.com", + "note": "stale 过期关闭 bot", + }, + { + "id": "todo", + "name": "todo[bot]", + "email": "32347756+todo[bot]@users.noreply.github.com", + "note": "todo 转 Issue bot", + }, + { + "id": "welcome", + "name": "welcome[bot]", + "email": "30606887+welcome[bot]@users.noreply.github.com", + "note": "welcome 欢迎 bot", + }, + { + "id": "wip", + "name": "wip[bot]", + "email": "29805525+wip[bot]@users.noreply.github.com", + "note": "WIP 状态检查 bot", + }, + { + "id": "hound", + "name": "hound[bot]", + "email": "30008653+hound[bot]@users.noreply.github.com", + "note": "Hound 代码风格审查 bot", + }, + { + "id": "stickler-ci", + "name": "stickler-ci[bot]", + "email": "41810448+stickler-ci[bot]@users.noreply.github.com", + "note": "Stickler CI 风格检查 bot", + }, + { + "id": "release-drafter", + "name": "release-drafter[bot]", + "email": "40829082+release-drafter[bot]@users.noreply.github.com", + "note": "Release Drafter 发布草稿 bot", }, ] @@ -149,12 +245,12 @@ def validate(trailers: list[str]) -> None: def print_catalog() -> None: - print("AI identities (verified-on-page / not-counted / cannot-display):") + print("A. AI identities verified on the Contributors page:") print(f"{'ID':<20} {'Trailer':<50} Free Recognized") for entry in CATALOG: print(f"{entry['id']:<20} {trailer_for(entry):<50} {str(entry['free']):<6} {entry['recognized']}") print() - print("Real GitHub bot accounts (guaranteed to show on Contributors):") + print("C. Real GitHub bot accounts (guaranteed to show on Contributors):") print(f"{'ID':<20} {'Trailer':<58} Note") for entry in BOT_ACCOUNTS: print(f"{entry['id']:<20} {trailer_for(entry):<58} {entry['note']}")