← Blog

How to Build a Claude-Powered Changelog Summarizer

2026-11-05 · 5 min read · SubToAPI Team

If you ship software regularly, you already know the pain of writing changelogs. Commit messages are inconsistent, PR titles range from detailed to "fix bug," and someone always has to translate dozens of git log lines into something a user or stakeholder can actually read. This article walks through building a Claude-powered changelog summarizer tool: a small pipeline that pulls raw commit or PR data, sends it to Claude with a structured prompt, and returns clean, categorized release notes.

The core idea is simple: Claude is good at taking messy, unstructured technical text and turning it into consistent, well-organized prose. You don't need a complex app for this — a script, a CI step, or a lightweight internal tool is enough. The rest of this guide covers the architecture, the prompt design that actually produces usable output, and how to wire it up to run automatically on every release.

What the tool actually needs to do

Before writing any code, define the job precisely. A changelog summarizer needs to:

Claude handles the classification and writing steps well. The collection and filtering steps are plain code — grep, git log formatting, or a GitHub API call.

Step 1: Collect the raw data

For a git-based project, the simplest source is commit messages since the last tag:

git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"- %s (%h)"

If your team relies more on PRs than commit hygiene, pull merged PR titles and descriptions from GitHub's API instead — PR descriptions usually contain more context than a one-line commit message.

Either way, you end up with a block of raw text. Don't over-clean it in code. Claude is better at filtering "bump lodash to 4.17.21" out of a changelog than a regex is, because it understands intent, not just keywords.

Step 2: Design the prompt

This is where most changelog summarizers succeed or fail. A vague prompt like "summarize these commits" produces vague output. Be explicit about format, tone, and what to exclude:

You are generating a public changelog from raw commit messages.

Rules:
- Group entries under: Added, Changed, Fixed, Removed, Security
- Omit dependency bumps, CI config changes, and typo fixes unless user-facing
- Write each entry as one sentence, present tense, no ticket IDs
- Omit empty categories
- Output valid Markdown only, no preamble

Raw commits:
{{commits}}

This level of specificity matters more than model choice. Claude follows structural instructions reliably when they're stated as rules rather than suggestions, so treat the prompt like a spec, not a request.

Step 3: Call Claude via SubToAPI

Rather than managing raw Claude API credentials, keys, and billing separately, you can route this through SubToAPI, which turns your existing Claude access into a standard HTTPS API with application-level keys. That's useful here because a changelog summarizer is often a shared internal tool — multiple repos, multiple CI pipelines, maybe multiple team members triggering it manually. SubToAPI gives you per-app keys and usage visibility without each integration needing its own setup.

A basic call looks like this:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": "You are generating a public changelog... [rules + commits here]"
      }
    ]
  }'

Or in JavaScript, if you're wiring this into a Node-based CI script:

const res = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-sonnet-4",
    max_tokens: 1024,
    messages: [{ role: "user", content: prompt }]
  })
});

const data = await res.json();
const changelog = data.content[0].text;

Full request and response shapes are documented at /docs/messages, and /docs/quickstart walks through getting an API key set up in a few minutes.

Step 4: Automate it in CI

The real value of this tool comes from running it automatically on every tagged release. A GitHub Actions step might:

  1. Checkout the repo on tag push
  2. Run the git log command to collect commits since the previous tag
  3. Call the summarizer script
  4. Write the output to CHANGELOG.md or post it as the GitHub release body
- name: Generate changelog
  run: node scripts/generate-changelog.js
  env:
    SUBTOAPI_KEY: ${{ secrets.SUBTOAPI_KEY }}

This removes the manual step entirely — no one has to remember to write release notes, and the output is consistent every time because the prompt enforces the format.

Step 5: Add a human review step (optional but recommended)

Even a well-tuned prompt occasionally miscategorizes an entry or over-summarizes something that needed more detail. For public-facing products, pipe the generated changelog into a draft PR or a Slack message for a quick human glance before it goes live, rather than auto-publishing directly. This keeps the speed benefit without losing editorial control.

Where this fits for teams

If you're building this as a shared tool across several repositories or a growing team, the per-seat structure matters. A single developer running this from a personal script works fine on the Solo plan, but once multiple people or multiple CI pipelines are hitting the same integration, Team and Scale plans add seat-based access and usage tracking so you can see which pipeline or person is generating the most calls. You can try the whole setup free by starting at /signup.

questions

Does the summarizer need access to full diffs, or just commit messages? Commit messages or PR titles/descriptions are usually enough. Full diffs add token cost without much improvement in output quality unless your commit messages are extremely sparse.

Can this work with Jira tickets instead of git commits? Yes — the input source doesn't matter to Claude. Pull ticket titles and resolution notes via the Jira API, format them as a text block, and feed them into the same prompt structure.

How do I keep the tone consistent across releases? Keep the prompt's rules section identical every run and avoid letting contributors inject freeform commit text into the final output unedited — the rules in the prompt, not the model's defaults, are what keep tone stable over time.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →