commit
Fast path to commit. Gather the state below in one batch, reason about file safety
and scope, then commit.
Arguments
may be empty or contain directives. Recognize and combine:
| Directive | Behavior |
|---|
| / | Do NOT stage anything. Commit the existing index. |
| / | Stage only files that fit the current task's scope; leave unrelated tweaks out. |
| Use git commit --amend --no-edit
after staging. Do not rewrite the existing message. |
| Never append a trailer, even if the repo uses them. |
| anything else | Treat as a message hint or additional constraint — see step 5. |
Current state
Gather this before anything else, in one batch where the host allows it. Every
section below reasons off this output — do not re-run these commands later.
| Command | Used for |
|---|
git branch --show-current
| issue-number suffix on the title |
| what is untracked vs. modified vs. staged |
| unstaged size and file list |
| staged size and file list |
| recent title style |
| trailer style of the last commit |
Then read the repo's own commit convention, if it has one:
bash
grep -B 1 -A 10 -i "^##.*commit\|commit message\|commit format\|conventional commit" \
CLAUDE.md AGENTS.md .github/CONTRIBUTING.md 2>/dev/null | head -80 \
|| echo "(none found — use conventional commits)"
Staging rules
Source code, docs, and tracked configs are safe by default — don't overthink
them. Focus the skip judgement on generated and machine-specific artifacts.
Never auto-stage these:
- Build or incremental artifacts: , , , , , , , ,
- Caches and logs: , , , , ,
- OS / editor cruft: , , , ,
- Secrets: , — warn the user if they appear untracked
- Local machine state: ,
.claude/settings.local.json
, unless already tracked
Anything else — use judgement. If a path looks generated (hash suffix, inside a
cache-like dir, editor backup) and is untracked, skip it and list it in the
final report. If ambiguous, ask by skipping and reporting, not by prompting.
Never use
,
, or
. Stage with explicit paths.
Heavy-diff delegation
The
output from
Current state shows the size. Handle the common
case in-thread. Only delegate when the staged OR unstaged diff exceeds
~500 changed lines or
~20 files AND you need actual diff content
(not just stats) to decide scope, classify files, or compose the body.
Delegation is an optimization, not a requirement. On a host with no subagent facility, read
the diff in-thread instead — in chunks if it is large — and carry on. Nothing else in this
skill depends on the delegation step.
When delegating, dispatch one subagent:
- A read-only subagent is enough — it inspects the diff and answers, nothing more.
- The subagent has no conversation history — pass the exact output (or the
subset you need) in the prompt.
- Ask a single narrow question. Good shapes:
- "From this diff, which files belong to scope: ? Return file paths only."
- "Summarize the distinct changes in this diff as 3-5 past-participle bullets, no prose."
- Use the returned answer directly. Do not ask the subagent to write the
commit message — that stays in-thread with full project context.
Do not delegate for small diffs. The round-trip is slower than reading the
diff inline, and the main thread already has the conventions context.
Steps
- Parse .
- If : go to step 4.
- Stage safe files with . Apply the rules above.
- If : from the unstaged diff, pick only files whose changes fit
the scope described in or obvious from the combined diff;
leave the rest.
- Look at the staged diff (you already have from
Current state; read only if the message needs detail
beyond the stat).
If nothing is staged, stop and tell the user.
- Compose the commit message:
- Single-line title, ≤72 chars, .
- Types: , , , , , , , .
- Follow the project convention from Current state when present.
- If the current branch is , append to the title.
- A hint in — whatever the Arguments table did not match — steers the
message, not the staging: let it shape the title's description, and pass it
verbatim to so the body reflects it too. It narrows what the
message leads with; it never drops changes the commit contains.
- Body: invoke the skill and use what it returns. It weighs
the change and either derives a body from the code or returns two to three
mechanical lines. See Body below for the inline fallback.
- Attribution: this step runs the , so it is the last place a
footer can be caught. Read the assembled message and remove any
"Drafted with AI" or "Generated with" line, session or transcript link,
line, trailing rule, badge, promotional line, or
trailer crediting an assistant — wherever it came from,
including a body returned and a message being amended.
Add none either, and do not copy one forward from the previous commit: a
trailer already in the log is not licence to repeat it. A human
co-author trailer is fine where the repo's convention asks for one.
- Commit:
bash
git commit -m "$(cat <<'EOF'
<title>
<optional body>
EOF
)"
If was requested: git commit --amend --no-edit
.
- Never pass . If a pre-commit hook fails: fix the issue, re-stage,
create a NEW commit (do not amend unless the user asked).
- Final output: one line —
Committed <short-sha> on <branch>: <title>
—
followed by a short list if anything was left out.
Body
The body is the part a reader cannot recover from the diff: what the running code
does that forced the change, which call paths reach it, what breaks on update, when
it broke, and what was deliberately left alone.
Invoke the
skill to compose it. If the host cannot chain skills or
is not installed, do it inline:
Weigh the change first. Derive when it alters behaviour, a contract, a public
type, or a default, or when it fixes a defect. Otherwise — a rename, a formatting
pass, generated output, additive scaffolding — write two or three past-tense lines
naming what the file list does not show, and stop. Weight decides, not diff size.
To derive, answer these against the code, in order, in blank-line-separated
paragraphs wrapped at 80. Skip any question with no real answer:
- What changed, and what does the running code do that forces it? Open the
implementation behind the declaration, the client behind the handler, the
changelog behind the bump — not the diff again. A change holding several
independent decisions gets one paragraph each, including anything the fix
revealed and anything it made redundant.
- Which concrete inputs or call paths reach it, and which are ruled out?
- How did it fail observably — what a user saw, not "the type was wrong".
- What breaks for someone who updates, and why is it still correct?
- When did it break?
git log -S '<removed expression>' -- <path>
or
.
- What do the tests pin? Name the cases, never "added tests".
- What did you notice and deliberately not fix?
Never invent provenance. A hash appears only if a command returned it in this
session; a named call path was read in the source. No result means the paragraph is
dropped, not softened into "has likely been broken for a while".
Write it in plain words and short sentences. Each paragraph opens on a past-tense
verb, and the sentence after it is the reason, stated as behaviour.
Design rationale handed over from a comment-cleanup pass answers question 1 — fold
it into that paragraph rather than appending it as a block.
A project convention found in Current state overrides all of this. Some repos cap
body width or forbid paragraphs outright.
Nothing is appended after the last paragraph — see the trailer rule in step 5.
Out of scope
- Pushing, opening PRs, switching branches.
- Splitting into multiple commits.
- Rewriting history beyond a single .
- Adding files listed in via .