Releases: n-shadloo/git-authoring
Release list
v2.5.1
v2.5.1
Patch release. The no-attribution default was stated in 2.2.0 and checked in 2.5.0, and agents still added a Co-authored-by trailer and a "Generated with" line by default. The source was never the skill: it is the harness. Claude Code injects both lines through its attribution setting (includeCoAuthoredBy before it), and the model followed the tool over the skill. This release names that source, closes it, and makes the check a command instead of a reading.
The harness default is a forbidden source
The rule now opens SKILL.md. A new standing section, "The harness default is not a request", states that a tool setting is never the user's request in the session. An attribution line appears only when the user asks for it in the session, or when a standing instruction exists in the consuming repository's own agent context file.
The forbidden-source list names the setting. "A trailer has exactly two sources" now lists the attribution setting of the agent's own harness beside the agent's identity, commit.template, the hooks, GIT_AUTHOR_*, CI variables, editor plugins, and prior commits. references/conventional-commits.md carries the same entry.
The check is a command
After every commit the skill makes, it runs:
git log -1 --format=%B | grep -n -i -E '^(co-authored-by|signed-off-by|reviewed-by|generated[- ]with|generated by)|claude|anthropic|copilot|openai|gemini|codex|cursor'The command must print nothing. A line the user did not ask for is amended out at once, the command runs again, and the report names the file or the setting that injected it. The same grep runs over git log --format=%B @{u}..HEAD before a push, and a hit stops the push. The reading of git log -1 --format=full that 2.5.0 relied on is replaced, because a reading is a judgment and a grep is not.
README names the setting that turns the default off
A new subsection, "Turning the harness default off", gives the settings.json that disables both lines in Claude Code:
{
"attribution": {
"commit": "",
"pr": ""
}
}It names the older boolean includeCoAuthoredBy and says that Codex, Cursor, and Gemini CLI carry their own equivalents.
Upgrade notes
SKILL.md stays under its size ceiling by pointing the type quick reference and the example gallery at references/conventional-commits.md and references/examples.md, which already carried them in full. No mode changed. The frontmatter description is unchanged.
v2.5.0
v2.5.0
Minor release, and the largest change to the skill's scope since 2.0.0. It stops being a
commit-and-PR authoring skill and becomes a git skill: a seventh mode takes branches,
rebases, conflicts, undo, and recovery. Alongside it, mode 4 becomes safe to run unattended,
and the no-attribution default becomes something the skill checks rather than something it
states. MINOR rather than MAJOR because nothing that worked in 2.4.0 works differently —
every one of the six existing modes behaves exactly as it did, and the read-only guarantee
that covered modes 1–3, 5, and 6 now covers mode 7 too.
One narrowing is worth knowing about before you upgrade. See Upgrade notes.
Mode 7: the rest of git
The skill used to say branching, rebasing, and merge-conflict resolution were "a different
task." For anyone whose only git skill this is, there is no different task — the exclusion
just handed those requests back to the agent's own priors, on exactly the operations that
lose commits. Mode 7 takes them: branches, merge against rebase, history editing, undo,
moving work, conflict resolution, and recovery.
It is read-only by default, like modes 1–3, 5, and 6. It reads the real state, tells you
what git is actually reporting, and gives you the commands. It executes only under the same
explicit autonomous request mode 4 needs.
Before anything that can lose work, it names the reflog entry or the branch that gets it
back — before the command runs, not after it fails.
The hard limits are absolutes, and they are the counter-instinct ones rather than a
restatement of the manual:
- Published history on a shared branch is never rewritten. Rebase, amend, squash, and
reset apply to unpublished work, or to your own feature branch once you say it is yours
alone. - Never force-push. Where you explicitly ask to update your own already-pushed branch
after a rebase, it uses--force-with-leaseand says what that protects: the lease refuses
the push if the remote branch moved since your last fetch, so a commit someone else pushed
there cannot be silently overwritten. A plain--forceoverwrites it and reports success. - Uncommitted work is never discarded without your confirmation —
reset --hard,
checkout -- <path>,restorewithout--staged,clean, andstash drop. - A conflict is never resolved by picking a side to make the sequence continue. Both
sides were written on purpose; it reads each one, decides from what the change was for, and
tells you what it decided and why. - No rebase, merge, cherry-pick, or bisect is ever left half-finished without the exact
state and the command that ends it.
The one piece of knowledge most worth having is that ours and theirs swap. In a merge
ours is the branch you are on. In a rebase your commits are replayed onto the base, so the
base is ours and your own work arrives as theirs. Assuming ours is your work is the
most common way conflict resolution goes quietly wrong, and the skill reads which operation
is in progress instead of assuming.
Two new reference files carry the depth: branching-and-history.md for everything except
conflicts, and conflicts.md for conflicts and rerere. They are separate on purpose — a
conflict is read mid-operation, and an agent stopped on a conflicted rebase needs one file it
can load on its own.
The no-attribution default is now checked, not asserted
The rule was already in 2.2.0 and agents kept breaking it. A rule stated once and ignored
is a defect in the skill, not in the agent.
The two lists are closed rather than sampled. The old wording said a trailer is never
inferred "from history, branch names, or the diff" — three examples, which left everything
unnamed reading as permitted. Now there are exactly two legitimate sources: your words in the
session, and the mode 6 squash transcription. Every forbidden one is named, because each of
them looks like permission: the agent's own identity, the model or tool name,
commit.template, a prepare-commit-msg or commit-msg hook, GIT_AUTHOR_* and
GIT_COMMITTER_*, CI variables, editor plugins, and the trailers on your existing commits.
A repository full of AI or Co-authored-by: lines grants nothing. Convention detection
matches subject shape, scope vocabulary, and tense — never trailers.
A template or hook that injects a trailer is a finding. The line is stripped and you are
told which file produced it, rather than it being kept silently.
The author identity is never set. No --author, no -c user.name, no -c user.email,
no writes to git config.
And it is verified after the fact. After every commit the agent makes itself, it reads
the whole message back from git log -1 --format=full; if an unrequested Co-authored-by:,
Signed-off-by:, generated-with line, or agent identity is present, it amends it out at once
and reads again to confirm. The same check runs across every commit a push would publish, and
a hit stops the push and names the commit and the line. A commit is not reported as done
until that check passes.
The opt-in is untouched: ask for a co-author, a sign-off, or agent credit in the session and
you get it.
Mode 4 is safe to run unattended
Its stops were real but scattered through prose, and nothing said what a stop must hand
back. A stop that produces an unusable handoff is barely better than not stopping. Every
stop now carries the same five fields: the goal and the exact blocked step; what was
attempted, with the git output verbatim; the causes eliminated and how; the single decision
needed from you; and the state that remains, with whether it is safe to leave.
Five conditions stop it, each naming the git state that is its evidence: a rejected push,
an ambiguous remote or upstream, staged work that will not group into one commit, a tag or
release that already exists, and a hook that fails. The state left behind is never tidied up
to make the report look clean.
Completion is proved rather than asserted. A command that exits zero is not evidence a
commit landed as intended, so mode 4 now claims completion only from git log -1 --format=full and git status read after the push, and shows you that output.
A rejected push is a concurrency signal, not an ambiguity. It means another writer moved
the branch. The skill fetches, reads the divergence, and reports the exact commits on each
side — and never rebases or merges it for you, because either choice picks an outcome that is
yours to make. The same rule reaches the release path: a rejected git push origin <tag>
means someone already published that version, and git tag -f is now named alongside
--force so neither reads as permitted.
Work in the repository the agent did not create is treated as yours — inspected, never
swept into a commit, never rewritten.
The frontmatter parses again
Releases 2.1.0 through 2.4.0 shipped YAML a strict parser rejected. description and
compatibility were plain scalars containing ": ", which YAML reads as a nested mapping
key, so safe_load aborted on the second line and never reached the rest. Claude Code's
loader tolerated it, so nothing surfaced the error. Both are folded block scalars now, with
the text preserved byte for byte.
Two CI workflows run on every push and pull request so this class of break cannot ship again.
One parses the frontmatter and holds description under its 1024-character limit. The other
resolves every references/ link, catches orphaned reference files and unclosed code fences,
and holds SKILL.md under its context-budget ceiling.
Files updated
SKILL.md— version 2.5.0; a rewrittendescriptioncovering the repository operations;
mode 7 with its hard limits; the read-only guarantee restated as modes 1–3 and 5–7; the
trailer-source and author-identity rules in the ground rules and their consequences in the
trailer block; stop conditions with the five handoff fields; a proof-of-completion section
carrying the post-commit and pre-push attribution checks; boundaries and freshness. To hold
the byte ceiling, depth moved into the reference files that already owned it — the
merge-commit message and theghgathering block topr-review.md, PR structure to
pull-requests.md, release style torelease-notes.md, the breaking-change example to
conventional-commits.md.references/branching-and-history.md— new. Branch naming and deletion, merge against
rebase and the criterion that decides, updating against a moved base, interactive rebase
and autosquash, splitting a commit, the three reset modes, cherry-pick, stash, worktrees,
recovery through the reflog, and the inspection commands.references/conflicts.md— new. Reading the markers, theours/theirsinversion,
resolving without a tool the repo does not have, proving the resolution before continuing,
ending or aborting each sequence, and rerere.references/conventional-commits.md— the attribution section names the forbidden trailer
sources and the author-identity ban.references/release-notes.md— the tag hard limits namegit tag -fand classify a
rejected tag push as another writer's publish.AGENTS.md— mode 7, the attribution rules, and the mode 4 changes, written self-contained.README.md— seven modes throughout; a mode 7 section; the attribution section documents
the closed source lists and the two checks; layout tree updated.GEMINI.md,.cursor/rules/git-authoring.mdc— mode 7, its hard limits, and the
attribution rules; both still defer toAGENTS.mdfor detail.
Upgrade notes
Drop-in for 2.4.0. All six existing modes behave identically, and the no-attribution default
still governs every one of them.
One narrowing. A hook bypass is no longer available, on request or otherwise. It
previously shared an exception with amend and rewrite — "unless the user explicitly reque...
v2.4.0
v2.4.0
Minor release. Adds a sixth mode: the skill now reviews an incoming pull request on the
maintainer's behalf. MINOR rather than PATCH because the new mode is new functionality.
Every request that worked in 2.3.1 behaves identically here — the five existing modes are
untouched — and the read-only guarantee that covered modes 1–3 and 5 now covers mode 6 too.
A mode for the receiving end
The five existing modes all serve the author of a change. Mode 6 serves the person on
the other side of the table: the maintainer deciding what to do with someone else's branch.
It runs only when asked — "review PR 412", "should I merge this", "look at this PR with
me" — and never fires as part of a commit or PR-authoring request.
It gathers the pull request read-only through gh: the diff, the CI checks, the existing
review threads, the linked issue, the commits and their authors, and the repository's merge
method. CONTRIBUTING.md is read when the project has one, and the existing review threads
are read before an opinion is formed, so a point someone already raised — or the contributor
already answered — isn't repeated back.
Empty gh pr checks output on a fork PR means "not run," not "passing." Workflows on a
cross-repository PR frequently need maintainer approval before they execute at all, so
silence there is reported as silence. A check is never asserted to have passed that wasn't
seen passing.
Blocking versus suggestion, calibrated to the project
Two buckets, and only two. Something blocks the merge only if merging leaves the project
worse off than not merging: it breaks the build or an existing test, loses or corrupts data,
opens a security hole, breaks a documented contract without notating it, or does not do what
the PR claims. Everything else — naming, structure, a nicer abstraction, broader tests — is
a suggestion, and a suggestion is never on its own a reason to withhold a merge.
The bar moves with the project. Applying a large project's standards to a small one is
the most common way this kind of review goes wrong. Where there is no stated convention, no
CI gate, and few contributors, most findings are suggestions the maintainer is free to take
or leave: missing tests in a repo with no test suite is not a blocker, and neither is
ignoring a rule the project never wrote down.
Two rules hold at any size. A blocker is never manufactured to make a review look
thorough — that produces exactly the reviews that make contributors stop contributing. And
the verdict leads: when nothing blocks, that is the first line, not the conclusion of
six paragraphs of observation.
It decides with you, and writes one block
The findings are talked through before anything is drafted — what the PR does, what blocks
it if anything, what is merely suggested — and then the maintainer decides. Taste is labelled
as taste. Merging over a raised finding is a legitimate call, noted once if genuinely risky
and not relitigated.
Then exactly one Markdown block is produced, for the decision actually made: either a
review comment (verdict first, blocking items separated from optional ones, pointing at
code and locations rather than at the contributor) or the merge-commit message. Not
both, and not preemptively.
The squash-merge message
On a squash merge GitHub prefills the subject from the PR title and the body from every
branch commit concatenated together. That default is usually noise, and it is the commit
that lands on the default branch and stays in git log forever — the most consequential
message in the PR lifecycle, and by default nobody writes it. Mode 6 writes it fresh from
the diff with the ordinary commit discipline; the contributor's subjects (wip, fix stuff) are a hint at intent, not source text to tidy. A merge commit keeps GitHub's
default, and a rebase merge has no new message to write, which is said rather than papered
over with one.
Co-authored-by: is transcribed here — the one exception to the no-attribution default.
A squash collapses every commit on the branch into one and credits only the PR author, so
when the branch has more than one commit author each is carried across as a
Co-authored-by: trailer copied exactly from the real commit metadata. This is the single
place the skill emits an attribution trailer without being asked, and it is still not
inference: it preserves authorship that already exists rather than asserting anything new.
The 2.2.0 default is otherwise untouched — no agent identity, no Signed-off-by:, and never
a name or address that didn't come from a real commit or from you.
It never touches the button
Mode 6 is read-only, and mode 4 does not extend to it. gh pr review, gh pr comment,
gh pr merge, gh pr close, gh pr edit, and any gh api call with a method other than
GET are never run. "Approve it", "go ahead", and "yes, merge" tell the skill what the block
should say — they are never permission to carry it out, and no autonomous request reaches
here. Mode 4 stages, commits, and pushes your work; it never lands someone else's.
Files updated
SKILL.md— version bump to 2.4.0; mode 6 added to the mode contract, the ground rules,
and the reference list; a mode 6 section with the gathering commands and the merge-message
rules; the trailer section gains the squash-merge exception. The frontmatter description
was recompressed to hold under the 1024-character limit — the mode enumeration gave up the
room, and every trigger phrase is intact.references/pr-review.md— new. Gathering the PR read-only, the fork-CI caveat, the
blocking-versus-suggestion test and how to calibrate it to project size, working the
decision through with the maintainer, the review comment, the merge-commit message with
co-author transcription, a worked example, and the hard limits.AGENTS.md— mode 6 and the mode 4 boundary, written self-contained.README.md— six modes throughout; mode 6 documented in the Use section; the attribution
section gains the one exception; layout tree updated.GEMINI.md,.cursor/rules/git-authoring.mdc— mode contract, review rules, and the
mode 4 boundary updated; both still defer toAGENTS.mdfor detail.
Upgrade notes
Drop-in replacement for 2.3.1. The five existing modes are unchanged, and the no-attribution
default from 2.2.0 still governs every one of them. Two things worth knowing: the read-only
guarantee now reads "modes 1–3, 5, and 6", and gh — optional for modes 1–5 — is required
for mode 6, since reading the pull request is the whole input. Mode 6 still only reads: it
writes Markdown for you to paste.
Full Changelog: v2.3.1...v2.4.0
v2.3.1
v2.3.1
Patch release. The v2.3.0 skill description ran to 1140 characters against the
1024-character limit for skill frontmatter. It is now 1000, leaving 24 characters of
headroom. No functional change: all five modes, the read-only guarantee on modes 1–3 and
5, and the no-attribution default behave exactly as they did in 2.3.0.
What changed
The description field in SKILL.md frontmatter is 140 characters shorter. The cuts
are redundant qualifiers — "request-selected", "the work itself", "explicitly", "a
complete" — not trigger surface. Every phrase the skill matches on is retained: commit
message or command, staged changes, which files belong in a commit, pull-request title or
description, release notes or a changelog entry, and stage-commit-push. So is the negative
trigger that keeps it from firing on branching, rebasing, and merge-conflict resolution.
Upgrade notes
Drop-in replacement for 2.3.0, and the only file that changed is SKILL.md. If your
tooling enforces the 1024-character description limit, 2.3.0 would not have passed it and
this release does.
Full Changelog: v2.3.0...v2.3.1
v2.3.0
v2.3.0
Minor release. Adds a fifth mode: the skill now writes the release note for a version
from what actually landed since the last release. MINOR rather than PATCH because the new
mode is new functionality. Every request that worked in 2.2.0 behaves identically here —
mode 4 gains the ability to publish a release, but only behind a further explicit ask —
and the read-only guarantee that governed modes 1–3 now covers mode 5 as well.
Release notes, grounded in the range
The note is built from the diff, not the commit subjects. Mode 5 establishes the range
from the last version tag reachable from HEAD, then reads git diff <base>..HEAD across
it. A commit whose message understated what it shipped doesn't get to understate it a
second time in the release note.
Tags are fetched first. A local tag list goes stale silently, and it goes stale most
often in exactly the repos that use this workflow: gh release create makes the tag on the
remote, so a machine that hasn't fetched since the last release doesn't have it. Skipping
the fetch produces a note for the wrong range that looks entirely correct.
A tag/version-field disagreement is reported, never resolved silently. When the last
tag and the project's version field disagree, the note says so and states which one it used
as the range base. The usual cause is benign — the field was bumped for the release you're
about to cut — but a tag ahead of the field means a release went out without one, which
is worth knowing before the next one does.
Entries are grouped by user-visible effect, not by file and not by commit. Several
commits delivering one change get one entry; internal churn that changes no behaviour is
left out unless it affects how someone uses or installs the project. Every line traces to a
real commit or hunk, and the commit-body discipline from 2.1.1 governs all of them — no
diff narration, no generic value claims, no invented rationale.
It won't invent your versioning scheme
If the repository has no versioning at all — no version-shaped tags, no version field,
no releases — mode 5 says so and stops. It will not create the repository's first-ever tag.
Whether a project uses SemVer or CalVer, whether tags carry a v prefix, and where the
canonical number lives are all decisions that a first tag settles silently, and they belong
to the maintainer. An unversioned summary of what changed is offered instead.
Your release style wins
Prior releases outrank the built-in template. Mode 5 detects tag prefix (v2.3.0 vs
2.3.0), title shape, heading levels, section names and their order, and bullet style from
gh release view or an existing CHANGELOG.md / RELEASE*.md, and follows them wherever
they conflict with the template — noting the deviation once rather than quietly
reformatting the project's release history.
The template is only the fallback for a repo with no release history: Breaking changes
first, because it's the thing a reader must not miss, then Added / Changed / Fixed /
Removed / Deprecated / Security / Upgrade notes / Full changelog. Sections with nothing
real in them are dropped rather than padded with "N/A" or "None", and the compare link is
emitted only when both tags actually resolve — no dead links.
Mode 4 can publish
Only on a further explicit request. Producing a release note is not a request to publish
one, and neither is approving the note you were just shown. Publishing takes mode 4 and an
explicit ask to publish, together.
gh is checked before anything else (command -v gh, gh auth status). If either check
fails, the note falls back to a Markdown block in the conversation and says which one
failed — an unavailable gh is never a reason to stop, because the note is the deliverable
and publishing is a convenience on top of it.
Hard limits. An existing tag or release for the version is never overwritten or moved —
mode 4 stops and reports it. No --force, no deleting a tag or a release. Mode 4's other
constraints are unchanged: it still never opens pull requests, still never force-pushes,
still never git add -A.
allowed-tools gains Bash(gh:*) for this. Without the grant, publishing degrades into the
read-only fallback instead of erroring, which makes a missing grant look exactly like an
uninstalled gh.
Files updated
SKILL.md— version bump to 2.3.0;allowed-toolsgainsBash(gh:*); mode 5 added to
the mode contract and the ground rules; a mode 5 section; mode 4 extended with publishing
and its limits; the reference list is now six files.references/release-notes.md— new. The versioning precondition, range establishment,
style detection, the fallback template, content rules, output targets, and thegh
publish workflow with its hard limits.AGENTS.md— mode 5 and the mode 4 publishing extension, written self-contained.README.md— five modes throughout; mode 5 documented in the Use section with example
output;ghnoted as an optional dependency with its fallback; layout tree updated.GEMINI.md,.cursor/rules/git-authoring.mdc— mode contract and release-note rules
updated; both still defer toAGENTS.mdfor detail.
Upgrade notes
Drop-in replacement for 2.2.0. The no-attribution default from 2.2.0 is untouched, and every
request that worked before behaves the same way now. Two things worth knowing: the read-only
guarantee now reads "modes 1–3 and 5", and gh is a new optional dependency — mode 5
uses it to detect your prior release style, and mode 4 needs it to publish, but the skill
degrades to Markdown output without it and tells you which check failed.
Full Changelog: v2.2.0...v2.3.0
v2.2.0
v2.2.0
Minor release. No attribution trailers by default, in every mode. Commits and
pull-request descriptions now carry no Co-authored-by:, Signed-off-by:,
Reviewed-by:, and no AI or agent identity of any kind. MINOR rather than PATCH
because the opt-in mechanism that replaces the old behavior is new functionality.
This changes a default you may be relying on. Mode 4 previously permitted AI
attribution; that carve-out is removed, and an autonomous commit now carries exactly
the trailers a mode-1 commit would — none. If you depended on autonomous commits
carrying an agent co-author, you now have to ask for it, per session or as a standing
line in your repository's agent context file. Nothing else about the four modes
changes, and the read-only guarantee on modes 1–3 is untouched.
What changed
The default is no attribution, in every mode. The commit author is whatever
git config user.name / user.email resolves to — the repo owner. The skill does not
look up, infer, or attribute the work to anyone else, and it will not invent a name or
an email address.
Mode 4's attribution carve-out is gone. It remains the sole execution exception,
but it is now an exception to the read-only rule and to nothing else. Being asked to do
the work is not a request to be credited for it.
Trailers are excluded from repo-convention detection. Convention matching covers
subject shape, scope vocabulary, and tense — never trailers. A repository whose every
commit carries Signed-off-by no longer produces one, because a trailer is an
assertion about who did the work and who vouches for it, which is not something to
infer from pattern matching. There is no contributor-detection step anywhere in the
workflow.
Still emitted by default, because neither is attribution: BREAKING CHANGE:,
required by the Conventional Commits grammar; and issue references (Closes #123,
Refs: #123) when you supply the issue or it is unambiguous from the branch name. It
never guesses an issue number.
Opting in
Markdown-only — no config file, no flag. A trailer is written when one of these holds:
-
You ask in the session — "sign this off", "add Sam as co-author", "credit the
agent". That applies to the session's work; the next session starts from the default
again. -
A standing instruction exists in the consuming repository's own agent context
file (AGENTS.md,CLAUDE.md, or equivalent). One line is enough:Sign off every commit with `Signed-off-by:` using my git user.name and user.email.
Supported on request: Co-authored-by:, Signed-off-by:, Reviewed-by: (and
Acked-by: / Tested-by:, Reported-by: / Suggested-by: / Helped-by:),
issue-reference footers, and AI or agent attribution. Named humans come only from
values you supply.
Files updated
SKILL.md— version bump to 2.2.0; ground rules rewritten around the new default;
trailer carve-out added to convention detection (step 4); footers step, self-check,
mode 4, and the "Footers / trailers" rules, which now carry the opt-in mechanism.AGENTS.md— the same, self-contained: ground rules, step 4, step 5, self-check,
mode 4, and a rewritten "Trailers" section with worked default and opt-in examples.README.md— new "Attribution and trailers" section covering the default and its
rationale, the absence of a mode-4 exception, per-session and persistent opt-in with
example wording and output, and the supported trailer list.GEMINI.md,.cursor/rules/git-authoring.mdc— mode contract and attribution rule
updated to match.references/conventional-commits.md— trailer catalogue reframed as off-by-default,
available on request. The grammar reference is unchanged; the sign-off example stays
as reference material, labelled opt-in.references/examples.md— the trailers example now shows the default form first,
then the same commit with attribution after an explicit request.references/scopes-and-repos.md— the Django and Linux-kernel trailer patterns stay
as reference material, marked explicitly as non-authorising.references/pull-requests.md— PR attribution aligned to the same default and opt-in.
Upgrade notes
Drop-in replacement for 2.1.1. The only behavioral difference is the one above: if you
want any attribution trailer, ask for it in the session or add a standing line to your
repository's agent context file.
Full Changelog: v2.1.1...v2.2.0
v2.1.1
v2.1.1
Patch release. Tightens what the skill puts in a commit body: every line now has to
carry information the diff can't, and omitting the body entirely is documented as the
normal, correct output rather than a gap. No new capability, no change to the four
modes, and the read-only and no-AI-attribution guarantees are untouched.
The problem this fixes: bodies were coming out padded with restatements of the subject,
file-by-file narration, and generic value claims. That costs the reader time and returns
nothing — worse, it teaches them that bodies in the repository carry nothing, so they
start skipping bodies, and the one commit that genuinely needed explaining gets skipped
along with the rest.
What changed
Every claim in a body must trace to evidence — a specific hunk in the diff, an issue
you referenced, or something you said in the session. A body assembled from filenames is
guesswork, and guesswork in permanent history is worse than silence. If no real reason
can be stated from that evidence, the body is omitted rather than invented.
A body earns its place only when it adds one of five things: the problem or trigger
behind the change; a non-obvious decision plus the alternative rejected and why; a
consequence a reader wouldn't predict from the diff; migration, operational, or
compatibility impact; or a reference the diff can't carry, such as an issue ID or an
incident. Anything else, and the commit is subject-only.
The test applied to every line: could a reviewer recover this from git show alone?
If yes, it gets cut.
Now banned outright: restating the subject in longer words; narrating what the diff
already shows ("updated X, then updated Y", file lists); generic value claims not
grounded in the change ("improves maintainability", "enhances readability", "better
developer experience", "for consistency", "for clarity"); preambles ("This commit…", "In
this change…"); and boilerplate section headers on a short message.
There is no minimum body length and no bullet quota. Fewer, denser lines beat more,
thinner ones.
What this looks like
Before — seven lines that narrate the diff and then make claims that would fit any
commit ever written:
refactor(payments): use tenacity for retries
This commit refactors the retry logic in the payment client. The custom
retry loop in `client.py` was replaced with the `tenacity` library, and
`pyproject.toml` was updated to add the dependency.
This improves maintainability and readability, and gives us a more
consistent approach to retries across the codebase.
After — four lines, each carrying something git show doesn't, and a corrected type:
fix(payments): stop retrying non-retryable card errors
The hand-rolled retry loop treated every exception as transient and
retried on 400-class card errors, which double-charged one customer
before we caught it. tenacity retries only on the transport errors we
list, so a declined card fails once.
Refs: INC-204
Pull-request descriptions
The same discipline now applies to mode 3. A section with nothing real to say is
dropped entirely rather than filled with "N/A", "None", or a paraphrase of the
summary — two substantive sections beat five padded ones.
This reverses a previous instruction. Up to 2.1.0 the skill was told to write "None"
under Breaking changes so a reviewer knew the question had been considered rather
than overlooked. The obligation to check for a breaking change is kept; only the
placeholder line is gone. A breaking-changes section that says something substantive
about compatibility — that a new header is optional, that existing clients keep working
— is a real claim and still belongs.
Files updated
SKILL.md— version bump to 2.1.1; step 5 (Compose the message) carries the body
ruleset with an explicit omit-unless instruction; step 6 (Self-check) gains the
git showtest; mode 3 gains the drop-empty-sections rule.AGENTS.md— the same step 5 and step 6 changes, kept in sync; the Body block
under "The format" rewritten to carry the full ruleset inline, sinceAGENTS.mdhas
to stand alone.references/craft.md— the canonical home for the ruleset: the body section expanded
with the evidence requirement, the five warranted categories, and thegit showtest;
five new entries in the anti-pattern catalogue.references/examples.md— new annotated "Cutting a padded body" before/after; existing
examples audited against the new rules.references/pull-requests.md— drop-empty-sections in the description guidance, the
revised breaking-changes rule, and the worked example updated to lead with substance.
Upgrade notes
Drop-in replacement for 2.1.0. Nothing about how you invoke the skill changes. Expect
two differences in output: more commits come back subject-only, and generated PR
descriptions no longer emit a "Breaking changes: None" line when there is nothing to
report.
Full Changelog: v2.1.0...v2.1.1
v2.1.0
v2.1.0
Minor release. Restructures how the skill's usage is described into four explicit, request-selected modes and adds a deliberate autonomous mode. The new mode is backward-compatible, so this is a MINOR bump rather than a patch; the guarantees that already governed commit-message, file-selection, and pull-request output are unchanged.
No breaking changes. Existing behavior for staged-commit messages, file selection, and pull requests is preserved; the change is that these are now framed as distinct modes with one new opt-in execution mode alongside them.
What changed
The skill now supports four modes, chosen by what you ask for:
- Commit command for already-staged changes (default) — reads the staged diff and presents the exact commit command to run.
- Choose files, then commit — selects one coherent set of files and presents the staging commands plus the commit command to run.
- Pull-request title and description — writes a title and structured Markdown description from the branch's diff against its base.
- Autonomous stage, commit, and push — runs the operations end to end, but only when explicitly asked to do the work itself.
Guarantees
- Modes 1–3 remain read-only. The agent never runs
git add,git commit, orgit pushin these modes — it presents the commands and you run them. A follow-up "go ahead" or "commit this" does not promote these modes to execution. - Modes 1–3 add no AI attribution of any kind.
- Mode 4 is the sole execution exception, and is never inferred — it activates only on an unmistakable request to carry out the work. In this mode only, the no-AI-attribution rule is relaxed (attribution is permitted but optional; other trailers must still be true), and pushing is done normally with no force-push and no
git add -A. - Every mode that emits a commit uses the quoted-heredoc form, including subject-only commits, for one consistent, expansion-safe shape.
Files updated
SKILL.md— version bump to 2.1.0; description and workflow reframed around the four modes; new "Choose the mode" section and Mode 4 section.AGENTS.md— four-mode usage section; inline commit examples converted to the heredoc form; Mode 4 workflow and attribution rules.README.md— usage section documents all four modes with example output; Layout note.GEMINI.md— pointer updated to describe the four modes and the mode boundary..cursor/rules/git-authoring.mdc— rule updated to the four-mode structure.references/conventional-commits.md— sign-off example aligned to the heredoc commit form.references/examples.md— worked examples and the file-selection walkthrough updated to the heredoc form and mode-2 framing.references/pull-requests.md— clarified as mode 3, read-only, distinct from the autonomous mode.
Upgrade notes
Drop-in replacement for 2.0.0. If you relied on subject-only commits being emitted as git commit -m "…", note that all emitted commit commands — including subject-only ones — now use the quoted-heredoc form.
Full Changelog: v2.0.0...v2.1.0
v2.0.0
v2.0.0 — git-commit-writer becomes git-authoring
This release grows the skill from a commit-message writer into a git authoring
assistant. It still does the one thing it always did — turn a staged diff into a commit
message worth reading six months later — and adds two capabilities on top: it can choose
which files belong in a commit, and it can write a complete pull request. The skill is
renamed to git-authoring to match its wider remit.
Everything is still plain Markdown, still portable across Claude, Codex, Cursor, and Gemini
CLI, and still has no dependency beyond git.
What's new
Pick the files to stage, then commit. Point the skill at a pile of unstaged work and ask
it to choose what belongs together. It reads the actual changes, selects the one coherent set
a careful developer would group — related work only, not everything, not an arbitrary mix —
and proposes the exact git add plus the commit. If several unrelated changes are in flight,
it stages the most coherent group first and tells you what it left for the next commit. This
runs only when you ask; the default flow still writes a message for whatever you have already
staged.
A pull-request expert. Ask for a PR and the skill detects the base branch (main/master)
automatically, reads the branch's commits and its diff against that base, and writes a strong
title with a structured description — summary, what changed, testing, and any breaking
changes — plus a reviewer checklist where it helps. It grounds every claim in the branch's
real history and diff, and is honest about what it can't verify (if the branch adds no tests,
it says testing is unverified rather than implying a pass). Output is plain Markdown you can
paste into the PR, or run yourself via gh.
A new reference, references/pull-requests.md, carries the deep PR guidance (base
detection, description structure, per-type emphasis, a worked example) and loads only when a
PR is actually being written, keeping routine commits fast.
Guarantees that hold throughout
- The agent never mutates git on its own. Staging, committing, pushing, and opening a PR
are proposed as exact commands and run only after you explicitly confirm. The only git the
skill runs unprompted is read-only inspection. This extends the original "commit only when
asked" promise to the new staging and PR paths. - No AI attribution, ever — unless you ask for it. Commits and pull requests read as your
own work. The skill never adds an AI co-author, a "generated by" line, or an AI author or
committer identity, so nothing places an AI in your repository's history or contributors. - The default is unchanged. If you never mention file selection or pull requests, the
commit flow behaves exactly as it did in 1.x.
Breaking change — upgrading from 1.x
The rename changes the skill's identity, its repository URL, and its install directory, so
existing installs must be re-pointed. Re-clone into the new path and remove the old copy so
your agent doesn't load both. For Claude:
# remove the old skill, then clone the renamed one
rm -rf ~/.claude/skills/git-commit-writer
git clone https://github.com/n-shadloo/git-authoring.git \
~/.claude/skills/git-authoringThe same applies to the other tools — swap the directory name (.agents/skills/…,
.cursor/skills/…) and the repo URL for git-authoring. Gemini CLI users re-copy
GEMINI.md. Nothing about how you use the skill changes; only where it lives and what it's
called. The MIT license and its holder are unchanged.
Files
- Renamed skill (
SKILL.md→git-authoring, version2.0.0) and the Cursor rule
(.cursor/rules/git-commits.mdc→.cursor/rules/git-authoring.mdc). - New
references/pull-requests.md. - Updated
AGENTS.md,GEMINI.md,README.md, andreferences/examples.md(now with a
file-selection walkthrough).
Compatibility
Requires git and a Git repository; language-agnostic. Pull-request output is plain Markdown,
so the GitHub CLI is optional — needed only if you choose to open the PR from the terminal.
Full Changelog: v1.1.2...v2.0.0
v1.1.2
This release refreshes the installation documentation for modern agent tooling.
Changes:
- Updated Codex CLI installation to use the recommended
.agents/skillsdirectory. - Improved installation instructions across Claude, Codex CLI, Cursor, and Gemini CLI.
- Refined wording for clarity and consistency.
This release contains documentation and metadata improvements only. No changes were made to the commit generation behavior or skill logic.
Full Changelog: v1.1.1...v1.1.2