
Release Notes
- 87 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Release Notes Expert is a Claude skill that translates technical changes into clear, user-benefit-oriented release notes and changelogs tuned to the target audience.
About
Release Notes Expert turns raw technical changes (tickets, changelogs, git logs, PRDs) into clear, user-benefit-oriented release notes. A PM or developer uses it to announce product releases, summarize sprint demos, maintain a changelog or prepare customer communications. It classifies each change into five categories, rewrites entries to lead with the outcome, adjusts tone by audience, and ships a release_notes_generator.py.
- Classifies every change into features, improvements, fixes, breaking changes or deprecations
- Rewrites technical changes to lead with the user benefit
- Adjusts tone for B2B, consumer, developer or internal audiences
Release Notes by the numbers
- 87 all-time installs (skills.sh)
- Ranked #120 of 248 Release Management skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
release-notes capabilities & compatibility
- Capabilities
- release orchestrator · changelog generation · documentation
- Works with
- jira · github
- Use cases
- documentation · copywriting · project management
- Pricing
- Free
What release-notes says it does
Structured release notes creation that translates technical changes into user-benefit-oriented communication.
Every entry must lead with the benefit to the user, not the technical change.
Assign every change to exactly one category:
npx skills add https://github.com/borghei/claude-skills --skill release-notesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 87 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Turn tickets, git logs and changelogs into clear, user-benefit-oriented release notes tuned to the audience.
Who is it for?
Turning tickets and git logs into audience-tuned release notes and changelogs.
Skip if: Automated version bumping or deployment gating (use release-orchestrator).
When should I use this skill?
Announcing a product release, summarizing a sprint demo, or maintaining a changelog.
What you get
Audience-tuned release notes that lead with user benefit and separate breaking changes and deprecations.
- user-facing release notes
- changelog
By the numbers
- 5 change categories
- ships release_notes_generator.py
Files
Release Notes Expert
Overview
Transform raw technical changes -- tickets, changelogs, git logs, PRDs -- into clear, user-benefit-oriented release notes. This skill ensures every release communicates value to the right audience in the right tone.
When to Use
- Product Releases -- Announcing new versions to customers, partners, or internal stakeholders.
- Sprint Demos -- Summarizing what shipped for sprint review audiences.
- Changelog Maintenance -- Keeping a running log of changes across releases.
- Customer Communication -- Preparing release announcements for email, in-app, or documentation.
Methodology
Step 1: Gather Raw Input
Collect all changes from the release cycle:
- Jira/Linear tickets -- Completed stories, bugs, and tasks
- Git log -- Merge commits since last release tag
- PRD references -- Feature specs that shipped
- Hotfix records -- Emergency fixes deployed between releases
Step 2: Classify Each Change
Assign every change to exactly one category:
| Category | Definition | Example |
|---|---|---|
| New Features | Net-new capabilities that did not exist before | New export-to-PDF option |
| Improvements | Enhancements to existing functionality | Faster dashboard loading |
| Bug Fixes | Corrections to broken or incorrect behavior | Fixed login redirect loop |
| Breaking Changes | Changes that require user action to adapt | API v2 replaces v1 endpoints |
| Deprecations | Features scheduled for future removal | Legacy CSV import will be removed in v4.0 |
Classification rules:
- If a change adds something entirely new, it is a New Feature.
- If it makes something existing better (faster, easier, more reliable), it is an Improvement.
- If it fixes something that was wrong, it is a Bug Fix.
- If users must change their behavior, configuration, or integration, it is a Breaking Change.
- If a feature still works but will be removed later, it is a Deprecation.
Step 3: Rewrite for User Benefit
The most critical step. Every entry must lead with the benefit to the user, not the technical change.
Rewriting principles:
1. Lead with the outcome. What can the user do now that they could not before, or what is better for them? 2. Use plain language. Avoid internal jargon, code references, or implementation details. 3. Keep it to 1-3 sentences. One sentence for minor items, up to three for significant features. 4. Include context when needed. If users need to take action, tell them exactly what to do.
Before and after examples:
| Technical (Bad) | User-Benefit (Good) |
|---|---|
| Implemented Redis caching layer for dashboard queries | Dashboards now load up to 3x faster |
| Refactored authentication module to use OAuth 2.0 PKCE flow | Sign-in is now more secure and works reliably across all browsers |
| Fixed null pointer exception in report export handler | Report exports no longer fail when date ranges include empty days |
| Migrated user preferences API from v1 to v2 schema | Action required: Update your API calls to use the new /v2/preferences endpoint by April 30. See migration guide. |
| Added feature flag for beta dashboard | You can now opt into the redesigned dashboard from Settings > Beta Features |
Red flags that an entry needs rewriting:
- Mentions a class name, function, or library
- Starts with "Refactored," "Migrated," or "Updated" without stating impact
- Uses acronyms the target audience would not know
- Describes what the team did instead of what the user gains
Step 4: Adjust Tone for Audience
| Audience | Tone | Style Notes |
|---|---|---|
| B2B / Enterprise | Professional, precise | Emphasize reliability, security, compliance. Avoid casual language. |
| Consumer | Friendly, conversational | Use "you" and "your." Celebrate new features. Keep it light. |
| Developer / API | Technical, direct | Include endpoint names, SDK versions, code snippets. Be specific. |
| Internal | Detailed, context-rich | Include ticket IDs, team names, technical details as needed. |
Step 5: Assemble the Release Notes
Use the output template below. Include only categories that have entries -- do not show empty sections.
Output Template
# [Product Name] v[X.Y.Z] Release Notes
**Release Date:** [YYYY-MM-DD]
---
## New Features
- **[Feature Name]** -- [1-3 sentence description of user benefit]. ([TICKET-ID])
## Improvements
- **[Improvement Name]** -- [1-2 sentence description of what is better]. ([TICKET-ID])
## Bug Fixes
- **[Bug Fix Name]** -- [1 sentence describing what was broken and that it is now fixed]. ([TICKET-ID])
## Breaking Changes
> **Action Required:** The following changes require updates on your end.
- **[Change Name]** -- [Description of what changed and exactly what the user must do]. ([TICKET-ID])
## Deprecations
> **Planned Removal:** The following features will be removed in a future release.
- **[Feature Name]** -- [What is being deprecated and when it will be removed. Recommend alternative if available]. ([TICKET-ID])
---
**Full changelog:** [link]
**Questions?** [support link or contact]Python Tool
Use scripts/release_notes_generator.py to generate formatted release notes from structured input.
# Generate from JSON input
python scripts/release_notes_generator.py --input changes.json --product-name "Acme App" --version "2.5.0"
# Run with demo data
python scripts/release_notes_generator.py --demo --product-name "Acme App" --version "1.0.0"
# Output as JSON instead of markdown
python scripts/release_notes_generator.py --input changes.json --format json --product-name "Acme App" --version "2.5.0"See scripts/release_notes_generator.py --help for full usage.
Integration with Other Skills
- Use
summarize-meeting/to capture release planning discussions. - Use
job-stories/orwwas/to trace features back to their original motivation. - Pair with
../senior-pm/for stakeholder communication planning around major releases.
Troubleshooting
| Symptom | Likely Cause | Resolution |
|---|---|---|
| Tool flags too many entries as "technical language" | TECHNICAL_PATTERNS regex is broad, catching common words like "update" or "add" | Review the flagged entries; the tool provides suggestions, not mandates -- ignore false positives for user-facing descriptions |
| All entries classified as same type | Input JSON uses wrong type values or inconsistent casing | Use exact lowercase types: feature, improvement, bugfix, breaking, deprecation |
| Empty sections appear in output | No entries of that type exist but template still renders the section | The tool only renders sections with entries; empty sections indicate a data issue in input |
| Breaking changes not highlighted prominently | Entries marked as improvement instead of breaking | Review classification rules: if users must change behavior, config, or integration, it is breaking, not improvement |
| Release notes sound like commit messages | Descriptions written from developer perspective, not user perspective | Apply the rewriting principles: lead with outcome, use plain language, 1-3 sentences per entry |
--demo flag requires --product-name and --version | These are required arguments regardless of input source | Always provide both: --demo --product-name "App" --version "1.0.0" |
| JSON output missing rewriting suggestions | No entries triggered technical language detection patterns | This is expected behavior; suggestions only appear when TECHNICAL_PATTERNS match entry descriptions |
Success Criteria
- Every release has structured notes published within 24 hours of deployment
- All entries lead with user benefit, not technical implementation details
- Breaking changes include explicit migration instructions with deadlines
- Deprecations include removal timeline and recommended alternatives
- Technical language flags reviewed and addressed before publication
- Release notes tone matches the target audience (B2B/consumer/developer/internal)
- Entries traceable to tickets via ticket_id for audit and context
Scope & Limitations
In Scope:
- Structured release note generation from JSON input with 5 entry categories
- Automatic technical language detection with rewriting suggestions
- Markdown and JSON output formatting with category grouping
- Audience tone guidance (B2B, consumer, developer, internal)
- Classification rules for New Features, Improvements, Bug Fixes, Breaking Changes, and Deprecations
Out of Scope:
- Automatic extraction of changes from git history (see
sprint-retrospective/for git analysis) - Jira/Linear ticket integration for pulling completed stories (manual JSON input required)
- Changelog maintenance across multiple releases (this tool generates per-release notes)
- Distribution to email, in-app, or documentation channels (output is markdown/JSON for further processing)
Important Caveats:
- The rewriting suggestions are pattern-based heuristics. They catch common technical language but cannot assess whether a description truly communicates user benefit.
- Semantic versioning alignment is the user's responsibility. The tool does not validate that version numbers follow semver conventions relative to the change types present.
- Breaking changes require special care. Always include: what changed, what the user must do, and by when. Vague breaking change notes erode user trust.
Integration Points
| Integration | Direction | Description |
|---|---|---|
sprint-retrospective/ | Receives from | Sprint commit data and type distribution inform what changes to include |
senior-pm/ | Complements | Stakeholder communication plans guide release note audience and tone |
execution/create-prd/ | Receives from | PRD feature descriptions (Section 7) become release note entry drafts |
scrum-master/ | Receives from | Sprint review outputs identify what shipped and needs documentation |
summarize-meeting/ | Receives from | Release planning meeting summaries capture context for release notes |
job-stories/ / wwas/ | Receives from | User story descriptions inform user-benefit framing of entries |
Tool Reference
release_notes_generator.py
Generates formatted release notes from structured JSON input. Groups entries by category, formats into markdown or JSON, and flags entries that may need user-benefit rewriting.
| Flag | Type | Default | Description |
|---|---|---|---|
--input | string | (optional) | Path to JSON file containing release entries |
--demo | flag | off | Run with built-in demo data (8 entries across all types) |
--product-name | string | (required) | Product name for the release notes header |
--version | string | (required) | Version string (e.g., 2.5.0) |
--format | choice | text | Output format: text (markdown) or json |
--date | string | today | Release date in YYYY-MM-DD format |
Input JSON schema:
{
"entries": [
{
"title": "Feature Name",
"description": "User-benefit description (1-3 sentences)",
"type": "feature|improvement|bugfix|breaking|deprecation",
"ticket_id": "PROJ-123 (optional)"
}
]
}References
- See
references/release-notes-guide.mdfor best practices, audience guidance, and examples. - See
assets/release_notes_template.mdfor a ready-to-use document template.
[Product Name] v[X.Y.Z] Release Notes
Release Date: [YYYY-MM-DD]
---
New Features
- [Feature Name] -- [1-3 sentence description of user benefit. What can the user do now that they could not before?] ([TICKET-ID])
Improvements
- [Improvement Name] -- [1-2 sentence description of what is better. Quantify the improvement when possible.] ([TICKET-ID])
Bug Fixes
- [Bug Fix Name] -- [1 sentence describing what was broken and that it is now fixed.] ([TICKET-ID])
Breaking Changes
Action Required: The following changes require updates on your end.
- [Change Name] -- [Description of what changed and exactly what the user must do. Include a deadline if applicable.] ([TICKET-ID])
Deprecations
Planned Removal: The following features will be removed in a future release.
- [Feature Name] -- [What is being deprecated, when it will be removed, and what to use instead.] ([TICKET-ID])
---
Full changelog: [link to changelog or diff] Migration guide: [link, if breaking changes exist] Questions? [support link or contact]
---
Entry Template
Use this template for each individual entry before adding it to the release notes:
| Field | Value |
|---|---|
| Title | Short, descriptive name |
| Category | New Feature / Improvement / Bug Fix / Breaking Change / Deprecation |
| User Benefit | What the user gains (1-3 sentences) |
| Ticket ID | PROJ-XXX (optional) |
| Action Required | Yes / No -- if yes, describe what the user must do |
| Audience | All users / Admins / Developers / Specific segment |
Release Notes Best Practices Guide
Purpose
Release notes are the bridge between what your team built and what your users understand. They serve three functions: inform users of what changed, build trust through transparency, and drive adoption of new capabilities.
Category Definitions
New Features
Something that did not exist before. The user gains a capability they previously lacked.
Test: Could the user do this yesterday? If no, it is a new feature.
Improvements
An existing capability that is now better -- faster, easier, more reliable, more accessible.
Test: Could the user already do this, but now it is better? If yes, it is an improvement.
Bug Fixes
Something was broken and is now fixed. The software was not behaving as intended or documented.
Test: Was this a defect report or unexpected behavior? If yes, it is a bug fix.
Breaking Changes
A change that requires the user to take action. Their existing workflow, integration, or configuration will stop working or behave differently without intervention.
Test: Will anything break for the user if they do nothing? If yes, it is a breaking change.
Deprecations
A feature that still works today but will be removed in a future release. This gives users time to migrate.
Test: Does it still work but is scheduled for removal? If yes, it is a deprecation.
Writing for Different Audiences
B2B / Enterprise
- Tone: Professional, precise, confident
- Emphasize: Reliability, security, compliance, productivity gains
- Avoid: Casual language, exclamation marks, emojis
- Include: Impact on workflows, admin actions required, compliance implications
- Example: "Role-based access controls now support custom permission sets, enabling administrators to define granular access policies aligned with organizational security requirements."
Consumer
- Tone: Friendly, conversational, enthusiastic (measured)
- Emphasize: Ease of use, delight, time saved, new possibilities
- Avoid: Technical jargon, implementation details
- Include: Visual descriptions, tips for getting started
- Example: "You can now organize your photos into custom albums. Tap the new Albums tab to get started."
Developer / API
- Tone: Technical, direct, specific
- Emphasize: Endpoints, parameters, SDK versions, migration steps
- Avoid: Marketing language, vague descriptions
- Include: Code snippets, request/response examples, version numbers
- Example: "The
GET /v2/usersendpoint now accepts an optionalfieldsquery parameter for sparse fieldsets. See the API reference for supported field names."
Internal Stakeholders
- Tone: Detailed, context-rich
- Emphasize: Business impact, metrics, team contributions
- Avoid: Over-simplification
- Include: Ticket IDs, team names, technical context as needed
Distribution Channels
| Channel | Best For | Format |
|---|---|---|
| In-app notification | All users, high visibility | Short summary with link to full notes |
| Active users, subscription-based | Full notes or curated highlights | |
| Blog post | Major releases, marketing value | Narrative format with screenshots |
| Changelog page | Developer audience, reference | Chronological, all versions |
| Documentation | API changes, migration guides | Technical, step-by-step |
| Social media | Brand awareness, feature highlights | Single feature spotlight |
| Slack / Teams | Internal stakeholders | Summary with links |
Good vs Bad Examples
Bad: Technical and self-centered
- Updated React from v17 to v18
- Refactored UserService to use repository pattern
- Fixed NPE in ReportExportHandler.java line 234
- Migrated auth to PKCE flow
Good: User-benefit-oriented
- Smoother interactions -- Page transitions and form updates are now noticeably faster and more responsive.
- Faster report exports -- Reports that previously timed out now complete reliably, even for large date ranges.
- More secure sign-in -- Your account is now protected by an upgraded authentication standard that works consistently across all browsers.
Bad: Vague and unhelpful
- Various bug fixes and improvements
- Performance enhancements
- Updated dependencies
Good: Specific and actionable
- Fixed: CSV exports now include all columns when custom fields are enabled. (BUG-3421)
- Faster: Dashboard widgets load 40% faster on pages with more than 20 widgets.
- Updated: The Python SDK now requires Python 3.9 or later. See the migration guide for upgrade instructions.
Checklist Before Publishing
- [ ] Every entry leads with user benefit, not technical change
- [ ] No internal jargon, class names, or function references
- [ ] Breaking changes clearly state what the user must do
- [ ] Deprecations include a timeline and recommended alternative
- [ ] Tone matches the target audience
- [ ] Ticket IDs are included for traceability (if appropriate for audience)
- [ ] Empty categories are removed
- [ ] Release date and version number are correct
- [ ] Links to full changelog, support, and migration guides are included
#!/usr/bin/env python3
"""
Release Notes Generator
Generates formatted release notes from structured JSON input.
Groups entries by category, formats into markdown or JSON output,
and flags entries that may need user-benefit rewriting.
Usage:
python release_notes_generator.py --input changes.json --product-name "App" --version "2.0.0"
python release_notes_generator.py --demo --product-name "App" --version "1.0.0"
python release_notes_generator.py --input changes.json --format json --product-name "App" --version "2.0.0"
Input JSON format:
{
"entries": [
{
"title": "Dashboard caching",
"description": "Dashboards now load up to 3x faster",
"type": "improvement",
"ticket_id": "PROJ-123"
}
]
}
Supported types: feature, improvement, bugfix, breaking, deprecation
"""
import argparse
import json
import sys
import re
from datetime import date
from typing import Any
# --- Constants ---
VALID_TYPES = {"feature", "improvement", "bugfix", "breaking", "deprecation"}
CATEGORY_MAP = {
"feature": "New Features",
"improvement": "Improvements",
"bugfix": "Bug Fixes",
"breaking": "Breaking Changes",
"deprecation": "Deprecations",
}
CATEGORY_ORDER = ["feature", "improvement", "bugfix", "breaking", "deprecation"]
# Patterns that suggest an entry is too technical and needs rewriting
TECHNICAL_PATTERNS = [
re.compile(r"\b(refactor|migrat|implement|updat|add|remov|fix)\w*\b", re.IGNORECASE),
re.compile(r"\b[A-Z][a-z]+[A-Z]\w+\b"), # CamelCase class names
re.compile(r"\b\w+_\w+\b"), # snake_case identifiers
re.compile(r"\b(API|SDK|CLI|OAuth|SAML|SSO|JWT|gRPC|REST)\b"),
re.compile(r"\b(null|undefined|exception|handler|module|endpoint|schema)\b", re.IGNORECASE),
]
DEMO_DATA: dict[str, Any] = {
"entries": [
{
"title": "Team Workspaces",
"description": "Create shared workspaces where your team can collaborate on projects in real time",
"type": "feature",
"ticket_id": "PROJ-201",
},
{
"title": "Bulk CSV Import",
"description": "Import up to 10,000 records at once using the new CSV bulk import tool",
"type": "feature",
"ticket_id": "PROJ-198",
},
{
"title": "Faster Dashboard Loading",
"description": "Dashboards now load up to 3x faster thanks to optimized data retrieval",
"type": "improvement",
"ticket_id": "PROJ-187",
},
{
"title": "Improved Search Relevance",
"description": "Search results now prioritize exact matches and recent items, making it easier to find what you need",
"type": "improvement",
"ticket_id": "PROJ-192",
},
{
"title": "Export Date Range Fix",
"description": "Report exports no longer fail when the selected date range includes days with no data",
"type": "bugfix",
"ticket_id": "PROJ-205",
},
{
"title": "Notification Delivery Fix",
"description": "Email notifications for overdue tasks are now delivered reliably within 5 minutes",
"type": "bugfix",
"ticket_id": "PROJ-210",
},
{
"title": "API v2 Migration",
"description": "The /v1/users endpoint has been replaced by /v2/users. Update your integrations before June 30.",
"type": "breaking",
"ticket_id": "PROJ-180",
},
{
"title": "Legacy CSV Import",
"description": "The single-record CSV import wizard will be removed in v4.0. Use the new bulk import tool instead.",
"type": "deprecation",
"ticket_id": "PROJ-199",
},
]
}
def validate_entries(entries: list[dict]) -> list[str]:
"""Validate input entries and return a list of error messages."""
errors = []
for i, entry in enumerate(entries):
if "title" not in entry or not entry["title"].strip():
errors.append(f"Entry {i}: missing or empty 'title'")
if "description" not in entry or not entry["description"].strip():
errors.append(f"Entry {i}: missing or empty 'description'")
if "type" not in entry:
errors.append(f"Entry {i}: missing 'type'")
elif entry["type"] not in VALID_TYPES:
errors.append(
f"Entry {i}: invalid type '{entry['type']}'. "
f"Valid types: {', '.join(sorted(VALID_TYPES))}"
)
return errors
def check_technical_language(description: str) -> list[str]:
"""Check if a description contains technical language that may need rewriting."""
warnings = []
for pattern in TECHNICAL_PATTERNS:
matches = pattern.findall(description)
if matches:
warnings.append(f"Possible technical language: {', '.join(set(matches))}")
return warnings
def group_entries(entries: list[dict]) -> dict[str, list[dict]]:
"""Group entries by their type category."""
groups: dict[str, list[dict]] = {t: [] for t in CATEGORY_ORDER}
for entry in entries:
entry_type = entry["type"]
groups[entry_type].append(entry)
return groups
def format_markdown(
groups: dict[str, list[dict]],
product_name: str,
version: str,
release_date: str,
warnings: dict[str, list[str]],
) -> str:
"""Format grouped entries as markdown release notes."""
lines = []
lines.append(f"# {product_name} v{version} Release Notes")
lines.append("")
lines.append(f"**Release Date:** {release_date}")
lines.append("")
lines.append("---")
for entry_type in CATEGORY_ORDER:
entries = groups[entry_type]
if not entries:
continue
lines.append("")
category_name = CATEGORY_MAP[entry_type]
lines.append(f"## {category_name}")
lines.append("")
if entry_type == "breaking":
lines.append("> **Action Required:** The following changes require updates on your end.")
lines.append("")
elif entry_type == "deprecation":
lines.append("> **Planned Removal:** The following features will be removed in a future release.")
lines.append("")
for entry in entries:
ticket = f" ({entry['ticket_id']})" if entry.get("ticket_id") else ""
lines.append(f"- **{entry['title']}** -- {entry['description']}{ticket}")
lines.append("")
# Append rewriting warnings if any
warning_entries = {k: v for k, v in warnings.items() if v}
if warning_entries:
lines.append("---")
lines.append("")
lines.append("## Rewriting Suggestions")
lines.append("")
lines.append("The following entries may contain technical language. Consider rewriting to focus on user benefit.")
lines.append("")
for title, title_warnings in warning_entries.items():
lines.append(f"- **{title}**")
for w in title_warnings:
lines.append(f" - {w}")
lines.append("")
lines.append("---")
lines.append("")
lines.append("**Full changelog:** [link]")
lines.append("**Questions?** [support link or contact]")
lines.append("")
return "\n".join(lines)
def format_json(
groups: dict[str, list[dict]],
product_name: str,
version: str,
release_date: str,
warnings: dict[str, list[str]],
) -> str:
"""Format grouped entries as JSON output."""
output = {
"product_name": product_name,
"version": version,
"release_date": release_date,
"categories": {},
"rewriting_suggestions": {},
}
for entry_type in CATEGORY_ORDER:
entries = groups[entry_type]
if entries:
output["categories"][CATEGORY_MAP[entry_type]] = [
{
"title": e["title"],
"description": e["description"],
"ticket_id": e.get("ticket_id", ""),
}
for e in entries
]
warning_entries = {k: v for k, v in warnings.items() if v}
if warning_entries:
output["rewriting_suggestions"] = warning_entries
return json.dumps(output, indent=2)
def main() -> None:
parser = argparse.ArgumentParser(
description="Generate formatted release notes from structured JSON input.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"Input JSON format:\n"
' { "entries": [\n'
' { "title": "...", "description": "...", "type": "feature", "ticket_id": "PROJ-1" }\n'
" ]}\n\n"
"Supported types: feature, improvement, bugfix, breaking, deprecation\n\n"
"Examples:\n"
' %(prog)s --input changes.json --product-name "Acme" --version "2.0.0"\n'
' %(prog)s --demo --product-name "Acme" --version "1.0.0"\n'
' %(prog)s --input changes.json --format json --product-name "Acme" --version "2.0.0"'
),
)
parser.add_argument(
"--input",
type=str,
help="Path to JSON file containing release entries",
)
parser.add_argument(
"--demo",
action="store_true",
help="Run with built-in demo data",
)
parser.add_argument(
"--product-name",
type=str,
required=True,
help="Product name for the release notes header",
)
parser.add_argument(
"--version",
type=str,
required=True,
help="Version string (e.g., 2.5.0)",
)
parser.add_argument(
"--format",
type=str,
choices=["text", "json"],
default="text",
help="Output format: 'text' for markdown (default), 'json' for structured JSON",
)
parser.add_argument(
"--date",
type=str,
default=None,
help="Release date in YYYY-MM-DD format (defaults to today)",
)
args = parser.parse_args()
if not args.input and not args.demo:
parser.error("Provide --input <file> or --demo")
# Load data
if args.demo:
data = DEMO_DATA
else:
try:
with open(args.input, "r", encoding="utf-8") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in {args.input}: {e}", file=sys.stderr)
sys.exit(1)
entries = data.get("entries", [])
if not entries:
print("Error: No entries found in input data.", file=sys.stderr)
sys.exit(1)
# Validate
errors = validate_entries(entries)
if errors:
print("Validation errors:", file=sys.stderr)
for err in errors:
print(f" - {err}", file=sys.stderr)
sys.exit(1)
# Check for technical language
warnings: dict[str, list[str]] = {}
for entry in entries:
entry_warnings = check_technical_language(entry["description"])
if entry_warnings:
warnings[entry["title"]] = entry_warnings
# Group and format
groups = group_entries(entries)
release_date = args.date or date.today().isoformat()
if args.format == "json":
output = format_json(groups, args.product_name, args.version, release_date, warnings)
else:
output = format_markdown(groups, args.product_name, args.version, release_date, warnings)
print(output)
if __name__ == "__main__":
main()
Related skills
FAQ
What categories does it classify changes into?
New Features, Improvements, Bug Fixes, Breaking Changes and Deprecations, each change assigned to exactly one.
How should each entry be written?
Lead with the user benefit, use plain language, keep it to 1-3 sentences, and tell users exactly what to do when action is required.