| 1 | /** |
| 2 | * Coding agents and bots that commit to repositories under their own |
| 3 | * names: Claude Code, Codex, Copilot, Gemini, Cursor and the dependency |
| 4 | * bots. None of them is an account on g1t, but each writes the same |
| 5 | * address on every commit (or a GitHub bot's noreply address), so g1t |
| 6 | * shows them as what they are, with their maker's mark and a card, |
| 7 | * rather than as an unknown author. |
| 8 | * |
| 9 | * Matched by the commit's address only, never by the name: anyone can |
| 10 | * write "Claude" as their name. An account that owns an address always |
| 11 | * wins (lib/commit-people.ts in the site, services/repos contributors.rs); |
| 12 | * these addresses can't be confirmed by anyone, so that never happens. |
| 13 | * |
| 14 | * The Rust side (crates/contracts/src/coding_agents.rs) keeps the same |
| 15 | * ids, addresses and logins; a test there reads this file to keep them |
| 16 | * equal. No imports, so it runs anywhere. |
| 17 | */ |
| 18 | |
| 19 | export type CodingAgentId = "claude" | "codex" | "copilot" | "gemini" | "jules" | "cursor" | "devin" | "aider" | "dependabot" | "renovate" | "github-actions"; |
| 20 | |
| 21 | /** Whether it writes code for people (`agent`) or keeps dependencies and builds running (`bot`). */ |
| 22 | export type CodingAgentKind = "agent" | "bot"; |
| 23 | |
| 24 | export type CodingAgent = { |
| 25 | id: CodingAgentId; |
| 26 | /** What it's called, as its card names it. */ |
| 27 | name: string; |
| 28 | /** Who makes it. */ |
| 29 | maker: string; |
| 30 | kind: CodingAgentKind; |
| 31 | /** One line about it, for its card. */ |
| 32 | about: string; |
| 33 | /** Its own page. */ |
| 34 | url: string; |
| 35 | /** Addresses it writes on commits, lowercase. */ |
| 36 | emails: readonly string[]; |
| 37 | /** GitHub logins whose noreply addresses (`123+login@users.noreply.github.com`) are its, lowercase. */ |
| 38 | logins: readonly string[]; |
| 39 | /** Its mark's background, as a hex colour without `#`; null to use the page's own colours. */ |
| 40 | color: string | null; |
| 41 | }; |
| 42 | |
| 43 | export const CODING_AGENTS: readonly CodingAgent[] = [ |
| 44 | { |
| 45 | id: "claude", |
| 46 | name: "Claude", |
| 47 | maker: "Anthropic", |
| 48 | kind: "agent", |
| 49 | about: "Anthropic's coding agent. Commits made with Claude Code carry its address as author or co-author.", |
| 50 | url: "https://claude.com/product/claude-code", |
| 51 | emails: ["noreply@anthropic.com"], |
| 52 | logins: ["claude", "claude[bot]"], |
| 53 | color: "D97757", |
| 54 | }, |
| 55 | { |
| 56 | id: "codex", |
| 57 | name: "Codex", |
| 58 | maker: "OpenAI", |
| 59 | kind: "agent", |
| 60 | about: "OpenAI's coding agent, committing through its GitHub app.", |
| 61 | url: "https://openai.com/codex", |
| 62 | emails: [], |
| 63 | logins: ["chatgpt-codex-connector[bot]", "codex[bot]"], |
| 64 | color: null, |
| 65 | }, |
| 66 | { |
| 67 | id: "copilot", |
| 68 | name: "Copilot", |
| 69 | maker: "GitHub", |
| 70 | kind: "agent", |
| 71 | about: "GitHub's coding agent.", |
| 72 | url: "https://github.com/features/copilot", |
| 73 | emails: [], |
| 74 | logins: ["copilot", "copilot-swe-agent[bot]", "copilot-pull-request-reviewer[bot]"], |
| 75 | color: null, |
| 76 | }, |
| 77 | { |
| 78 | id: "gemini", |
| 79 | name: "Gemini", |
| 80 | maker: "Google", |
| 81 | kind: "agent", |
| 82 | about: "Google's coding assistant, committing through its GitHub app.", |
| 83 | url: "https://codeassist.google", |
| 84 | emails: [], |
| 85 | logins: ["gemini-code-assist[bot]", "gemini-cli[bot]"], |
| 86 | color: "8E75B2", |
| 87 | }, |
| 88 | { |
| 89 | id: "jules", |
| 90 | name: "Jules", |
| 91 | maker: "Google", |
| 92 | kind: "agent", |
| 93 | about: "Google's asynchronous coding agent.", |
| 94 | url: "https://jules.google", |
| 95 | emails: [], |
| 96 | logins: ["google-labs-jules[bot]"], |
| 97 | color: "8E75B2", |
| 98 | }, |
| 99 | { |
| 100 | id: "cursor", |
| 101 | name: "Cursor", |
| 102 | maker: "Anysphere", |
| 103 | kind: "agent", |
| 104 | about: "Cursor's background agent.", |
| 105 | url: "https://cursor.com", |
| 106 | emails: ["cursoragent@cursor.com"], |
| 107 | logins: ["cursor[bot]", "cursor-agent[bot]"], |
| 108 | color: null, |
| 109 | }, |
| 110 | { |
| 111 | id: "devin", |
| 112 | name: "Devin", |
| 113 | maker: "Cognition", |
| 114 | kind: "agent", |
| 115 | about: "Cognition's coding agent.", |
| 116 | url: "https://devin.ai", |
| 117 | emails: [], |
| 118 | logins: ["devin-ai-integration[bot]"], |
| 119 | color: null, |
| 120 | }, |
| 121 | { |
| 122 | id: "aider", |
| 123 | name: "Aider", |
| 124 | maker: "Aider", |
| 125 | kind: "agent", |
| 126 | about: "An open-source coding agent for the terminal.", |
| 127 | url: "https://aider.chat", |
| 128 | emails: ["noreply@aider.chat", "aider@aider.chat"], |
| 129 | logins: [], |
| 130 | color: null, |
| 131 | }, |
| 132 | { |
| 133 | id: "dependabot", |
| 134 | name: "Dependabot", |
| 135 | maker: "GitHub", |
| 136 | kind: "bot", |
| 137 | about: "Opens pull requests that update dependencies.", |
| 138 | url: "https://github.com/dependabot", |
| 139 | emails: ["support@dependabot.com"], |
| 140 | logins: ["dependabot[bot]", "dependabot-preview[bot]"], |
| 141 | color: "025E8C", |
| 142 | }, |
| 143 | { |
| 144 | id: "renovate", |
| 145 | name: "Renovate", |
| 146 | maker: "Mend", |
| 147 | kind: "bot", |
| 148 | about: "Opens pull requests that update dependencies.", |
| 149 | url: "https://docs.renovatebot.com", |
| 150 | emails: ["bot@renovateapp.com"], |
| 151 | logins: ["renovate[bot]"], |
| 152 | color: "308BE3", |
| 153 | }, |
| 154 | { |
| 155 | id: "github-actions", |
| 156 | name: "GitHub Actions", |
| 157 | maker: "GitHub", |
| 158 | kind: "bot", |
| 159 | about: "Commits made by a workflow on GitHub.", |
| 160 | url: "https://github.com/features/actions", |
| 161 | emails: ["action@github.com"], |
| 162 | logins: ["github-actions[bot]"], |
| 163 | color: "2088FF", |
| 164 | }, |
| 165 | ]; |
| 166 | |
| 167 | const GITHUB_NOREPLY = /^(?:\d+\+)?([^@]+)@users\.noreply\.github\.com$/; |
| 168 | |
| 169 | /** The coding agent a commit's address belongs to, or null. */ |
| 170 | export function codingAgentFor(email: string | null | undefined): CodingAgent | null { |
| 171 | const address = (email ?? "").trim().toLowerCase(); |
| 172 | if (!address) return null; |
| 173 | const login = GITHUB_NOREPLY.exec(address)?.[1] ?? null; |
| 174 | return CODING_AGENTS.find((agent) => agent.emails.includes(address) || (login !== null && agent.logins.includes(login))) ?? null; |
| 175 | } |
| 176 | |
| 177 | /** An agent by id, or null for one this g1t doesn't know (an older or newer list). */ |
| 178 | export function codingAgentById(id: string | null | undefined): CodingAgent | null { |
| 179 | return CODING_AGENTS.find((agent) => agent.id === id) ?? null; |
| 180 | } |