Skip to content
Back to skills

Ll Manage Release

ASecurity

Manage releases - create git tags, generate changelogs, and manage GitHub releases. Integrates with Issue Management to include completed issues in release notes. Trigger keywords: "manage release", "create release", "new release", "tag release", "publish release", "make release", "bump version"

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentspythongobashnodegitdocumentation

Works with

  • cli

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add BrennonTWilliams/little-loops --skill ll-manage-release --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ll Manage Release?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ll Manage Release
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/brennontwilliams-ll-manage-release/badge)](https://www.skillsdirectory.com/skills/brennontwilliams-ll-manage-release)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ll-manage-release
description: |
  Manage releases - create git tags, generate changelogs, and manage GitHub releases.
  Integrates with Issue Management to include completed issues in release notes.

  Trigger keywords: "manage release", "create release", "new release", "tag release", "publish release", "make release", "bump version"
argument-hint: "[action] [version]"
arguments:
  - name: action
    description: "Action to perform (tag|changelog|release|bump|full). Omit for interactive mode."
    required: false
  - name: version
    description: "Version string (e.g., v1.2.3 or 1.2.3) or bump level (patch|minor|major)"
    required: false
  - name: flags
    description: "Optional flags: --dry-run, --push, --draft"
    required: false
allowed-tools:
  - Bash(gh:*, git:*)
  - AskUserQuestion
  - Task
  - Read
  - Glob
  - Grep
---

# Manage Release

You are tasked with managing releases for this project. This includes creating git tags, generating changelogs from commits and completed issues, creating GitHub releases, and bumping version numbers.

## Configuration

Read settings from `.ll/ll-config.json`:

- **Issues base**: `{{config.issues.base_dir}}` (default: `.issues`)

Version is tracked in files like these (discovered per-project by Agent 3, not a fixed checklist):
- Manifest `version` fields — e.g. `pyproject.toml`, `package.json`, `Cargo.toml`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`
- A `__version__ = "X.Y.Z"` assignment, if the project has one
- Any other declaration Agent 3 finds under the project's actual layout

Changelog: `CHANGELOG.md` (follows [Keep a Changelog](https://keepachangelog.com/) format)

---

## Process

### 1. Check Prerequisites

```bash
# Verify gh CLI is authenticated
gh auth status

# Verify we're in a git repo
git rev-parse --git-dir

# Check for uncommitted changes
git status --porcelain
```

If `gh` is not authenticated, instruct the user to run `gh auth login` and stop.

If there are uncommitted changes, warn the user and ask whether to proceed or stop:

```yaml
questions:
  - question: "There are uncommitted changes in the working tree. Proceed with release anyway?"
    header: "Uncommitted"
    multiSelect: false
    options:
      - label: "Proceed anyway"
        description: "Continue with release despite uncommitted changes"
      - label: "Stop"
        description: "Abort release to commit or stash changes first"
```

### 2. Parse Arguments

```
ACTION="${action}"        # tag|changelog|release|bump|full (optional)
VERSION="${version}"      # vX.Y.Z, X.Y.Z, patch, minor, major (optional)
DRY_RUN=false            # set true if flags contains "--dry-run"
PUSH=false               # set true if flags contains "--push"
DRAFT=false              # set true if flags contains "--draft"
```

### 3. Interactive Mode (No Arguments)

If `ACTION` is empty, use `AskUserQuestion` to gather preferences:

```yaml
questions:
  - question: "Which release actions should be performed?"
    header: "Actions"
    multiSelect: true
    options:
      - label: "Full release (Recommended)"
        description: "Run bump + changelog + tag + release in sequence"
      - label: "Tag only"
        description: "Create a git tag for the release version"
      - label: "Changelog only"
        description: "Generate changelog from commits and completed issues since last tag"
      - label: "GitHub release only"
        description: "Create a GitHub release with generated notes"
  - question: "Which version bump level should be used?"
    header: "Version"
    multiSelect: false
    options:
      - label: "Auto-detect (Recommended)"
        description: "Infer from conventional commits (feat→minor, fix→patch, BREAKING→major)"
      - label: "Patch"
        description: "Bug fixes and minor changes (x.y.Z)"
      - label: "Minor"
        description: "New features, backwards compatible (x.Y.0)"
      - label: "Major"
        description: "Breaking changes (X.0.0)"
```

Map user responses to action and version variables:
- "Full release" → `ACTION=full`
- "Tag only" → `ACTION=tag`
- "Changelog only" → `ACTION=changelog`
- "GitHub release only" → `ACTION=release`
- If multiple selected, execute each in order: bump → changelog → tag → release
- "Auto-detect" → `VERSION=auto`
- "Patch" / "Minor" / "Major" → `VERSION=patch|minor|major`

**After initial prompt**: Execute ALL selected actions without stopping for further confirmation.

---

### 4. Wave 1: Parallel Information Gathering

**IMPORTANT**: Spawn all 3 agents in a SINGLE message with multiple Task tool calls, each with `run_in_background: false`, and wait for all results in this same turn.

#### Agent 1: Git History Analysis

```
Use Task tool with subagent_type="Explore"

Prompt:
Analyze git history for release preparation.

1. List all existing tags:
   git tag --list --sort=-version:refname

2. Identify the baseline tag for this release using smart detection:
   # If HEAD is exactly at a tag (e.g., changelog run after tagging), use the tag before it
   if git describe --exact-match HEAD >/dev/null 2>&1; then
     CURRENT_TAG=$(git describe --exact-match HEAD)
     PREV_TAG=$(git describe --tags --abbrev=0 "${CURRENT_TAG}^")
   else
     PREV_TAG=$(git describe --tags --abbrev=0)
   fi

3. Get commits since baseline tag (or all commits if no tags):
   git log ${PREV_TAG}..HEAD --pretty=format:"%H|%s|%an|%ad" --date=short

4. Parse conventional commits and categorize:
   - feat: → Features (minor bump)
   - fix: → Bug Fixes (patch bump)
   - refactor:/perf: → Improvements
   - docs: → Documentation
   - chore:/ci:/build: → Maintenance
   - BREAKING CHANGE or !: → Breaking Changes (major bump)

   Exclusions (ENH-2467): skip commits whose subject starts with `chore(issues):`
   — these are `.issues/`-only housekeeping commits (issue status flips,
   session-log appends) deliberately demoted by /ll:manage-issue. They must not
   appear in ANY changelog section, including Maintenance. As defense-in-depth,
   also skip any commit whose changed files are entirely under `.issues/`
   (check with: git diff-tree --no-commit-id --name-only -r <sha>).

5. Suggest next version based on commit types:
   - If any BREAKING CHANGE → major
   - If any feat: → minor
   - Otherwise → patch

Return: last tag, commit breakdown by type, suggested next version, full commit list.
```

#### Agent 2: Completed Issues Since Last Tag

```
Use Task tool with subagent_type="Explore"

Prompt:
Scan completed issues for release notes using frontmatter status fields.

1. Determine the baseline tag using smart detection (handles running before or after tagging):
   if git describe --exact-match HEAD >/dev/null 2>&1; then
     CURRENT_TAG=$(git describe --exact-match HEAD)
     PREV_TAG=$(git describe --tags --abbrev=0 "${CURRENT_TAG}^")
   else
     PREV_TAG=$(git describe --tags --abbrev=0)
   fi

2. Get the full ISO timestamp of the baseline tag commit:
   PREV_TIMESTAMP=$(git log -1 --format="%aI" "${PREV_TAG}")

3. List issues with status: done whose completed_at falls on/after the baseline
   tag's date. `ll-issues list --json` emits `completed_at` as a day-granularity
   ISO date (e.g. "2026-07-09") or null; compare on DATE, not raw strings —
   completed_at day-values and the tag's tz-offset timestamp do not order
   correctly under lexical `>=`:

   ll-issues list --status done --json | python3 -c "
import sys, json
from datetime import date, datetime
data = json.load(sys.stdin)
prev_ts = '$(echo ${PREV_TIMESTAMP})'
prev_date = datetime.fromisoformat(prev_ts).date()
def _cad(v):
    if not v:
        return None
    try:
        return date.fromisoformat(v[:10])
    except ValueError:
        return None
results = [i for i in data if (_cad(i.get('completed_at')) or date.min) >= prev_date]
print(json.dumps(results))
"

4. For each issue in the result:
   - Extract id, title, type (BUG/FEAT/ENH/EPIC) from the JSON data
   - Extract github_issue from frontmatter (if present)

5. Categorize issues:
   - Features: FEAT-* issues
   - Bug Fixes: BUG-* issues
   - Enhancements: ENH-* issues

6. Format each entry as:
   - With github_issue: "ISSUE-ID: Title (#github_number)"
   - Without github_issue: "ISSUE-ID: Title"

Return: categorized list of issues, count per category.
```

#### Agent 3: Version References

```
Use Task tool with subagent_type="Explore"

Prompt:
Find all version declarations in the project.

1. Glob (non-recursive, top-level only — never `**/`) for manifest files at
   the repo root and at `{{config.project.src_dir}}`: `pyproject.toml`,
   `package.json`, `Cargo.toml`, `.claude-plugin/plugin.json`,
   `.claude-plugin/marketplace.json`. For each match, report its `version`
   field(s) — some files (e.g. `marketplace.json`) may declare it more than once.
2. Grep for `^__version__\s*=` scoped to `{{config.project.src_dir}}` — do
   not Glob for `__init__.py`, which matches every package in the tree.
3. Grep the project for the current version string and classify each hit as
   a **declaration** (a manifest `version` field or `__version__ =`
   assignment — already covered by steps 1–2) or **incidental** (e.g.
   `CHANGELOG.md`, lockfiles, `node_modules/`, `.issues/`, docs). Report
   incidental hits separately, for visibility only — they are never bump
   targets.

For each declaration found, report:
- File path and line number
- Current version value
- The exact line content (for precise editing)

Return: list of version declarations with current values, plus a separate
list of incidental matches (informational only).
```

### 5. Wave 2: Synthesis and Execution

After all Wave 1 agents complete:

#### 5a. Merge Results

1. **Determine target version**:
   - If `VERSION=auto`: use Wave 1 Agent 1's suggestion based on conventional commits
   - If `VERSION=patch|minor|major`: calculate from current version (Wave 1 Agent 3)
   - If `VERSION=vX.Y.Z` or `X.Y.Z`: use as-is (normalize to `vX.Y.Z` for tags, `X.Y.Z` for files)

2. **Build release notes** by merging:
   - Completed issues (Agent 2) grouped by type
   - Commits without associated issues (Agent 1) grouped by conventional commit type

3. **Empty-result guard** (do not ship a silent empty changelog): if Agent 1
   returns 0 commits AND Agent 2 returns 0 issues, STOP and warn loudly —
   report the baseline tag (`PREV_TAG`) and range used, and ask the user to
   confirm before continuing. A double-zero almost always means a broken
   baseline or a query regression (see BUG-942), not a genuinely empty release.
   Also warn (but continue) if Agent 2 returns 0 issues while Agent 1 found
   commits that reference issue IDs — the completed_at query may be stale.

#### 5b. Execute Actions

If `DRY_RUN` is true, show preview of each action instead of executing.

Execute actions in order: **bump → changelog → tag → release**

The changelog is written and committed **before** the tag is created, so the
tag points at a commit that contains its own `CHANGELOG.md` entry (and so the
GitHub release, whose notes are derived from that entry, is consistent with the
tagged tree).

##### Action: `bump`

Update version in exactly the declaration files Agent 3 found — never the
incidental matches:

```bash
# For each version declaration Agent 3 reported, use Edit tool to update the
# version string in place (e.g. version = "X.Y.Z" → version = "NEW_VERSION",
# or __version__ = "X.Y.Z" → __version__ = "NEW_VERSION").
```

After bumping, commit the version change, staging exactly the files you just
edited above (explicit paths — never `git add -A` or `git add -u`):

```bash
git add <declaration files edited above>
git commit -m "chore(release): bump version to NEW_VERSION"
```

##### Action: `changelog`

Generate a changelog entry following the [Keep a Changelog](https://keepachangelog.com/) format.

Build the entry:

```markdown
## [X.Y.Z] - YYYY-MM-DD

### Added

- **Feature name** - Description (FEAT-NNN)
- feat: commit message (abc1234)

### Fixed

- **Bug fix name** - Description (BUG-NNN)
- fix: commit message (abc1234)

### Changed

- **Enhancement name** - Description (ENH-NNN)
- refactor: commit message (abc1234)

### Other

- docs: commit message (abc1234)
- chore: commit message (abc1234)
```

Rules:
- Completed issues take priority over commits — if a commit is associated with an issue, only list the issue
- Include GitHub issue links where `github_issue` exists: `(FEAT-NNN, #42)`
- Omit empty sections (e.g., if no "Fixed" entries, don't include the heading)
- Add comparison link at bottom: `[X.Y.Z]: https://github.com/OWNER/REPO/compare/PREV_TAG...vX.Y.Z`

Insert the entry into `CHANGELOG.md`:
1. Read current `CHANGELOG.md`
2. Replace `## [Unreleased]` section content with the `### Planned` items (keep section header)
3. Insert new version section between `## [Unreleased]` and the previous version section
4. Add comparison link

Commit the changelog:

```bash
git add CHANGELOG.md
git commit -m "docs(release): add changelog for vX.Y.Z"
```

##### Pre-Release: Learning Test Audit

Before creating a tag, run the learning-test pre-release audit if `learning_tests.enabled` is true in `.ll/ll-config.json`:

```bash
python -c "
import sys, pathlib
from little_loops.learning_tests.release_gate import run_release_gate
sys.exit(run_release_gate(pathlib.Path.cwd()))
"
```

- If the script exits **1**, abort the release and display the warning table output above.
- If the script exits **0**, continue to tag creation.
- If `learning_tests.enabled` is false or absent, skip this step silently.

The audit behavior is controlled by `learning_tests.release_gate` in `.ll/ll-config.json`:
- `warn` (default) — prints a warning table but continues
- `block` — aborts the release with exit 1

##### Action: `tag`

Create an annotated git tag:

```bash
git tag -a vX.Y.Z -m "Release vX.Y.Z"
```

If `--push` flag is set:

```bash
git push origin vX.Y.Z
```

##### Action: `release`

Create a GitHub release:

```bash
# Write release notes to temp file
# (Same content as changelog entry, formatted for GitHub)
mkdir -p .loops/tmp

gh release create vX.Y.Z \
  --title "vX.Y.Z" \
  --notes-file .loops/tmp/ll-release-notes.md
```

If `--draft` flag is set, add `--draft` to the command.

If the tag hasn't been pushed yet:

```bash
git push origin vX.Y.Z
```

##### Action: `full`

Execute all actions in sequence: bump → changelog → (learning test audit) → tag → release

---

### 6. Dry-Run Output

When `--dry-run` is active, output:

```
=== DRY RUN: Release vX.Y.Z ===

Current version: A.B.C
Target version:  X.Y.Z
Last tag:        vA.B.C

Actions to perform:
  [bump]      Update version in N files
  [tag]       Create annotated tag vX.Y.Z
  [changelog] Add changelog entry with N issues and M commits
  [release]   Create GitHub release vX.Y.Z

--- Changelog Preview ---

## [X.Y.Z] - YYYY-MM-DD

### Added
- ...

### Fixed
- ...

--- Version Files ---
  <declaration file>:<line> → version = "X.Y.Z"
  <declaration file>:<line> → version = "X.Y.Z"
  ... (one line per version declaration Agent 3 found — paths and count are
  project-specific, not fixed)

=== END DRY RUN (no changes made) ===
```

### 7. Report Result

After successful execution, output:

```
Release vX.Y.Z completed successfully!

  Tag:       vX.Y.Z (annotated)
  Changelog: CHANGELOG.md updated
  Release:   https://github.com/OWNER/REPO/releases/tag/vX.Y.Z
  Version:   X.Y.Z (updated in 3 files)

  Issues included: N (F features, B bug fixes, E enhancements)
  Commits included: M
```

---

## Arguments

$ARGUMENTS

- **action** (optional): Action to perform
  - `tag` — Create an annotated git tag
  - `changelog` — Generate changelog from commits and completed issues since last tag
  - `release` — Create a GitHub release with generated notes
  - `bump` — Update version strings in project files
  - `full` — Run bump + changelog + tag + release in sequence
  - If omitted, enters interactive mode

- **version** (optional): Version target
  - `vX.Y.Z` or `X.Y.Z` — Use specific version
  - `patch` — Bump patch version (x.y.Z)
  - `minor` — Bump minor version (x.Y.0)
  - `major` — Bump major version (X.0.0)
  - If omitted, auto-detects from conventional commits

- **flags** (optional):
  - `--dry-run` — Preview actions without executing
  - `--push` — Push tag to remote after creation
  - `--draft` — Create GitHub release as draft

---

## Examples

```bash
# Interactive mode — prompts for actions and version
/ll:manage-release

# Create a specific version tag
/ll:manage-release tag v1.5.0

# Generate changelog (auto-detect version from commits)
/ll:manage-release changelog

# Full release pipeline with specific version
/ll:manage-release full v1.5.0

# Full release with auto-detected version
/ll:manage-release full

# Bump version only
/ll:manage-release bump minor

# Preview what a release would do
/ll:manage-release full v1.5.0 --dry-run

# Create a draft GitHub release
/ll:manage-release release v1.5.0 --draft

# Tag and push to remote
/ll:manage-release tag v1.5.0 --push
```

---

## Error Handling

- **gh not installed**: Suggest installation via `brew install gh` (macOS) or platform docs
- **gh not authenticated**: Suggest `gh auth login`
- **Not a git repo**: Inform user and stop
- **Uncommitted changes**: Warn and ask to proceed or stop
- **Tag already exists**: Inform user, offer to use `--force` or choose different version
- **No commits since last tag**: Inform user there are no changes to release
- **Version parse failure**: Show current version and ask user to provide explicit version
- **GitHub release creation fails**: Show error, suggest checking `gh auth status` and repository permissions

---

## Integration

This command works well with:
- `/ll:commit` — Commit changes before releasing
- `/ll:check-code` — Ensure code quality before releasing
- `/ll:run-tests` — Verify tests pass before releasing
- `/ll:manage-issue` — Complete issues that should be in the release
- `/ll:sync-issues` — Sync issues with GitHub before including in release notes
- `/ll:open-pr` — Open PR for release branch if using release branches

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…