Creating releases json
Product release note registry, made for agents
npx -y skills add buildinternet/releases --skill creating-releases-jsonAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Create or update a releases.json manifest so registries and agents can find where a company or project publishes its release notes. Use this whenever someone wants to "list our product on Releases", "add a releases.json", "get indexed on releases.sh", "here's our website, make us a manifest", declare a changelog / updates page / RSS feed / GitHub releases / App Store listing / CHANGELOG for discovery, publish a /.well-known/releases.json, or edit an existing one — even if they don't say "releases.json" by name. The usual input is just a company website or domain (no codebase required); the manifest is a small, schema-validated file the owner hosts on their domain or repo. This skill discovers the real publish locations, models products without over-fragmenting, writes the correctly-scoped file, validates it locally, and guides publishing it.
SKILL.md
15.8 KB, as published. Nobody here has run it
Creating a releases.json manifest
releases.json is a small, owner-declared file that states what products a
company ships and where it publishes release notes — a changelog, an updates
page, an RSS/Atom feed, GitHub Releases, an App Store listing, or a hosted
CHANGELOG file. Any registry or agent that reads the format (starting with
releases.sh) learns where the release notes live directly
from the owner instead of guessing.
Authority comes from where the file lives, not from what it claims. A file at
https://{domain}/.well-known/releases.json speaks for that whole company; a
releases.json at a repository root speaks only for that repo. That is the entire
trust model — if you can publish the file at the URL, you can declare what it says.
Your job with this skill: figure out which scope applies, discover the locations where release notes actually live (never invent them), model the company's products without over-fragmenting, write a correctly-shaped file, validate it, and help get it published.
How you'll be asked to run this
Most of the time there is no codebase — someone just says "here's our website, make us a releases.json". That's the primary path: you work from the live site (and GitHub org), browsing to find where updates are published, and you hand back a finished file plus instructions on where to host it. You cannot commit it for them.
Less often you're working inside a repo — the one that serves the company's site, or a single product/library repo — in which case you can also read the codebase and write the file into place. Both paths use the same steps below; only Step 1 (scope) and Step 5 (publish) differ.
The one rule that matters most
Only declare locations that really exist and respond. The manifest states plain facts about where updates are published. A guessed or aspirational URL is worse than omitting it — it creates a broken source. Before writing any locator, confirm the URL resolves (fetch it, or check it against the codebase/site). If you cannot verify a location, leave it out and say so.
Step 1 — Choose the scope
There are two scopes. They share one schema but declare different things:
| Scope | Lives at | Declares |
|---|---|---|
| Domain | https://{domain}/.well-known/releases.json | Company identity, products[], and top-level releases[] |
| Repo | releases.json at a repository root | A product binding + where this repo's releases land |
Default to domain scope — it's what "here's our website, make us a manifest" wants, and it's the only scope that can describe the whole company and multiple products. Choose repo scope only when you're clearly operating inside one product/library repo and the goal is to bind that repo to a product. A repo file cannot declare company identity; a domain file cannot speak for a repo it doesn't host. If genuinely unsure, ask which one they want.
Step 2 — Discover the real publish locations
Search the codebase and the company's site for every place product updates are published. Look for, and verify each one responds:
- Changelog / what's-new / updates pages —
/changelog,/whats-new,/releases,updates.<domain>, a "Product updates" blog category. - RSS/Atom feeds — often linked from the changelog page's
<head>(<link rel="alternate" type="application/rss+xml">), or a Canny/Beamer/Headway feed. - GitHub repos with Releases — repos under the org that cut tagged releases or keep a
CHANGELOG.md. Useowner/repo. - App Store listings — for mobile apps whose release notes live in the store.
- Hosted
CHANGELOGdocuments — a rawCHANGELOG.mdserved at a URL (not in GitHub).
Map each finding to a locator key:
| Key | Use it for | Notes |
|---|---|---|
url | The page a person reads — changelog / updates / what's-new | The canonical human page. https:// only. |
feed | The RSS/Atom feed machines follow | May be third-party-hosted (Canny, Beamer…). https:// only. |
github | A repo whose Releases are the record | "owner/repo". In a repo file only, "self" = this repo. |
appstore | An App Store listing URL | https:// only. |
file | A raw changelog document at a URL (hosted CHANGELOG.md) | https:// only. |
One releases[] entry can combine keys when they describe the same source — most
commonly url + feed (the page a human reads and the feed a machine follows for it).
Add "title" to name a location, and "canonical": true on the single primary entry
when several locations describe one product/repo scope (at most one canonical per
scope).
source.url is for humans. If a URL is purely a machine endpoint nobody would visit,
it belongs in feed/file, not url.
Step 2b — Model products correctly (don't over-fragment)
This is where agents most often go wrong. products[] is optional, and a manifest with
just a top-level releases[] is often the right answer. Only reach for products[]
when the company genuinely ships multiple things that publish releases in different
places. The test for whether something deserves its own product entry is simple:
Does it have its own release location? A product earns an entry only if it has a release stream distinct from the company changelog — its own repo, its own feed, its own changelog page, or its own App Store listing.
Marketing structure is not release structure. Companies segment their site into "capabilities", "solutions", or "modules" for buyers; that segmentation almost never maps to distinct release feeds. Follow where the releases are published, not how the site is organized.
Two anti-patterns to actively avoid:
- One product per marketing capability, all pointing at the same changelog. If you find
yourself writing eight products — "Analytics", "Session Replay", "Experiment", …— that all
carry the identical
url/feed, stop. Those aren't eight products; they're one company with one changelog. Declare it once in the top-levelreleases[]and drop the redundant product entries. Duplicated locators just dedup back to one source and add noise. - One product per language SDK. A company with client libraries in Python, JS, Swift,
Kotlin, Go, … has one developer/SDK offering, not one product per language. Bundle
the libraries as multiple
githublocators under a single product (e.g."API & SDKs"or"SDKs"), mark the primary/most-active onecanonical. If there are more than 8 repos (the per-product cap), list the most active or the monorepo — don't spill into extra products to fit them all.
When separate products are right: each has its own release location. Vercel is the
model — Next.js (github: vercel/next.js), Turborepo (vercel/turborepo), the AI SDK
(vercel/ai), and v0 (v0.app/changelog) each publish releases independently, so each is
a real product entry with its own canonical locator.
Rule of thumb: if every product entry you're about to write shares one changelog, you
want zero products and one top-level releases[]. Add products only for the pieces that
break out into their own release stream.
For worked before/after examples of all three cases, see references/modeling-products.md.
Step 3 — Write the file
Start from the matching example in examples/ and adapt it:
examples/domain-minimal.json— the smallest useful file:version+releases[].examples/domain-products.json— company identity +products[]+ registry pin.examples/repo.json— a repo-root file with aproductbinding andgithub: "self".
Rules to honor (the validator in Step 4 enforces all of these):
"version": 2is required. v1 files are no longer read.$schema(https://releases.sh/schemas/releases.json) is optional but recommended — it gives editors autocomplete and validation.- Every
releases[]entry needs ≥1 locator (url/feed/github/appstore/file). url/feed/appstore/file/avatarmust behttps://. (website/docs/supporton a product may be any valid URL.)- Limits: ≤ 24 products, ≤ 8 locations per product, and ≤ 32 locations per file total
(top-level
releases[]plus every product'sreleases[]combined). - Don't over-declare identity.
name,description,category,tags,avatar,socialshape how the org appears but are all optional — the locations are the point. Fill them only from facts you can confirm. - Domain scope is flat — no
orgwrapper; the root object is the org. Only a domain file may useproducts[],name,avatar,tags. Only a repo file may useproduct(singular) andgithub: "self". - Products carry a fixed, small set of fields and nothing else — the schema is strict and
rejects unknown keys. A product may have:
name(required),slug,kind,category,description,website,docs,support,social,tags,archived,releases. Put company-wide tags at the root; use a product'stagsonly for labels specific to that product. Any key outside this set (or the org-only fields likeproducts/avatar) fails validation. - Taxonomy is advisory.
category,kind,tagsare suggestions; unrecognized values are ignored by the registry, never an error. Don't agonize over exact values.
Pinning (optional). If the org/product is already in a registry and you want the file
bound to exact records instead of matched by URL, add a registries block. IDs live on the
org/product pages. The block is namespaced so other registries can coexist:
"registries": { "releases.sh": { "org": "org_abc123" } }
(In a repo file, use "product": "prd_…" instead.) Unknown registries/keys are ignored.
Step 4 — Validate locally
Run the bundled zero-dependency validator (Node 18+ or Bun, no install) — it checks the full contract, including rules the plain JSON Schema can't express (at-least-one-locator, at-most-one-canonical, the 32-total cap):
node <skill-dir>/scripts/validate.mjs path/to/releases.json
It auto-detects domain vs repo scope; pass --scope domain|repo to force it. A ✓ with
exit code 0 means valid; a ✗ lists every problem with a JSON path. Fix and re-run until
it passes. The canonical schema (kept in sync at
https://releases.sh/schemas/releases.json, snapshot at references/schema.json) is the
tie-breaker if anything is ambiguous.
Also confirm the file is syntactically clean JSON and, if you added $schema, that it points
at https://releases.sh/schemas/releases.json.
Step 5 — Publish and confirm it's live
If you don't have the repo (the common "here's our website" case), you can't publish it for them — hand back the finished file and tell them exactly where it goes:
- Save the file where they can grab it and tell them its target URL:
https://{domain}/.well-known/releases.json. - Explain the one requirement: it must be served at that exact path, over HTTPS, with
Content-Type: application/jsonand no auth wall or redirect. How they do that depends on their host (apublic/.well-known/directory for most static/Next.js sites, an app route, or their CDN's static-file rules — the same mechanism that serves theirrobots.txt). - Then Step 2 of the confirmation below is something they run after deploying.
If you do have the repo, write it into place:
- Domain scope: wherever that project serves
/.well-known/— e.g. Next.jspublic/.well-known/releases.json, a static site's public root, or an app route serving that exact path. Match how it already servesrobots.txt. - Repo scope: commit
releases.jsonat the repository root.
After it's deployed and publicly reachable, confirm it end-to-end:
- Fetch the live URL and check it returns HTTP 200 with valid JSON (no auth wall, no HTML
redirect):
curl -fsSL https://{domain}/.well-known/releases.json | node <skill-dir>/scripts/validate.mjs /dev/stdin - Optional — ask releases.sh to validate the live domain (no account needed; reads the
file the same way the daily sweep will):
The response previews the parsed identity, per-product location counts, and how each locator will be tiered. This is read-only — it does not create a listing.curl -fsS -X POST https://api.releases.sh/v1/listing/validate \ -H 'content-type: application/json' \ -d '{"domain":"{domain}"}'
Then stop. There's nothing else to run: the registry re-reads the file on a regular sweep, so future edits are picked up automatically. Feeds/GitHub/App Store locations go live after a quick automated check; plain web pages are reviewed by a person before any crawling starts. The file is fill-if-empty and never overwrites curator decisions, so it's safe to leave in place.
Updating an existing manifest
- Read the current file and run it through
scripts/validate.mjsfirst to see where it stands (it may predate a rule). - Re-verify that every already-declared location still responds; drop dead ones.
- Add newly-found locations, keeping within the caps and the one-canonical rule.
- If it's still a v1 file (
"version": 1or profile-only fields like a nestedorg), rewrite it to v2 — v1 is no longer read. Keep only what maps to the v2 shape above. - Validate again, then redeploy. No re-submission step is needed.
Reference
references/modeling-products.md— worked good/bad examples of the product-modeling rules in Step 2b (capability sprawl, one-product-per-SDK, and when separate products are right). Read it when a company has many offerings and you're unsure how to split them.references/schema.json— the bundled JSON Schema snapshot (canonical:https://releases.sh/schemas/releases.json).examples/— minimal domain, full domain-with-products, and repo-root files.- Owner-facing prose docs:
https://releases.sh/docs/listing(fuller narrative + a copy-paste agent prompt; this skill is the operational version of that guidance). - To get an org into the registry in the first place (if it isn't listed yet), the owner can
submit a changelog URL at
https://releases.sh/submit, or the live-domainPOST /v1/listing/validate→activatelane can create a listing from a published file.