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

@polygonlabs/sync-github-releases

v1.0.2

Published

Sync GitHub Release bodies from each package's CHANGELOG.md, mirroring the canonical changesets/action algorithm. Useful after the local recovery publish that follows the standard CI 403 on a brand-new package's first release.

Downloads

326

Readme

@polygonlabs/sync-github-releases

CLI tool that syncs GitHub Release bodies and titles from each package's CHANGELOG.md, byte-equal to what changesets/action emits natively. Idempotent — the comparison is strict, so re-runs over an already-synced repo report [match] and write nothing.

When you'd reach for this

GitHub Releases produced by changesets/action (with createGithubReleases: true) extract the ## <version> section of the matching package's CHANGELOG.md and post it as the release body. A few situations leave releases that don't match that shape:

  • A new package's first release. When CI tries to publish a brand-new @scope/pkg to npm, OIDC auth fails because no trusted publisher is configured for the package yet (and configuring one requires org-admin access). The standard recovery is to merge the Version Packages PR, accept the CI 403, and pnpm exec changeset publish locally. That path leaves the GitHub release with the wrong body (auto-generated PR list, or empty). Run this tool to sync it from CHANGELOG.md.
  • Migrating off gh release create --generate-notes. Older release flows posted GitHub's repo-wide "what's changed" list as the body — fine for single-package repos, useless for monorepos. Run this once per repo to canonicalise historical bodies.

Usage

GH_TOKEN must have contents:write on every target repo. The most common source is gh auth token.

The tool has two subcommands: release for a single release in one repo (the path you'll usually want), and repos for whole-repo sync across one or more repos.

release <repo> <tag> — most common

Use after a local recovery publish for one new package:

# Dry-run
GH_TOKEN=$(gh auth token) npx @polygonlabs/sync-github-releases \
  release 0xPolygon/apps-team-packages @polygonlabs/[email protected]

# Apply (prompts for confirmation)
GH_TOKEN=$(gh auth token) npx @polygonlabs/sync-github-releases \
  release 0xPolygon/apps-team-packages @polygonlabs/[email protected] --apply

# Apply without prompting (after you've inspected a dry-run)
GH_TOKEN=$(gh auth token) npx @polygonlabs/sync-github-releases \
  release 0xPolygon/apps-team-packages @polygonlabs/[email protected] --apply --yes

repos <repo>... — whole-repo, one or more

# Dry-run, one repo
GH_TOKEN=$(gh auth token) npx @polygonlabs/sync-github-releases \
  repos 0xPolygon/apps-team-packages

# Dry-run, multiple repos
GH_TOKEN=$(gh auth token) npx @polygonlabs/sync-github-releases \
  repos 0xPolygon/apps-team-packages 0xPolygon/proof-generation-api 0xPolygon/lst-api

# Apply across the lot
GH_TOKEN=$(gh auth token) npx @polygonlabs/sync-github-releases \
  repos 0xPolygon/apps-team-packages 0xPolygon/lst-api --apply

--apply runs the analysis pass first, then prints the count of pending updates and prompts for confirmation. --yes / -y skips the prompt; --summary suppresses per-release diffs (verdict lines only).

The tool works from any directory — clones land in $TMPDIR/sync-github-releases/repos/<owner>-<name>/, never the cwd.

Output

Per release, one line:

| Marker | Meaning | |---|---| | [match] <tag> | Current body and title already byte-match the canonical extraction; nothing to do. | | [would-update] <tag> | Dry-run; body or title would change. Followed by a diff of current vs. proposed. | | [updated] <tag> | --apply mode; body was rewritten. | | [would-create] <tag> | Dry-run; no GitHub release exists for this tag yet but the underlying git tag does. Followed by the proposed body. (release subcommand only.) | | [created] <tag> | --apply mode; a new GitHub release was created from CHANGELOG.md. (release subcommand only.) | | [skip: no changelog match] <tag> | Either no package matches the tag's <name>@<version> parse, or the package's CHANGELOG.md has no ## <version> section. Skipped to avoid clobbering a real body with empty notes. | | [skip: tag unparseable] <tag> | Tag is not in <name>@<version> or @scope/name@version form (e.g. v1.0.0). | | [error] tag '<tag>' not found … and no matching git tag | release subcommand only. The tag has no GitHub release and no underlying git tag — likely a typo. |

A per-repo summary is printed at the end.

What it does

  1. Shallow-clones each target repo into $TMPDIR/sync-github-releases/repos/<owner>-<name>/. Re-runs reuse the checkout, doing git fetch && git reset --hard origin/<default-branch>.
  2. Walks every package.json in the clone (skipping node_modules, .git, dist) to build a name → CHANGELOG.md map.
  3. Lists every release via Octokit repos.listReleases (paginated). The release subcommand filters down to the single tag; repos walks all of them.
  4. For each release, parses the tag at the last @, looks up the matching CHANGELOG.md, and runs the canonical getChangelogEntry algorithm against the version. Output is serialised with bullet: '-', listItemIndent: 'tab' to byte-match changesets/[email protected]'s remark-stringify@7 defaults.
  5. Compares to the current release body and title. In dry-run, prints a diff. With --apply, calls repos.updateRelease with name = tag and body = extracted.

Releases whose tag doesn't parse, or whose package has no matching CHANGELOG.md section, are skipped. The tool never replaces a populated body with an empty one.

Requirements

  • Node.js ≥ 24
  • git and pnpm on $PATH (used to clone target repos and enumerate their workspace packages)
  • GH_TOKEN env var with contents:write on each target repo