agentleFS
MarvellbrazilMarvellbrazil/my-agentic-skills/skills/conventional-commit/SKILL.md

conventional-commit

Skill4 starsChanged 48 days agoMITView on GitHub

Commits staged work using the Conventional Commits specification, landing one commit per concern with a short subject message only (no multi-line body or description by default), so the commit count follows the working tree. Short imperative subjects, scopes only when localized, breaking-change marker only at real breaking point. The repository's own commit convention and an explicit user instruction both override the per-concern default. Use when user types /conventional-commit, says "commit this conventionally", "make a conventional commit", or asks to commit staged changes. Never adds co-author trailer.

  • Reads credentials

30 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use api-design and grill-me.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • MicrosoftSkill

    api-design

    Guide for designing and documenting RESTful APIs. Use when asked to design an API, create endpoints, or document an API.

    Also in 3 projects that use this

  • GoogleSkill

    grill-me

    A relentless interview to sharpen a plan or design.

    Also in 5 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

22 rules. 0 are common, 10 are distinctive, and 12 are very specific to this project.

  • 0

    Common

    Most of the 27 edited versions we read keep these.

  • 10

    Distinctive

    This author's own choices. Worth a look.

  • 12

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Commit the working tree as one or more atomic commits that follow the Conventional Commits 1.0.0 specification, with short subjects and no …

    line 9
  2. Very specific:

    No co-author trailer: Do not append Co-Authored-By:, Signed-off-by:, Generated with, or any attribution line.

    line 13
  3. Very specific:

    Short message only by default: Do not write a commit body, multi-line paragraph, or detailed description unless explicitly requested by the …

    line 14
  4. Distinctive:

    Run these three and read the output.

    line 23
  5. Very specific:

    If a secret is staged, stop the commit entirely, unstage it with git restore --staged <path>, and report it.

    line 51
  6. Distinctive:

    Only rank 3 applies when nothing else speaks.

    line 69
  7. Distinctive:

    Every commit must still be independently revertable and leave the tree in a working state — builds and tests pass at that commit.

    line 83
  8. Distinctive:

    When you do split, order the commits so each builds on a valid previous state:

    line 106
  9. Very specific:

    Do not invent types such as wip, update, improvement, or misc.

    line 185
  10. Distinctive:

    Add a scope only when the change is confined to one module, package, or bounded context, and the scope name helps a reader locate it.

    line 189
  11. Very specific:

    Use a scope: fix(payment-service): ..., feat(auth): ..., refactor(api)!: ...

    line 191
  12. Distinctive:

    Use the vocabulary the codebase already uses for that directory or package.

    line 198
  13. Very specific:

    Put ! immediately before the : when the change breaks a contract that a consumer depends on.

    line 203
  14. Very specific:

    When you use !, you must also add a BREAKING CHANGE: footer explaining the migration.

    line 220
  15. Very specific:

    Omit by default: Do not add a body, multi-line paragraph, or detailed explanation unless the user explicitly requests one, or when …

    line 232
  16. Distinctive:

    If explicitly requested, separate it from the subject with one blank line.

    line 234
  17. Very specific:

    Use BREAKING CHANGE: (uppercase, with the space) — it is the token the spec defines and the one changelog and release tooling keys on for …

    line 246
  18. Very specific:

    Use a heredoc only when a footer (e.g.

    line 269
  19. Distinctive:

    Check for an accidental co-author trailer using git's own trailer parser:

    line 311
  20. Very specific:

    Prefer the trailer parser over grepping git log --format='%B', for two reasons:

    line 322
  21. Very specific:

    If you do grep, always pass -i:

    line 327
  22. Distinctive:

    Verify the count is what you intended before pushing:

    line 368

“Common” is measured: kept by at least 6 in 10 of the 27 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Conventional Commit

Commit the working tree as one or more atomic commits that follow the Conventional Commits 1.0.0 specification, with short subjects and no co-author trailer.

Non-Negotiables

  1. No co-author trailer. Do not append Co-Authored-By:, Signed-off-by:, Generated with, or any attribution line. Not from the agent, not from a tool. This is the whole difference from /conventional-commit-with-coauthor.
  2. Short message only by default. Do not write a commit body, multi-line paragraph, or detailed description unless explicitly requested by the user or required for a BREAKING CHANGE: migration footer. Keep the commit message single-line and concise.
  3. Never use git commit -a. Stage deliberately so unrelated edits cannot leak into a commit.
  4. Never git add . or git add -A blindly. Inspect the tree first. git add . is how .env, build output, and a half-finished refactor end up in an unrelated commit.
  5. Never commit without reading the diff. The message must describe what actually changed, not what the task description said would change.
  6. Never invent a scope or a type that does not fit. A wrong scope is worse than no scope.
  7. Never amend, rebase, or force-push a commit you did not just create in this session. If the user asked for a commit, make a commit.

Step 1 — Inspect Before Staging

Run these three and read the output. Do not skip the diff.

git status --short --branch
git diff --stat
git diff --cached --stat

Then read the actual changes. For a large diff, read per file rather than dumping everything:

git diff -- <path>
git diff --cached -- <path>

Classify every changed path before writing a single commit message:

CategoryExamplesAction
Source changesrc/, app/, lib/Commit, grouped by intent
Test change*.test.ts, tests/, *_test.goCommit with its subject, or separately if the tests are independent
DocumentationREADME.md, docs/, *.mdSeparate docs: commit
Dependency manifestpackage.json, pyproject.toml, lockfilesSeparate build: or chore: commit
CI configuration.github/workflows/, .gitlab-ci.ymlSeparate ci: commit
Generated outputdist/, build/, *.min.js, coverageDo not commit. Add to .gitignore and tell the user
Secret or credential.env, *.pem, id_rsa, credentials.jsonDo not commit. Stop and warn the user immediately
Unrelated work in progressAnything you cannot attribute to this taskLeave unstaged and tell the user

If a secret is staged, stop the commit entirely, unstage it with git restore --staged <path>, and report it. A secret in a commit is not fixable with a follow-up commit.

Step 2 — Decide How Many Commits

One logical change per commit. A commit must be revertable on its own without dragging unrelated work with it.

Precedence: who wins when commit rules disagree

The per-concern rule below is this skill's default, not an override. Resolve any conflict in this order, highest first:

RankSourceExample
1The user's instruction"just make one commit", "one commit per file", "squash it all"
2The repository's conventionAGENTS.md, CONTRIBUTING.md, commitlint.config.*, an established git log pattern
3This skill's defaultOne commit per concern, short messages

Ranks 1 and 2 are authoritative. If the user asks for a single commit, make a single commit — do not argue the per-concern default at them. If the repo's commitlint config or CONTRIBUTING.md prescribes a different granularity, shape, or message form, follow it and note the divergence from this skill's default.

Only rank 3 applies when nothing else speaks.

One commit per concern

A concern is one conventional type applied to one bounded context — a module, package, or subsystem. The commit count is a property of the working tree, not a target: enumerate the concerns, then land one commit each.

Working treeCommit count
One concernOne commit
Trivial tree (≤3 files, one type, one module)One commit — do not manufacture a split
Two or more concernsOne commit per concern

Enumerate before you stage anything. Read the diff, name each concern as a type(scope) pair, and let that list be your plan. A tree that yields four concerns yields four commits; a tree that yields one yields one.

Every commit must still be independently revertable and leave the tree in a working state — builds and tests pass at that commit. If a split violates either, the split is wrong and those pieces belong together.

Do not split when

Splitting is the default, but it stops at the boundary of a single concern. Never split one concern across commits:

  • A feature and the test that proves it. The test is part of the feature, not a separate concern — splitting leaves the test commit red on its own.
  • Several files changed for one reason — a rename plus its call sites, a new field plus its migration plus its serializer.
  • A fix and its regression test.
  • The "second commit" is a subset of the first with no independent meaning (feat: add thing + refactor: tidy thing you just wrote).
  • You would have to invent a scope or a type for the second commit that does not really fit. That is a signal there was only one concern.
  • The split would produce a commit that does not build or does not pass tests.

Split when

  • The working tree spans two or more conventional types that are genuinely independent (feat + fix, refactor + docs).
  • The tree touches unrelated subsystems a reviewer would not review together.
  • A mechanical rename or format sweep is mixed with a behavioral change. Always split these — a reviewer cannot see the real change inside 400 renamed lines.
  • A dependency bump is mixed with the code that uses the new dependency.
  • Tests for feature A are mixed with feature B.
  • Two unrelated fixes landed in one working tree.
  • Source changes, documentation, and CI configuration are mixed. Each is its own concern and gets its own commit.

When you do split, order the commits so each builds on a valid previous state:

  1. Dependency and config changes (build:, ci:, chore:)
  2. Mechanical refactors and renames (refactor:)
  3. Behavior changes (feat:, fix:, perf:)
  4. Tests (test:) when they do not belong with the subject
  5. Documentation (docs:)

Step 3 — Stage One Logical Change

Stage by path, or interactively when a single file contains two logical changes.

git add <path> <path>

For a file with mixed concerns, stage hunks:

git add -p <path>

-p prompts per hunk with y (stage), n (skip), s (split hunk), e (edit hunk manually), q (quit). Use s first — it splits a hunk into the smallest pieces git can separate. Use e only when s cannot isolate the change, and remember that editing a hunk changes what gets committed.

To stage a new file's content without staging the file for creation, or to stage only deletions:

git add -N <new-file>
git add -p <new-file>
git rm --cached <path>

Then verify exactly what is staged, and nothing more:

git diff --cached --stat
git diff --cached

Step 4 — Compose the Message

Grammar

Default format (short message only):

<type>[optional scope][!]: <short description>

Extended format (only when explicitly requested by user, or for breaking changes):

<type>[optional scope][!]: <short description>

[optional body]

[optional footer(s)]

By default, omit the body entirely. A concise single-line subject message is the standard.

Type

Choose exactly one. The type describes the intent of the change, not the file type.

TypeUse forNot for
featA new capability visible to a user or a consuming systemRefactors that add no capability
fixA correction to broken behaviorA change that was never broken
docsDocumentation onlyCode comments that ship with a feature
styleFormatting, whitespace, semicolons, no behavior changeAnything that alters behavior
refactorRestructuring with no behavior changeA refactor that also fixes a bug — split it
perfA change whose purpose is speed or memoryA refactor that happens to be faster
testAdding or fixing tests onlyProduction code
buildBuild system, bundler, dependency manifests, lockfilesApplication source
ciCI configuration and pipeline scriptsThe app's own scripts
choreMaintenance that fits nothing above: .gitignore, tooling configUser-visible behavior
revertReverting a previous commitA forward fix

Do not invent types such as wip, update, improvement, or misc. If nothing fits, the change is probably two commits.

Scope

Add a scope only when the change is confined to one module, package, or bounded context, and the scope name helps a reader locate it.

Use a scope: fix(payment-service): ..., feat(auth): ..., refactor(api)!: ... Omit a scope: a change that spans the whole project, or a project small enough that the scope is always the same.

Rules for scope names:

  • Name a module or bounded context, never a file. fix(payment-service) not fix(payment.service.ts).
  • Lowercase, kebab-case, no spaces, no dots.
  • Use the vocabulary the codebase already uses for that directory or package.
  • Never invent a scope for a change that is not actually localized. A general change with a scope is a lie about blast radius.

The breaking-change marker !

Put ! immediately before the : when the change breaks a contract that a consumer depends on. ! is not "this commit is big" or "this commit is important to me" — it is a promise that upgrading requires the consumer to act.

! is warranted for:

  • A removed or renamed public function, class, endpoint, or CLI flag.
  • A changed API request or response schema, a changed version in a URL, a changed event payload.
  • A changed database schema that existing code or migrations cannot read.
  • A changed default that silently alters behavior for existing callers.
  • A bumped major version of a public dependency contract.

! is not warranted for:

  • A large diff.
  • A refactor with identical external behavior.
  • An internal rename that no consumer can observe.
  • A bug fix that restores documented behavior.

When you use !, you must also add a BREAKING CHANGE: footer explaining the migration. ! in the subject is the signal; the footer is the substance.

Subject description

  • Imperative mood: add, fix, remove, rename — not added, adds, adding. It must complete the sentence "This commit will ___".
  • Lowercase first letter unless the word is a proper noun or an identifier (feat: add OAuth2 support, not feat: Add oauth2 support).
  • No trailing period.
  • Aim for 50 characters; hard-stop at 72. Git tooling truncates at 72 and the rest is invisible in git log --oneline.
  • Describe the what, concretely. fix: handle null user id in session lookup beats fix: bug fix.

Body

Omit by default. Do not add a body, multi-line paragraph, or detailed explanation unless the user explicitly requests one, or when documenting a BREAKING CHANGE: migration.

If explicitly requested, separate it from the subject with one blank line. Wrap at 72 characters. Explain the problem and constraints. Never restate the diff.

Footers

One blank line after the subject (or body if extended). Each footer is a token followed by : or #.

BREAKING CHANGE: <what breaks and what the consumer must do>
Refs: #412
Closes: #418

Use BREAKING CHANGE: (uppercase, with the space) — it is the token the spec defines and the one changelog and release tooling keys on for the major version bump. BREAKING-CHANGE: is accepted as an alias by the spec and by those tools.

Note that git's own trailer parser does not recognize BREAKING CHANGE: — a token containing a space is not a trailer to git interpret-trailers, so %(trailers:key=BREAKING CHANGE) returns empty and the footer shows up in %b. That is expected, not a malformed message: Closes: and Refs: parse as trailers, BREAKING CHANGE: does not. Use the space form regardless, because the spec and the release tooling are the consumers that matter here. If you need git's parser to see it, the hyphenated BREAKING-CHANGE: is the form that works.

The four required shapes

feat: added chatbot feature using claude
perf!: optimalized images across all features
fix(payment-service): fixed request timed out error on client side
refactor(api)!: changed schema version into v3

In practice, prefer the imperative form (feat: add chatbot feature using claude) — it matches the convention git itself uses and reads correctly in a changelog. If the project's existing log uses past tense, match the log.

Step 5 — Commit

Default: single short message. Pass the message with -m:

git commit -m "fix(payment-service): handle client-side request timeout"

Use a heredoc only when a footer (e.g. BREAKING CHANGE:) or an explicitly requested body is needed:

git commit -F - <<'EOF'
fix(payment-service)!: drop legacy v1 callback protocol

BREAKING CHANGE: v1 callback protocol removed. Use webhook endpoints.
EOF

Step 6 — Verify and Repeat

Record how many commits you intended before you started. Then loop back to Step 1 until the tree is clean.

git status --short
git log --oneline -5

Confirm:

  • Every commit's subject matches ^[a-z]+(\([a-z0-9-]+\))?!?: .+$
  • No subject exceeds 72 characters
  • No commit contains a co-author or attribution trailer
  • Each commit is independently revertable
  • No commit mixes a mechanical sweep with a behavioral change
  • Every ! has a matching BREAKING CHANGE: footer
  • No secrets, build output, or editor cruft was committed
  • Nothing left staged that should not have been
  • The commit count equals the number of concerns you enumerated in Step 2
  • No commit carries a body it did not need

Set N to the number of commits you just made — the concern count from Step 2, which is whatever the working tree produced — then run these.

Validate the subjects mechanically rather than by eye:

# N is the concern count from Step 2, not a fixed 1
N=4
git log --format='%s' -"$N" | grep -vE '^[a-z]+(\([a-z0-9-]+\))?!?: .+$' && echo 'INVALID SUBJECTS ABOVE' || echo 'all subjects valid'

Check for an accidental co-author trailer using git's own trailer parser:

N=4
for sha in $(git log --format=%H -"$N"); do
  t=$(git log -1 --format='%(trailers:key=Co-authored-by,valueonly)' "$sha")
  [ -n "$t" ] && echo "UNWANTED TRAILER on ${sha:0:7}: $t"
done
echo 'trailer check done'

Prefer the trailer parser over grepping git log --format='%B', for two reasons:

  • It tells you which commit. A grep over a range reports that a trailer exists somewhere; the loop above names the offending SHA, which is what you need to fix it.
  • It is case-insensitive on the key. The trailer is conventionally written Co-Authored-By (capital A and B), so a case-sensitive grep 'Co-authored-by' silently matches nothing and reports a false all-clear. The parser accepts Co-authored-by, Co-Authored-By, and CO-AUTHORED-BY alike.

If you do grep, always pass -i:

N=4
git log --format='%B' -"$N" | grep -i -E 'co-authored-by|generated with' && echo 'UNWANTED TRAILER (find the SHA above)' || echo 'clean'

Confirm the tree is clean and every commit is pushed as expected:

git status --short --branch

If you split one concern across commits

The defect is not the commit count — it is a concern that got cut in half. feat: add the token service followed by test: cover the token service is two commits where one concern existed, and the first one is red on its own. Fix it before pushing; history is cheap to rewrite while it is local.

Squash the fragments back into one commit, keeping the message you want:

# Collapse the last 3 commits into one, reusing the message of the oldest
git reset --soft HEAD~3
git commit -F - <<'EOF'
feat(scope): the single message for the whole change
EOF

--soft keeps every change staged and rewrites nothing on disk.

HEAD~3 requires four commits to exist, not three — the count is the number of parents to walk back, so squashing the last three needs a fourth behind them. On a repository where the commits to squash reach the root commit, git reset --soft HEAD~3 fails with fatal: ambiguous argument.

To collapse everything down to a single root commit, reset to the root and then --amend it. A plain git commit here would leave two commits — the root plus a new one:

# Collapse all history into one commit
git reset --soft "$(git rev-list --max-parents=0 HEAD)"
git commit --amend -F - <<'EOF'
feat: the single initial message
EOF

Verify the count is what you intended before pushing:

git rev-list --count HEAD

Only do this for commits you created in this session and have not pushed. If the branch is already pushed, do not rewrite it — leave the history as it is and note the mistake, rather than forcing a rewrite on anyone who has fetched it.

Common Mistakes

MistakeWhy It BreaksCorrect Approach
git add . then one commitUnrelated work, secrets, and build output land together and the commit cannot be reverted cleanlyStage by path, or git add -p for mixed files
fix: fixed the bugSubject says nothing a reader can act onfix(auth): reject expired refresh tokens on rotation
feat: Add LoginCapitalized and vaguefeat(auth): add OAuth2 login
Scope on a project-wide changeMisleads the reader about blast radiusOmit the scope
! on a large but compatible refactorSignals a breaking change that does not exist; breaks downstream versioningUse ! only for real contract breaks
! without a BREAKING CHANGE: footerConsumers learn nothing about the migrationAlways pair them
Body or footer passed as a second -mWorks — git inserts a blank line — but the message is easy to mis-assemble and impossible to read in the commandPrefer -F - with a heredoc for anything past a subject
Rename sweep mixed with a real fixThe real change is invisible in reviewSplit into refactor: then fix:
Amending a pushed commitRewrites shared historyMake a new commit
Committing a generated lockfile alone with no manifest changeLockfile drifts from the manifestCommit the manifest and the lockfile together as build:
Trailing period on the subjectBreaks the conventional grammar and changelog toolingNo trailing punctuation
Message describing the plan, not the diffLog lies about what shippedRead git diff --cached before writing the message
Everything in one commit when the tree holds several concernsA revert drags unrelated work with it; git bisect lands on the wrong changeEnumerate concerns, then one commit each
feat: add X + refactor: tidy XThe second commit is a subset of the first and cannot be reverted independentlyOne commit — the tidy-up is part of adding X
Feature and its test in separate commitsThe test commit is red on its own; each commit must be a working stateCommit them together
Inventing a scope to justify a second commitIf no scope or type genuinely fits, there was only one concernReconsider; one concern means one commit
Ignoring a repo convention or a user's "one commit" requestThe precedence chain puts both above the per-concern defaultFollow the instruction or the convention
A body written out of habit on a self-explanatory subjectThe reader pays for prose they do not need on every git logShort message by default; a body only when asked

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.