git:20260826.1ed54bd to git:20260911.eb3bd31

5 added, 0 removed. Audit A to A.

---
name: wise-commit-message
description: >-
Draft one Conventional-Commits subject line from your pending git
changes — both staged (`git diff --cached`) and unstaged-but-modified
tracked files (`git diff`) — Jira-scoped (e.g. `feat(PROJ-777): …`)
when a key is detectable from the branch, diff, recent commits, or the
live session, and unscoped (e.g. `feat: …`) otherwise. Pass `--copy`
to also drop the subject onto the macOS clipboard. Strictly read-only
against git — never runs `git commit`, `git add`, or anything that
mutates the repo. Invoked as `/wise-commit-message` (bare alias) or
`/wise:wise-commit-message` (canonical). Use when the user says "write
a commit message", "draft a commit", "conventional commit", "prepare
commit message", "one-line commit", or types `/wise-commit-message`.
argument-hint: "[--copy]"
model: opus
effort: low
allowed-tools: Read, Bash(git:*), Bash(pbcopy:*), Bash(pbpaste:*), Bash(printf:*), Bash(command:*)
---
# /wise-commit-message — draft a Conventional-Commits subject from pending changes
+ Before asking any user question, read and follow the
+ [question lifecycle](../../references/workflow-host-control.md#keep-asynchronous-questions-open).
+ Keep asynchronous prompts open until answered; this rule does not authorize
+ questions in autonomous or otherwise prompt-free procedures.
+
## Why this skill exists
Every PR should land with a Conventional-Commits first line, and the
"right" first line is mostly mechanical:
1. Pick the type (`feat` / `fix` / `refactor` / `docs` / …) from what
the diff actually does.
2. Scope it with the Jira ticket the branch is working on — when
there is one.
3. Summarise the change in the imperative voice, short enough that
the reader can scan a list of commits without hitting the wrap.
This skill automates the mechanical part. It looks at everything
**pending** — both what's staged (`git diff --cached`) and what's
modified-but-not-yet-staged in tracked files (`git diff`), but **not**
the last commit and **not** untracked files — prints one line in
Conventional-Commits form, and — with `--copy` — drops that line onto
the clipboard so the user can paste it straight into
`git commit -m "…"`. When some of the described changes are not yet
staged, the suggested commit command uses `git add -A` so the user
doesn't end up committing only half of what the subject describes.
The user stays in control of the actual `git commit` call. Drafting
and committing are intentionally separate here so the user can read
the subject, tweak it, combine it with a body of their own, or
reject it entirely without ever having created a commit object they'd
need to amend.
> **Shared rules.** §3–§5 below follow
> `${CLAUDE_PLUGIN_ROOT}/references/subject-drafting.md` — the single
> source of truth that `/wise-commit`, `/wise-commit-push`, and
> `draft-body.md` also read. Change a Jira-detection, classification,
> or subject-formatting rule there, not here.
## Invocation
```
/wise-commit-message
/wise-commit-message --copy
/wise:wise-commit-message # canonical namespaced form
/wise:wise-commit-message --copy
```
No positionals. The only supported flag is `--copy`. Anything else
the user types is treated as an error (see [§Arguments](#arguments)).
This is a **standalone slash-command skill** — invoke it directly,
either as `/wise-commit-message` (bare alias) or
`/wise:wise-commit-message` (canonical).
## Arguments
Claude Code hands this skill the user's raw argument string (whatever
comes after `/wise-commit-message` on the prompt line). Parse `$ARGUMENTS`
yourself.
| Token | Meaning |
|------------|-------------------------------------------------------------|
| _(empty)_ | Draft the message and print it. No clipboard write. |
| `--copy` | Draft, print, AND copy the subject line to the clipboard. |
If the argument string contains anything other than optional
whitespace or the single literal token `--copy`, stop with:
```
Unknown argument(s): <the extra tokens>
Usage: /wise-commit-message [--copy]
```
Do not try to "helpfully" interpret unknown tokens as a path, a
subject override, or anything else. The argument surface of this
skill is deliberately tiny; silent interpretation of extra input
produces surprises.
## Procedure
### 1. Verify we're inside a git working tree
```bash
git rev-parse --show-toplevel
```
If that fails (exit code ≠ 0), stop immediately with:
```
Not inside a git working tree. /wise-commit-message only makes sense
when you have staged changes in a repository.
```
No fallback, no prompt. There is nothing to draft from.
### 2. Inspect pending changes (staged + unstaged tracked)
Collect views of both the staged index and the unstaged
working-tree changes to tracked files — one compact, one full — so
you can reason about scope and content without re-running git
repeatedly:
```bash
git diff --cached --stat # staged: file list + line deltas
git diff --cached --unified=0 --no-color # staged: the actual changes
git diff --cached --name-only # staged: just paths
git diff --stat # unstaged tracked: file list + line deltas
git diff --unified=0 --no-color # unstaged tracked: the actual changes
git diff --name-only # unstaged tracked: just paths
```
Let `STAGED` = the paths from `git diff --cached --name-only` and
`UNSTAGED` = the paths from `git diff --name-only`. Either (or both)
may be non-empty.
- If **both** `STAGED` and `UNSTAGED` are empty, stop with:
```
No pending changes. /wise-commit-message describes what you're about
to commit; there's nothing staged and nothing modified in tracked
files. Make some changes (or stage existing ones) and re-run.
```
- Otherwise continue. For the purpose of classifying the diff in
[§4](#4-classify-the-change) and summarising it in [§5](#5-write-the-subject), treat **the union of both
diffs** as one combined change set. In practice this means when
you read diff content for reasoning, read `git diff HEAD`
(index + working tree combined for tracked files) so you see the
full picture in one pass:
```bash
git diff HEAD --stat
git diff HEAD --unified=0 --no-color
```
Remember whether `UNSTAGED` is non-empty — [§6](#6-print-the-output) uses that to decide
whether the suggested commit command should use `git add -A` first.
Do not look at untracked files (`git status --porcelain | grep '^??'`).
A user that hasn't `git add`ed a brand-new file may be deliberately
excluding it — scooping it in silently is the same class of surprise
as falling back to `HEAD~1`. If the user wants untracked files in
the commit, they stage them first and re-run.
Do not silently fall back to `HEAD~1` — that would draft a message
for a different set of changes than what the user is about to
commit.
### 3. Detect a Jira ticket key
Follow `${CLAUDE_PLUGIN_ROOT}/references/subject-drafting.md` §1. Your
change set is the **pending** diff — grep `git diff HEAD` (staged +
unstaged tracked) for source 2. Run the chain in full before drafting;
never skip straight to §4.
### 4. Classify the change
Pick the type per `${CLAUDE_PLUGIN_ROOT}/references/subject-drafting.md`
§2, judging from the diff *content* — not the branch name, not the
ticket, not what sounds nicest.
### 5. Write the subject
Draft the subject per `${CLAUDE_PLUGIN_ROOT}/references/subject-drafting.md`
§3 (format with/without Jira key, imperative, lowercase, ≤72 chars, no
attribution trailer).
If the change genuinely warrants a body (breaking change,
context-heavy fix that won't fit in 72 chars), note that as a
*separate sentence* in the rationale line below — do **not** extend
the commit subject into a multi-line message. This skill's one and
only output is a single subject.
### 6. Print the output
Print exactly three things, in this order, each separated by a
blank line:
```
<the commit subject>
<one short sentence explaining why you picked this type and scope —
reference the file(s) or diff region(s) that drove the call. If
the change warrants a body, flag that here in plain prose. If
some of the described changes are unstaged, mention it in the
same sentence.>
Commit with:
<suggested commit command>
```
The rationale sentence exists so the user can override your call if
they disagree — if the line reads "chose `chore` because the diff is
mostly lockfile churn", the user can retype it as `fix` themselves.
It's cheap to include and it makes your decisions legible. Keep it
to one sentence; this is a drafter, not a code review.
**Suggested commit command.** Pick one based on what you saw in
[§2](#2-inspect-pending-changes-staged--unstaged-tracked):
- If `UNSTAGED` was **empty** (everything described is already
staged):
```
git commit -m "<the commit subject>"
```
- If `UNSTAGED` was **non-empty** (some or all of the described
changes are still in the working tree):
```
git add -A && git commit -m "<the commit subject>"
```
The `git add -A` form is correct here because the subject we drafted
describes the union of staged + unstaged tracked changes — committing
without staging the rest would leave the commit describing more than
it actually contains, which is exactly the kind of drift the skill is
trying to prevent.
### 7. Copy to clipboard (only if `--copy` was passed)
If `--copy` was passed you MUST actually run the Bash tool — printing
"Copied to clipboard" as prose without running it is this skill's #1
failure mode (the user pastes stale content). If `--copy` was not
passed, skip this step entirely: no clipboard write, no prompt.
1. Verify the backend: `command -v pbcopy`. Non-zero (e.g. Linux) →
print `Skipped --copy: pbcopy not found.` and stop the step. Do
**not** substitute `xclip` / `wl-copy` — this skill is macOS-scoped.
2. Copy AND verify in one Bash call:
```bash
printf '%s' '<subject>' | pbcopy && pbpaste
```
The `&& pbpaste` tail is mandatory — it echoes the clipboard back
so success is *observable*, not claimed. `printf '%s'` avoids the
trailing newline `echo` would add. Single-quote `<subject>`,
escaping any literal single quote as `'\''`.
3. Stdout matches `<subject>` exactly → print
`Copied to clipboard: <subject>`. Otherwise print the actual
problem (include pbpaste's output / the error) — never claim
success the verification didn't confirm.
## Examples
**Example 1** — staged: new React component for an a11y audit panel;
branch is `feat/PROJ-812-a11y-panel`.
```
feat(PROJ-812): add a11y audit panel to PR review surface
Picked feat because the diff introduces a new user-visible panel
(`AuditPanel.tsx`) rather than touching existing behaviour; branch
name supplied the PROJ-812 scope.
Commit with:
git commit -m "feat(PROJ-812): add a11y audit panel to PR review surface"
```
**Example 2** — staged: one-line fix in `SectionQuery.php`; branch is
`main`; no ticket key anywhere.
```
fix: prevent null dereference when section has no active teacher
Picked fix because the diff adds a `!== null` guard in
`SectionQuery::getActiveTeacher()`; no ticket key found in branch,
diff, recent log, or this session, so the subject is unscoped.
Commit with:
git commit -m "fix: prevent null dereference when section has no active teacher"
```
**Example 3** — staged: dependency bump in `package-lock.json`;
`--copy` passed.
```
build: bump vitest 1.6.0 → 1.6.2
Picked build because the only changed file is `package-lock.json`
with a vitest version delta; no tests or source code are touched.
Commit with:
git commit -m "build: bump vitest 1.6.0 → 1.6.2"
Copied to clipboard: build: bump vitest 1.6.0 → 1.6.2
```
(The closing line is only printed after the Bash tool actually ran
`printf '%s' 'build: …' | pbcopy && pbpaste` and its stdout came
back as the exact subject. If pbpaste returned something else, the
skill prints the failure instead.)
**Example 4** — nothing staged, but `src/IndexController.php` is
modified in the working tree; branch is `fix/PROJ-901-null-teacher`.
```
fix(PROJ-901): guard against null teacher in IndexController
Picked fix because the diff adds a null-check before dereferencing
`getActiveTeacher()`; changes are unstaged, so the command below uses
`git add -A` to include them.
Commit with:
git add -A && git commit -m "fix(PROJ-901): guard against null teacher in IndexController"
```
## Guardrails
- **Never run `git commit`, `git add`, `git stash`, `git reset`, or
any other mutating git operation.** This skill is strictly
read-only against git, plus one optional write to the clipboard.
Drafting a subject and committing it are intentionally separate
concerns — the user reviews the subject before any commit object
exists.
- **Never invent a Jira ticket key.** If all four detection sources
come up empty, emit an unscoped subject. An unscoped subject is
better than a wrong scope — the latter attributes commits to the
wrong ticket in Jira's activity log and takes real cleanup to
untangle.
- **Never reach for `HEAD~1` or untracked files as a "recovery" when
nothing is pending.** [§2](#2-inspect-pending-changes-staged--unstaged-tracked)'s
"no pending changes" stop is intentional; silently describing a
different set of changes (the previous commit, or files the user
deliberately didn't `git add`) would be strictly worse than an
error. Staged and unstaged tracked changes are both fair game;
untracked files and the prior commit are not.
- **Never prompt the user for missing information.** This skill runs
off what it can see. If the diff or branch context is too thin for
a confident subject, say so in the rationale line and let the user
either add context (comments, a ticket in the branch name) and
re-run, or write the subject themselves.
- **Never append an Anthropic or Claude attribution trailer.** No
`Co-Authored-By: Claude …`, no `🤖 Generated with Claude Code`,
no `Co-Authored-By: Anthropic …`. The subject is the user's prose
describing their own work; this skill is a drafter, not a
co-author.
- **Never reach for `xclip` / `wl-copy` / other clipboard backends.**
`pbcopy` is the only supported clipboard integration; on
non-macOS, report the skip and let the user copy manually.
- Profile-insensitive by design: the session token-budget profile (`/wise-profile`) does not change this skill — read-only drafting; correctness territory profiles MUST never touch.