npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@releasenote/core

v0.1.17

Published

Core pipeline, linker, and config for releasenote

Downloads

144

Readme

releasenote

A modular release note generator that aggregates changes from GitHub and Azure DevOps, links them together, enriches with LLM-generated summaries, and renders via customizable Handlebars templates.

Quick Start

# Generate a starter config interactively
npx @releasenote/cli init

# Generate release notes
npx @releasenote/cli generate

That's it. The init wizard detects your git remote, walks you through source and LLM setup, and writes a ready-to-use .releasenote.yaml.

Minimal Example — GitHub Only

If you just want release notes from a GitHub milestone:

export GITHUB_TOKEN=ghp_...
npx @releasenote/cli generate \
  --set release.name=v2.0.0 \
  --set sources.0.milestone=v2.0.0

Or create a .releasenote.yaml:

version: 1

release:
  name: "v2.0.0"

sources:
  - type: github
    owner: your-org
    repo: your-repo
    milestone: "v2.0.0"
    primary: true
    token_env: GITHUB_TOKEN

output:
  type: markdown
  template: preset:compact
  file: RELEASE_NOTES.md
export GITHUB_TOKEN=ghp_...
npx @releasenote/cli generate

Install

npm install -g @releasenote/cli

Or use directly with npx (no install needed):

npx @releasenote/cli generate

