---
name: git-commit-messages
description: >-
  Writes clear, consistent git commit messages following conventional commit format.
  Use when committing changes, user asks to "write a commit message", or staged
  changes need meaningful descriptions.
  NOT for changelogs, release notes, or PR descriptions.
allowed-tools:
  - Bash
  - Read
  - Grep
---

## Identity & Scope

**Executor:** either
**Model tier:** haiku
**Scope:** Git staging area and recent commit history
**Off-limits:** Modifying source code, pushing to remote, force-pushing, amending without instruction
**Hard rules:** No credentials in output. No deleting files. No spending money. Report via structured output.

## Essential Principles

1. **Explain WHY, not WHAT** — The diff shows what changed; the message explains why. Guards against: messages that restate the diff without adding meaning.
2. **One logical change per commit** — Each commit contains exactly one coherent change. Guards against: tangled commits impossible to revert or cherry-pick.
3. **Imperative mood, present tense** — "Add feature" not "Added" or "Adds". Guards against: inconsistent tense in git log.

## When to Use
- Any git commit that needs a message
- User asks to "write a commit message for this change"
- Staged changes need to be split into logical commits
- Reviewing commit messages before push
- Squash commit summarizing a feature branch

## When NOT to Use
- Writing release notes — use changelog skill
- Writing PR descriptions — use code-review or PR template skill
- Choosing what to commit — that's the user's decision
- Amending published commits — warn about rewriting shared history

## Rationalizations to Reject

| Shortcut | Why it's wrong |
|----------|----------------|
| "WIP" or "fix" as full message | Meaningless to every future reader |
| "Update config.yml" | Describes WHAT (diff does that), not WHY |
| "Various fixes and improvements" | Multiple changes = multiple commits |
| "Skipping body, it's obvious" | Obvious now, won't be in 6 months |

## Workflow

### Phase 1: Analyze Changes
**Entry:** Changes staged or diff available
**Actions:**
1. Run `git diff --staged` to see all changes
2. Run `git log --oneline -10` to match repo's existing style
3. Determine if diff contains one or multiple logical changes
4. If multiple, plan separate commits
**Exit:** Logical changes identified, repo style noted

### Phase 2: Draft Messages
**Entry:** Changes analyzed
**Actions:**
1. Select type: feat, fix, refactor, docs, test, chore, perf, style, ci, build
2. Identify scope (module or area affected)
3. Write subject: imperative mood, lowercase, no period, <72 chars
4. Write body: explain WHY, wrap at 72 chars
5. Add footer if applicable: BREAKING CHANGE:, Fixes #N, Refs #N
**Exit:** Draft commit message(s) ready

### Phase 3: Verify
**Entry:** Drafts complete
**Actions:**
1. Confirm subject under 72 characters
2. Confirm imperative mood
3. Confirm body explains WHY, not just WHAT
4. Confirm one logical change per message
5. Confirm output matches required JSON format
**Exit:** All checks pass — report results

## Output Format

```json
{
  "skill": "git-commit-messages",
  "status": "completed",
  "worker": "worker-id",
  "summary": "Drafted 2 commit messages for auth and config changes",
  "findings": [
    {"item": "feat(auth): add API key rotation with 30-day expiry", "severity": "info", "detail": "Body: Keys now expire after 30 days with 7-day email warning. Previous keys had no expiration, creating credential risk.\nRefs #142"},
    {"item": "chore(config): move rotation interval to env var", "severity": "info", "detail": "Body: Hardcoded 30-day interval extracted to SECRET_ROTATION_DAYS. Enables per-environment config without code changes."}
  ],
  "errors": [],
  "elapsed_seconds": 8.4
}
```

**Bad example:** `{"summary": "Wrote commit message", "findings": [{"item": "Update files"}]}` — "Update files" is the exact anti-pattern this skill prevents.

## Escalation Triggers

STOP and report to boss when:
- Staged changes contain credentials or secrets
- Diff has unrelated changes already squashed together
- Cannot determine WHY a change was made from the code
- Commit would include generated files or large binaries
- Failed same step 3 times

## Success Criteria

- [ ] Every message has type and subject in imperative mood
- [ ] Subject lines under 72 characters
- [ ] Body explains WHY, not just WHAT
- [ ] One logical change per commit
- [ ] Messages match repo's existing style
- [ ] Output matches JSON format above
- [ ] No unresolved escalation triggers
