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

@hasna/changelog

v0.1.1

Published

Reusable changelog collection, generation, publishing, CLI, API, SDK, and MCP server for Hasna-coded apps

Readme

Open Changelog

Reusable changelog collection, generation, and publishing for Hasna-coded apps.

Open Changelog stores entries locally as JSONL, exposes the same model through an SDK, HTTP API, CLI, and MCP tools, and generates Keep a Changelog style Markdown. Publishing to a target CHANGELOG.md is dry-run by default and writes only when explicitly requested.

Install

bun add @hasna/changelog

CLI binaries:

changelog --help
changelog-mcp --help
changelog-serve --help

Local data defaults to ~/.hasna/changelog. Override it with CHANGELOG_DATA_DIR.

CLI

changelog init

changelog add "Initial changelog scaffold" --kind added --task 731aace9

changelog list --app open-changelog
changelog show <entry-id>
changelog update <entry-id> --kind fixed --message "Tighten publish safety"
changelog release --version 0.1.0
changelog generate --app open-changelog --version 0.1.0 --kind added --title "Open Changelog Notes"
changelog export --app open-changelog --format jsonl

changelog publish --app open-changelog --dry-run --diff
changelog publish --app open-changelog --write --target CHANGELOG.md --title "Open Changelog Notes"

publish prints a Markdown preview by default. It writes only with --write. When writing over an existing file, the previous file is backed up under ~/.hasna/changelog/backups unless --no-backup is supplied.

Remote CLI commands use CHANGELOG_API_TOKEN from the environment when --api-url is supplied.

When --app is omitted for add, generate, release, or publish, the CLI infers an app id from the local package.json name.

SDK

import { LocalChangelogStore, generateChangelogMarkdown, publishChangelog } from "@hasna/changelog";

const store = new LocalChangelogStore();

await store.createEntry({
  appId: "my-app",
  kind: "fixed",
  title: "Fix duplicate release notes",
  tasks: ["APP-123"],
});

await store.releaseEntries({ appId: "my-app", version: "1.2.0" });

const entries = await store.listEntries({ appId: "my-app", version: "1.2.0" });
const markdown = generateChangelogMarkdown(entries, { appId: "my-app" });

await publishChangelog({ store, appId: "my-app", diff: true }); // dry run
await publishChangelog({ store, appId: "my-app", write: true });

Remote API client:

import { createChangelogClient } from "@hasna/changelog";

const changelog = createChangelogClient({
  baseUrl: "http://127.0.0.1:8788",
  token: process.env.CHANGELOG_API_TOKEN,
});

await changelog.add({
  appId: "my-app",
  version: "1.2.0",
  kind: "added",
  title: "Add export command",
});

HTTP API

Start the server:

CHANGELOG_HOST=127.0.0.1 CHANGELOG_PORT=8788 changelog-serve

If CHANGELOG_API_TOKEN is set, API requests must include Authorization: Bearer <token> or x-changelog-token: <token>.

API publish write mode requires CHANGELOG_API_TOKEN to be configured and supplied. Dry-run publish remains available without a token for local development.

Endpoints:

  • GET /health
  • POST /v1/entries
  • GET /v1/entries?appId=my-app&version=1.2.0&kind=added
  • GET /v1/entries/:id
  • PATCH /v1/entries/:id
  • POST /v1/release
  • GET /v1/generate?appId=my-app
  • POST /v1/generate
  • POST /v1/publish
  • GET /v1/stats
  • GET /v1/export.jsonl

Example:

curl -X POST http://127.0.0.1:8788/v1/entries \
  -H 'content-type: application/json' \
  -d '{"appId":"my-app","version":"1.2.0","kind":"fixed","title":"Fix changelog publishing"}'

Publish dry-run:

curl -X POST http://127.0.0.1:8788/v1/publish \
  -H 'content-type: application/json' \
  -d '{"appId":"my-app","targetPath":"CHANGELOG.md"}'

Write mode:

curl -X POST http://127.0.0.1:8788/v1/publish \
  -H "authorization: Bearer $CHANGELOG_API_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"appId":"my-app","targetPath":"CHANGELOG.md","write":true}'

MCP

Run the MCP server over stdio:

changelog-mcp

Tools:

  • add_changelog_entry
  • list_changelog_entries
  • get_changelog_entry
  • update_changelog_entry
  • release_changelog
  • generate_changelog
  • publish_changelog
  • changelog_stats
  • export_changelog_jsonl

publish_changelog is dry-run unless its write argument is true.

Entry Model

Each stored JSONL entry includes:

  • appId
  • version
  • kind and category
  • title, message, details, and body
  • date
  • author and source
  • tags, links, commits, and tasks
  • metadata
  • id, createdAt, and updatedAt

Text fields and metadata are redacted for obvious secret-shaped values before storage. Duplicate entries are rejected by default using a stable fingerprint derived from app, version, kind, title, tasks, and commits. Pass the explicit duplicate override when a repeated entry is intentional.

Environment

  • CHANGELOG_DATA_DIR: local JSONL data directory
  • CHANGELOG_API_TOKEN: optional HTTP API bearer token
  • CHANGELOG_HOST: server bind host
  • CHANGELOG_PORT: server bind port