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

polyblog

v1.2.1

Published

Polyblog CLI — manage multilingual blogs and articles: create, update and publish content, generate SEO articles, research topics from Reddit and RSS, and run the publishing autopilot

Readme

polyblog

Command-line access to your multilingual blogs on app.polyblog.io: articles and their locale versions, blog settings, AI article generation, topic research from Reddit and RSS sources, sitemap inspection, AI cover images, and a scheduled publishing autopilot.

Install

npm install -g polyblog   # global install
npx polyblog --help       # or run without installing

Requires Node.js 18 or newer.

Quick start

polyblog login            # opens the browser to authorize the CLI
polyblog blogs list       # your blogs with their ids
polyblog articles list --blogId YOUR_BLOG_ID
Found 2 blog(s):
1. Example Blog (example.com) (64a1f2c9e4b0a1b2c3d4e5f6)
2. Example Docs (64a1f2c9e4b0a1b2c3d4e5f7)

Signing up

No account yet? Create one from the terminal — the generated password prints exactly once, and the session is stored so every other command works immediately:

polyblog signup --email [email protected] --json

Authentication

Two ways in:

  • Browserpolyblog login --browser (or plain polyblog login and pick it) starts a temporary localhost server, opens your browser to app.polyblog.io, and receives the session tokens on the redirect back. Tokens are stored in ~/.polyblog/.
  • API keypolyblog login --with-key prompts (masked) for an API key secret and stores it, or export POLYBLOG_API_KEY=<key secret> and skip the login step entirely — ideal for CI and headless agents. Keys are created in the app under a blog's API keys page; new keys default to read-only scopes, so widen the key there before using write commands. The secret is never accepted as a command-line argument.

A stored browser session takes precedence over a stored key, which takes precedence over the environment variable. polyblog logout clears the stored session and any stored key (an exported POLYBLOG_API_KEY stays in effect — the CLI tells you so).

On an interactive terminal, a command run without credentials prints "Please login first" and starts the browser flow by itself. Without a terminal it fails immediately with exit code 1 and instructions on stderr — it never opens a browser headlessly.

The three AI image commands (articles generate-image, articles generate-cover, blogs generate-cover) call the OpenAI API directly from your machine and additionally require an OPENAI_API_KEY environment variable — your own key, billed to your OpenAI account. Everything else, including full article generation, runs server-side and needs no key.

Commands

Session

polyblog login --browser    # log in via browser; tokens land in ~/.polyblog/
polyblog login --with-key   # store an API key secret (masked prompt)
polyblog login              # interactive chooser between the two
polyblog logout             # clear the stored session and any stored key

Machine-readable output

Every subcommand accepts --json and prints real, parseable JSON (no color) to stdout — lists and get/read print the raw documents, mutations print a small { "ok": true, "id": "…" } result. Errors go to stderr with exit code 1; with --json the error is a single JSON line, so stdout stays parseable.

polyblog blogs list --json | jq '.[] | {id: ._id, name}'
polyblog schema                     # the whole command tree as JSON
polyblog schema articles list       # one subtree: options, arguments

Articles

polyblog articles list --blogId <blogId>        # 10 most recent (-n/--limit, -l/--locale, -s/--slug, --published)
polyblog articles get <idOrSlug>                # raw JSON for one article (--json; -l for slug+locale lookup)
polyblog articles get                           # raw JSON array (--json, -n, -l, -s, --published, --blogId)
polyblog articles read <idOrSlug>               # formatted view with content as plain text (-l)
polyblog articles add --title "..." --content "..." --blogId <blogId> -l en
polyblog articles update <idOrSlug> --title "..." --content-file ./content.md
polyblog articles update <idOrSlug> --cover-url https://www.imagelato.com/images/my-article-a1b2c3d4.jpg
polyblog articles delete <articleId>            # permanent

add creates a draft unless --published is passed; missing --title, --content or blog id fall back to interactive prompts. Other add flags: --format (markdown or html), --slug, --description, --author, --categories (comma-separated). update preserves every field you do not pass; --published true publishes, --published false unpublishes, and --categories replaces the whole list. --cover-url points the article at an image that is already hosted — typically the cover another locale of the same article already uses — and must be an absolute http(s) URL; unlike generate-cover it costs nothing. Prefer a Polyblog-hosted image: the blog derives its responsive -640w.jpg-1920w.webp variants from the cover filename, so a cover on any other host renders a broken hero and a broken card even though the stored URL itself resolves.

Generated articles

polyblog articles generate --topic "How to localize a SaaS blog" --angle "practical checklist"
polyblog articles generate --topic-id <topicIdeaId> --locale en --auto-publish
polyblog articles generate --topic "..." --no-images --no-persist   # inspect without saving

