git clone --depth 1 https://github.com/testdouble/han /tmp/han-release && cp -r /tmp/han-release/.claude/skills/han-release ~/.claude/skills/han-releaseSKILL.md
## Pre-requisites
- gh CLI: !`which gh || echo MISSING`
- jq: !`which jq || echo MISSING`
- git repo: !`git rev-parse --is-inside-work-tree 2>/dev/null || echo NO`
**If `gh` or `jq` is MISSING, 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.
## Project Context
- repo: !`gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || git config --get remote.origin.url`
- current branch: !`git branch --show-current`
- default branch: !`git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##' || echo unknown`
- working tree: !`git status --porcelain`
- parent plugin name: !`jq -r .name .claude-plugin/marketplace.json 2>/dev/null`
- plugins (name source version): !`jq -r '.plugins[] | "\(.name)\t\(.source)\t\(.version)"' .claude-plugin/marketplace.json 2>/dev/null`
- latest release tag: !`git fetch --tags --quiet >/dev/null 2>&1; git tag -l 'v*.*.*' --sort=-v:refname | head -n1`
- changelog head: !`grep -m1 '^## v' CHANGELOG.md 2>/dev/null`
### Vocabulary used throughout this skill
- **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}`.
- **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.
- **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).
- **current** of a plugin — the version in its working-tree `plugin.json`.
- **target** of a plugin — the version being released for it. The release tag is `v{parent target}`.
- `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.
- 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 `./`.
## Step 1: Parse the invocation and check release safety
1. **Parse `$ARGUMENTS`** for two independent flags, then treat the remaining free text as optional release context that informs the changelog narrative:
- `pause_before_publish` — true if the argument contains "pause", "review", or "confirm before publish" (case-insensitive). Default **false**.
- `draft_release` — true if the argument contains "draft". Default **false**.
- The leftover text (anything that is not those flag phrases) is `$release_context`, passed into the narrative dispatch in Step 5. May be empty.
2. **Working tree must be clean.** If `working tree` from Project Context is non-empty, there are uncommitted or untracked changes. Stop and tell the operator to commit or stash them first. Releasing an unknown working state is unsafe and a pushed tag is hard to reverse. This is a hard stop, not a pause gate.
3. **Branch note (non-blocking).** If `current branch` is not the `default branch`, do not stop — note in the Step 7 summary that the release is being cut from `current branch` and the tag will point at that branch's `HEAD`. The operator chose autonomous; surface the fact, do not block.
## Step 2: Determine previous version, commit range, and PR list
1. **`prev`** is `latest release tag`. If it is empty, this is the **first release**: there is no previous tag, the commit range is the full history, all compare links are omitted, and every plugin is treated as new (Step 3).
2. **Commit range.** With a previous tag: `${prev}..HEAD`. First release: the full history (`HEAD` with no range base).
3. **Nothing to release check.** Run `git log {range} --oneline`. If it is empty, there are no commits since `prev`. Stop and tell the operator there is nothing to release.
4. **Collect merged PRs in the range.** Extract PR numbers from both squash subjects and merge commits:
```
git log {range} --pretty=%s%x00%b | grep -oE '#[0-9]+' | tr -d '#' | sort -un
```
For each number `N`, run `gh pr view N --json number,title,author,url,mergedAt,state`. Keep only entries where `state` is `MERGED`. Sort the survivors by `mergedAt` ascending (newest merge last). This is `$pr_list`. The PR list is repo-wide and appears once per release; it is not split per plugin. Build the PR lines and the changelog bullets per [references/release-notes-format.md](./references/release-notes-format.md) and [references/changelog-rules.md](./references/changelog-rules.md).
5. **No-PR fallback.** If `$pr_list` is empty (local-only or squash history with no PR refs), record the notable commit subjects from `git log {range} --oneline` instead, and use the commits form documented in both reference files.
6. **Collect closed issues and their attribution.** For each merged PR `N` in `$pr_list`, find the issues that PR closed and credit everyone involved. This relates each closed issue to the fix that resolved it.
- **Find the closed issues for the PR.** Take the issue numbers from `gh pr view N --json closingIssuesReferences --jq '[.closingIssuesReferences[]?.number]'` (the GitHub-tracked closing links). As a fallback for older PRs that linked via text, also scan the PR body and commit messages for GitHub closing keywords: `gh pr view N --json body,commits --jq '[.body, (.commits[].messageBody>
>
>
>
>
Performs deep architectural analysis of a specified module, directory, or feature area by examining structural coupling, data flow, concurrency patterns, risk, and SOLID alignment. Use when the user wants to assess, evaluate, or review the architecture, design quality, dependency structure, coupling, cohesion, or technical debt of an existing part of the codebase. Not for investigating specific bugs, runtime errors, or failures — use investigate. Not for test planning — use test-planning. Not for file-level code review — use code-review. Not for researching open-ended options, prior art, or how something works — use research. Not for writing documentation or architectural decision records.
Run a comprehensive code review on local source files. Use this skill when the user asks to review, audit, inspect, evaluate, or check code, even if they never use the word \"review.\" Does not post comments to GitHub pull requests — use post-code-review-to-pr for that. Does not analyze architectural structure or module boundaries — use architectural-analysis for that. Does not capture feedback on Han's own skills — use han-feedback for that.
>