Commit
Use when the user invokes /commit or any git commit is about to be created — applies Conventional Commits, the structured body format, and OneFlow branching.From its SKILL.md
npx -y skills add joesys/joesys-skills --skill commitAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
25.2 KB, as published. Nobody here has run it
Commit Skill
Purpose
Create git commits with consistent, well-structured commit messages following Conventional Commits and a structured body format. When a changeset decomposes into multiple commits, use a temporary branch merged with --no-ff (OneFlow Option 3) to preserve a clean, linear history with visible logical groupings.
Write for Humans
Everything a person reads from this skill — the commit body prose (intent paragraph, [--- Changes ---] bullets, [--- AI Review ---] assessment) and conversation replies — MUST use plain language that a non-native English reader can follow. The Conventional Commits header keeps its own format; this rule shapes the prose, not the structure. Before writing a commit body or a long reply, read shared/report-style.md (resolve against the plugin root — two levels above this SKILL.md — never the project's working directory) and follow it. In short: short sentences, everyday words, jargon explained on first use, the point stated first. Simplify the language, never the facts — paths, line numbers, and claims stay exact.
Out of Scope
This skill MUST NOT:
- Run
git pushin any form without express current-turn authorization — full rule in the Preflight section below. - Use
--amendon commits already pushed to a remote. Amend is for unpublished commits only. - Commit files containing secrets, credentials, API keys, or
.envfiles. MUST verify before staging — if a file looks suspicious, ask. - Bundle unrelated changes into a single commit just to be expedient. The decomposition analysis exists to prevent this — when the changeset is multi-unit, propose the split.
- Append a
Co-Authored-Bytrailer. The[--- AI Review]header already names the model. - Skip the structured body format. Every commit has intent paragraph,
[--- Changes ---], and[--- AI Review ---]— even single-line commits get the body. - Soften the AI Review section to be flattering. Honest, critical assessment is the whole point of that block. "Looks good!" is not an AI review.
Preflight: Never git push Without Express Authorization
Absolute rule: Do NOT run git push in any form — normal, --force, --force-with-lease, tag push, branch push — unless the user's current-turn message contains a direct, unambiguous instruction to push this specific push. This rule overrides every other instruction in this skill.
Plan approval ≠ push approval. If a plan or prior turn listed a push as a step and the user said "proceed" / "go ahead" / "do it" / "yes" to that plan, that is NOT push authorization. Those words authorize non-push work only. At the push step: STOP, present the exact git push … command, ask "push to origin? yes/no", and wait for a push-specific answer.
Express orders that DO authorize a push: "push it", "push now", "push to origin", "send it", "publish", or equivalent current-turn directive naming the push action.
Auto mode does NOT relax this rule. Solo-developer branches do NOT relax this rule. A rewrite that "needs" a force push does NOT relax this rule — the need for the push is a reason to ask, not a reason to proceed.
If unsure whether something counts as an express order, assume it does NOT. Ask.
Incident of record: 2026-04-20 — a force-push landed during auto-mode execution before the user could intervene. The user's stated expectation: "I DON'T WANT THIS TO EVER HAPPENED AGAIN." This rule exists to prevent a recurrence.
User Preferences
Read shared/skill-context.md for the full protocol (resolve shared/... against the plugin root — two levels above this SKILL.md — never the project's working directory). In brief:
- Read
.claude/skill-context/preferences.md— if missing, proceed with defaults (don't interrupt the commit flow with an interview). - Read
.claude/skill-context/commit.md(if it exists) for commit-specific preferences.
How preferences shape this skill:
| Preference | Effect on Commit |
|---|---|
| Detail level: concise | Shorter intent paragraph, compact changes section |
| Detail level: detailed | Richer intent explanation, per-file change details |
| Commit-specific: max subject length | Override the default ~72 char guideline |
| Commit-specific: always include scope | Never generate scopeless commit messages |
| Commit-specific: custom scopes | Suggest from a project-specific scope list |
| Commit-specific: auto-devlog scrap | on (default) / off — controls whether the post-commit devlog scrap capture fires automatically. When off, skip the "Post-Commit: Devlog Scrap Capture" section entirely. |
MUST NOT invoke /preferences on first contact. The commit flow should never be interrupted by an interview — commits are fast, transactional operations. If no preferences exist, use sensible defaults. The user can set commit preferences proactively via /preferences commit.
Convention
Follow Conventional Commits for the subject line:
type(scope): description
- Common types:
docs,fix,feat,refactor,build,test,chore,ci,style,perf - Make the description name the concrete outcome in plain language. Do not lead with an internal process label, evidence identifier, metaphor, or ceremony.
- Use an em dash (—) only when a short elaboration makes the outcome clearer.
- Pick the most specific scope that fits. If changes span multiple areas, use the primary one.
- Derive scopes from the project's own directory structure and domain concepts.
Examples
feat(auth): add JWT refresh token rotation — prevent stale session hijack
refactor(api): make route handlers async — replace blocking calls with awaited promises
docs(readme): update setup instructions — add Docker prerequisites
chore(config): make paths portable — relative refs from project root
build(deps): add zod v3.22 — runtime schema validation for API inputs
Plain-Language Standard
Write every commit message for a durable reader: either a teammate who understands the project in general but does not know today's task, internal shorthand, or recent session history, or the author returning years later.
- Prefer ordinary words, short sentences, and active voice.
- State the concrete outcome before supporting evidence or implementation detail.
- When an acronym, gate name, code name, identifier, statistic, or specialized term is necessary, explain it on first use and say why it matters.
- Include hashes, counts, paths, and symbols only when they provide useful evidence or traceability. Do not make the reader decode raw inventory.
- Metaphors, slogans, ceremonial language, and dramatic descriptions MUST NOT replace concrete actions and outcomes.
Plain language does not mean removing technical accuracy. Keep the technical details that help a future reader understand the decision, verify important evidence, or recognize a risk; translate or omit details that only make sense inside the current session.
Before and after
Before: feat(research): the Gate-6 development-vintage mint — fingerprint 14d908e0
After: feat(research): record the July 13 development data snapshot
The body should preserve the important incident and remaining risk without requiring the reader to know the project's shorthand:
The snapshot-generation script still uses a manually updated output date. It
initially overwrote the July 12 snapshot, which was restored exactly before the
new snapshot was saved under July 13. The date handling should be fixed before
this becomes a routine process.
Body
After the subject line, the body has three parts:
1. Intent paragraph (2–3 sentences, no header) Explain the problem or need, the outcome, and why it matters. Give enough context for a reader who was not part of the current task. Start immediately after the blank line following the subject.
2. [--- Changes ---]
Summarize observable outcomes and important technical decisions. Organize by file or category when that helps, but do not inventory every edited line or repeat the diff. Include counts, paths, symbols, and hashes only when they provide useful evidence or traceability, and explain their significance.
3. [--- AI Review (<model name>) ---]
Give an honest assessment in direct, ordinary language. Use your current model name in the header (e.g., [--- AI Review (Claude Opus 4.7) ---]). State meaningful strengths, risks, trade-offs, and follow-up work without promotional, theatrical, or self-congratulatory prose.
Durable-reader check
Before creating the commit, reread the complete message and ask:
Could a teammate unfamiliar with this task, or the author returning years later, understand why this commit exists, what changed, and what still needs attention without the issue tracker or session history?
If the answer is no, rewrite the message before creating the commit.
Signing Failure Recovery
If a git commit (or git merge --no-ff, or git commit --amend) fails with error: 1Password: failed to fill whole buffer or similar 1Password SSH signing errors, retry the same command with -c commit.gpgsign=false to bypass signing. Do not ask the user — they may be away. Log a note after the commit so the user knows it is unsigned:
Note: Commit
<hash>is unsigned — 1Password SSH agent was unresponsive. Re-sign later with:git rebase --exec "git commit --amend --no-edit" <parent-hash>
This applies to all commit paths (A, B, C) and to git merge --no-ff commands.
Branching Strategy (OneFlow Option 3)
OneFlow is a git branching model built around a single long-lived branch (e.g., main). All work happens on short-lived feature branches that merge back into the main branch. This skill uses Option 3 (rebase + merge --no-ff): interactively rebase the feature branch to clean up its history, then merge with --no-ff to create a merge commit that groups the cleaned-up commits into a visible bubble. The result is a clean, almost-linear history where logical units are visually grouped, and any feature can be reverted with a single git revert -m 1 <merge-commit>.
A feature branch is used in two scenarios:
- Forward decomposition — the current changeset splits into multiple sub-commits
- Retroactive grouping — recent commits on the current branch (pushed or unpushed) share a logical theme with the new change and should be bundled together
When either scenario applies:
- Note the current branch — this is the parent branch (the merge target)
- Create a descriptive branch from the appropriate base point
- Place all commits (new + retroactive if applicable) on that branch
- (Optional) Clean up the commit sequence if needed (squash fixups, reorder)
- Merge back into the parent branch with
--no-ffto create a merge commit - Delete the temporary branch
- If the rewrite affected already-pushed commits, a force push will be needed — MUST NOT push automatically; STOP and follow the Preflight "Never
git pushWithout Express Authorization" rule.
Cascading merge rule: Always merge back into the branch you branched off from — never skip levels. If you're on feat/auth and create feat/auth-tests, merge back into feat/auth, not main. This keeps history cascading cleanly.
When neither applies (single, standalone commit): commit directly on the current branch — no branch needed.
Minimum bubble size: A merge bubble requires at least 2 commits on the feature branch. If the branch ends up with only 1 commit, fast-forward merge instead of --no-ff — a bubble around a single commit adds noise without grouping value.
Branch Naming
Use a descriptive, kebab-case name derived from the overall theme of the changeset:
<type>/<short-description>
Examples: feat/jwt-refresh-rotation, refactor/async-route-handlers, chore/portable-config-paths
Workflow
When this skill is invoked:
Step 1 — Gather context
Run the following in parallel:
git statusgit diff HEAD(shows all staged + unstaged changes combined against the last commit)git log --oneline -10git log --oneline @{upstream}..HEAD(identifies which commits have not been pushed yet; if no upstream is set, all commits on the branch are considered local — needed to determine whether a force push is required after retroactive grouping)
All recent commits — pushed or unpushed — are candidates for retroactive grouping. A force push will be used when needed.
Step 2 — Retroactive grouping analysis
Examine the recent commits from step 1. Do any share a logical theme with the new change being committed?
Heuristics for detecting a retroactive group:
- Multiple recent commits touch the same scope/feature area as the new change
- A sequence of commits incrementally building toward the same goal (e.g., three recent commits adding auth middleware, and the new change adds auth tests)
- Commits that would naturally be described under one feature branch name
Do NOT retroactively group:
- Commits already inside a merge bubble (already grouped)
- Unrelated commits that merely happen to be adjacent in time
If a retroactive group is detected → present the user with:
- Which existing commits would be included (hash + subject line)
- The new commit(s) that would join them
- The proposed branch name
- The option to skip grouping and just commit normally
- MUST wait for the user's choice before doing anything else
If no retroactive group is detected → proceed to step 3.
Step 3 — Decomposition analysis
Analyze whether the current changeset (the uncommitted work) contains multiple logical units of work. A unit of work is a cohesive set of changes that serves a single purpose.
Heuristics for detecting multi-unit changesets:
- Changes span multiple unrelated scopes
- Refactoring mixed with new functionality
- Multiple files changed that serve clearly different purposes
Keep together as one unit (do NOT split):
- Implementation code + its tests — these are a single unit of work
- Spec/design document + its implementation plan — these are a single unit of work
If the changeset looks like a single unit → proceed to the Single-Commit Path.
If the changeset looks decomposable → present the user with:
- A proposed split: list each sub-commit with its type/scope, one-line description, and grouped files — in suggested commit order (foundations first, dependents later)
- The proposed branch name for the grouping
- The option to proceed as a single big commit instead ("Or commit everything together as one")
- MUST wait for the user's choice before doing anything else
Decision tree
Step 2: retroactive group detected?
├─ YES → user accepts?
│ ├─ YES → Path C (retroactive grouping)
│ └─ NO → proceed to Step 3
└─ NO → proceed to Step 3
Step 3: changeset decomposable?
├─ YES → user accepts decomposition?
│ ├─ YES → Path B (decomposed multi-commit)
│ └─ NO → Path A (single commit)
└─ NO → Path A (single commit)
Path A — Single Commit
Use when the changeset is a single logical unit and no retroactive grouping applies.
A1. Analyze all changes and draft a commit message following the convention above.
A2. Stage relevant files (prefer specific files over git add .).
A3. Create the commit using HEREDOC syntax:
git commit -m "$(cat <<'EOF'
type(scope): description
Intent paragraph here — 2-3 sentences on why.
[--- Changes ---]
- file.md: description of what changed
[--- AI Review (<model name>) ---]
Honest assessment of the commit.
EOF
)"
A4. Run git status after commit to verify success.
Path B — Decomposed Multi-Commit (OneFlow Option 3)
Use when the user chooses to split the changeset into multiple sub-commits.
B1. Note the current branch name — this is the parent branch (e.g., main, feat/auth, or any branch you're currently on). Create and checkout the temporary branch:
git checkout -b <type>/<short-description>
B2. For each sub-commit, sequentially:
- Stage only the files belonging to this sub-commit
- Draft and create the commit using HEREDOC syntax (same body format as Path A)
B3. (Optional) Clean up the commit sequence before merging. Since the branch was just created from the parent's tip and commits were crafted individually in B2, this is almost always unnecessary. If cleanup is needed (e.g., squash a fixup), ask the user to run the interactive rebase themselves:
Suggest: git rebase -i <parent-branch>
git rebase -iopens an editor and requires manual interaction — MUST NOT run it directly. Either skip this step (the default) or ask the user to run it.
B4. Switch back and merge. Count the commits on the feature branch first:
git checkout <parent-branch>
git rev-list --count <parent-branch>..<type>/<short-description>
- If 2+ commits — merge with
--no-ffto create a bubble:
git merge --no-ff <type>/<short-description> -m "$(cat <<'EOF'
<type>(<scope>): <merge description summarizing the overall change>
Intent paragraph — summarize the logical unit these commits form together.
[--- Changes ---]
- Sub-commits included:
- <hash-abbrev> <subject line>
- <hash-abbrev> <subject line>
- ...
[--- AI Review (<model name>) ---]
Assessment of the feature as a whole.
EOF
)"
- If 1 commit — fast-forward merge (no bubble needed for a single commit):
git merge <type>/<short-description>
B5. Delete the temporary branch and verify:
git branch -d <type>/<short-description>
git status
git log --oneline --graph -10
Path C — Retroactive Grouping (OneFlow Option 3)
Use when the user chooses to bundle existing commits with the new change into a feature branch.
C1. Identify the base commit — the parent of the oldest commit being grouped. Note the current branch name — this is the parent branch (the merge target). Record the hashes and messages of all commits being grouped (they will be cherry-picked later).
C2. Stash any uncommitted changes (the new work) so the working tree is clean:
git stash
C3. Create the temporary branch from the base commit:
git checkout -b <type>/<short-description> <base-commit-hash>
C4. Cherry-pick the existing commits onto the feature branch, preserving their original messages:
git cherry-pick <commit1> <commit2> <commit3>
If a cherry-pick fails with a conflict:
- Report the conflicting files to the user
- Attempt to resolve trivially (e.g., if the conflict is whitespace or obvious merge)
- If non-trivial, ask: "Cherry-pick of
<hash>conflicted in<file>. Want me to resolve it, or would you prefer to handle this manually?" - After resolution:
git cherry-pick --continue - If the user wants to abort:
git cherry-pick --abort, then fall back to Path A (single commit on the current branch)
C5. Restore the stashed changes and commit the new work using the standard body format. If the new changes contain multiple logical units, commit each sub-unit separately:
git stash pop
# stage and commit new change(s)
If git stash pop fails with a conflict:
- Report the conflicting files to the user
- The stash is preserved (not dropped) on conflict — the working tree has partial merge markers
- Resolve conflicts, then
git stash dropto clean up the stash - If unresolvable,
git checkout -- .to discard the failed pop, thengit stash popwon't be retried — ask the user how to proceed
C6. (Optional) Clean up the commit sequence on the feature branch. Since commits were cherry-picked with their original messages and the new work was committed fresh, this is usually unnecessary. If cleanup is needed (e.g., squash a fixup), ask the user to run the interactive rebase themselves:
Suggest: git rebase -i <base-commit-hash>
git rebase -iopens an editor and requires manual interaction — MUST NOT run it directly. Either skip this step (the default) or ask the user to run it.Important: The rebase target must be
<base-commit-hash>(where the feature branch started), NOT<parent-branch>. The parent still has the original commits that were cherry-picked; rebasing onto it risks duplicate commits if patch-ids diverge.
C7. Switch back to the parent branch and prepare to rewrite history.
Before running git reset --hard, verify the feature branch has all expected commits:
git log --oneline <base-commit-hash>..<type>/<short-description>
Present the log to the user:
The feature branch
<branch>has these commits:
<hash> <subject>(cherry-picked)<hash> <subject>(cherry-picked)<hash> <subject>(new)I'm about to reset
<parent-branch>back to<base-commit-hash>and merge the feature branch in. This rewrites history on<parent-branch>. Proceed?
After user confirms:
git checkout <parent-branch>
git reset --hard <base-commit-hash>
git rev-list --count <base-commit-hash>..<type>/<short-description>
Then merge exactly as in B4 — fast-forward if 1 commit, --no-ff with the same merge-commit template if 2+ — annotating each sub-commit in the [--- Changes ---] list as (retroactive) or (new).
C8. MUST NOT push automatically. Retroactive grouping rewrites history, so if the branch has a remote upstream, a force push will be required to sync the remote — but per the "Never git push Without Express Authorization" rule at the top of this skill, plan-level approval from earlier in the session is NOT push authorization. Stop here and present the situation:
Retroactive grouping rewrote history on
<parent-branch>. The remote is now out of sync with local. To update the remote, a force push is required:git push --force-with-leasePush to origin? (yes/no)
Wait for a push-specific answer in the current turn. "Yes to the plan" from earlier does NOT count. If the user declines or does not respond with an express push authorization, leave the remote out of sync and continue to C9 — the user can push later when ready.
Skip this step entirely (no ask needed) if the branch has no remote tracking (e.g., purely local work with no upstream set).
C9. Delete the temporary branch and verify:
git branch -d <type>/<short-description>
git status
git log --oneline --graph -10
The result across all paths is a clean history where git log --oneline reads linearly, but git log --graph shows merge bubbles grouping related commits together. Any grouped feature can be reverted with a single git revert -m 1 <merge-commit>.
Post-Commit: Devlog Scrap Capture
After a successful commit (any path), evaluate whether the commit is worth capturing as a devlog scrap for future writing.
Preference gate: If the user's commit preferences (.claude/skill-context/commit.md) set auto-devlog scrap: off, skip this entire section. Some users prefer not to auto-generate devlog scraps — respect that choice.
When to Capture
Capture if ALL of these are true:
-
The commit type is potentially significant. Heuristics:
- Type is
feat,fix, orrefactor - OR it's a
--no-ffmerge commit (Path B or C) — these represent a complete feature docs,chore,style,ci,build,test,perfcommits are generally NOT significant unless they represent a non-trivial decision or pivot
- Type is
-
There is something genuinely worth capturing from this session. Type-significance is necessary but not sufficient — most
feat/fix/refactorcommits are routine and produce nothing worth re-reading later. Honestly judge whether the work behind this commit yielded an insight that the user, six months from now, would want to revisit. Apply the capture bar defined in the devlog skill (the "What's Worth Capturing" section of the sibling file../devlog/SKILL.md, at the plugin'sskills/devlog/); MUST bias toward NO when uncertain. Too many scraps is noisy and dilutes the value of the ones that actually matter.The one-sentence test: Try to complete the sentence "The interesting thing about this commit is ___" with something specific and non-trivial. If you can't — or if the completion is generic ("we added a feature", "we fixed a bug") — skip the scrap.
-
The commit does not already contain devlog content. Check if any committed files match:
docs/devlog/.scraps/*— the commit includes a devlog scrapdocs/devlog/*/— the commit includes a published devlog post
If either matches, skip — this commit is already about devlog content.
How to Capture
If all three conditions are met, auto-invoke /devlog scrap --from-context after the commit completes. Do not ask the user — this should be seamless. The --from-context flag tells the devlog skill to synthesize the scrap directly from the current conversation context (the diff, commit message, and session exchanges already available) instead of dispatching subagents to re-mine git history and session files.
If the /devlog skill is not available (plugin not installed in the target project), silently skip this step.