How It Works

  1. Fetch items from configured sources (GitHub PRs, Azure DevOps work items)
  2. Link items across sources using pattern matching (e.g., AB#1234 in a PR links to ADO work item 1234)
  3. Facet each change deterministically from source fields (e.g. a platform), with no LLM — see Facets
  4. Enrich with LLM-generated summaries and categories
  5. Render via Handlebars templates with full control over output

Configuration

Create a .releasenote.yaml in your project root:

version: 1

release:
  name: "v2.0.0"
  date: "2026-03-10"

sources:
  - type: github
    owner: your-org
    repo: your-repo
    milestone: "v2.0.0"
    primary: true
    token_env: GITHUB_TOKEN

output:
  type: markdown
  file: RELEASE_NOTES.md

Or run npx @releasenote/cli init to generate one interactively.

Environment Variables

Use ${VAR} or ${VAR:-default} anywhere in the config:

release:
  name: "${RELEASE_NAME:-Unnamed Release}"

sources:
  - type: github
    owner: ${GITHUB_ORG}
    repo: ${GITHUB_REPO}
    compare:
      base: ${COMPARE_BASE}
      head: ${COMPARE_HEAD:-main}

Missing variables without defaults will throw a clear error at load time.

Sources

GitHub

Fetch PRs by milestone:

sources:
  - type: github
    owner: your-org
    repo: your-repo
    milestone: "v2.0.0"
    token_env: GITHUB_TOKEN

Use milestone: latest to automatically pick the most recent open milestone — no need to pass a name:

sources:
  - type: github
    owner: your-org
    repo: your-repo
    milestone: latest
    primary: true
    token_env: GITHUB_TOKEN

This is ideal for CI where you always want the current milestone without parameterising.

Or by commit range (compare two refs):

sources:
  - type: github
    owner: your-org
    repo: your-repo
    compare:
      base: v1.0.0          # tag, SHA, or branch
      head: main             # defaults to HEAD
    token_env: GITHUB_TOKEN

The compare source also fetches the code diff for each PR, which is included in the LLM context. Control this with:

compare:
  base: v1.0.0
  head: main
  include_diff: false       # disable diff fetching (default: true)
  max_diff_lines: 500       # cap diff lines per PR (default: 500)

Azure DevOps

Fetch work items by WIQL query:

sources:
  - type: azure-devops
    org: your-org
    project: your-project
    query: "SELECT [System.Id] FROM WorkItems WHERE ..."
    token_env: AZURE_DEVOPS_TOKEN

Or fetch only items linked from other sources:

sources:
  - type: azure-devops
    org: your-org
    project: your-project
    linked: true
    token_env: AZURE_DEVOPS_TOKEN

When linked: true, the pipeline extracts IDs from other sources using linking rules and fetches only those specific work items.

Linking

Link items across sources using regex patterns:

linking:
  rules:
    - pattern: "AB#(\\d+)"        # regex with capture group
      search_in: [title, body]     # fields to scan (default)
      links_to: externalId         # field to match on target items
      from: github                 # only scan items from this source
      to: azure-devops             # only match items from this source

This scans GitHub PR titles and bodies for AB#1234, extracts 1234, and links to the Azure DevOps work item with that external ID.

You can add multiple rules to link different sources:

linking:
  rules:
    - pattern: "AB#(\\d+)"
      from: github
      to: azure-devops
      links_to: externalId
    - pattern: "JIRA-(\\d+)"
      from: github
      to: jira
      links_to: externalId

Facets

Facets deterministically derive a field (e.g. platform) for each group from its source items — no LLM involved — so you can route sections in your template without relying on the model to classify prose.

facets:
  platform:
    rules:
      # Rules are evaluated in order; the first one matching ANY item in a
      # group wins. Patterns are matched case-insensitively.
      - when: { field: title, pattern: "\\bios\\b|android|mobile" }
        value: "Mobile"
      - when: { field: meta.areaPath, pattern: "Mobile" }
        value: "Mobile"
    default: "Web"   # used when no rule matches (optional)

Each rule's when takes a regex pattern and an optional field:

| field | Matches against | | --- | --- | | omitted | the item's title + body + labels joined together | | title, body, labels, author | that top-level field | | meta.<key> | a source-specific metadata field (e.g. meta.areaPath) |

Because rules are checked against every item in a group (not just the primary), a signal can come from a linked work item — e.g. an Azure DevOps area path — even when the PR title is silent.

The result is available in templates as {{this.facets.<name>}}, which composes with the {{eq}} helper for deterministic sectioning:

{{#each groups}}
{{#if (eq this.facets.platform "Mobile")}}
- {{this.summary}}
{{/if}}
{{/each}}

Primary Source

Mark a source as primary: true to control what appears in the output. Only groups containing at least one item from a primary source are included. Other sources provide supporting context.

sources:
  # Defines what's in the release
  - type: github
    milestone: "v2.0.0"
    primary: true

  # Adds PR writeups and diffs as context
  - type: github
    compare:
      base: v1.0.0
      head: main

  # Linked work items as context
  - type: azure-devops
    linked: true

Build Metadata

Resolve metadata about a CI build and expose it in templates as {{build.*}} — useful for stamping the exact build a release came from.

build:
  type: azure-devops
  org: your-org
  project: your-project
  definition: Your-Pipeline       # optional — narrow to one pipeline by name
  tag: production                 # required — match builds carrying this tag
  token_env: AZURE_DEVOPS_TOKEN   # optional (this is the default)

It picks the most recent completed build carrying the given tag (optionally narrowed to a definition/pipeline by name) — so tag the build you want to ship in Azure DevOps and this finds it. Set the PAT in AZURE_DEVOPS_TOKEN (or whatever token_env names).

Available in templates:

| Variable | Description | |---|---| | {{build.number}} | Build number (e.g. 2.110.0+159722) | | {{build.id}} | Numeric build ID | | {{build.url}} | Link to the build in Azure DevOps | | {{build.result}} | succeeded, failed, etc. | | {{build.status}} | Build status (e.g. completed) | | {{build.sourceBranch}} | Branch the build ran from | | {{build.finishTime}} | When the build finished (ISO timestamp) |

LLM Enrichment

Add an llm section to enable AI-generated summaries and categorization:

OpenAI

llm:
  provider: openai
  model: gpt-4.1-mini

categories:
  - Features
  - Bug Fixes
  - Breaking Changes
  - Maintenance

Set OPENAI_API_KEY in your environment.

Azure OpenAI

llm:
  provider: azure-openai
  model: gpt-4o
  endpoint: https://your-resource.openai.azure.com
  deployment: your-deployment-name   # defaults to model name
  api_version: "2025-01-01-preview"  # optional

Set AZURE_OPENAI_API_KEY in your environment (or use AZURE_OPENAI_ENDPOINT instead of endpoint in config).

Anthropic (Claude)

llm:
  provider: anthropic
  model: claude-sonnet-4-20250514

Set ANTHROPIC_API_KEY in your environment.

Temperature

Control how deterministic the generated prose is with temperature (0–2, default 0.3):

llm:
  provider: openai
  model: gpt-4.1-mini
  temperature: 0.1   # lower = more consistent run-to-run

This applies to the creative passes — summarization and {{#llm}} template blocks. Categorization always runs at temperature 0 regardless, so classification stays stable.

Audience

Control the tone and detail level by setting a target audience:

llm:
  provider: openai
  model: gpt-4.1-mini
  audience: "end users"        # or "developers", "stakeholders"

Built-in audiences:

  • end users — plain language, no technical terms, focuses on what users can do
  • developers — includes API changes, new methods, configuration details
  • stakeholders — business value, UX improvements, risk reduction

You can also use a custom string (e.g. "mobile app testers") and the LLM will adapt.

Custom Prompts

Override the default LLM instructions for full control:

llm:
  provider: openai
  model: gpt-4.1-mini
  prompts:
    summarize: "Write a one-line changelog entry. Use past tense. Be specific."
    categorize: "Classify into: {categories}. Return only the name."
    refine: "Review these summaries for consistency. Return a JSON array."

Use {categories} in the categorize prompt — it's replaced with the configured category list. Use {audience_instruction} in any prompt to inject the audience-specific guidance.

Refinement Pass

After individual items are summarized and categorized, a refinement pass reviews all summaries together to:

  • Ensure consistent verb tense and level of detail
  • Remove any remaining jargon or implementation details
  • Deduplicate entries that describe the same change differently

This happens automatically when an LLM provider is configured.

The LLM receives structured context from all linked items — PR descriptions, work item details, and code diffs — organized with clear section headers so it can distinguish between what changed, why it changed, and how it changed.

Templates

Control the output with a Handlebars template:

output:
  type: markdown
  template: .releasenote.template.md
  file: RELEASE_NOTES.md

Built-in Presets

Use a preset instead of writing your own template:

output:
  type: markdown
  template: preset:compact
  file: RELEASE_NOTES.md

| Preset | Description | |---|---| | preset:compact | Bullet list grouped by category — clean and minimal | | preset:detailed | Full descriptions with LLM executive summary and contributor credits | | preset:executive | Stakeholder-focused with summary, what's new, fixes, and breaking changes | | preset:changelog | Keep-a-changelog style — great for appending to CHANGELOG.md |

The detailed and executive presets use {{#llm}} blocks to generate executive summaries, so they work best with an LLM provider configured.

Custom Templates

Write your own Handlebars template and reference the file path:

output:
  type: markdown
  template: .releasenote.template.md
  file: RELEASE_NOTES.md
# {{release.name}}
> {{release.date}}

{{#llm}}
Write a 2-3 sentence executive summary of this release:
{{#each groups}}
- {{this.summary}}
{{/each}}
{{/llm}}

{{#each categories}}
## {{this.name}}

{{#each this.groups}}
- {{this.summary}} ({{refs}})
{{/each}}

{{/each}}

Run releasenote init to generate a starter template from any preset.

Template Variables

| Variable | Description | |---|---| | {{release.name}} | Release name from config | | {{release.date}} | Release date from config | | {{#each categories}} | Iterate categorized groups | | {{this.name}} | Category name | | {{this.groups}} | Groups in this category | | {{#each groups}} | Iterate all groups (flat) |

Group Fields

Inside {{#each groups}} or {{#each this.groups}}:

| Field | Description | |---|---| | {{this.summary}} | LLM-generated or PR title | | {{this.title}} | Primary item title | | {{this.body}} | Primary item description | | {{this.category}} | Assigned category | | {{this.facets.<name>}} | Deterministically-derived facet (see Facets) | | {{this.context}} | Full assembled context | | {{this.author}} | Primary item author | | {{this.authors}} | All authors (deduplicated) | | {{this.labels}} | Primary item labels | | {{this.items}} | All linked items | | {{refs}} | Formatted source links |

{{#llm}} Block

Place anywhere in the template. The content inside is rendered as a prompt, sent to the LLM, and replaced with the response:

{{#llm}}
Write a paragraph summarizing these bug fixes for end users:
{{#each categories}}
{{#if (eq this.name "Bug Fixes")}}
{{#each this.groups}}
- {{this.summary}}
{{/each}}
{{/if}}
{{/each}}
{{/llm}}

CLI Reference

releasenote init

Interactive setup wizard — walks you through configuring sources, LLM, linking, and template selection. Generates .releasenote.yaml and optionally a starter template file.

releasenote generate

Options:
  -c, --config <path>       Config file path (default: ".releasenote.yaml")
  -o, --output <path>       Override output file path
  --dry-run                 Print to stdout instead of writing
  --set <key=value...>      Override config values with dot notation
  --artifact [path]         Copy output to artifact directory
  -v, --verbose             Show all pipeline messages for debugging

--set examples

# Override release name
releasenote generate --set release.name=v2.0.0

# Override compare base ref
releasenote generate --set sources.0.compare.base=abc123

# Multiple overrides
releasenote generate \
  --set release.name=v2.0.0 \
  --set release.date=2026-03-10 \
  --set sources.0.primary=true

Values are auto-coerced: true/false become booleans, numeric strings become numbers.

--artifact

Copy the output to an artifact directory for CI/CD:

# Explicit path
releasenote generate --artifact ./artifacts

# Auto-detect from Azure DevOps pipeline variable
releasenote generate --artifact
# Uses BUILD_ARTIFACTSTAGINGDIRECTORY

When running in Azure DevOps (TF_BUILD is set), --artifact automatically attaches the release notes to the build summary tab via ##vso[task.uploadsummary].

--verbose

Shows all pipeline messages including fetched items, formed groups, LLM results, and resolved config. Useful for debugging why items are missing or miscategorized.

releasenote generate --verbose
# or
releasenote generate -v

Also activatable via DEBUG=1 environment variable (useful in CI).

CI/CD Integration

GitHub Actions

Add to your workflow (e.g. .github/workflows/release-notes.yml):

name: Release Notes

on:
  milestone:
    types: [closed]

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Generate release notes
        run: >
          npx @releasenote/cli generate
          --set "release.name=${{ github.event.milestone.title }}"
          --set "release.date=$(date +%Y-%m-%d)"
          --dry-run
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

      # Or commit to the repo:
      # - name: Generate and commit
      #   run: |
      #     npx @releasenote/cli generate \
      #       --set "release.name=${{ github.event.milestone.title }}"
      #     git config user.name "github-actions"
      #     git config user.email "[email protected]"
      #     git add RELEASE_NOTES.md
      #     git commit -m "docs: release notes for ${{ github.event.milestone.title }}"
      #     git push

Trigger on release tag

on:
  push:
    tags:
      - "v*"

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Generate release notes
        run: >
          npx @releasenote/cli generate
          --set "release.name=${GITHUB_REF_NAME}"
          --set "release.date=$(date +%Y-%m-%d)"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

      - name: Create GitHub Release
        run: gh release create "$GITHUB_REF_NAME" --notes-file RELEASE_NOTES.md
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Azure DevOps Pipeline

Follow these steps to add release note generation to an existing Azure DevOps build pipeline.

Step 1: Add config files to your repo

Create .releasenote.yaml in your repo root. Use environment variable references for anything that changes per build:

version: 1

release:
  name: "${RELEASE_NAME:-dev}"
  date: "${RELEASE_DATE}"

sources:
  - type: github
    owner: ${GITHUB_ORG}
    repo: ${GITHUB_REPO}
    milestone: "${MILESTONE_NAME}"
    primary: true
    token_env: GITHUB_TOKEN

output:
  type: markdown
  file: RELEASE_NOTES.md

Optionally add a .releasenote.template.md for custom output formatting (see Templates).

Run npx @releasenote/cli init to generate a starter config interactively.

Step 2: Set up pipeline variables

In Azure DevOps, go to Pipelines > Your Pipeline > Edit > Variables (or use a Variable Group under Pipelines > Library).

Add the following variables:

| Variable | Value | Secret? | |---|---|---| | GITHUB_TOKEN | GitHub PAT with repo read access | Yes | | OPENAI_API_KEY | OpenAI API key (only if using LLM enrichment) | Yes | | AZURE_DEVOPS_TOKEN | ADO PAT (only if using ADO source — or use $(System.AccessToken) for same-org access) | Yes |

Any variables referenced in your .releasenote.yaml via ${VAR} (e.g. MILESTONE_NAME, GITHUB_ORG) should also be defined here or passed via --set.

Step 3: Add the pipeline steps

Add these steps to your azure-pipelines.yml after your existing build steps but before any PublishBuildArtifacts task:

# Install Node.js (skip if your pipeline already has it)
- task: NodeTool@0
  displayName: "Install Node.js"
  inputs:
    versionSpec: "20.x"

# Generate release notes
- script: >
    npx @releasenote/cli generate
    --config .releasenote.yaml
    --set "release.name=$(Build.BuildNumber)"
    --artifact "$(Build.ArtifactStagingDirectory)"
  displayName: "Generate Release Notes"
  env:
    GITHUB_TOKEN: $(GITHUB_TOKEN)
    AZURE_DEVOPS_TOKEN: $(System.AccessToken)
    OPENAI_API_KEY: $(OPENAI_API_KEY)

This will:

  • Generate the release notes markdown
  • Write it to the artifact staging directory
  • Automatically attach it to the Build Summary tab (the ##vso[task.uploadsummary] command is emitted automatically in Azure DevOps)

If you already have a PublishBuildArtifacts step that publishes from $(Build.ArtifactStagingDirectory), the release notes file will be included alongside your other artifacts. Otherwise, add one:

- task: PublishBuildArtifacts@1
  displayName: "Publish Release Notes"
  inputs:
    pathToPublish: "$(Build.ArtifactStagingDirectory)"
    artifactName: "release-notes"

Step 4: Pass dynamic values

Use --set to inject build-time values without modifying the config file:

- script: >
    npx @releasenote/cli generate
    --config .releasenote.yaml
    --set "release.name=$(Build.BuildNumber)"
    --set "release.date=$(Build.SourceVersion)"
    --set "sources.0.milestone=$(MilestoneName)"
    --set "sources.0.compare.base=$(PreviousTag)"
    --artifact "$(Build.ArtifactStagingDirectory)"
  displayName: "Generate Release Notes"
  env:
    GITHUB_TOKEN: $(GITHUB_TOKEN)

Optional: Error handling

If release note generation should not fail the build, add continueOnError:

- script: >
    npx @releasenote/cli generate ...
  displayName: "Generate Release Notes"
  continueOnError: true

To only generate release notes on successful builds:

- script: >
    npx @releasenote/cli generate ...
  displayName: "Generate Release Notes"
  condition: succeeded()

Full pipeline example

trigger:
  - main

pool:
  vmImage: "ubuntu-latest"

variables:
  - name: nodeVersion
    value: "20.x"

steps:
  # ... your existing build steps ...

  - task: NodeTool@0
    displayName: "Install Node.js"
    inputs:
      versionSpec: "$(nodeVersion)"

  - script: >
      npx @releasenote/cli generate
      --config .releasenote.yaml
      --set "release.name=$(Build.BuildNumber)"
      --artifact "$(Build.ArtifactStagingDirectory)"
    displayName: "Generate Release Notes"
    condition: succeeded()
    continueOnError: true
    env:
      GITHUB_TOKEN: $(GITHUB_TOKEN)
      AZURE_DEVOPS_TOKEN: $(System.AccessToken)
      OPENAI_API_KEY: $(OPENAI_API_KEY)

  - task: PublishBuildArtifacts@1
    displayName: "Publish Artifacts"
    inputs:
      pathToPublish: "$(Build.ArtifactStagingDirectory)"
      artifactName: "release-notes"

Troubleshooting

Config file not found

✖ ERROR ─ Config file not found: .releasenote.yaml

Run releasenote init to create one, or pass a path with -c:

releasenote generate -c path/to/.releasenote.yaml

Missing environment variable

✖ ERROR ─ Environment variable ${GITHUB_TOKEN} is referenced in config but not set

Export the variable before running, or add a default:

# In config — use a default so it doesn't fail:
token_env: ${GITHUB_TOKEN:-}

Milestone not found

✖ ERROR ─ Milestone "v2.0.0" not found in your-org/your-repo

Check the milestone name matches exactly (case-sensitive). In GitHub, go to Issues > Milestones to see the exact title.

TLS warnings in corporate environments

If you see NODE_TLS_REJECT_UNAUTHORIZED warnings, the CLI suppresses them automatically. If they still appear, set NODE_OPTIONS=--no-warnings:

NODE_OPTIONS=--no-warnings releasenote generate

Azure OpenAI API version errors

If you get a 400 API version not supported error, set api_version explicitly:

llm:
  provider: azure-openai
  api_version: "2025-01-01-preview"

LLM timeout or rate limit

API calls retry automatically (up to 3 times with exponential backoff). If you're hitting rate limits consistently, reduce concurrency by splitting into smaller milestones or using --set to target fewer items.

Debugging

Use --verbose to see every pipeline step:

releasenote generate --verbose

This shows the resolved config, every fetched item, formed groups, and LLM results.

Architecture

packages/
  cli/                      CLI entry point and commands
  core/                     Pipeline, linker, config, types
  source-github/            GitHub source (milestone + compare)
  source-azure-devops/      Azure DevOps source (query + linked)
  output-markdown/          Markdown output with Handlebars templates
  llm-openai/               OpenAI and Azure OpenAI LLM provider
  llm-anthropic/            Anthropic Claude LLM provider

The pipeline is pluggable — each source, output, and LLM provider is a separate package implementing a simple interface.

Environment Variables

| Variable | Used By | Description | |---|---|---| | GITHUB_TOKEN | source-github | GitHub personal access token | | AZURE_DEVOPS_TOKEN | source-azure-devops | Azure DevOps PAT | | OPENAI_API_KEY | llm (openai) | OpenAI API key | | AZURE_OPENAI_API_KEY | llm (azure-openai) | Azure OpenAI API key | | AZURE_OPENAI_ENDPOINT | llm (azure-openai) | Azure OpenAI resource endpoint | | ANTHROPIC_API_KEY | llm (anthropic) | Anthropic API key |

License

MIT