Skip to content
180 linesCodeBlameRaw
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
19export 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`). */
22export type CodingAgentKind = "agent" | "bot";
23
24export 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
43export 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
167const GITHUB_NOREPLY = /^(?:\d+\+)?([^@]+)@users\.noreply\.github\.com$/;
168
169/** The coding agent a commit's address belongs to, or null. */
170export 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). */
178export function codingAgentById(id: string | null | undefined): CodingAgent | null {
179 return CODING_AGENTS.find((agent) => agent.id === id) ?? null;
180}