Skip to content
180 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.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}