git-attribution
Claude Code appends Co-Authored-By: Claude ... <noreply@anthropic.com> to every commit
it writes and Generated with Claude Code to every PR body, unless a setting turns it
off. Codex appends Co-authored-by: Codex <noreply@openai.com> and Generated with [Codex](https://openai.com/codex/). when its ChatGPT workspace policy says so. Copilot,
Cursor, Gemini, Devin and Aider sign in their own ways. Nothing tells the user it
happened; they find out when the agent appears in their contributor graph. This skill
answers three questions - is it still being added, where is it already, and how do I
get it out - and installs a guard so it does not come back, whichever agent wrote it.
The script is scripts/attribution.py next to this file - run it from wherever this
skill was installed (~/.claude/skills/git-attribution/, ~/.agents/skills/git-attribution/,
~/.cursor/skills/..., or a project's .agents/skills/). Stdlib-only Python 3.10+,
read-only unless --apply is passed, and it never pushes.
Which host
The report's host line names the agent it detected from the environment
(CLAUDECODE, CODEX_SANDBOX, CURSOR_AGENT, GEMINI_CLI) and where that agent's
attribution switch lives:
| Host |
Switch |
| Claude Code |
attribution.commit / attribution.pr in settings.json (older: includeCoAuthoredBy). Read, with the file that decided it; --fix flips it off. |
| Codex |
A ChatGPT workspace policy fetched at runtime - nothing on disk to read or flip. The pre-push guard is the local control. |
| Cursor, Gemini CLI, Copilot |
No documented local switch. The guard is the local control. |
The Claude Code lines are printed whatever the host, because a machine often runs
more than one agent. The commit scan, the rewrite and the guard cover every agent in
--agents regardless of host.
Pick the mode from what the user asked
| The user says |
Run |
| "is Claude / Codex still being added", "did the setting take", "check this repo" |
default |
| "Claude is in my contributors", "remove the co-authored-by lines" |
default, then --fix |
| "just turn it off" (Claude Code) |
--fix --settings-only --apply |
| "scrub the history" (they already confirmed the force-push) |
--fix --history-only --apply |
| "make sure it never gets pushed again" (any agent) |
--guard --apply |
| "check my PRs too" |
add --prs |
| "only Codex, I don't care about Copilot" |
add --agents codex |
| "check before I publish" / CI |
--strict --no-settings |
Pass --repo PATH when they mean a repo other than the cwd. --json when you need to
process the result.
Steps
Run the default report first, even when they asked for the fix. It tells you what
the fix would touch. Read the lines that matter:
- host - which agent you are and where its switch is. Under Codex, say plainly
that the trailer is workspace policy and the guard is what they control locally.
- claude -
ON means new Claude Code commits will keep getting the trailer. The
source column says which file decided it. A project .claude/settings.json can
override a user-level OFF; say so when that is the case, because switching it
off in the user file will not help.
- commits - how many carry attribution, from which agent (the hit column names
it), and how many are pushed. Local-only ones can be rewritten freely. Pushed
ones need a force-push afterwards, and that is the user's call, not yours.
- prs (with
--prs) - PR bodies with the footer. These are not fixed by a
history rewrite; the plan lists the URLs to edit by hand.
- guard - whether the pre-push hook is installed.
Answer the question first. If they asked "is it still being added", the host and
claude lines are the answer; do not lead with the commit list. If they asked "why
is Claude in my contributors", the commit list is the answer; name the oldest one.
Before --fix --apply, show them the plan (--fix alone) and confirm two things
out loud: that the working tree is clean (the rewrite refuses otherwise), and, if
any tainted commit is pushed, that they are willing to force-push and that nobody
else has a clone that will break. Do not run the rewrite on a shared branch
without that confirmation. The rewrite keeps a backup at refs/original/* and
leaves remotes alone; commits older than the first tainted one keep their sha.
After a rewrite the tool re-scans and reports how many remain (it should be 0).
The push is yours to run only if the user asks:
git push --force-with-lease --all && git push --force-with-lease --tags.
Tell them GitHub's contributor graph lags the push by a few hours, so the agent
will still show for a while after the history is clean. Once they are happy, the
backup can go: git for-each-ref --format='%(refname)' refs/original | xargs -n1 git update-ref -d.
Offer --guard --apply when the guard line says none. It installs a pre-push
hook that refuses any push whose commits still carry a trailer (from any of the
known agents unless --agents narrows it). If a pre-push hook already exists that
is not ours, the tool refuses; --guard prints the hook text so you can merge it
in by hand. git push --no-verify bypasses it once, which is fine to mention.
The settings change writes attribution: {commit: "", pr: ""} to
~/.claude/settings.json and removes the older includeCoAuthoredBy key. It
takes effect for new Claude Code sessions; in the current one, just leave the
trailer off when you write commit messages, whatever your host's reminder says.
Under Codex there is no equivalent write; leave the trailer off yourself and rely
on the guard.
Related, not overlapping
git log --grep finds the commits; this adds the pushed/local split, the per-host
switch, the rewrite with a backup and the hook. git filter-repo is the better
engine for a huge repo, but it needs a pip install and strips the remotes on purpose;
filter-branch ships with git and keeps them, which is the right trade for a
handful of commits.
What this cannot do
It does not push, and it does not edit PR bodies - both are outward-facing and stay
with the user. It cannot turn the setting off for a managed (enterprise) policy file
or for a project file it does not own; it tells you which file to edit. It cannot
change Codex's workspace policy, Cursor's or Gemini's behaviour - only Claude Code has
a local switch it can write. Host detection is best effort; with no marker the host
line is simply omitted. It finds the agents it knows (--agents lists them); a
trailer from an unfamiliar tool needs the name added to AGENTS. And it does not
make GitHub recompute the contributor graph faster; only time does that.
1---2name: git-attribution3description: Find and remove AI co-author attribution from a git repo - the Co-Authored-By trailer and "Generated with" footer that Claude Code, Codex, Copilot, Cursor, Gemini and others add to commits and that put the agent in your GitHub contributor graph. Works from any agent. Use this whenever the user says Claude, Codex or an AI showed up as a contributor, asks to remove Co-Authored-By or stop adding it, asks whether commits are still being attributed to an agent, wants AI attribution scrubbed from history, wants a repo checked for AI co-authors before pushing or publishing, or asks to block pushes that carry the trailer. Also use it before you push to a repo where the user has said they want no AI attribution.4license: MIT5---67# git-attribution89Claude Code appends `Co-Authored-By: Claude ... <noreply@anthropic.com>` to every commit10it writes and `Generated with Claude Code` to every PR body, unless a setting turns it11off. Codex appends `Co-authored-by: Codex <noreply@openai.com>` and `Generated with12[Codex](https://openai.com/codex/).` when its ChatGPT workspace policy says so. Copilot,13Cursor, Gemini, Devin and Aider sign in their own ways. Nothing tells the user it14happened; they find out when the agent appears in their contributor graph. This skill15answers three questions - is it still being added, where is it already, and how do I16get it out - and installs a guard so it does not come back, whichever agent wrote it.1718The script is `scripts/attribution.py` next to this file - run it from wherever this19skill was installed (`~/.claude/skills/git-attribution/`, `~/.agents/skills/git-attribution/`,20`~/.cursor/skills/...`, or a project's `.agents/skills/`). Stdlib-only Python 3.10+,21read-only unless `--apply` is passed, and it never pushes.2223## Which host2425The report's `host` line names the agent it detected from the environment26(`CLAUDECODE`, `CODEX_SANDBOX`, `CURSOR_AGENT`, `GEMINI_CLI`) and where that agent's27attribution switch lives:2829| Host | Switch |30|---|---|31| Claude Code | `attribution.commit` / `attribution.pr` in `settings.json` (older: `includeCoAuthoredBy`). Read, with the file that decided it; `--fix` flips it off. |32| Codex | A ChatGPT workspace policy fetched at runtime - nothing on disk to read or flip. The pre-push guard is the local control. |33| Cursor, Gemini CLI, Copilot | No documented local switch. The guard is the local control. |3435The Claude Code lines are printed whatever the host, because a machine often runs36more than one agent. The commit scan, the rewrite and the guard cover every agent in37`--agents` regardless of host.3839## Pick the mode from what the user asked4041| The user says | Run |42|---|---|43| "is Claude / Codex still being added", "did the setting take", "check this repo" | default |44| "Claude is in my contributors", "remove the co-authored-by lines" | default, then `--fix` |45| "just turn it off" (Claude Code) | `--fix --settings-only --apply` |46| "scrub the history" (they already confirmed the force-push) | `--fix --history-only --apply` |47| "make sure it never gets pushed again" (any agent) | `--guard --apply` |48| "check my PRs too" | add `--prs` |49| "only Codex, I don't care about Copilot" | add `--agents codex` |50| "check before I publish" / CI | `--strict --no-settings` |5152Pass `--repo PATH` when they mean a repo other than the cwd. `--json` when you need to53process the result.5455## Steps56571. Run the default report first, even when they asked for the fix. It tells you what58 the fix would touch. Read the lines that matter:59 - **host** - which agent you are and where its switch is. Under Codex, say plainly60 that the trailer is workspace policy and the guard is what they control locally.61 - **claude** - `ON` means new Claude Code commits will keep getting the trailer. The62 source column says which file decided it. A project `.claude/settings.json` can63 override a user-level `OFF`; say so when that is the case, because switching it64 off in the user file will not help.65 - **commits** - how many carry attribution, from which agent (the hit column names66 it), and how many are **pushed**. Local-only ones can be rewritten freely. Pushed67 ones need a force-push afterwards, and that is the user's call, not yours.68 - **prs** (with `--prs`) - PR bodies with the footer. These are not fixed by a69 history rewrite; the plan lists the URLs to edit by hand.70 - **guard** - whether the pre-push hook is installed.71722. Answer the question first. If they asked "is it still being added", the host and73 claude lines are the answer; do not lead with the commit list. If they asked "why74 is Claude in my contributors", the commit list is the answer; name the oldest one.75763. Before `--fix --apply`, show them the plan (`--fix` alone) and confirm two things77 out loud: that the working tree is clean (the rewrite refuses otherwise), and, if78 any tainted commit is pushed, that they are willing to force-push and that nobody79 else has a clone that will break. Do not run the rewrite on a shared branch80 without that confirmation. The rewrite keeps a backup at `refs/original/*` and81 leaves remotes alone; commits older than the first tainted one keep their sha.82834. After a rewrite the tool re-scans and reports how many remain (it should be 0).84 The push is yours to run only if the user asks:85 `git push --force-with-lease --all && git push --force-with-lease --tags`.86 Tell them GitHub's contributor graph lags the push by a few hours, so the agent87 will still show for a while after the history is clean. Once they are happy, the88 backup can go: `git for-each-ref --format='%(refname)' refs/original | xargs -n1 git update-ref -d`.89905. Offer `--guard --apply` when the guard line says none. It installs a `pre-push`91 hook that refuses any push whose commits still carry a trailer (from any of the92 known agents unless `--agents` narrows it). If a pre-push hook already exists that93 is not ours, the tool refuses; `--guard` prints the hook text so you can merge it94 in by hand. `git push --no-verify` bypasses it once, which is fine to mention.95966. The settings change writes `attribution: {commit: "", pr: ""}` to97 `~/.claude/settings.json` and removes the older `includeCoAuthoredBy` key. It98 takes effect for **new** Claude Code sessions; in the current one, just leave the99 trailer off when you write commit messages, whatever your host's reminder says.100 Under Codex there is no equivalent write; leave the trailer off yourself and rely101 on the guard.102103## Related, not overlapping104105`git log --grep` finds the commits; this adds the pushed/local split, the per-host106switch, the rewrite with a backup and the hook. `git filter-repo` is the better107engine for a huge repo, but it needs a pip install and strips the remotes on purpose;108`filter-branch` ships with git and keeps them, which is the right trade for a109handful of commits.110111## What this cannot do112113It does not push, and it does not edit PR bodies - both are outward-facing and stay114with the user. It cannot turn the setting off for a managed (enterprise) policy file115or for a project file it does not own; it tells you which file to edit. It cannot116change Codex's workspace policy, Cursor's or Gemini's behaviour - only Claude Code has117a local switch it can write. Host detection is best effort; with no marker the host118line is simply omitted. It finds the agents it knows (`--agents` lists them); a119trailer from an unfamiliar tool needs the name added to `AGENTS`. And it does not120make GitHub recompute the contributor graph faster; only time does that.