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

cnlp-kit

v3.1.3

Published

CNL-P — a format for documents an agent has to read precisely. The standard, its profiles, and the checker that holds them.

Readme

CNLP-Kit

CNL-PControlled Natural Language for Prompts. A format for documents an agent has to read precisely: agent skills, decision records, repository guides, review checklists, and the documentation of a library the agent writes code against. It replaces prose with key: blocks, so a reader stops inferring which sentence is intent, which is policy, and which is a step.

This repository is the format itself — the standard, the profiles, and the checker that holds them — with no tie to any project, language or agent runtime.

  • cnlp-format.md — the standard. Read it first with npx cnlp print format; nothing else makes sense before it.
  • profiles/ — one profile per kind of document. A profile declares values, never rules.
  • src/cnlp.js — the checker. Node builtins only, no dependencies.
  • bin/cnlp.js — the cnlp command.

Install it

Node >= 22.14, and no runtime dependency of any kind — the checker is builtins only, which is what lets any repository run it.

npm i -g cnlp-kit      # cnlp on your PATH; the same line upgrades it
npm i -D cnlp-kit      # a pinned version, for a repository whose CI runs the check

Take the global one to work on a repository you do not want to add a dependency to, and the dev-dependency wherever the check has to be reproducible. Both can hold at once: npx cnlp inside a project with the local install runs that version rather than the global one.

Use it

Two commands. The first one installs and updates:

cnlp init      # config, profiles directory, and the skills for the agents you pick
cnlp check     # holds every document the corpus names to its profile

cnlp init asks once which agents to write skills for — Claude Code, Codex — and remembers the answer in cnlp.config.json. Run it again after every upgrade of the package: the skills are versioned with the standard they teach, so init replaces every copy with the installed version. Nothing else about your config moves on that second run.

  • --agents claude-code,codex skips the menu, for a script.
  • With no terminal to draw a menu in — Git Bash under MinTTY, or CI — it repeats the choice the config records, and installs for every agent when there is no config yet.
  • cnlp check reports skills left behind by an upgrade, and its exit code stays the corpus's own answer: a stale copy is not a defect in a document.

On the first run cnlp init also looks at what the repository already has — AGENTS.md, CLAUDE.md, skills/*/SKILL.md, docs/adr/*.md, docs/references/*.md, docs/docsets/*/… — and writes a corpus entry for each layout that matches a file, naming the count it found. Nothing is guessed from content: a decision record filed under docs/references/ reads as a reference until you say otherwise. Edit the entries it wrote, or add your own:

{
  "profiles": "cnlp/profiles",
  "corpus": [
    { "profile": "skill",     "glob": "skills/*/SKILL.md" },
    { "profile": "reference", "glob": "docs/references/*.md" },
    { "profile": "guide",     "glob": "AGENTS.md" }
  ]
}

profiles is optional and names a directory of your own profiles. A profile there resolves before the packaged one of the same name, which is how a project adds its own vocabulary: copy profiles/skill.md into it and fill custom_sections:.

cnlp init also prints the block to paste into AGENTS.md, CLAUDE.md or whatever file your agent reads. Three lines, and they are the rules no skill announces: an installed skill states its own task in its description, and a standing rule answers to no task description. It prints rather than writes: your agent file is yours.

Convert documentation you already have

Run cnlp init, then the cnlp-migrate skill over the directory — /cnlp-migrate docs/ in Claude Code, or read the body and follow it. It works on any kind of document, not one: it groups what it finds by kind, reads the packaged profile nearest each kind, and writes a new profile for a kind none of them fits — a new kind of document is one profile and no change to the checker.

It does not start by rewriting. First it inventories the vocabulary your documents already use and fills custom_sections: in a local copy of each profile, so the format bends to your words before a single body moves. Then, per document, it lists the source's rules, rewrites the body, ticks each rule against the result, and names anything in the result that has no origin in the source. A contradiction it finds in the prose is reported, never resolved silently — that decision is yours.

Document a library or a framework

Add the docsets key and the four corpus entries shown under Document sets to the config cnlp init already wrote, then run the cnlp-docset skill against the library's sources at a fixed revision.

It builds in the order that keeps a half-finished set honest: the closed catalog of every public capability first, every record at status: source, so the set itself says what has no page yet; then the type names of one running example; then canonical_use:, one whole working program in the index; then the concept, API and task pages. cnlp check resolves every reference and reports any page no other page reaches. cnlp docs is the single command you point your agent at.

The commands

| | | |---|---| | cnlp init [<dir>] [--agents <a,b>] | install or update: config, profiles directory, skills for the agents you pick | | cnlp check [--config <path>] | hold every document the corpus names to its profile; exit non-zero on any issue | | cnlp docs [<set>] | write every document set's index, in reading order | | cnlp print [<doc>] [--path] | write the standard, or a profile, to stdout |

