Create a git commit with a subject matching the resolved convention (layered `source-control.md` config → project convention → Conventional Commits default), a Claude Co-authored-by trailer, and surgical staging (never `git add -A`), feeding the message to git via Bash heredoc. Use when: 'commit this', 'make a commit', 'commit with message <hint>', not for push, branch creation, or PR creation (use /pull-request).
Scanned 9/2/2026
Install to Claude Code
npx -y skills add melodic-software/claude-code-plugins --skill commit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Commit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/melodic-software-commit)More formats (shields.io, HTML) on the badges page.
---
description: "Create a git commit with a subject matching the resolved convention (layered `source-control.md` config → project convention → Conventional Commits default), a Claude Co-authored-by trailer, and surgical staging (never `git add -A`), feeding the message to git via Bash heredoc. Use when: 'commit this', 'make a commit', 'commit with message <hint>', not for push, branch creation, or PR creation (use /pull-request)."
argument-hint: "[message-hint]"
user-invocable: true
disable-model-invocation: false
shell: bash
metadata:
workflow-stage: implement
summary: Commit with the resolved convention and surgical staging
---
## Pre-computed context
Exec-bit backstop: !`bash "${CLAUDE_PLUGIN_ROOT}/skills/commit/scripts/exec-bit-check.sh" --probe 2>/dev/null || echo "unavailable — run the check manually"`
Config layer (user-global): !`test -f "$HOME/.claude/source-control.md" && echo present || echo absent`
## Repository context. Gather first
Collect these with **individual** Bash calls, one command per call, never combined into a single
invocation:
- Current branch, `git branch --show-current`
- Staged, `git diff --cached --stat | tail -1`
- Unstaged, `git status --short | head -20`
- Recent commits, `git log --oneline -5`
**The two pipes are the bound and belong in the command.** They were briefly restated as read-time
prose ("read only the summary line", "read at most the first 20 entries"); that bounds nothing. The
Bash tool returns a command's complete output into context before there is anything to decide about,
so a read-time cap is advisory after the fact. `head` / `tail` truncate before the result exists.
These are ordinary body Bash calls, not pre-compute: the shape #1619 is about is the harness
composing the whole `## Pre-computed context` block into one shell invocation, which does not apply
to a call the body tells you to make.
Then resolve the two repo-scoped config layers as separate calls. Each uses git's repo-root-relative
magic pathspec `:/`, which resolves against the top of the working tree, so these are correct from
any subdirectory and never need the repository root substituted into them. **Quote the pathspec**,
the leading `:` is pathspec magic, and quoting keeps the shell from reinterpreting it.
1. Team layer, trackedness.
`git ls-files --error-unmatch -- ":/.claude/source-control.md"`
2. Team layer, existence.
`git ls-files --cached --others -- ":/.claude/source-control.md"`
3. Personal overlay, existence.
`git ls-files --cached --others -- ":/.claude/source-control.local.md"`
Probes 2 and 3 deliberately omit `--exclude-standard`, so a gitignored file is still listed: the
personal overlay is expected to be gitignored, and an untracked team file has to be *seen* before it
can be reported as untracked.
**Combine probes 1 and 2 in that order**. Trackedness first, existence consulted only when
trackedness comes back nonzero. They are not independent readings:
| Trackedness (1) | Existence (2) | Team-layer state |
|---|---|---|
| exit 0 | not consulted | `present (tracked)`, this is a config layer |
| nonzero | non-empty output | `present but UNTRACKED`, deliberately **not** a config layer |
| nonzero | empty output | `absent` |
A single `--error-unmatch` call cannot separate the last two rows; it exits nonzero for both. That
is the whole reason probe 2 exists.
**A nonzero exit from probe 1 is a result, not a failure**. It answers the question, and probe 2
resolves which answer it is. The unknown-value rule is narrower than that: only a probe that could
not run at all (git unavailable, not a repository) yields an unknown value to carry on past. Never
fold `present but UNTRACKED` into "unknown". They mean opposite things, and treating the untracked
case as unknown is exactly what readmits a file the resolution rules exclude.
The git lines above moved out of pre-compute in #1619, the harness composes that block into one
shell invocation and a worktree-isolated agent refuses a git-bearing compound command it cannot
statically verify. Do not fold them back into `## Pre-computed context`.
**These are snapshots taken when they run, not substitutes for the checks.** The exec-bit
line only sees what was already staged when the skill loaded; anything staged in step 2 below is
invisible to it, so step 4 re-runs the script for real. The config-layer probes report **presence**,
not merged content. Read the layers that are present and merge them per key. A tracked-team layer
reported `present but UNTRACKED` is deliberately not a layer: resolution requires the team file to
be git-tracked, so an untracked or gitignored file at that well-known path must not drive the
convention.
**Every repo-scoped probe anchors at the repository root**, via the `:/` magic pathspec, never at
the session's current directory. A session started in a subdirectory would otherwise look for
`<cwd>/.claude/` and report both repo-scoped layers absent. Silently dropping the team convention
and `trailer_policy`, and producing a commit with the wrong subject shape or attribution. The
pathspec form also retires the older two-step (`git rev-parse --show-toplevel`, then substitute the
literal path it printed into the probe): a repository root containing `$(…)`, a backtick, or a
double quote is *not* made safe by wrapping the substituted text in double quotes, the substitution
still evaluates, or the quote terminates the argument, and `:/` never substitutes the root at all.
This matches the root-resolution requirement
[`${CLAUDE_PLUGIN_ROOT}/reference/config-resolution.md`](../../reference/config-resolution.md)
already states for resolution itself; the probes must not disagree with it. `exec-bit-check.sh`
anchors itself the same way.
## Per-commit checklist
Run this list **for every commit**, including the second and tenth commit of a session. These are
commands, not remembered facts. Steps 3–5 are the ones a long session silently stops performing.
1. **Resolve** the convention + `trailer_policy` across all three config layers (once per session
is enough; re-resolve if a layer changed).
2. **Stage** surgically by explicit path, after checking the four pre-existing conditions below.
3. **Format**, run the discoverable formatter scoped to this commit's paths, re-stage its fixes.
4. **Exec-bit**, run `exec-bit-check.sh --fix -- <this commit's paths>`, AFTER step 3.
5. **Pre-check** the drafted subject against the resolved pattern before invoking git.
6. **Commit** via the Bash tool: `git commit -F -` heredoc-piped, `--trailer` per `trailer_policy`.
7. **Report** the resulting SHA + subject to the user.
## Purpose
Encapsulates the canonical mechanic for building a commit message that honors a subject convention,
appending a `Co-authored-by:` trailer, and feeding the result to `git commit` via stdin, without
these failure modes:
- **PowerShell here-string syntax (`@'...'@`) inside a Bash tool call** produces `unexpected EOF` and
triggers fallback to writing the message to `.git/<TEMP>.txt`. `.git/` is git's internal directory;
scratch files there collide with `COMMIT_EDITMSG` and other internals.
- **`git commit -m "<multi-line>"`** flattens newlines unpredictably across shells.
- **`git add -A` / `git add .`** stages secrets, build artifacts, unrelated edits, the convention is
surgical staging.
## Subject convention ladder
The subject convention (WHAT shape a subject must take) resolves via a ladder, checked in order:
1. **The layered `source-control.md` convention config**. User-global, tracked team, and gitignored
personal overlay, merged per key by
[`${CLAUDE_PLUGIN_ROOT}/reference/config-resolution.md`](../../reference/config-resolution.md).
Resolve all three layers before using any value; a value from one layer alone is not the effective
config. When any layer declares one, the effective `subject_pattern` (and `type_list`, when
Conventional-Commits-shaped) is authoritative. Before pre-checking, expand `subject_pattern`: the
literal keyword `Conventional Commits` expands to the anchored pattern in step 3 below; anything
else is already a single anchored regex and is used as-is.
2. **The consuming project's own `CLAUDE.md`, `AGENTS.md`, rules, or commit-msg hook**, if the
effective merged config declares no `subject_pattern` (absent from every layer, which includes the
case where layers exist but only contribute other keys) but the project declares (or enforces via
lefthook, husky, commitlint, or a plain `.git/hooks/`) a different convention, follow that instead.
3. **Conventional Commits (11-type vocabulary)**, the default when neither of the above is present.
Every subject must match this anchored pattern:
```text
^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .+
```
Compliant examples:
- `feat(auth): add OAuth login flow`
- `fix(api): handle null user in /me endpoint`
- `docs: clarify rebase guidance`
- `refactor(skills)!: rename /simplify to /code-review`
The 11 types. `build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test`. Come from
the Conventional Commits spec, the Angular convention, commitlint's `@commitlint/config-conventional`
source, and `amannn/action-semantic-pull-request`'s default `types` list; all four agree on this exact
set. `security` is **not** a Conventional Commits type in any of them, never add it.
When the project enforces its convention with a commit-msg git hook, that hook is the authoritative
gate regardless of which rung above resolved the pattern; this skill's pre-check exists to fast-fail
client-side before the hook round-trip. When no layer resolves a `subject_pattern` and nothing is
inferable from the project's own files, point the user at `/source-control:setup` to persist a
convention instead of re-inferring one every commit.
## Task
1. Survey working tree (`git status`, `git diff --cached --stat`) to confirm what's staged.
2. Stage the intended files, but first check each path against these four pre-existing conditions,
*before* this step touches anything. Each is a state `git add` silently overwrites rather than
errors on, and in three of the four `git diff --stat` reads empty, so the obvious check misses it.
Rationale for each is in [reference/staging-preconditions.md](reference/staging-preconditions.md).
| Condition | Detect with | Action |
|---|---|---|
| Already-staged deletion | `git diff --cached --name-status -- <path>` reports `D` | Skip the path entirely; never `git add` over it |
| Staged rename, old side | same command reports `R<score> <old> <new>` whose `<old>` falls under `<path>` | Skip the old side entirely; never `git add` over it |
| Untracked files under a directory path | `git status --porcelain -- <path>` shows `??` entries | Stop and surface them; enumerate the intended files instead of adding the directory |
| Partial-staging split | `git diff --cached --stat -- <path>` **and** `git diff --stat -- <path>` both non-empty | Leave as-is; a blanket add sweeps in hunks the user deliberately left unstaged |
Run `git add <path>...` only for paths in none of those four states.
3. Run the [format-before-push check](reference/format-check.md) against the files just staged for
this commit (not the full staged set), re-staging any fixes.
4. Run the exec-bit check, **after** step 3, not before: a formatter re-stage in step 3 reads the
worktree file mode, so setting the exec bit any earlier would be silently undone by that later
`git add`.
```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/commit/scripts/exec-bit-check.sh" --fix -- <path>...
```
The script reports and fixes every newly-added file whose staged blob starts with a shebang while
its staged mode is `100644`, setting the worktree bit and the index entry in the order that
survives a later `git add`. **The pathspec is required**. `--fix` refuses an unscoped run
(exit 2) rather than sweeping mode changes across another session's staged work; pass `--all`
only to opt into a deliberate whole-index sweep. Rationale, cross-platform caveats, and the
manual fallback live in [reference/exec-bit.md](reference/exec-bit.md).
5. Draft a subject + optional body, scoped to the staged diff, shaped to satisfy the active subject
convention (default: the Conventional Commits pattern above).
6. Pre-check the subject against the pattern (fast-fail before invoking git).
7. Invoke `git commit -F -` via the Bash tool, heredoc-piped, adding `--trailer` per the trailer
template below only when the resolved `trailer_policy` calls for one.
8. Surface the resulting commit SHA + subject to the user.
## Canonical bash form
Use the **Bash tool**, not the PowerShell tool. Bash heredoc is the canonical form across all
platforms (Git Bash, Linux, macOS).
```bash
# --trailer line below applies only when the resolved trailer_policy calls for one
# (default: yes). trailer_policy "none" -> drop the --trailer line entirely.
# trailer_policy naming a different template -> substitute that template's text.
git commit -F - --cleanup=verbatim \
--trailer "<Co-authored-by trailer, placeholders filled>" \
<<'EOF'
<subject — shaped to satisfy the active subject convention>
<body — wrap at ~72 cols, blank line between paragraphs>
EOF
```
PowerShell here-string is shown only for reference. `git commit -F - <message-via-stdin>` with
`@'...'@` would work in a pure PowerShell tool call, but **never mix syntaxes inside one tool
invocation**. A PowerShell `@'...'@` block inside a Bash tool call leaves the bash parser unable to
terminate the heredoc.
**Hard rules:**
- Invoke via the **Bash tool**.
- Heredoc delimiter is single-quoted: `<<'EOF' ... EOF`. Single quotes prevent `$variable` and
backtick interpolation inside the message.
- `--cleanup=verbatim` preserves the message exactly, no auto-stripping of comments or whitespace.
- Never write the message to `.git/<TEMP>.txt`. If a real file is unavoidable, use `mktemp`.
- Never mix Bash heredoc with PowerShell `@'...'@` inside one invocation.
For a dirty shared index (concurrent sessions, pre-existing mixed WIP), the pathspec-limited form
and its staged-deletion hazards live in [reference/pathspec-commits.md](reference/pathspec-commits.md).
Default remains the plain index commit.
**The exec bit does not survive the pathspec form under `core.filemode=false`** (the Windows
default). `--only` records the working-tree mode, and with filemode off git cannot see the
`chmod +x`, so a correctly-set `100755` index entry is rebuilt as `100644`. Verified both
directions: the plain index commit preserves `100755`; the pathspec commit loses it. If this
commit includes a newly-added shebang file the exec-bit check corrected, commit that path with the
**plain index form** (splitting the commit if the rest needs a pathspec), and confirm with
`git ls-tree HEAD -- <path>` rather than trusting the index. Full detail and the two workarounds
that were tested and failed are in [reference/pathspec-commits.md](reference/pathspec-commits.md).
## Pre-check
Before invoking `git commit`, regex-match the drafted subject against the active convention's pattern
(default: the Conventional Commits pattern above).
On mismatch, surface the convention name and the compliant examples as actionable guidance, and ask
for a compliant subject before invoking git. The pre-check is shape-only; the project's `commit-msg`
git hook (when one exists) is the authoritative gate at commit time. The pre-check exists to save the
hook-startup floor when the drafted subject is obviously wrong, plus to give the user a fast
actionable error rather than an opaque hook failure.
## Trailer resolution
The default template is:
```text
Co-authored-by: Claude <model> <noreply@anthropic.com>
```
**The `<model>` placeholder is filled from your own knowledge of the running session** (e.g.
`Opus 4.8`, `Fable 5`). If uncertain, invoke `/usage` to confirm before committing. There is no
environment variable that auto-fills it, the trailer is part of the message body sent to
`git commit`, not git config.
**An optional context clause**, `Co-authored-by: Claude <model> (<context>) <noreply@anthropic.com>`,
e.g. `(1M context)`, may be added when the context window is a genuinely distinguishing fact about
the session and is known with confidence. It is **not** required, and its absence is not a defect.
Earlier versions of this skill mandated it; the mandate was removed because it matched neither the
harness's own injected guidance nor the observed practice in consuming repositories, so it was a
default that was silently ignored rather than followed.
### Which authority wins
Four sources can specify a trailer. Resolve in this order:
1. **A resolved `trailer_policy`** from any config layer. Authoritative. `none` means omit
`--trailer` from the `git commit` invocation entirely (do not append an empty or default
trailer); a value naming a different template means substitute that template exactly. `none` and
absence are different states.
2. **The consuming project's own conventions** (`CLAUDE.md`, `AGENTS.md`, rules), if they specify a
different attribution trailer, or none, follow those.
3. **Harness-injected commit guidance.** The Claude Code harness may inject its own commit
instruction into the session, naming a `Co-Authored-By` trailer. Adopt its **shape**, this is why
the context clause above is optional rather than mandatory, but **never copy its literal text**.
Observed first-hand: that injected guidance can carry a **hardcoded model name that does not match
the running session** (a `Fable 5` trailer injected into an Opus 5 session). Copying it verbatim
writes a false provenance claim into durable git history, which is precisely the harm this
template exists to avoid. Always fill `<model>` from actual session knowledge.
4. **This skill's default**, above.
Rung 3 is the rung earlier versions of this skill omitted entirely: the harness guidance is neither a
config layer nor a project convention, so a session receiving both it and this skill had no stated
tiebreak and silently followed whichever it saw last.
**Key spelling.** This skill emits `Co-authored-by`, the spelling GitHub's own documentation uses
exclusively and the one GitHub itself writes when it appends co-author trailers to a squash-merge
message. GitHub's attribution is not case-sensitive in practice (verified empirically: a commit
carrying a `Co-Authored-By:` trailer resolves its co-author in the GraphQL `Commit.authors`
connection just the same, the docs do not state case sensitivity either way), so this is a
consistency choice, not a correctness one: branch commits and the forge-written squash merges now
agree. Git preserves a trailer key's case verbatim (`git interpret-trailers` does not normalize it,
and no `trailer.*` config here changes that), so existing history keeps whatever spelling it was
written with, this skill never rewrites it. A consumer who wants a different spelling expresses it
as a `trailer_policy` template. Decision recorded in #1604.
## Unrelated uncommitted changes
If the working tree contains unstaged or untracked files that fall outside this commit's scope,
classify each before staging per `/source-control:pull-request create` (its unrelated-changes classification:
include / stash / separate-commit / discard). Do not duplicate that classification here; invoke
`/source-control:pull-request create` to surface it to the user.
## Staging discipline
Always `git add <specific-files>`, never `git add -A` or `git add .`. The risk is including secrets,
build artifacts, or unrelated changes that the user did not approve for this commit. If multiple
files are intentionally part of the commit, stage them by explicit list, not by wildcard, and skip
the partial-staging split per step 2 above.
Immediately after staging, run the format-before-push check and then the exec-bit check, in that
order. Catch what CI would otherwise catch on the push round-trip.
## Composition policy
This skill is the single source of truth for the commit mechanic. Other skills should compose
`/source-control:commit` rather than invoking `git commit` directly. Direct calls bypass the pre-check, trailer
logic, and surgical-staging discipline this skill exists to enforce.
**"Compose" means one of exactly two things, and a composing skill must name which:**
- **Re-invoke `/source-control:commit`** for the commit. This is the default and the only form that re-renders this
skill's full content. Prefer it whenever the commit is a discrete step a user could have asked for.
- **Run the per-commit checklist above yourself**, per commit, as commands. Permitted for a workflow
skill making many commits in one flow, where re-invocation per commit is disproportionate.
What is **not** composition is treating this skill as a convention absorbed once and thereafter
remembered. The mechanic that decays is not the shape of the commit message, that is reinforced
every commit, it is the ordered per-commit *checks*: the formatter run, the exec-bit fix, the
subject pre-check. Those produce no visible signal when skipped, so a session that has stopped
running them looks identical to one that has not, until CI says otherwise. That is the observed
failure this policy exists to prevent, not a hypothetical one.
Two properties make step 4 the cheap re-anchor for the checklist form: it is a command with an exit
code rather than a paragraph to recall, and the pre-computed probe at the top of this file surfaces
its finding before anything else is read.
Workflow skills without explicit commit semantics should report status at phase boundaries and let
the user or the next workflow stage decide commit timing; a skill with commit semantics in its
documented contract (e.g. `/source-control:pull-request create`) composes this one.
## Recorded deviations
- **Model invocation stays enabled** (`disable-model-invocation: false`, declared explicitly rather
than left to the default). The fleet archetype for a `/source-control:commit`-shaped skill is
`disable-model-invocation: true`. User-invoked only. This skill deliberately deviates because its
composition design requires the model to reach it: a workflow skill that composes the commit
mechanic cannot do so if the model may not load it. Compensating controls: surgical staging (never
`git add -A`), the subject pre-check before any git invocation, no hook bypass, and a
user-visible SHA + subject report after every commit.
## What this skill does NOT do
- **No `git push`**, that's `/source-control:pull-request create`.
- **No branch creation**, that's the project's branch-naming / branch-protection mechanisms (or
`/source-control:worktree create`).
- **No PR body composition**, that's `/source-control:pull-request create`.
- **No `git merge` / `gh pr merge`**, that's `/source-control:pull-request merge`.
- **No rebase**, that's `/source-control:pull-request create`.
- **No `--no-verify` or hook bypass**, if the project's `commit-msg` hook rejects the message,
surface the error and re-draft; never bypass.
## Reference index. Load on demand
| File | Load when |
|------|-----------|
| [reference/staging-preconditions.md](reference/staging-preconditions.md) | A step-2 precondition fired and the right action is not obvious, or judging whether a new condition belongs in the set. |
| [reference/format-check.md](reference/format-check.md) | Running step 3, formatter discovery, path scoping, the two-stage filter, partial-staging preservation. |
| [reference/exec-bit.md](reference/exec-bit.md) | Understanding or hand-running step 4, why both the worktree bit and the index, `core.filemode` caveats, manual fallback. |
| [reference/pathspec-commits.md](reference/pathspec-commits.md) | The index holds staged work outside this commit's scope. Pathspec `--only` semantics and the staged-deletion hide/restore sequence. |
| [`${CLAUDE_PLUGIN_ROOT}/reference/config-resolution.md`](../../reference/config-resolution.md) | Resolving or merging the three config layers, or interpreting a key's `none`-vs-absent state. |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!