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

sentences

v0.5.1

Published

Effects-rich, interactive Reed-Kellogg sentence-diagram renderer

Readme

Reed-Kellogg Sentence Diagram Engine

Automatic Reed-Kellogg sentence diagramming in the browser. Type a sentence; a neural constituency parser (benepar, run client-side via ONNX Runtime Web) produces a parse, which is lowered to a grammatical IR and laid out as a Reed-Kellogg diagram. No server, no install.

Why this exists

Reed-Kellogg diagrams are the traditional pedagogical sentence diagram: a horizontal baseline, a vertical bar splitting subject and predicate, modifiers on slanted lines below the words they modify. Existing tools split into two camps — manual editors that render nothing automatically, and NLP parsers that stop at dependency or constituency trees. The one tool that ever generated Reed-Kellogg diagrams from arbitrary text automatically (1AiWay) runs on Silverlight and no longer works in a modern browser. This project fills that gap: automatic parse → Reed-Kellogg, entirely client-side. RESEARCH.md documents the landscape survey behind that claim.

Status

  • Automatic constituency-parse → Reed-Kellogg, in-browser, with a rule-based fallback parser.
  • 90/90 clean on a battery of sentences drawn from real diagramming lessons — zero dropped words, zero label/line collisions — across imperatives, questions, relative / noun / adverb clauses, gerund / infinitive / participle verbals, appositives, correlatives, indirect and objective complements, causative small clauses, and absolute phrases.
  • Ambiguous sentences surface alternative parses instead of guessing.
  • SVG export.
  • 216 tests; a geometric collision detector gates layout correctness.

Not yet: in-place correction of a wrong diagram, export formats beyond SVG, and validation on non-pedagogical prose. See ROADMAP.md.

Run

npm install
npm run dev

just wraps the common tasks (just on its own lists them):

just check        # typecheck, tests, lint-dist build — what CI runs on a PR
just lint-docs    # score every markdown doc in the repo; `just lint-docs 3` for strict
just smoke        # pack the tarball, install it clean, drive the installed bin over JSON-RPC
just release 0.3.0   # verify, bump, commit, tag. Pushes nothing.
just release-push    # push main and the tag, which publishes to npm

CI runs the same three things on every push and pull request (.github/workflows/ci.yml): the suite, the packaged-artifact smoke test, and a pass that lints every doc at all three strictness levels and fails on a rule that throws — the report records rule errors per-rule rather than failing loudly, so a broken rule would otherwise reach a release looking clean. Publishing runs on a version tag, re-runs everything including the smoke test, and only then calls npm publish; npm is append-only, so a burned version number is the one mistake that cannot be taken back.

The neural parser weights (~72 MB — benepar exported to int8 ONNX) are a build artifact and are not committed. Regenerate them with the scripts in parser-export/ (Python + benepar). Without them, the app falls back to a pure-TypeScript rule-based parser.

Build and test

npm run build      # static site into dist/
npm test           # unit + collision-regression suites

Architecture

text → neural constituency parse (src/parser/) → Clause IR (src/lower.ts) → footprint layout (src/layout.ts) → Scene → Canvas / WebGPU or SVG renderer. The parse → IR lowering is the piece no existing tool provides. DESIGN.md covers the architecture; RESEARCH.md the motivating gap.

De-stink

destink.html is a second, thin app on the same engine: a deterministic linter for AI-writing tropes, built off the same constituency parse and Clause IR rather than a second model. It covers the syntactic, lexical, formatting, and measurable-discourse tiers (repetition, anaphora, dilution) with located findings, a stink score, and mechanical fixes limited to deleting, moving, or lightly repairing the author's own words; semantic tropes (stakes inflation as tone, false vulnerability, dead metaphors beyond lemma counting) are out of scope for a parser and are not claimed. See docs/DESTINK.md for the architecture and scripts/destink-score.mjs for the no-browser CLI.

CLI

node scripts/destink-score.mjs [--markdown] <file>