Writes a complete SEO article server-side — LLM text, sitemap-validated internal links, and images unless --no-images — and saves it as a draft unless --auto-publish is passed. Spends paid model usage on every call, including with --no-persist. Requires a blog id (--blog-id or polyblog.json) and either --topic or --topic-id.

AI images

polyblog articles generate-cover <articleId> --prompt "..."    # sets the article cover
polyblog articles generate-image --prompt "..." --slug my-img  # standalone image, prints its URL
polyblog blogs generate-cover <blogIdOrName> --prompt "..."    # sets the blog cover

All three require OPENAI_API_KEY in the environment. Without --prompt, cover generation derives one from the article or blog title. To reuse an image that already exists instead of paying for a new one — for instance giving a translation the cover of its original — use polyblog articles update <idOrSlug> --cover-url <url>.

Blogs

polyblog blogs list                              # id, name, domain per blog
polyblog blogs get <blogIdOrName>                # raw JSON (omit the argument for all blogs)
polyblog blogs read <blogIdOrName>               # formatted: locales, domain, quality minimums
polyblog blogs create <name>                     # create a new blog
polyblog blogs update-cover <blogIdOrName> ./cover.png   # upload a local image (jpg/jpeg/png/webp)

Autopilot

polyblog blogs set-autopilot <blogIdOrName> --enabled --cadence weekly --day-of-week 1 --hour 8
polyblog blogs set-autopilot <blogIdOrName> --disabled
polyblog blogs run-autopilot <blogIdOrName> --dry-run   # shows which topic idea would be picked
polyblog blogs run-autopilot <blogIdOrName>             # generates and saves one article now

The autopilot publishes one generated article per tick from the highest-scored saved topic idea. --cadence is daily, weekly or monthly; --hour is UTC 0-23; weekly adds --day-of-week 0-6 (0=Sun), monthly --day-of-month 1-28. Articles are saved as drafts unless --auto-publish is set (--no-auto-publish reverts to drafts). Flags merge into the existing autopilot configuration. run-autopilot without --dry-run spends paid model usage exactly like articles generate.

Topics

polyblog topics refresh --since-hours 168 --min-score 5   # fetch + score new candidates
polyblog topics list --status new --min-score 6           # saved ideas (--json, --limit, --blog-id)
polyblog topics dismiss <id>                              # mark an idea dismissed
polyblog topics use <id> [articleId]                      # mark used, optionally linking the article
polyblog blogs set-topic-sources <blogIdOrName> --reddit selfhosted,seo --rss "https://example.com/feed"

refresh fetches the blog's configured Reddit/RSS sources, scores candidates with a paid model, dedupes against published articles, and saves ideas at or above --min-score (further flags: --limit-per-source, --per-source-cap, --blog-id). set-topic-sources replaces the whole Reddit or RSS list it is given; --clear removes both.

Sitemap

polyblog sitemap urls --locale en --max-urls 100   # URLs from <customDomain>/sitemap.xml (--json, --blog-id)

Lists the URLs of the blog's public sitemap — the same set the article generator validates internal links against. Requires the blog to have a custom domain configured.

Skills

polyblog skills list           # names and descriptions of the bundled agent guides
polyblog skills get polyblog   # print a bundled SKILL.md to stdout

Configuration

A polyblog.json in the directory you run the CLI from supplies the default blog and organization:

{
  "organizationId": "64a1f2c9e4b0a1b2c3d4e5f6",
  "blogId": "64a1f2c9e4b0a1b2c3d4e5f7"
}

An optional topicSources.json beside it overrides the topic sources used by topics refresh for that run (keys: audience, reddit, rss).

Environment variables:

  • POLYBLOG_API_KEY authenticates headlessly with an API key secret — no login step needed.
  • POLYBLOG_API_URL overrides the API endpoint — only needed against a non-production deployment.
  • OPENAI_API_KEY is required only by the three AI image commands.

Usage with AI agents

Install the Polyblog agent skills — polyblog (this CLI) and multilingual-editorial-workflow (the MCP-based editorial workflow) — for Claude Code, Cursor, Codex, and any other agent that supports the Skills standard:

npx skills add polyblog-io/skills

The same guides ship inside the npm package, version-matched to the installed CLI:

polyblog skills list           # what is bundled
polyblog skills get polyblog   # the CLI guide matching this version

Or paste this into your AGENTS.md / CLAUDE.md:

## Blog publishing

Use the `polyblog` CLI for blog content: articles and their translations,
AI article generation, topic research, and autopilot publishing. Run
`npx polyblog skills get polyblog` for the full guide, and
`polyblog --help` for the command reference. Log in once with
`polyblog login`.

Prefer a connector? The Polyblog MCP server at https://mcp.polyblog.io/mcp exposes the same data as tools for Claude, ChatGPT, and any MCP-capable host — see polyblog.io/developers.

License

Apache-2.0