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

ai-journalist

v0.8.2

Published

A domain-agnostic AI journalist / blog-generation engine behind hexagonal ports.

Readme

AI Journalist

npm CI License: MIT Node

An autonomous AI journalist behind hexagonal ports — feed it your data signal, get a researched, fact-guarded, edited article out.

Point it at whatever your product knows (a live dataset, an API, an RSS feed) and it runs the full newsroom loop on top: discover a story worth telling, research it across the web, write it section by section against the evidence, then push it through an editor + fact-integrity gate chain before a single word is published. Domain-agnostic by construction: the engine imports nothing from any host app, and everything brand- or domain-specific arrives through typed ports.

Why it's different

Most "AI blog writers" are a prompt in a trench coat. This is a pipeline with editorial machinery, hardened in production:

  • Grounded by construction — sections are written from researched, source-tiered material (wire/gov primary sources ranked first, low-authority hosts down-ranked and labeled; re-reported claims chased to their primary source). An extractive digest layer keeps prompts sharp while the raw corpus remains the guards' ground truth.
  • Fact-integrity gates — a fact-guard pass strips fabricated people, scenes, quotes, relationships, and unsourced statistics; a fact-check audit rates every claim against the research and can force a weak article to DRAFT instead of publishing it.
  • A real editorial desk — a theme statement recast against what research actually found (stale or dead stories get killed, not published), newspaper line-edit and managing-editor passes with explicit length floors, headline candidates judged against a corpus of editor-written exemplars, structure and corroboration gates.
  • Deterministic guardrails around every LLM step — repetition budgets, figure-grounding checks, attribution budgets, title-candidate membership, length-ratio guards. Models drift; the gates don't.
  • Total provenance — every prompt, response, search, digest, and gate verdict is recorded per run through a pluggable run-context, so any article can be audited after the fact.

Install

npm i ai-journalist

Node 20+ (CI tests 20 and 22), ESM, ships as TypeScript source — consume it via tsx or your own bundler. Dependencies are small and boring: zod, p-limit, @openrouter/sdk, firecrawl, rss-parser, date-fns, remark.

Quickstart

A minimal adopter brings four things — a Source (your data), an LlmClient, a SearchClient, and a BrandProfile — and the batteries-included preset assembles the rest:

import { runPipeline } from "ai-journalist";
import { createDefaultInternals } from "ai-journalist/presets";
import { createHttpSource } from "ai-journalist/sources";
import { createOpenRouterLlm } from "ai-journalist/clients/openrouter-llm";
import { createFirecrawlSearch } from "ai-journalist/clients/firecrawl-search";

const source = createHttpSource({ signalUrl: "https://my-api/signal" });
const llm = createOpenRouterLlm({}); // OPENROUTER_API_KEY; dynamic model selection
const search = createFirecrawlSearch({ apiUrl: process.env.FIRECRAWL_API_URL });
const brand = {
  name: "My Outlet",
  publication: "My Outlet (myoutlet.com)",
  beat: "your beat",
  bylines: ["A. Writer"],
};

await runPipeline({
  source,
  sink: { publish: async (post) => ({ url: `out/${post.slug}.md`, status: "DRAFT" }) },
  config: { llm, search, brand },
  internals: createDefaultInternals({ llm, search, brand, source }),
});

Two runnable demos ship in examples/: basic.ts (fully offline — also the CI wiring proof) and live-minimal.ts (real LLM + search, writes out/<slug>.md, safely prints SKIP without keys).

The contract

ports.ts is the entire customization surface — four public ports, typed and documented in place:

| Port | What you decide | | -------------- | -------------------------------------------------------------------------------- | | Source | Where signal + grounding facts come from (Http/Rss/File ship; or your own) | | Sink | Where finished posts land — one publish(post) function, no class to subclass | | EngineConfig | Which LLM, which search backend, your brand identity/voice, ~70 documented knobs | | Linker | Optional on-site entity links |

"I want to change X" → CUSTOMIZING.md maps every seam. Search is fully swappable (SearchClient port): reference clients ship for Firecrawl (cloud or self-hosted) and self-hosted SearXNG, with no baked-in hosts.

Guarantees, enforced in CI

  • Purity — an AST guard fails the build on any process.env read or hardcoded brand/host literal inside the core: everything host-specific must arrive through the ports.
  • Prompt stability — 300+ byte-lock checks pin the exact text of every LLM prompt, so prompt drift is a failing test, never a production surprise.
  • Wiring — the offline end-to-end example runs under vitest on every push.

Releases

Semver, with a CHANGELOG.md section per version. Every release is published to npm and GitHub Releases from the same tag by CI (npm trusted publishing, tokenless).

License

MIT. Extracted from a production news pipeline and maintained as a standalone, host-agnostic engine — contributions that keep it universal are welcome (see CONTRIBUTING.md).