release — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited release (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Wraps bin/release.sh. Default strategy is conventional-changelog: walk the commit range since the last matching tag, group commits by Conventional Commits type, prepend a CHANGELOG block, commit the changelog, then annotated-tag v<version>.
Run bash bin/session-check.sh --flow=release. If it produces output, surface the line to the user verbatim. Do not block the flow.
When the user does not supply an explicit --version, run bin/recommend-version.sh to suggest one:
bin/recommend-version.sh --target <cwd> [--tag-prefix <p>]Show the recommendation to the user:
Based on commits since<current>, I'd suggest `<recommended>` (<bump>bump —<reason>). Shall I proceed with<recommended>, or would you prefer a different version?
Wait for confirmation before proceeding. The user can accept, override with a different version, or abort.
When the user does supply an explicit version (e.g. "release v2.0.0"), skip this step entirely — don't second-guess them.
When the user says "bump minor" / "bump major" / "bump patch", run recommend-version.sh to get the current version, then apply the requested bump type (ignore the script's own recommendation). Confirm the computed version with the user before proceeding.
release.sh. Must be semver(or x.y.z-prerelease). Populated from step 1 when the user accepted the suggestion, or from their explicit input.
conventional-changelog. Override tomanual when the user says "just tag it, skip the changelog". changesets / release-please are soft-skip values: the script emits a skip record pointing the user at those tools directly.
CHANGELOG.md. Override whenthe repo uses a different file.
v. Override for monoreposthat namespace tags (api-v1.2.0).
tag_prefix. Override when the user says "include everything since commit X".
When the repo's active profile has a release block, prefer its values over the defaults. Ask before overriding what the profile declares.
<prefix><version> must not already exist. If it does,release.sh dies with a clear message; relay it.
to commit or stash first. (Exception: --dry-run skips this gate so the user can preview.)
the common case, but patch releases from release/* or hotfix/* are legitimate).
bash bin/pre-action-guard.sh --flow release --target <cwd> [--profile <resolved-profile.json>]The built-in clean-tree guard catches a dirty working tree before release.sh would (with a more actionable message). Profiles may add flow-specific guards via guards.release — surface those too.
| Exit | Meaning | Action |
|---|---|---|
| 0 | Pass | Continue |
| 3 | Critical guard failed | Refuse to release. Relay the guard's message verbatim — the user fixes the underlying issue. --skip-guards is available but should be rare for a release. |
| 4 | Confirm-severity guard failed | AskUserQuestion: "Proceed with the release?" — explicit confirm. |
Skip this step entirely on --dry-run (the user is previewing, not mutating).
When the user says "what would a release look like" or the commit range is large (>20 commits), run --dry-run first and show the rendered changelog block back to them. Ask for confirmation before the real run.
bin/release.sh --target <cwd> --version <x.y.z> \
[--strategy conventional-changelog|manual] \
[--changelog <path>] [--tag-prefix <p>] \
[--from <ref>] [--push] [--dry-run] [--yes] \
[--wait-for-checks] [--no-wait-for-checks] [--allow-no-pr] \
[--wait-for-checks-timeout <sec>] \
[--wait-for-checks-interval <sec>] \
[--bump-manifests] [--gh-release] [--profile <path>]Preview-before-mutate: when --strategy conventional-changelog and the run is NOT --dry-run, release.sh requires --yes. Without it, the script prints the rendered CHANGELOG block to stderr and exits 2 so the caller is forced to confirm before the mutation. The intended flow is:
--dry-run → show the rendered changelog block backto the user and ask if it looks right.
--yes (same args) → actual write + commit.Skip --yes handling for --strategy manual (no CHANGELOG write).
--push pushes the tag (and the release commit for conventional-changelog) to origin. Use when the user explicitly says "push the tag" or "publish the release". For quieter flows, skip --push and let the user run git push --follow-tags themselves.
CI gate by default: because a pushed tag is consumed by the marketplace, --push enables the CI gate automatically when the origin is a GitHub remote and gh is authenticated. The auto-enabled gate degrades gracefully — a release cut on main with no open PR, or a host without authenticated gh, proceeds with a warning instead of failing. Pass --no-wait-for-checks to push without any gate (e.g. a repo whose CI genuinely lives elsewhere).
--wait-for-checks resolves the PR by exact HEAD SHA (headRefOid) and gates the tag step on that PR's CI via bin/wait-for-pr-checks.sh. Passing it explicitly makes the gate strict: CI failure / timeout / unreachable gh / no PR all hard-fail (the auto-enabled default would instead degrade). Use the explicit form when the user says "tag the release once CI is green" or "don't tag if CI is broken".
When no PR matches HEAD (legitimate for first-cut releases or local-only commits, but also a CI-bypass risk in squash/rebase release flows), the default is also a hard-fail. Pass --allow-no-pr alongside --wait-for-checks to opt in to the proceed-without-PR path (output shows ci_gate.outcome:"no-pr-found"); without it the release exits 2 and points the user at the flag.
Skipped silently on --dry-run so a "show me the plan" call never burns 30 minutes polling.
--bump-manifests, --gh-release)When the resolved profile declares release.bump_files[], default the invocation to --bump-manifests so manifest version strings (plugin.json, marketplace.json, package.json, pyproject.toml, etc.) get rewritten to --version and land in the same release commit as CHANGELOG.md. The dry-run output's bumped_files[] array previews each file's from_version and action (bumped vs unchanged); show that to the user before the real run.
When the user says "publish the release on GitHub" / "create the GH release too", also pass --gh-release. The flag requires --push (the GH release attaches to the just-pushed tag) and runs gh release create <tag> --notes-file <rendered-changelog> after the push succeeds. For pre-release versions (-rc.N, -beta.N), the script auto-passes --prerelease to gh. There is no profile-level auto-toggle for --gh-release in v1.5.0 — gate on explicit user intent only.
Both flags are opt-in. On profiles WITHOUT release.bump_files, do not auto-pass --bump-manifests — it would be a no-op but the skill should stay quiet about flags the profile doesn't declare. On --strategy manual, --bump-manifests is rejected up-front (no commit for the bumps to land in).
When passing --bump-manifests, also pass --profile <path> if the caller already has the resolved profile snapshot in hand (otherwise release.sh re-resolves the default profile via load-profile.sh).
| status | what happened |
|---|---|
released | Tag created (and CHANGELOG + release commit for conventional-changelog). Report the tag name and pushed? flag. |
noop | No commits since from ref. Nothing to release. Tell the user. |
skipped | Strategy was changesets or release-please — relay the reason; point them at those tools. |
The JSON includes the rendered changelog block — read it back to the user so they know what landed in CHANGELOG.md before pushing.
When --bump-manifests was used, the JSON includes bumped_files[] with one record per declared file (bumped vs unchanged). Read this back too — especially the unchanged entries, which surface as a quiet "the file was already at the target version" rather than a silent no-op.
When --gh-release was used, the JSON includes gh_release with one of three outcomes: created (URL), skipped (gh missing/unauthed, with a recovery command in next_steps[]), or failed (with the redacted error string and a manual recovery command). The tag stays on origin in every case — nyann never undoes a successful tag push.
Commits with ! after the type/scope (e.g. feat(api)!: remove X) and commits whose body contains BREAKING CHANGE: are currently detected from the subject ! marker only (body parsing is out of scope for v1). If the user tells you a commit without ! is actually a breaking change, suggest they either amend the subject or manually edit the rendered CHANGELOG block before committing.
--push, or invoke git push --follow-tagsvia Bash after the fact.
gh release create <tag> --generate-notes as a follow-up.
undo skill for the commit;git tag -d <tag> + git push origin :refs/tags/<tag> for the tag itself. Destructive — always confirm first.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.