/oss-publish — cut a versioned release
SkillDev toolsCut a versioned LoreWeave release — draft the CHANGELOG.md entry from what shipped since the last tag, confirm the SemVer bump and pre-release/release decision with the user, then tag and push to trigger the automated build + GitHub Release. Use when the user says "cut a release", "publish v...", "ship v0.1.0", "make a release", or asks what's changed since the last version.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the /oss-publish — cut a versioned release skill
What this skill tells your AI
The instructions your AI receives, as published by letuhao/lore-weave in .claude/skills/oss-publish/SKILL.md and read by ahel’s review.
Reads what actually shipped since the last tag, drafts the CHANGELOG.md entry, gets the
version-bump decision from a human (never inferred silently), then tags and pushes — which is
the one action that triggers .github/workflows/oss-release.yml to build every service image
and publish the GitHub Release. See
docs/standards/versioning-and-releases.md
for the policy this skill implements; this file is the procedure, that doc is the authority —
if they ever disagree, the standards doc wins and this file has drifted.
Why this is a skill and not just "edit the changelog and tag it"
Three things need to happen in order and the order matters: draft → human confirms the
version number and release/pre-release status → changelog is written and self-checked → THEN
tag and push. Tagging first and asking questions after means oss-release.yml has already
fired on a version nobody actually chose. This skill exists to keep those in sequence, and to
make step 2 an actual question (AskUserQuestion), not a guess dressed as one — a version
number is a promise to whoever pulls the tag, not a formatting choice.
Step 0 — orient
git fetch --tagsthen find the last release:git tag --list 'v*' --sort=-v:refname | head -1. If empty, this is the first release — the version floor isv0.1.0, not something to derive (seedocs/standards/versioning-and-releases.md§1; it is the literal starting point this project chose, not a default this skill invented).- Read
CHANGELOG.md. Note what's already sitting under## [Unreleased]— entries land there by hand as work merges, so this is not starting from zero even on a fresh repo. - Run
python scripts/changelog-gate.py(structural mode, no--release). If it fails, STOP and fixCHANGELOG.md's structure before doing anything else — everything below assumes a well-formed file to parse and rewrite.
If the user's argument was check, stop here and just report: last tag, current
[Unreleased] contents, and whether the changelog is structurally clean. Don't draft or tag.
Step 1 — gather what shipped
git log <last-tag>..HEAD --oneline(or the full history if there is no last tag) for the raw commit list.gh pr list --state merged --search "merged:>=<last-tag date>"(or--base mainwith no date filter for the first release) for PR-level titles and numbers — usually a cleaner unit than individual commits, since squash-merged PRs collapse a feature's commit noise to one line already.- Ask the user what this release should headline as shipping — don't only derive silently from git history. Something like: "Here's what I found merged since : [list]. Anything missing, anything that shouldn't be called out, or a theme you want this release to lead with?" This is the "capture the user's shipping requirement" step — git history says what CHANGED, only the user reliably knows what actually MATTERS to call out for this version's audience.
- Classify each item into Keep a Changelog's buckets (
Added/Changed/Fixed/Removed/Security) by what it reads as to someone outside this repo, not by its commit-type prefix verbatim — afix:commit that closes a CVE goes underSecurity, notFixed, because that is the fact a consumer actually needs surfaced. Dependency-bump PRs (dependabot) do NOT get one line each — summarize them once (e.g. "Updatedfastapi,boto3, and 6 other dependencies to their latest patch releases"), per the exemption already noted insideCHANGELOG.mditself.
Step 2 — the version decision (never silent)
Ask the user directly — this is a real AskUserQuestion, not a default this skill picks for
them:
- If there is no prior tag: the version IS
0.1.0. Confirm it, don't re-derive it — but still ask whether this first release is itself a pre-release (0.1.0-rc.1,0.1.0-beta.1) or the real0.1.0. - Otherwise, given the drafted entries from Step 1, ask which applies (SemVer 2.0.0, with
the pre-1.0 reading
docs/standards/versioning-and-releases.md§1 states plainly — while major is0, MINOR is the ceiling either a breaking change or a new feature bumps to; PATCH is fixes-only; MAJOR is reserved until this project commits to1.0.0):- PATCH (
x.y.Z+1) — fixes/dependency bumps only, nothing new or changed in behavior. - MINOR (
x.Y+1.0) — new features, or ANY breaking change (pre-1.0 — see above). - Pre-release of either — append
-alpha.N/-beta.N/-rc.N(stage order in that sequence; N starts at 1 per stage). This is the pre-release/release brand split GitHub's ownprereleaseflag reads at publish time.
- PATCH (
Compute the final version string from the answer. Do not proceed past this step on an assumed answer, including "obviously patch" — the cost of asking is one question; the cost of guessing wrong is a published tag, which this repo's own convention treats as something you don't undo by force-pushing over it.
Step 3 — write the changelog entry
- In
CHANGELOG.md, insert a new section directly below## [Unreleased]:## [X.Y.Z] - <today, YYYY-MM-DD>, populated with the classified entries from Step 1 under their Keep a Changelog subheadings (omit empty subheadings in the new section — the seed file keeps them empty at the top only so contributors know the vocabulary). - Reset
## [Unreleased]back to the empty subheading template (see the current top of the file for the exact shape) — anything moved into the new version section is removed from here, not duplicated. - Run
python scripts/changelog-gate.py --release X.Y.Z. If it fails, the edit is wrong — fix it and re-run before touching git. This is the same checkoss-release.ymlruns at tag time; failing it here is cheap, failing it there means redoing this step anyway after a failed workflow run.
Step 4 — commit, tag, push (confirm before this line)
Stop and get an explicit go-ahead before this step. Pushing a v* tag is what triggers
oss-release.yml: it builds and pushes ~41 Docker images to ghcr.io and publishes a public
GitHub Release. That is outward-facing and not casually reversible (images are pullable the
moment they push; a GitHub Release can be deleted but a tag someone already pulled cannot be
un-pulled) — this is exactly the class of action this project's own working convention treats
as needing a human's explicit yes, not an inferred one from "the changelog looks done."
Once confirmed:
git add CHANGELOG.md
git commit -m "docs(changelog): v X.Y.Z"
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin main
git push origin vX.Y.Z
Then tell the user: the tag push just triggered oss-release.yml (link:
https://github.com/<org>/<repo>/actions/workflows/oss-release.yml); it will fail closed at
the changelog gate if anything about this got out of sync, before building a single image.
Guardrails
- Never invent a version number the user hasn't confirmed, including for a "trivial" release.
- Never tag/push without the Step 4 confirmation, even if every earlier step went cleanly.
- Never let a dependency-bump PR accumulate its own changelog line — summarize once per
release, per the exemption already written into
CHANGELOG.md. - If
scripts/changelog-gate.pyfails at ANY point, that is the file telling you it is currently unsafe to release from — fix the structure, don't work around the check.
Signals
- GitHub stars
- 25
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
oss-publish- Source
- github.com/letuhao/lore-weave