The ownership rule

The package owns the standard, the checker, the generic profiles and the skills. The project owns its cnlp.config.json, its custom_sections, and any local profile. Nothing the project writes is ever overwritten.

The line falls between the two. A skill is generated from this package and versioned with the standard it teaches, so cnlp init replaces every installed copy on every run — editing a copy under .claude/skills/ is editing a file the next upgrade discards. To change what the kit does in your project, edit a local profile or the config, which no command here ever rewrites: on a second run init touches only the agents key it wrote itself.

This is the whole design. The format previously travelled as a zip of files copied into a target repository, where the corpus was configured by editing constants at the top of a file the next unpack would overwrite. One consumer got stranded 2 versions behind because upgrading meant a three-way merge. A package boundary is what makes upgrading a version bump.

The profiles that ship

| Profile | The document it shapes | |---|---| | profile | a profile — the profile of a profile, checked against itself | | skill | an agent SKILL.md body | | adr | an architecture decision record | | guide | a repository guide such as AGENTS.md | | reference | a checklist or protocol a skill loads by path | | docset | the index of a library's documentation — one whole working program, and the closed list of what the library offers | | concept | one model the API pages of that library rest on | | api | one capability: its symbols, its contract, and one example | | task | one outcome in steps, with the example that composes several calls |

The last four are one kind of document set: the documentation of one library, written so a reader loads a page at a time rather than a file that holds everything. See below.

skill, guide and reference ship with custom_sections: - none: the vocabulary is yours, and the inventory step of cnlp-migrate is where it comes from. adr declares its own closed block list and no custom section, because an ADR block outside that list is prose looking for a home.

Severity is the caller's. The checker returns issues; cnlp check treats every issue as an error. A project that gates on a lifecycle status — a warning while a decision is a draft, an error once it is accepted — applies that policy in its own validator, calling bodyIssues directly.

Document sets

A document set is a directory of documents written to be read a page at a time, where one page sends the reader to another instead of restating it. §9 of the standard defines them: a page id is <set>:<name>, a bare name is local, a prefixed one crosses into another set, and page:, by: and docset: are the only forms a reference takes.

The index is not a table of contents. It carries canonical_use: — one complete working program using every load-bearing element of the library — so the common case is written from the index with no other page open, and walkthrough: names the page owning each part of it. Its catalog: is the closed list: a capability with no page of its own keeps its line and says where to read the source instead, so a reader can always tell absence from omission.

Point the config at them and §9 turns on:

{
  "docsets": "docs/docsets",
  "corpus": [
    { "profile": "docset",  "glob": "docs/docsets/*/index.md" },
    { "profile": "concept", "glob": "docs/docsets/*/concepts/*.md" },
    { "profile": "api",     "glob": "docs/docsets/*/api/*.md" },
    { "profile": "task",    "glob": "docs/docsets/*/tasks/*.md" }
  ]
}

docsets names the directory whose immediate subdirectories are one set each. With the key present, cnlp check also resolves every reference, reports any that names no file, and reports any page no other page reaches. Without it a project has no document set and nothing about its run changes.

npx cnlp docs writes the index of every set present, the overridden set first — so a library and the integration package that corrects it arrive together, and loading the second is not an action anyone can forget. That command is what a project points its agent at.

Two worked sets live in examples/: a C# ECS framework and its engine integration. They are the corpus that proves the four profiles, they are checked by the same cnlp check as everything else, and they do not ship — files: in package.json names neither. docs/DOCSETS.md records why the design is what it is and which file holds each decision as a rule.

Without Node

The checker is a convenience; the standard is the authority. A repository with no Node runtime holds its profiles by review against cnlp-format.md and sets enforcement: accordingly. Nothing else changes.

What is deliberately not here yet

  • an intra-block line grammar. A profile can declare the form of a block but not the shape of the lines inside one, so a project needing that writes a test of its own. The fix is an item_pattern: key on a section, and six rules are waiting on it: the canonical verb set in §5, the status and relation vocabularies of a docset, the keys a status implies, and §9's rule that a name reaches exactly one page. The status one fails open — an unrecognised value reads as covered and sends a reader to a page that is absent.
  • a checker for three of §9's rules. A fact stated on one page of a set is not restated on another, a set has one running example, and a name routes to exactly one page. All three are rules a review holds; §9 says so rather than implying the checker reaches them.
  • a declarative severity policy. Today enforcement: states the severity in prose and nothing reads it.
  • a language-neutral conformance corpus. Until it exists, "any technology" means any technology that can read the standard, not any technology that can check it.
  • profiles/plan.md. It is the acceptance test for the three above: if adding it costs one file and no code change, the generalization held.

quality-rules.md is a seed, not part of the standard: a rubric to prune and copy into the skills that carry one. It travels with a byte-identity test you write yourself — without that test the copies drift and the rubric stops being shared.