| name | eforge-release |
| description | Release a new version of eforge with release notes, changelog, and GitHub Release - supports patch, minor, and major release types |
| argument-hint | [--patch|--minor|--major] |
| disable-model-invocation | true |
/eforge-release
Release a new version of eforge. Parses the release type from arguments, checks git status, optionally commits staged changes, generates release notes from the git log, updates CHANGELOG.md, bumps the version on a release PR, tags merged main, pushes the tag, and creates a GitHub Release.
Workflow
Step 1: Parse Release Type
Read $ARGUMENTS to determine the release type:
--patch or empty/unrecognized - patch (default)
--minor - minor
--major - major
Store the resolved bump type (one of patch, minor, major) for use in later steps.
Step 2: Check Git Status
Run git status --porcelain to determine the repo state.
Three possible outcomes:
- Clean (no output) - proceed to Step 4
- All changes staged (all lines start with
M , A , D , R - no ?? or M or D lines) - proceed to Step 3
- Unstaged or untracked changes exist (any
??, M, D, or MM lines) - STOP. Tell the user there are unstaged/untracked changes and they need to stage or stash them first. List the problematic files.
Step 3: Commit Staged Changes
Use the /git:commit-message-policy skill to create a commit for the staged changes. Follow the standard commit workflow:
- Run
git diff --cached to see what's staged
- Run
git log --oneline -5 for recent commit style
- Draft a commit message following Conventional Commits format
- Create the commit
Step 4: Generate Release Notes
Generate release notes from the git log between the previous tag and HEAD.
Find the previous tag:
git describe --tags --abbrev=0
If no tags exist, fall back to the root commit:
git rev-list --max-parents=0 HEAD
Collect commits:
git log <PREV_TAG>..HEAD --oneline
Filter noise - remove any lines matching these patterns:
- Version bump messages: lines matching
^\w+ \d+\.\d+\.\d+$ (e.g., 0.2.5)
- Contains
enqueue( (eforge queue entries)
- Contains
cleanup( (eforge cleanup commits)
- Contains
plan( (eforge planning artifacts)
- Contains
Merge (merge commits)
- Contains
bump plugin version
Clean up commit messages:
- Strip the leading commit hash
- Strip
plan-NN- and hardening-NN- prefixes from conventional commit scopes (e.g., feat(plan-01-foo): bar becomes feat(foo): bar, feat(hardening-03-foo): bar becomes feat(foo): bar)
- Extract the scope (text inside parentheses) and the description (text after
: )
- Normalize the scope: if it maps to a known package/module, use that; if no scope, use
core
Known scope → package mappings:
engine → engine
client → client
monitor → monitor
monitor-ui → monitor-ui
eforge → eforge (CLI)
pi-eforge → pi-eforge
plugin → plugin (Claude Code plugin)
mcp → mcp
backends → backends
deps, dependencies → deps
queue → queue
revert-* or revert → core
gap-close → core
cleanup → skip these commits entirely (they're PRD cleanup)
- Any other scope → use as-is (e.g.,
backend-new → backend-new)
- No scope → core
Deduplicate by description text - keep only the first occurrence of each description.
Group by conventional commit type into markdown sections:
feat - ### Features
fix - ### Bug Fixes
refactor - ### Refactoring
perf - ### Performance
docs - ### Documentation
chore, ci, build, test - ### Maintenance
- Anything else -
### Other
Within each section, format entries as:
- **<package>**: <description>
For example:
- **pi-eforge**: add searchable overlays for provider and model selection
- **engine**: subprocess-per-build with crash-safe reconciler
- **core**: Parent scheduler owns sessionId and emits session:start at spawn
Sort entries alphabetically by package name within each section.
Omit empty sections. If no meaningful commits remain after filtering, use "Maintenance release" as the release notes.
Store the generated markdown for use in Steps 5 and 7.
Step 5: Update CHANGELOG.md
Compute the new version before bumping by reading the source-of-truth version from packages/eforge/package.json and incrementing the appropriate component:
node -e "
const v = require('./packages/eforge/package.json').version.split('.');
const bump = '$BUMP_TYPE';
if (bump === 'major') { v[0]++; v[1]=0; v[2]=0; }
else if (bump === 'minor') { v[1]++; v[2]=0; }
else { v[2]++; }
console.log(v.join('.'));
"
(Where $BUMP_TYPE is the resolved bump type from Step 1.)
Note: the root package.json has no version field - this is a pnpm workspace and packages/eforge/package.json is the lockstep source of truth.
Create or update CHANGELOG.md:
- If
CHANGELOG.md does not exist, create it with a # Changelog heading
- Prepend a new entry immediately after the
# Changelog heading line:
## [X.Y.Z] - YYYY-MM-DD
<release notes from Step 4>
- Trim to a maximum of 20
## [ sections. If entries are removed, ensure this footer exists at the bottom of the file:
---
For older releases, see [GitHub Releases](https://github.com/eforge-build/eforge/releases).
Commit the changelog:
git add CHANGELOG.md
git commit -m "docs: update CHANGELOG.md for vX.Y.Z"
Step 6: Bump Version, Open PR, and Tag Merged Main
Use the protected-main flow. Create a release branch, bump without tagging, open a PR, and enable auto-merge:
git checkout -b release/v<version>
pnpm release <bump-type> --no-tag
git push -u origin release/v<version>
gh pr create --base main --head release/v<version> --title "release: v<version>" --body-file <notes-file>
gh pr merge release/v<version> --auto --merge --delete-branch
(Where <bump-type> is the resolved bump type from Step 1.)
pnpm release --no-tag (scripts/bump-version.mjs) bumps packages/eforge/package.json (source of truth), propagates the version to the other lockstep packages (client, engine, monitor, pi-eforge), and commits the lockstep package/version surfaces with message X.Y.Z, but does not create a tag.
After the PR merges, update main, verify the version, create the annotated tag on the merged main commit, and push only that tag:
git checkout main
git pull --ff-only origin main
git tag -a v<version> -m "v<version>"
git push origin refs/tags/v<version>
Step 7: Create GitHub Release and Summary
Create a GitHub Release from the generated release notes:
gh release create v<version> --title "v<version>" --notes-file <notes-file>
Report:
- The new version number
- The release type (patch, minor, or major)
- Link to the GitHub Release
- Remind the user that npm publish is handled by the tag-triggered GitHub Action