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

@simplrstudio/cli

v0.3.0

Published

Sync a SimplrStudio site to your editor: pull, push, publish.

Readme

@simplrstudio/cli

Sync a SimplrStudio site to your editor. Pull the site's files down, edit them in VS Code (or anything else), push them back, publish when ready.

simplr login --host https://your-server
simplr pull acme
cd acme
# …edit…
simplr push
simplr publish

Requires Node 20 or newer.

Installing

npm install -g @simplrstudio/cli
simplr --help

A token and Node are the whole prerequisite list — nothing else has to be checked out or configured.

Prefer not to install globally? npx @simplrstudio/cli pull acme runs the same command without putting anything on your PATH.

From inside Claude Code

The same package is a Claude Code plugin, so one install covers both:

/plugin marketplace add https://www.simplrstudio.com/claude-plugin/marketplace.json
/plugin install simplr@simplrstudio

That puts simplr on PATH, connects the theme authoring tools to the session, and loads a skill describing both loops — so a session that has never heard of this CLI can still be asked to publish a design. Log in once with simplr login --host https://www.simplrstudio.com and every later session and directory reads the same stored token.

Install it either way, not both: a global install and the plugin put two simplr binaries on PATH, and which one wins is down to PATH order. They are the same program, so the worst case is a version skew — simplr --version says which you are running.

Upgrading

npm install -g @simplrstudio/cli@latest

simplr --version reports what you are running. If an upgrade ever changes the workspace format, commands say so and name the fix rather than reading a manifest they do not understand.

Getting a token

Tokens are per-user and are minted in the app under Settings → API tokens. The plaintext is shown once, at creation — it is stored only as a digest, so a lost token has to be replaced rather than recovered.

Supplying a token

Every command that talks to the server takes --token, and honours SIMPLR_TOKEN — that is everything except login and logout, which manage the stored token rather than using one. Nothing has to be logged in first, so a CI job or a container can set the variable and run.

Precedence: --token → SIMPLR_TOKEN → the token stored for that host.

simplr login is the convenience for people at a terminal: it prompts for a token, verifies it against the server, and stores it in ~/.simplr/config.json with mode 0600 so later commands need no token at all. Tokens are stored per host, so a staging and a production token can coexist.

SIMPLR_TOKEN is not per-host, so it is only used against a host something actually named — a --host, SIMPLR_HOST, or a pulled site's manifest. If the host would have come from stored config, the command errors instead of sending the token somewhere you did not choose.

Choosing a server

Every command takes --host, and honours SIMPLR_HOST. Inside a pulled site the host recorded at pull time is used, so day-to-day you never pass it.

Precedence: --host → SIMPLR_HOST → the pulled site's manifest → the host you logged into first. There is no built-in default — the platform domain is deployment-specific, and guessing it could point a push at the wrong server.

simplr status reports which host it actually reached and where both the host and the token came from, when you need to know which of several won.

Local layout

acme/
  .simplr/manifest.json      # what the last sync saw — do not hand-edit
  .claude/skills/authoring-sections/SKILL.md   # the markup rules, for an agent session
  conflicts/                 # your version of any file a pull overwrote
  docs/README.md             # this document, refreshed on every pull
  site/                      # shared across every theme on the site
    site/data/work.csv       # a collection's rows — yes, site/ twice; see below
    site_assets/logo.png     # the media library
  themes/
    aurora/                  # one directory per theme, its name slugged
      pages/
        home/
          index.html         # read-only: the page as its sections compose it
          hero-41.html       # a section — edit this
          story-42.html
          layout/
            navigation-7.html  # a layout partial — edit this too; see below
        about/
          index.html
          hero-58.html       # a different section from home's hero
      styles/main.css
      data/work.csv          # a collection's rows as the theme ships them
      data/work/41.csv       # one section's own rows for that collection
      metadata/pages/3.json  # the app's own snapshots — leave these alone
      metadata/components/41.json
      settings/heading-typeface.json  # the heading typeface picked in the editor

