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

seodraft

v0.2.0

Published

Local-first SEO/AEO content engine: installs an agent skill + deterministic scripts into your AI coding harness.

Readme

seodraft

An SEO/AEO content engine that lives in your repo, powered by the agent you already pay for.

It installs an agent skill plus a set of dependency-free Node scripts into the AI coding harness you already run, and turns it into a content operation: keyword research, a content calendar, article generation, and a deterministic pre-publish gate.

No SaaS. No model API keys. Your articles are Markdown files in your repo and your state is JSON files you can read in a diff.


How it works

The division of labor is the whole design:

| | | | --- | --- | | Your agent proposes | keywords, schedules, research, drafts, fix suggestions | | Deterministic code decides | 15 gate rules, 18 audit rules — no LLM, no network, milliseconds | | You approve | nothing is published or edited without you |

A reviewer gets tired; a gate does not. Every question with a factual answer (is the meta description within budget, does this internal link resolve, is this term already covered by another post) is answered by a script on every article, forever. Everything that needs taste stays with you and your agent.

Requirements

  • Node.js ≥ 18
  • A Markdown/MDX content repo — Astro, Next.js, Hugo and Jekyll are detected automatically, and Markdoc/Keystatic (.mdoc) works too
  • One of: Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Pi

Install

From your content repo:

npx seodraft@latest install

install detects which harness folders exist (.claude, .agents, .cursor, .gemini, .opencode, .pi), asks you to confirm, and copies the skill in. Pass --providers=claude,cursor to skip the prompt, or --scope=global to install into ~ instead of the project. update re-copies over an existing install — run it after bumping the package.

Installing globally works too, and gives you the CLI on PATH for CI use:

npm install -g seodraft

To run from source instead:

git clone https://github.com/ch0rch/seodraft.git
cd seodraft && pnpm install
node /path/to/seodraft/bin/seodraft.mjs install   # from your content repo

Then open your agent in your content repo and run /seodraft init.

Pi

Pi loads seodraft as a native package, in place — nothing is copied into your repo and pi update keeps it current:

pi install npm:seodraft

Install it without a version: a pinned spec (npm:[email protected]) is deliberately skipped by pi update --extensions. The package ships the skill plus a /seodraft <command> prompt template; /skill:seodraft works too.

Pick one route per machine — the package or seodraft install --providers=pi. Both at once makes pi report a seodraft name collision and load only the first one it finds. And if you already installed for Codex, pi picks the skill up for free: it reads .agents/skills/ as well.

Commands

Inside your agent:

| command | what it does | | --- | --- | | /seodraft init | detects your framework, interviews you about the business, writes .seodraft/config.json + profile.md | | /seodraft keywords | builds the keyword bank — real metrics via DataForSEO, or clearly-marked estimates without it | | /seodraft plan | schedules stored keywords into a content calendar | | /seodraft write | research → outline → draft → gate, then updates state. Max 2 redrafts, then it stops | | /seodraft audit | deterministic audit of existing content, with fixes applied only after you approve | | /seodraft status | queue, keyword coverage, AEO coverage, last audit counts |

The same scripts run headless for CI, no harness required:

seodraft gate content/blog/my-post.mdx --term "target keyword"   # exit 1 on failure
seodraft audit --file content/blog/my-post.mdx                   # always exit 0
seodraft state validate

The gate

gate.mjs blocks an article on any error-severity failure. It is the reason a bad draft never becomes a file you have to clean up later.

| rule | severity | | --- | --- | | frontmatter-parse, frontmatter-complete | error | | title-length, description-length | error | | h1-policy, heading-structure | error | | internal-links-min, broken-internal-link | error | | cannibalization | error | | placeholder-text | error | | tldr-length, faqs-min | error | | cannibalization-fuzzy, thin-content, faq-answer-length | advisory |

cannibalization is a hard stop, never a redraft: if a term is already covered by one of your posts, a second post splits your own ranking. The agent is forbidden from rephrasing around it.

AEO

Three canonical fields exist because answer engines quote structure, not prose:

  • tldr — a self-contained answer to the target term. This is the block an answer engine lifts.
  • faqs — quotable question/answer pairs.
  • updatedDate — the freshness signal. It feeds dateModified in your structured data; without it, a post you refreshed can never say so.

The same AEO rules are errors in the gate and advisories in the audit. That split is deliberate. New content has no excuse for missing fields your own schema declares. But erroring every post in a legacy archive that predates the standard produces noise, not signal, and buries the findings that matter. A missing TL;DR on a three-year-old post is an opportunity; a missing meta description was always a defect.

Configuration

.seodraft/config.json (committed) maps nine canonical fields onto whatever your repo actually calls them. Values may be dot-paths into nested frontmatter, and null means the field is unused here — which also disables its rules.

{
  "schemaVersion": 1,
  "siteUrl": "https://example.com",
  "language": "en",
  "locationCode": 2840,
  "framework": "astro",
  "contentDir": "src/content/blog",
  "extension": ".mdx",
  "frontmatter": {
    "title": "seo.seoTitle",
    "description": "seo.seoDescription",
    "date": "publishedDate",
    "updatedDate": "updatedDate",
    "image": "coverImage",
    "tags": "category",
    "draft": "draft",
    "tldr": "seo.tldr",
    "faqs": "faqs"
  },
  "bodyH1": false,
  "internalLinksMin": 2,
  "thinContentWords": 500,
  "titleMax": 60,
  "descriptionMax": 160,
  "tldrMin": 100,
  "tldrMax": 400,
  "faqsMin": 3,
  "faqAnswerMax": 500
}

State files, all in .seodraft/:

| file | committed | holds | | --- | --- | --- | | config.json | yes | the above | | profile.md | yes | business, audiences, competitors, voice, instructions | | keywords.json | yes | the keyword bank | | calendar.json | yes | scheduled entries | | config.local.json | no | DataForSEO credentials |

Keyword metrics are optional

DataForSEO gives you real search volume and difficulty. Without credentials, dataforseo.mjs exits 0 with {"degraded": true, "reason": "missing-credentials"}, and the agent estimates the metrics instead, marking those entries source: "estimate". Real measurements always overwrite an estimate; an estimate never overwrites a measurement.

Credentials go in DATAFORSEO_LOGIN / DATAFORSEO_PASSWORD or in the gitignored .seodraft/config.local.json.

Contributing

Issues and pull requests are welcome. Start with CONTRIBUTING.md — it is short, and it covers the one constraint that is easy to break by accident: everything under skill/scripts/ must stay dependency-free, because those scripts run inside someone else's harness with no node_modules anywhere near them. CI enforces it mechanically.

pnpm install
pnpm test                # 81 tests, 10 suites
pnpm check:deps          # the dependency guard
pnpm smoke               # packs the tarball, installs it, runs it

There is no build step: skill/ is the product and the installer copies it. prompts/ is Pi-only: it is loaded straight out of the package and is never part of the copied skill.

fixtures/astro-blog/ is a content repo seeded with a known set of defects that the gate and audit tests assert against exactly — it is a test fixture, not sample data.

By participating you agree to the Code of Conduct. For vulnerabilities, see SECURITY.md — please do not open a public issue.

Not in v1

Google Search Console OAuth and opportunity scoring, AI-citation tracking (v1 enforces citable structure; it does not measure citations), WordPress publishing, and image generation.

License

Apache-2.0