Gemini CLI: This skill is triggered by description matching. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in
/submit-for-reviewmust be done manually using the review-agent prompt in a separate session.
What /deploy does
/deploy is the final step in the workflow. It combines version bumping and release creation into a single command: check state, optionally bump the version, then create a GitHub Release (and in multi-branch mode, promote the deploy branch to production first).
The branching mode changes the shape of the release: in trunk mode (BRANCH_PROD only) /deploy tags and releases the current branch directly; in multi-branch mode (BRANCH_DEV set, optionally with BRANCH_TEST) it first opens and merges a release PR from the deploy branch into production, and that merge is what closes the linked issues.
Step 1 — Verify branch and sync
Run git branch --show-current. The deploy branch for this project is:
dev (two-branch mode).
If not on the deploy branch, abort: "Switch to <deploy-branch> before running /deploy."
Then sync it to the remote. The script guards against uncommitted local changes, then runs git checkout, git fetch, and git reset --hard origin/<deploy-branch> as one atomic operation. The deploy branch is never edited locally under the CodeCannon workflow (only fast-forwarded from merges), so the hard reset is the correct sync; the dirty-tree guard catches accidental local edits before they are silently discarded.
python3 CodeCannon/skills/github-agile/scripts/sync-base-branch.py <deploy-branch>
If the script exits non-zero, stop and resolve the issue it reports before continuing.
Step 2 — Check current state
Find the latest version tag (git describe --tags --abbrev=0; if none, note this is the first release) and read the current version with cat VERSION.
Show the merge commits (and their PRs) since the last tag. The range depends on the mode:
Fetch main first — Step 1 only synced the deploy branch, so the local main ref may lag origin/main. Comparing against a stale local main over-reports the release (it re-lists already-promoted PRs and already-closed issues). Compute the range against the freshly-fetched remote ref:
git fetch origin main
git log origin/main..<deploy-branch> --merges --pretty=format:"%s"
Merge-commit subjects have the form Merge pull request #N from branch/name — parse the PR numbers, then retrieve each body with gh pr view <N> --json number,title,body.
From those PR bodies, compile the release's issue links:
Keep closing keywords and context references separate — do not merge them into one set:
- Close set — the union of every
Closes #Nline across all constituent PR bodies. These auto-close when the release PR merges intomain. Record, per constituent PR, the exactCloses #Nlines so they can be reproduced verbatim in the release PR body. - Reference set — issues mentioned only via
Related to #Nor the legacyIssue #Nform. These are context links and will not close. LegacyIssue #Ncarries no recoverable close-intent, so it stays in the reference set rather than being guessed into the close set; the human gate surfaces it so you can manually close any straggler that should have closed. - PRs included (number + title).
Also check for open unmerged PRs (gh pr list --state open --json number,title,headRefName).
Present a summary — current version, latest tag, the PRs/issues since that tag, any open PRs — and ask whether to bump the version before deploying (patch → X.Y.C, minor → X.B.0, major → A.0.0, a specific version, or skip to release the latest existing tag). Wait for their response.
Step 3 — Version bump (if requested)
If the user chose skip, use the latest existing tag (git describe --tags --abbrev=0) as the release version. If none exists (first release), warn "No version tag found. You must bump the version before deploying." and return to the bump prompt.
If the user chose a bump level, map it to a bump command and run bump-and-tag.py, which performs the bump, verifies the resulting tag (creating an annotated fallback if tag.forceSignAnnotated silently rejected a lightweight tag), and pushes both the commit and the tag. The resolved version is printed on stdout — capture it as <new-version>.
| User says | --bump-cmd |
|---|---|
| "patch" / anything mentioning patch | make bump-patch |
| "minor" | make bump-minor |
| "major" | make bump-major |
| A specific version e.g. "2.4.5" | make set-version V=2.4.5 |
python3 CodeCannon/skills/github-agile/scripts/bump-and-tag.py \
--bump-cmd "<bump-command-from-table>" \
--version-read-cmd "cat VERSION"
If the script exits non-zero, stop and resolve the issue it reports before continuing.
Step 4 — Compute release contents
Determine the release version tag (from the bump just performed, or the existing HEAD tag if the user skipped). Find the previous tag for the changelog range: git describe --abbrev=0 <version-tag>^.
Reuse the PR list, close set, and reference set already computed in Step 2. If the version bump added new commits, re-fetch as needed.
Step 5 — HUMAN GATE
Show the release summary — the target version, the PRs included, and the issue links:
- Issues that will close on merge (the close set, reproduced verbatim from constituent PRs).
- Issues referenced but not closing (the reference set — confirm none of these should actually close).
Confirm the deploy branch has been tested: "Have you tested all of the above on preview? Type 'release' to confirm."
Wait for "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change.
Step 6 — Promote: <deploy-branch> → main
Create a temp directory for this invocation (python3 CodeCannon/skills/github-agile/scripts/make-workdir.py) and note the returned path — use it for all temp files here.
Use your file-writing tool (not Bash) to create <tmpdir>/release_pr_body.md:
Release vX.Y.Z
PRs included:
- #17 — Add /docs directory
- #18 — Fix checkout runtime error
Closes #14
Closes #15
Related to #20
Reproduce every Closes #N line from the close set — verbatim, one per line, omitting none. Add a Related to #N line for each issue in the reference set so the links appear without triggering an auto-close; if the reference set is empty, omit the Related to lines entirely.
Create the PR (do NOT use --body, --body-file -, or heredocs), with --head set to the deploy branch:
gh pr create --base main --head <deploy-branch> \
--title "Release vX.Y.Z" \
--body-file <tmpdir>/release_pr_body.md
Critical: Use the unqualified
#Nform only. Never writeCloses owner/repo#N, even for same-repo refs — GitHub's closing-keyword parser only populatesclosingIssuesReferencesfor the unqualified form, and the qualified form silently breaks auto-close. TheCloses #Nlines auto-close the linked issues because this PR merges intomain(the default branch).
Then merge. Do NOT use make merge — it refuses PRs targeting main. Use gh pr merge <pr-number> --merge directly.
Step 7 — Create the GitHub Release
Publish confirmation — required before writing the release notes or creating the Release. The GitHub Release is a public-surface action; the confirmation from Step 5 authorized the promotion, but the public publish gets its own explicit confirmation. Tell the user (substitute the actual tag, e.g. v0.13.0):
Publishing GitHub Release
<version-tag>— the final public step. Confirm by pasting:publish <version-tag>
Wait for the user to paste publish <version-tag> (or an explicit version-named variant such as ship <version-tag>). Any other response → stop and ask what they'd like to change. The version-named phrase is deliberate: Claude Code's auto-mode safety classifier requires authorization that names the release before gh release create runs, so the generic Step 5 confirmation is not relied on for the public publish. If a harness still blocks the call after this confirmation (e.g. an older client), the user can re-confirm with publish <version-tag> release to unblock.
The version tag and PR/issue list are already known; the previous tag comes from Step 4 (if there is no previous tag, omit the "Full changelog" line). Create a temp directory if you haven't already (python3 CodeCannon/skills/github-agile/scripts/make-workdir.py), then use your file-writing tool (not Bash) to create <tmpdir>/release_notes.md:
## Changes
- #<issue> — <PR title> (PR #<pr-number>)
[... one line per PR included in this release ...]
**Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag>
Format each PR line as - #<linked-issue> — <PR title> (PR #<N>); if a PR had no linked issue, use just the PR title. Then create the release (do NOT use --notes, --notes-file -, or heredocs):
gh release create <version-tag> \
--title "<version-tag>" \
--notes-file <tmpdir>/release_notes.md
Note the release URL from the output.
Step 8 — Report
Tell the user: "Released vX.Y.Z. Linked issues are closed. GitHub Release vX.Y.Z created at <url>. Run make deploy-prod to ship to production."