Each page owns a directory, so a page's files move together. A section's file is named for its title plus a number the app assigns (a built-in's, by the app alone), and keeps that name if the section is retitled later — so two pages' "Hero" never share a file, and a file never moves out from under you.

index.html is the page assembled from its sections — it is there to show how they compose, and pushing an edit to one is refused, naming the section files to edit instead. Reordering is simplr page add-section --at and simplr page replace. A successful push refreshes the index.html of a page whose own section files it changed, so the composed view keeps matching what you just pushed. A shared file — a layout/ partial, or data under site/ — composes into pages the push has no way to name, so pull to see those pages recomposed.

A layout/ partial is editable markup, exactly like a section — the nav, the footer, anything every page shares. It sits in a layout/ directory under the page that owns it, almost always pages/home/layout/: a page without a layout of its own renders through home's, so an edit there reaches every such page. A partial arrives rendered — real text, real links — rather than as the template the site stores, and pushing it back updates the partial itself, so the text you changed stays editable in the app afterwards. The one thing an edit there cannot change is the navigation links: those are built from the site's own pages, so a push that changes them says so and leaves them alone — rename or reorder pages in the editor instead. Deleting a partial's file is refused too; every page renders it, so remove it in the editor.

The directory a file sits in decides where it uploads. site/ and themes/<theme>/ are separate namespaces on the server and may hold the same path as genuinely different files. A file you create in your editor is routed by where you put it, so put a new theme file inside the theme's directory; a file already tracked keeps the destination the last pull recorded, which is how the page directories can differ from the paths the server uses.

Inside site/, a file keeps the server's own path, and the server stores a collection's rows at site/data/<field>.csv — so they land at site/site/data/<field>.csv. Leave the doubling alone: a file moved to site/data/ would push as a different file.

A collection's rows can sit at three layers, and a page shows the first it finds: the site's own site/site/data/<field>.csv, then a section's own themes/<theme>/data/<field>/<section-id>.csv, then the theme's shipped themes/<theme>/data/<field>.csv. A theme's metadata/ and settings/ files are written by the app for itself; leave them alone.

Files outside those two trees (notes, a README of your own) are listed by push as ignored rather than uploaded. The directories the CLI writes for you — conflicts/, docs/, .claude/ and .simplr/ — are passed over silently instead, since they are not your doing. It is safe to git init a pulled site; .git/ is skipped too.

Seeing your changes

push writes drafts, and drafts show up in the theme's preview immediately — no publish step needed to see your own work.

pull prints a preview link for every theme, and push prints one for each theme it just changed:

$ simplr push
uploaded  themes/hackathon/pages/home/hero-41.html

1 uploaded, 0 deleted, 0 failed.

preview hackathon:
  http://lvh.me:3000/accounts/5ed19a9c…/sites/1/themes/43/preview

Keep that tab open — a reload picks up whatever you last pushed.

The site's public URL keeps serving the published version until you deploy, so you can push freely without touching what visitors see.

Publishing is usually done in the app — the theme gallery's Deploy changes button, which reports how many unpublished changes it is about to promote. simplr publish is the same operation for scripts and headless use.

Editor workflow

The CLI is editor-agnostic — a pulled site is just files, so any editor works:

simplr pull acme
code acme/          # or your editor of choice
# …edit…
simplr push         # from the integrated terminal

Anything outside site/ and themes/ is ignored by push, so editor config (.vscode/), notes and your own README can live in the directory safely. .git/ is skipped too, so a pulled site can be version-controlled locally. push names the first few files it ignored and counts the rest; simplr status lists them all.

There is no file watcher yet: v1 is edit, save, then simplr push by hand.

Commands

| Command | What it does | | --- | --- | | simplr login | Store an API token for a server | | simplr logout | Forget a stored token (--all for every host) | | simplr sites | List the sites your token can reach | | simplr pull <site> | Download a site into ./<subdomain>/ | | simplr pull | Refresh the workspace you are in, from its root or any directory below | | simplr push | Upload files you changed | | simplr publish | Make the current drafts live | | simplr status | Compare local files against the server | | simplr page create <file> | Build a new page from authored section HTML | | simplr page add-section <file> | Add authored sections to an existing page | | simplr page replace <file> | Replace every section on a page with the file's | | simplr theme push [dir] | Publish a theme you authored, or update your own | | simplr theme validate [dir] | Check a theme bundle without publishing it | | simplr theme skill | Fetch the contract a theme bundle must satisfy | | simplr theme list | List the themes you have published |

<site> accepts an id, a subdomain or a name.

push, publish, status and page act on the pulled site you are standing in: its root, or anywhere under its site/ or themes/. Run from anywhere else beneath a pulled site and they refuse, naming the site they found, rather than guess that you meant it. Before writing, they print the site, server and directory they resolved.

Every command below takes --host. Every command that acts on your behalf also takes --token — that is, all of them except login, whose own --token stores a token rather than using one once, and logout, which has no token to use. Both are described under Supplying a token and Choosing a server rather than repeated per command. simplr <command> --help prints the options for one command.

login

Stores an API token for a server, so later commands need neither flag.

  • --token <token> stores this token instead of prompting for one. Its meaning differs from every other command's --token, which uses a token once without storing it.

logout

Forgets a stored token.

  • --all forgets every stored token, for every host, rather than just the one for the current server.

sites

Lists the sites the token can reach, with the id, subdomain and name that pull accepts. No options of its own.

pull

Downloads the site and rewrites the manifest. Files you edited are not overwritten just because you pulled:

  • Changed on the server, untouched here — downloaded.
  • Edited here, unchanged on the server — your file is left alone. It stays a pending change that the next push uploads.
  • Changed on both sides — you are asked, one file at a time.

Answering yes to a conflict takes the server's version and saves yours to conflicts/<path> first, so nothing is discarded outright. Answering no keeps your file as it is; simplr push --force then publishes it. The same question covers a file deleted on the server that you edited, and a path created on both sides.

conflicts/ sits at the top of the workspace, in plain sight — it mirrors the layout of what it saved, so conflicts/themes/aurora/pages/home/hero-41.html holds your version of that file. Nothing ever removes it; delete it yourself once you have merged what you need. A second conflict on the same path replaces its backup.

Off a terminal (scripts, agents) there is nobody to ask, so conflicts are kept as-is, listed, and the command exits non-zero.

  • --dir <path> pulls into this directory instead of one named after the site's subdomain. The directory is created if it does not exist; pulling into an existing workspace reconciles against it as described above.
  • --force takes the server's version everywhere without asking, discarding local changes. No backups are written and conflicts/ stays empty, so an edit you had not pushed is gone. Prefer answering the per-file conflict prompts, which save your version before overwriting it.

push

Uploads created and modified files. Each upload carries If-Match, so if the asset changed on the server since your last sync the write is refused rather than silently overwriting someone else's edit:

index.html: changed on the server since your last sync — run `simplr pull` to reconcile, or `simplr push --force` to overwrite it

A file that fails does not stop the rest. Successful uploads are recorded before the command exits, so re-running push retries only what failed.

Pushing a built-in section converts it into an authored one. The section keeps its content and position, but it becomes a section you own outright: the editor no longer offers its variation switcher, and any field its owner left blank is gone, because a built-in's file is rendered from its current state rather than its full schema. The conversion is one-way.

It also renames the file. A built-in's file name is assigned by the app, while an authored section takes the name its markup declares, so push moves the local file to the new name and reports it:

uploaded  themes/aurora/pages/home/page-1.html
  → now themes/aurora/pages/home/exclusions-63.html

Only files you have actually edited are uploaded, so pulling a site and pushing it straight back converts nothing.

  • --dry-run reports what the push would do, without doing it. See below.
  • --force uploads without If-Match, overwriting whatever is on the server. This is how you publish a version you kept during a pull conflict.
  • --prune also deletes assets whose local file is gone. Off by default: deleting an asset removes its published version too, so a stray rm would take a live page down. --prune lists the paths and asks before deleting.
  • --yes answers the confirmation prompts — currently --prune's — with yes, for scripts and agents that have nobody to ask.

HTML is checked against the theme contract on write. A rejection prints the specific violations rather than a bare error.

Individual assets are capped at 10 MB.

What --dry-run checks

A dry run sends every file to the server as a real write, and the server runs every check the push runs — then declines to commit. So it reports what the push would actually answer, not a local guess at it: server-side drift, theme-contract violations, refusals like pushing a page document or a section the site has no component for, and the same compile warnings a real push prints. --dry-run --prune rehearses the deletions the same way.

Nothing is written. No version, no draft, no published asset — including on a file that is refused partway through.

Two things it cannot tell you:

  • A failure that happens only as the write is finalized. Storing the file's bytes and extracting CSV metadata both happen after the commit a dry run never reaches, so a failure confined to those steps is invisible to it.
  • What someone else does next. A dry run reports the server as it is at that moment. If the file changes in the app's editor between the dry run and the push, the push can still be refused.

publish

With no arguments, publishes the drafts of the theme that is already live.

simplr publish --theme <dir> publishes a different theme — and that deploys it: the named theme becomes what visitors see, and whatever was live is taken down. The command spells this out and asks before proceeding. Use --yes to skip the prompt in scripts.

Publishing changes the version of every asset on the site, so publish refreshes your local manifest afterwards. Without that, the next push would report a conflict on files you never touched.

status

Shows, in both directions: what you changed locally, what changed on the server, what is missing on either side, and what push will ignore. No options of its own — it only reports, and never writes.

page

Turns HTML you wrote — by hand or with an agent — into real, editable sections.

simplr page create sections.html --title "Our Work"
simplr page add-section hero.html --page our-work
simplr page add-section hero.html --page our-work --at 0
simplr page add-section hero.html --page our-work --replacing hero
simplr page replace pricing.html --page pricing

The file holds one or more sibling <section> elements and nothing else — no <head>, no <html>. The site wraps them in its own layout, which already loads the theme's fonts and defines its colours.

Each subcommand takes --theme <name>, which is required only when the workspace holds more than one theme; with a single theme it is inferred.

This is deliberately not part of push, which still refuses to create a page or a section from a file: a page and its collections are too much to bring into being by dropping a file in a directory.

Every pull fetches .claude/skills/authoring-sections/SKILL.md from the server and writes it into the workspace. It is generated from the same rules that server validates against, so an agent session started here already knows what conformant markup looks like — and it comes from the server you just talked to rather than from whenever this CLI was installed. If it cannot be fetched, pull says so and carries on; the ingest commands work without it.

A rejection names each violation with the rule that caught it. Warnings mean the markup imported but something in it could not be made editable — worth reading.

New pages are drafts; run simplr publish to make one visible.

page create

Creates a new page from a file of authored sections.

  • --title <title> required. The page title, which also derives its slug.
  • --theme <name> the theme to add the page to. Required only if the workspace holds more than one.

page add-section

Adds the file's sections to a page that already exists.

  • --page <slug> required. The page to add to.
  • --at <position> inserts at this position, counting from 0. Appends if omitted.
  • --replacing <slug> swaps this section out while keeping its place. The original is soft-deleted, so it can be restored in the app.
  • --theme <name> the theme the page belongs to. Required only if the workspace holds more than one.

page replace

Replaces every section on a page with the ones in the file. This is the verb to reach for when you have edited a whole file and want the page to match it — add-section --replacing swaps a single section and would leave the ones your edit dropped still on the page.

Sections the file no longer declares are soft-deleted, so a replace you regret is undone in the app rather than re-authored. Markup that fails validation changes nothing at all — the page keeps the sections it had.

  • --page <slug> required. The page to replace.
  • --theme <name> the theme the page belongs to. Required only if the workspace holds more than one.

theme

Publishes a theme you designed somewhere else — by hand, or in an agent session that never touched this repo — into your own private gallery.

simplr theme push
simplr theme push ./broadside --slug broadside
simplr theme validate ./broadside
simplr theme skill ./broadside
simplr theme list

Unlike pull/push, there is no workspace and no manifest. Those exist to diff per-asset state against a site; a theme you author has no site until you install it on one, and publishing is a whole-bundle upsert with nothing to diff. The directory is the unit, and its name is the slug unless --slug says otherwise.

The bundle is the top-level *.html files in the directory, one of which must be index.html — nested directories are ignored, because a theme's assets are derived when it is packaged rather than uploaded. An optional theme.json carries the metadata, and every key inside it is optional.

{ "persona": "gallery-owner", "design_language": "broadside", "tags": ["dark"] }

An optional DESIGN.md beside it is sent as the theme's design language — the ordinary push carries it, with no flag to remember. Bring one if you already hold one; without it the theme publishes exactly as before, and the design language is simply absent. The server parses it and refuses the whole submission if it is malformed, naming the part that failed, so a broken file never lands a theme that then answers every later edit in a house style you did not choose.

Publishing the same slug again updates your own theme in place rather than creating a second. Validation runs first and fails closed, so a bad republish leaves the live one exactly as it was. A theme we ship under the same name is untouched and stays untouched — yours is yours.

push prints a preview URL when it succeeds. That page is authorized by your ordinary logged-in session, not by your token: open it in a browser you are signed into. It renders the theme's home page only — push says so explicitly when your bundle has pages the preview will not cover.

validate sends the same bundle to a route that writes nothing, and is the way to find out what is wrong before anything of yours is replaced. It exits non-zero and names each violation when the bundle fails.

theme push

Publishes the bundle, creating the theme or updating your own of that slug, then prints what conversion changed or dropped, page and section, as warning: lines.

  • --slug <slug> the name to publish under. Defaults to the directory's name.

theme skill

Writes .claude/skills/authoring-themes/SKILL.md into the directory: the contract a theme bundle is confirmed against, fetched from the server rather than shipped with this CLI. Run it before converting a design you already have, so the session doing the conversion knows the bar rather than discovering it as refusals.

It is a different document from the one pull writes. That one teaches authoring sections into a site that already exists; this one is the bar a whole bundle must clear to publish at all.

theme validate

Checks the bundle against the theme contract and writes nothing. It reads the same bundle push sends, so a DESIGN.md beside the theme is checked here too — a malformed one is refused before you publish rather than after.

A bundle that passes can still lose something in conversion — a row style that cannot be carried, an interaction the site cannot run. Each is printed as a warning: line naming the page and section, and push prints the same list.

  • --slug <slug> the name to check under. Defaults to the directory's name.

theme list

Lists the themes you have published, with the preview URL for each.

mcp-headers

Prints {"Authorization": "Bearer …"} for the named server, reading the token you are already logged in with. Claude Code runs this itself — the plugin's .mcp.json names it as the helper supplying the MCP endpoint's headers — so there is no reason to type it.

Being a helper rather than a report, it stays silent when no token is stored instead of saying so: its stdout is parsed as the header map, and a sentence would be parsed along with it. A session that finds the authoring tools missing is one simplr login away from having them.

Source and bugs

The CLI is developed in the SimplrStudio repository, under cli/. Report a bug at https://github.com/Ranauro-net/zcommerce/issues; the development setup is in that repository's own README.