Skip to main content
Glama
README.md
# releasenotes

One changelog, two audiences. The people who use a product
want to know what changed for them, in plain terms, with
anything that breaks their workflow called out at the top.
The people who build on it want everything, including the
internal entries users never see. Writing both by hand from
the same changelog invites drift, so releasenotes renders
both from one list of entries: **two outputs, not one
copy-paste**.

You supply the release version and the changelog entries —
each with a group (such as `Added`, `Fixed`, or `Changed`)
and its text, plus two optional flags: `breaking` for a
breaking change, and `audience` (`user`, `developer`, or
`both`) for who the entry is written for. From that,
releasenotes produces:

- **User notes** (`## What's new in <version>`) — a
  `Breaking changes` section first, holding every
  user-facing breaking entry in input order, then one
  section per group holding that group's remaining
  non-breaking user-facing entries. Developer-only entries
  never appear here. If nothing is user-facing, the notes
  are just the title and the line "No user-facing changes
  in this release.".
- **Developer notes** (`## Release <version> — developer
  notes`) — one section per group holding *every* entry in
  input order, with breaking entries rendered as
  `- BREAKING: <text>` so they stand out in the full list
  instead of being pulled into their own section.

Sections follow the resolved group order: groups named in
the optional `group_order` come first in that sequence
(duplicates are ignored), and any groups it does not name
follow in the order they first appear in the entries.
Inside a section, entries keep their input order. Both
notes use exactly one blank line between sections and carry
no trailing newline.

The result also counts the entries: the total, how many
are breaking, how many are user-facing (`user` or `both`),
and how many are developer-only. Malformed input — a
missing or empty version or entry list, an entry without a
group or text, a non-boolean `breaking`, an `audience`
outside the three values, or a bad `group_order` — is a
validation error naming the offending path (for example
`entries[1].audience must be one of "user", "developer",
"both"`), and no partial result is produced. The
computation is pure and local: no network, no
configuration, no files read beyond the input you pass in.

## CLI

```bash
npx @reneturcios/releasenotes generate --input args.json
npx @reneturcios/releasenotes --version
```

`args.json` contains the full arguments object:

```json
{
  "version": "2.0.0",
  "group_order": ["Added", "Fixed"],
  "entries": [
    { "group": "Changed", "text": "Config files now use TOML.", "breaking": true },
    { "group": "Added", "text": "Dark mode for the dashboard." },
    { "group": "Fixed", "text": "Crash on empty search.", "audience": "user" },
    { "group": "Changed", "text": "Internal cache rewritten.", "audience": "developer" }
  ]
}
```

`breaking` defaults to `false` and `audience` defaults to
`"both"` per entry; `group_order` is optional. The result is
printed as JSON:

```json
{
  "version": "2.0.0",
  "user_notes": "## What's new in 2.0.0\n\n### Breaking changes\n- Config files now use TOML.\n\n### Added\n- Dark mode for the dashboard.\n\n### Fixed\n- Crash on empty search.",
  "developer_notes": "## Release 2.0.0 — developer notes\n\n### Added\n- Dark mode for the dashboard.\n\n### Fixed\n- Crash on empty search.\n\n### Changed\n- BREAKING: Config files now use TOML.\n- Internal cache rewritten.",
  "counts": { "entries": 4, "breaking": 1, "user_facing": 3, "developer_only": 1 }
}
```

Exit codes: `0` on success, `1` on a runtime error (including
an unreadable or invalid input file, or invalid input), `2`
on a usage error.

## MCP server

```bash
npx -y @reneturcios/releasenotes mcp
```

MCP client config:

```json
{
  "mcpServers": {
    "releasenotes": {
      "command": "npx",
      "args": ["-y", "@reneturcios/releasenotes", "mcp"]
    }
  }
}
```

### Tools

- `generate_release_notes` — Render two release notes from
  changelog entries.
  - `version` (string, required, non-empty) — release
    version the notes are for
  - `entries` (array, required, at least one) — changelog
    entries as `{group, text, breaking?, audience?}`
    objects; `group` and `text` are non-empty strings,
    `breaking` defaults to `false`, and `audience` is one
    of `user`, `developer`, or `both` (default `both`)
  - `group_order` (array, optional) — group ordering;
    unlisted groups follow in first-appearance order, and
    duplicate entries are ignored

  Returns `{version, user_notes, developer_notes, counts}`.

## Docker

```bash
docker run -i ghcr.io/cloudcorprecords/releasenotes
docker run --rm -v "$PWD:/w" ghcr.io/cloudcorprecords/releasenotes generate --input /w/args.json
```

## Development

```bash
npm install
npm run build   # tsc -> dist/
npm test        # tsx --test test/*.test.ts (pure local, no network)
npm run dev     # run the CLI from source
```

## License

MIT — Copyright (c) 2026 Rene Turcios