From ring-default
Enforces scope allowlists, groups changes into atomic commits, and creates GPG-signed conventional commits for clean PR-ready history.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ring-default:committing-changesThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Analyze changes, enforce scope policy, group them into coherent atomic commits, and create signed commits following repository conventions. This skill transforms a messy working directory into a clean, logical commit history — with a scope that will actually pass PR validation.
Analyze changes, enforce scope policy, group them into coherent atomic commits, and create signed commits following repository conventions. This skill transforms a messy working directory into a clean, logical commit history — with a scope that will actually pass PR validation.
The scope is REQUIRED in every commit message. It MUST come from the repo's allowlist.
MUST detect the allowlist in Step 0 before analyzing or drafting any commit message. A commit with an invented or omitted scope will fail PR validation and block the PR.
Many repos enforce an allowlist of valid scope values via a GitHub Actions workflow. Failing this check blocks the PR, so MUST detect it before proposing any commit message.
Check in this order:
.github/workflows/pr-validation.yml (primary).github/workflows/pr-title.yml.github/workflows/commitlint.yml.github/workflows/semantic-pull-request.ymlcommitlint.config.{js,cjs,mjs,ts}, .commitlintrc*Common forms to look for:
| Form | Example |
|---|---|
scopes: block (one per line) | Under amannn/action-semantic-pull-request |
scopes: a,b,c inline | Comma-separated on one line |
scope-enum rule | In commitlint config arrays |
Also note any type restrictions — some repos limit types beyond the default Conventional Commits set.
| Situation | Required Action |
|---|---|
| Policy found, scope is clear | Use only scopes from the allowlist |
| Policy found, scope is ambiguous | STOP and ask the user which allowed scope to use |
| No policy file found | MUST still include a scope — ask the user what scope to use |
NEVER omit the scope. NEVER invent a scope not in the allowlist. A bare type: description is FORBIDDEN.
State the policy source and chosen scope to the user before proceeding.
Run in parallel:
git status
git diff
git diff --cached
git log --oneline -10
For each changed file determine:
feat, fix, chore, docs, refactor, test, style, perf, ci, build| Principle | Description |
|---|---|
| Feature + Tests | Implementation and its tests go together |
| Config Changes | package.json, tsconfig, etc. grouped separately |
| Documentation | README, docs/ changes grouped together |
| Refactoring | Pure refactors (no behavior change) separate |
| Bug Fixes | Each fix is atomic with its test |
Single commit when:
Multiple commits when:
Order matters for bisectability:
MUST get user confirmation before executing.
Proposed Commit Plan:
─────────────────────
Scope policy: .github/workflows/pr-validation.yml → allowed scopes: [api, auth, docs, ci]
Chosen scope: auth
1. feat(auth): add OAuth2 refresh token support
- src/auth/oauth.ts (modified)
- src/auth/oauth.test.ts (modified)
2. chore(deps): update authentication dependencies
- package.json (modified)
- package-lock.json (modified)
3. docs(docs): update OAuth2 setup guide
- docs/auth/oauth-setup.md (modified)
Proceed with this plan? [Execute plan / Single commit / Let me review]
Use AskUserQuestion to confirm before proceeding.
Every commit message MUST follow:
<type>(<scope>): <subject>
<body — optional>
THE MOST COMMON MISTAKE: Putting trailer text INSIDE the -m quotes.
# ❌ WRONG — trailer text is INSIDE the -m quotes
git commit -m "feat(auth): add feature
X-Lerian-Ref: 0x1"
# ✅ CORRECT — --trailer is a SEPARATE argument OUTSIDE quotes
git commit -m "feat(auth): add feature" --trailer "X-Lerian-Ref: 0x1"
Before writing ANY git commit command, verify:
-m "..." contains ONLY the commit message (no trailer text inside)--trailer flags are OUTSIDE and AFTER the -m parametergit commit -S -m "msg" --trailer "key: value"git commit -S \
-m "<type>(<scope>): <subject>" \
-m "<body if needed>" \
--trailer "X-Lerian-Ref: 0x1"
For each commit group, in order:
Stage only the files for this commit:
git add <file1> <file2> ...
Create signed commit with trailer:
git commit -S \
-m "<type>(<scope>): <subject>" \
-m "<body if needed>" \
--trailer "X-Lerian-Ref: 0x1"
If GPG signing fails: check git config user.signingkey and gpg --list-secret-keys.
If no usable key is found, STOP — do NOT offer an unsigned path. Inform the user:
GPG signing is required. No usable signing key was found.
To proceed:
1. Generate a key: gpg --gen-key
2. Configure git: git config --global user.signingkey <key-id>
3. Re-run this skill.
Committing without -S is not an option — Step 7 will reject unsigned commits.
MUST wait for the user to configure a key before continuing. NEVER drop -S silently or offer "unsigned" as a fallback.
First, resolve the range ref for verification. $BASE may be provided by an orchestrating skill (e.g., ring:shipping-changes). Resolve in this order:
# 1. Upstream tracking ref (works when branch already has a remote tracking branch)
if git rev-parse @{u} >/dev/null 2>&1; then
RANGE_REF="@{u}"
# 2. $BASE propagated by the orchestrating skill (e.g., ring:shipping-changes)
elif [ -n "$BASE" ]; then
RANGE_REF="origin/$BASE"
# 3. Standalone: detect base branch via GitHub API
else
BASE=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null \
|| git remote show origin 2>/dev/null | grep 'HEAD branch' | awk '{print $NF}')
RANGE_REF="origin/$BASE"
fi
Then verify every commit in the batch:
git log --oneline "$RANGE_REF..HEAD"
for commit in $(git rev-list "$RANGE_REF..HEAD"); do
# %G? returns: G=good, U=unknown-validity, X/Y=expired, B=bad, E=missing key, N=no signature
sig_status=$(git log -1 --format="%G?" "$commit")
echo "$sig_status" | grep -qE '^[GU]' \
|| { echo "Commit $commit: signature invalid or insufficient (status=$sig_status)"; exit 1; }
git log -1 --format="%(trailers)" "$commit" | grep -q '^X-Lerian-Ref: ' \
|| { echo "Commit $commit: X-Lerian-Ref trailer missing"; exit 1; }
done
git status
For each commit:
G (good) or U (unknown validity). Reject X/Y (expired key), B (bad signature), E (missing key), N (unsigned).grep fails → stop and report the missing trailer.Why U is accepted: U means the commit is cryptographically signed with a valid key, but GPG has not established a trust chain for that key (e.g., the key was not signed by a trusted introducer). This is the normal state for freshly generated keys or keys imported from colleagues without manual trust assignment. The signature itself is valid — it proves authorship. G additionally requires GPG's web-of-trust to vouch for the key identity, which is stricter than needed for commit attribution. Both are acceptable; only unsigned (N), bad (B), missing-key (E), and expired-key (X/Y) commits are rejected.
Note: when called from ring:shipping-changes, $BASE is already resolved in Phase 0 and propagated here — the @{u} and standalone detection paths are only needed for standalone invocations.
After successful commit, ask the user:
AskUserQuestion({
questions: [{
question: "Push commits to remote?",
header: "Push",
options: [
{ label: "Yes", description: "Push to current branch" },
{ label: "No", description: "Keep local only" }
]
}]
});
If yes:
# Branch with upstream:
git push
# Branch without upstream:
git push -u origin <current-branch>
git commit -S \
-m "feat(auth): add OAuth2 refresh token support" \
-m "Implements automatic token refresh when access token expires." \
--trailer "X-Lerian-Ref: 0x1"
git commit -S \
-m "fix(api): handle null response in user endpoint" \
--trailer "X-Lerian-Ref: 0x1"
git commit -S \
-m "chore(deps): update dependencies to latest versions" \
--trailer "X-Lerian-Ref: 0x1"
# ❌ WRONG — no scope
git commit -m "feat: add feature"
# ❌ WRONG — invented scope not in allowlist
git commit -m "feat(custom-scope): add feature"
# ❌ WRONG — trailer text inside -m
git commit -m "feat(auth): add feature
X-Lerian-Ref: 0x1"
# ❌ WRONG — emoji or hashtags in message body
git commit -m "feat(auth): add feature
🤖 Generated with Claude"
# ✅ CORRECT
git commit -S \
-m "feat(auth): add feature" \
--trailer "X-Lerian-Ref: 0x1"
# Find commits with specific trailer value
git log --all --format="%H %s %(trailers:key=X-Lerian-Ref,valueonly)" | grep "0x1"
# Show all trailers for a commit
git log -1 --format="%(trailers)"
If the user provides a commit message as an argument:
| Rationalization | Why It's WRONG | Required Action |
|---|---|---|
| "I'll omit the scope for this one" | Every commit MUST carry a scope. A bare type: description fails PR validation. | MUST include scope from allowlist |
| "This scope isn't in the allowlist but it makes sense" | Invented scopes fail automated checks. The allowlist exists for a reason. | MUST use only allowlist scopes or ask user |
| "No policy file, so scope is optional" | Scope is always required. Without a policy, ask the user which scope to use. | MUST ask user for scope if no policy found |
| "I'll commit everything at once" | Mixed changes = messy history, hard to bisect/revert. | Analyze and group changes first |
| "Grouping takes too long" | Clean history saves hours of debugging later. | Always propose commit plan |
| "I'll put the trailer text in the message body" | --trailer is a GIT FLAG, not message text. | Use --trailer "X-Lerian-Ref: 0x1" as separate argument |
| "I'll skip GPG signing" | Unsigned commits fail Step 7 verification. There is no unsigned fallback path — configure a key and retry. | MUST stop and instruct user to configure GPG key. NEVER drop -S |
| "HEREDOC will format trailers correctly" | HEREDOC puts everything in the message body. | Use --trailer flag, NOT HEREDOC |
npx claudepluginhub p/lerianstudio-ring-default-defaultAnalyzes uncommitted changes, groups by cohesion, and generates commit messages matching project style. Outputs git commands or executes with --execute.
Creates git commits with repo detection, pre-commit checks, submodule support, and conventional messages. Activates on /commit or git commit requests.
Guides conventional commit messages, explicit git staging workflows, logical change grouping, and best practices with issue linking. Useful for git add, commit, or staging tasks.