| name | tag |
| description | Create a SemVer annotated git tag on main by inferring the bump type (major/minor/patch) from conventional commits since the last tag, push the tag to remote, and optionally create a GitHub Release via gh. Global and project-agnostic. Trigger when the user says "tag main", "create a tag", "tag release", "cut a release", "release v1.2.3", "tag version", "bump version and tag", "create a release", "release this", or "/tag". SKIP when the user is asking about Docker image tags or non-git tagging. |
Git Tag — SemVer release skill
Creates a SemVer annotated tag on main by reading conventional commits since the last
tag, inferring the correct bump (major / minor / patch), showing a full proposal for
approval, then tagging, pushing, and optionally opening a GitHub Release. Never tags
silently — the user always approves the version first.
Accepted flags (parsed from the user's invocation text)
| Flag | Effect |
|---|
--major | Force a major bump regardless of commit types |
--minor | Force a minor bump regardless of commit types |
--patch | Force a patch bump regardless of commit types |
--pre <suffix> | Append a pre-release suffix (e.g. --pre rc.1 → v1.3.0-rc.1) |
--release | Create a GitHub Release after pushing without prompting |
--no-release | Skip GitHub Release creation entirely without prompting |
--draft | Create the GitHub Release as a draft (implies --release) |
--no-push | Create the tag locally only; do not push to the remote |
If the user provides a bare version string in the invocation (e.g. /tag v2.0.0), use it
verbatim and skip bump inference entirely.
Procedure
1. Parse flags and version hint
Extract all flags above from the invocation text. If a string matching v?\d+\.\d+\.\d+
(with an optional pre-release suffix) is present, treat it as an explicit version override
and jump to step 6 after confirming it does not already exist.
2. Pre-flight checks (run in parallel)
git fetch --tags origin — pull latest tags and remote refs before any inference.
git status --short — working tree must be clean.
git rev-parse main vs git rev-parse origin/main — check local main is up-to-date.
gh run list --branch main --status completed --limit 1 --json conclusion,workflowName,url
— check the latest completed CI run on main. If gh is available and authenticated,
verify the conclusion is success.
If the working tree is dirty: stop and tell the user to commit or stash first.
If local main is behind origin/main: offer to run git pull --ff-only origin main
and continue; stop if they decline.
If the latest CI run on main is not success: warn clearly — show the workflow name
and URL — and require explicit confirmation to proceed ("CI is failing on main. Tag
anyway? This may release broken code."). If gh is absent or unauthenticated, skip the
CI check and note it was skipped.
3. Find the last SemVer tag
Run git describe --tags --match 'v[0-9]*' --abbrev=0 main 2>/dev/null.
- If a tag is found: record it as
LAST_TAG; parse its M.N.P triple.
- If no tag exists: set
LAST_TAG to empty and base version to 0.0.0.
4. Gather commits since the last tag
Run git log ${LAST_TAG:+$LAST_TAG..}main --oneline --no-merges.
If no commits exist since the last tag: inform the user ("nothing new since <LAST_TAG>")
and stop.
5. Infer the SemVer bump
Apply the first matching rule in priority order:
| Condition | Bump |
|---|
Any commit subject ends with ! OR any commit body contains BREAKING CHANGE: | major |
Any commit type is feat | minor |
Any other conventional-commit type (fix, perf, refactor, docs, test, build, ci, chore) | patch |
| No conventional-commit pattern detected | patch (conservative default; note this to the user) |
If --major, --minor, or --patch was given, use it unconditionally.
Compute the new version by incrementing the chosen component and zeroing lower ones:
- major →
(M+1).0.0
- minor →
M.(N+1).0
- patch →
M.N.(P+1)
Append the pre-release suffix if --pre <suffix> was given: vM.N.P-<suffix>.
6. Show proposal and wait for approval
Present a summary block before doing anything:
Proposed tag : v<new_version>
Bump reason : <major feat! | minor feat | patch fix | … | explicit>
Last tag : <LAST_TAG or "none (first release)">
Commits : <N> commit(s) since last tag
Push : origin [or "local only" if --no-push]
GitHub Release: <yes (draft) | yes | no | will ask>
Commits included:
- <subject line>
- <subject line>
… (show up to 20; "and N more…" if longer)
Ask the user to approve, adjust the version manually, or cancel. Do not proceed until
explicitly approved.
7. Create the annotated tag
Build a tag message:
Release v<new_version>
<bullet list: "- <subject>" for each commit since last tag>
Run against the main ref explicitly (not HEAD, so this works from any branch):
git tag -a "v<new_version>" main -m "<message>"
If the tag already exists locally: stop immediately; never overwrite an existing tag.
8. Push the tag (unless --no-push)
git push origin "v<new_version>"
Confirm the push succeeded before continuing. If it fails, surface the error verbatim and
stop — do not attempt --force.
9. GitHub Release (unless --no-release)
If --release or --draft was given, create without prompting.
Otherwise ask: "Create a GitHub Release for v<new_version>? (y/n)"
If creating:
gh release create "v<new_version>" \
--title "v<new_version>" \
--notes "<same bullet list as step 7>" \
[--draft if --draft was passed]
If gh is not installed or not authenticated: skip gracefully, print a note, and
suggest gh auth login.
10. Report
- Tag name and the commit SHA it points to (
git rev-list -n1 "v<new_version>").
- Whether the tag was pushed and to which remote.
- GitHub Release URL if one was created.
Guardrails
- Never tag without explicit user approval of the proposed version and commit list.
- Never tag a dirty working tree —
git status must be clean.
- Never overwrite an existing tag — if
v<new_version> already exists locally or
remotely, stop and say so. Use --pre <suffix> for iteration.
- Always
git fetch --tags first — stale local tag state produces wrong base versions.
- Always check CI status on main before tagging; warn and require confirmation if it
is not green. A tag pointing to broken code is worse than a delayed release.
- Always tag
main explicitly — pass main as the ref, not HEAD.
- Never push a tag before it exists locally and never attempt
--force on a pushed tag.
- Only attempt
gh release create after a successful push — a release pointing to an
unpushed tag is broken.
- Pre-release tags (e.g.
v1.3.0-rc.1) must not be used as the bump baseline for the
next stable release; note this to the user if relevant.
- If no conventional-commit pattern is found in the commit list, apply a patch bump but
clearly tell the user the inference was a conservative default.