git-attribution
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 pu
SKILL.mdskills/git-attribution/SKILL.md
GitHubgit-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 -
ONmeans new Claude Code commits will keep getting the trailer. The source column says which file decided it. A project.claude/settings.jsoncan override a user-levelOFF; 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 (--fixalone) 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 atrefs/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 --applywhen the guard line says none. It installs apre-pushhook that refuses any push whose commits still carry a trailer (from any of the known agents unless--agentsnarrows it). If a pre-push hook already exists that is not ours, the tool refuses;--guardprints the hook text so you can merge it in by hand.git push --no-verifybypasses it once, which is fine to mention.The settings change writes
attribution: {commit: "", pr: ""}to~/.claude/settings.jsonand removes the olderincludeCoAuthoredBykey. 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.