$npx -y skills add testdouble/han --skill han-releaseCut a Han release: update CHANGELOG.md with the changes since the last release, bump every plugin that changed, tag the suite version vX.Y.Z, and publish a GitHub release whose notes attribute every merged pull request to its author, credit every closed issue to the person who op
| 1 | ## Pre-requisites |
| 2 | |
| 3 | - gh CLI: !`which gh 2>/dev/null || echo "not installed"` |
| 4 | - jq: !`which jq 2>/dev/null || echo "not installed"` |
| 5 | - git repo: !`git rev-parse --is-inside-work-tree 2>/dev/null || echo NO` |
| 6 | |
| 7 | **If `gh` or `jq` reads `not installed`, or this is not a git repo:** tell the operator which prerequisite is missing and that it must be installed/configured before `/han-release` can run, then **immediately stop**. The skill cannot proceed without all three. |
| 8 | |
| 9 | ## Project Context |
| 10 | |
| 11 | - repo: !`gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || git config --get remote.origin.url` |
| 12 | - current branch: !`git branch --show-current 2>/dev/null || echo unknown` |
| 13 | - default branch: !`git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##' || echo unknown` |
| 14 | - working tree: !`git status --porcelain 2>/dev/null || echo NO` |
| 15 | - parent plugin name: !`jq -r .name .claude-plugin/marketplace.json 2>/dev/null` |
| 16 | - plugins (name source version): !`jq -r '.plugins[] | "\(.name)\t\(.source)\t\(.version)"' .claude-plugin/marketplace.json 2>/dev/null` |
| 17 | - latest release tag: !`git fetch --tags --quiet >/dev/null 2>&1; git tag -l 'v*.*.*' --sort=-v:refname | head -n1` |
| 18 | - changelog head: !`grep -m1 '^## v' CHANGELOG.md 2>/dev/null` |
| 19 | |
| 20 | ### Vocabulary used throughout this skill |
| 21 | |
| 22 | - **parent** — the meta-plugin whose name equals the marketplace `name` (`parent plugin name` above, normally `han`). It has no skills or agents of its own; it exists to install the children via `dependencies`. The git tag tracks the parent's version, so the release tag is `v{parent target}`. |
| 23 | - **children** — every other entry in `marketplace.json.plugins[]` (`han-core`, `han-github`, `han-reporting`, and any future `han-*` plugin). Each child has its own version line, bumped independently of the others. |
| 24 | - **baseline** of a plugin — its version at `prev` (the latest release tag). For the parent this is `prev#`. For a child it is the version recorded in that child's `plugin.json` at `prev`; if the child did not exist at `prev`, it is a **new plugin** (see Step 3). |
| 25 | - **current** of a plugin — the version in its working-tree `plugin.json`. |
| 26 | - **target** of a plugin — the version being released for it. The release tag is `v{parent target}`. |
| 27 | - `prev` is the `latest release tag` (for example `v2.7.0`; the number without the leading `v` is `prev#`). On the first release `prev` is empty. |
| 28 | - Each plugin's source directory comes from the `source` field in `marketplace.json` (for example `./han-core`), so its `plugin.json` is `{source}/.claude-plugin/plugin.json`. Use `{source}` verbatim in every git command: the `./`-prefixed form works both after a `{ref}:` colon (`git show {prev}:{source}/...`) and as a pathspec (`git diff ... -- {source}/`). Do not strip the leading `./`. |
| 29 | |
| 30 | ## Step 1: Parse the invocation and check release safety |
| 31 | |
| 32 | 1. **Parse `$ARGUMENTS`** for two independent flags, then treat the remaining free text as optional release context that informs the changelog narrative: |
| 33 | - `pause_before_publish` — true if the argument contains "pause", "review", or "confirm before publish" (case-insensitive). Default **false**. |
| 34 | - `draft_release` — true if the argument contains "draft". Default **false**. |
| 35 | - The leftover text (anything that is not those flag phrases) is `$release_context`, passed into the narrative dispatch in Step 5. May be empty. |
| 36 | |
| 37 | 2. **Working tree must be clean.** If `working tree` from Project Context is non-empty, there are uncommitted or untra |