Prints the same versioned JSON report the browser app renders (findings with source spans, errors, the rule set, and a stink score) to stdout. It only reads the file; nothing is written back.

  • --markdown runs the file through markdown-prose.ts first, blanking code fences, tables, inline code, link targets, HTML blocks, and admonition directives to spaces before linting. Offsets still index the original file. Use it on .md files — without it, markdown structure reads as prose and floods the report with false findings (~64% of findings on a measured technical-docs corpus, per that module's header).
  • Needs Node >=22.6 (re-execs itself with --experimental-strip-types) or >=23 (the flag is on by default). The script runs the TypeScript sources directly with no build step; older Node exits with an explanation instead of adding a transpiler dependency.
  • --strictness=N (1, 2 or 3) sets how hard it looks. 2 is the default and the level every threshold was calibrated against; 3 drops density floors to zero, so one instance of a shape reports the same as six, and raises each finding a severity step; 1 doubles the floors. Rules honour the dial only where they were written to — six of forty-eight so far. Use 3 on text you already know a model wrote, when the question is whether the shape is there at all rather than whether it is a tic.
  • --reduce=N (1-3) answers a different question: not what reads as a tell, but what the document could lose. Candidates are ranked by how far below the diagram's baseline they hang, and each is verified by cutting it and re-parsing — if the subject, verb or complement moves, it is withdrawn. --reduce-to=N stops once the document would reach N words. Nothing is edited. This is its own dial and has nothing to do with --strictness.
  • Not a published bin — it only runs from a checkout of this repo, not via npx. The MCP server below is the published one.

MCP server

npx -y --package=sentences destink-mcp

The same linter over stdio, for an agent that wants its prose checked while it writes. Register it with any MCP client; in .mcp.json that is:

{
  "mcpServers": {
    "destink": { "command": "npx", "args": ["-y", "--package=sentences", "destink-mcp"] }
  }
}

Two read-only tools.

  • destink_lint takes text (a draft you are holding) or path (a file to read), plus markdown (on by default for a .md/.markdown/.mdx path), strictness (1-3, as above), and a format. summary renders each finding as line:col with the offending excerpt and why it reads as a tell, printed once per rule rather than repeated down a run. score drops the located findings and keeps the counts — the cheap call for "did my edit help?". json is the full versioned report, byte-identical to what the CLI prints for the same input.
  • destink_reduce reports what a document could lose without its sentences changing shape, ranked by depth below the diagram's baseline and verified by re-parsing each cut. Its level (1-3) is a separate dial from destink_lint's strictness.
  • destink_rules lists the rule set (id, tier and name, in the order findings are attributed when two rules tie), so a caller can see what a clean report actually covers before trusting one.

Neither writes a file nor touches the network. The mechanical fixer is not exposed, since which of an author's words get cut is not a decision to make over a socket. docs/DESTINK.md has the details.

A lead-in for agents

Paste this into your agent's instructions once the server is registered. It describes the loop the tool is built for (lint, edit only what was flagged, re-lint to check the edit actually helped) rather than leaving the model to invent one:

You have a prose linter available as the `destink` MCP server. It detects the structural and
lexical patterns that make writing read as AI-generated: negative parallelism ("it's not X, it's
Y"), self-posed rhetorical questions, tricolon and anaphora runs, punchy one-line fragments,
filler transitions, ornate nouns, em-dash density, bold-first bullets, trailing tacked-on
phrases, and cross-sentence repetition.

Use it whenever you have written more than a paragraph of prose a human will read — a README, a
design doc, a PR description, a report, a commit message body.

The loop:

1. Draft normally. Do not try to write around the rules; negative constraints held in mind while
   drafting degrade everything else in the output.
2. Call destink_lint on the draft. Use strictness 3 on text you wrote yourself, since at that
   level a shape is reported on its first appearance instead of waiting for it to become a rate.
3. Edit ONLY the spans it flagged. Do not rewrite whole paragraphs: the parts that were already
   fine get re-rolled at fresh risk of a different tell, and you will not be able to tell whether
   the pass helped.
4. Call destink_lint again with format: "score" and compare. Keep the edit if the score fell and
   no new rule appeared; otherwise put the original back. The number is the judge, not your
   impression of the rewrite.
5. Repeat until the score stops falling, or until the remaining findings are ones you can defend.

Two things the linter does not claim, so do not treat it as claiming them. A clean score means the
text is free of the tells this rule set can name. It is not a statement that the writing is good.
And a finding is not automatically a defect: severity "candidate" means the rule narrowed a
suspect structurally but cannot confirm it without reading for meaning. Read those and decide.

Call destink_rules if you need to know what a rule id means or what a clean report covers.

The design behind that loop, and why the model edits while the linter referees rather than the linter asking a model whether the prose is good, is in docs/DESTINK.md under "Oracle-gated".

Release notes are in CHANGELOG.md.

License

MIT. See LICENSE.