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

@bettercms-ai/cli

v0.3.0

Published

Work on a BetterCMS project locally: sign in, clone the source, install dependencies.

Readme

@bettercms-ai/cli

Work on a BetterCMS project on your own machine.

npx @bettercms-ai/cli@latest init --project-id <id>

Copy the command (with your project's id already in it) from the Work on this project locally button in the project top bar. Requires Node 20 or newer.

Commands

| Command | What it does | |---|---| | init --project-id <id> [--dir <path>] | Signs you in, fetches the source, installs dependencies, pre-fills .env. | | status [--project-id <id>] [--json] | What is live and what is building. Defaults to the cached sign-in. | | whoami | The cached sign-in — project, workspace, API, token expiry. Offline. | | logout | Forgets the cached sign-in. Idempotent. | | --version / --help | The installed version / this usage. |

| Flag | Default | What it is | |---|---|---| | --project-id <id> | init: required · status: the cached sign-in | The project to work on. | | --dir <path> | the project slug | Directory init creates. Must not already exist. | | --api <rung\|url> | prod | A rung by name — prod, dev, local — or a full URL. Also BETTERCMS_API_URL. | | --json | off | status only: print the API response verbatim. |

Both --flag value and --flag=value work. Unknown flags are refused, not ignored.

Environments

--api takes a rung by name, so nobody has to remember a hostname:

| Name | API | |---|---| | prod (default) | https://api.bettercms.ai | | dev | https://dev-api.bettercms.ai | | local | http://localhost:3000 |

npx @bettercms-ai/cli@latest init --project-id <id> --api dev

A full https:// URL still works. A bare word that is not a rung is refused on the command line, listing the ones that are — it is never sent to DNS as a hostname.

The dev API is on bettercms.ai while the rest of the dev estate is on bettercms.dev (dashboard app.bettercms.dev, MCP mcp.bettercms.dev). bettercms.dev has a wildcard DNS record, so dev-api.bettercms.dev resolves and answers an nginx 404. Use --api dev and the question does not come up.

Each rung caches its own sign-in under its own origin in ~/.bettercms/cli-credentials.json, so switching never presents one rung's token to another — but the first command against a new rung asks you to approve it.

Sign-in is global, not per directory: init or status for another project replaces the cached grant, so a bare status afterwards reports that other project. status prints the project id on its first line for exactly this reason.

init

  1. Signs you in. An OAuth device code opens your browser with the project pre-selected. Approve it there. If the browser cannot be opened, the link is printed instead. The grant is cached in ~/.bettercms/cli-credentials.json, so the next run is silent — unless the cached grant is for a different project, in which case you are asked again.
  2. Reads the project — its slug (the directory name) and workspace.
  3. Fetches the source. A connected GitHub repo is cloned with git clone; otherwise the latest deployed build is downloaded and unpacked.
  4. Installs dependencies with the package manager the lockfile names — bun.lock/ bun.lockb → bun, pnpm-lock.yaml → pnpm, yarn.lock → yarn, otherwise npm. A project with no package.json skips this step.
  5. Pre-fills .env from .env.example when there is one and no .env yet: BETTERCMS_API_URL / PUBLIC_BCMS_API_URL / BCMS_API_URL are set to the API, and BETTERCMS_WORKSPACE / PUBLIC_BCMS_WORKSPACE / BCMS_WORKSPACE to your workspace slug. Every other line — your keys included — is left exactly as it was.
  6. Prints the next steps.

The one manual step

init does not mint an API key. Create a content:read key under Project → Settings → API Keys and paste it into .env, then start the dev server.

status

$ bettercms status
Project   proj_abc123
State     idle
Live      a1b2c3d since 2026-01-01T00:00:00.000Z
Serving   staging — promote pending (production a1b2c3d)
Editor    https://app.bettercms.ai/projects/proj_abc123

Serving appears only on projects that use the staging/production split. Live reads nothing deployed yet until the first publish, and State carries the API's own reason when a build failed: State failed — <reason>.

--json prints the API response verbatim — state, lastLiveSha, liveAt, publishing, failed, error, slot, productionSha, awaitingPromote, canvas, editorUrl. It is also non-interactive: if signing in would need a browser it exits 1 with Sign-in needed for <id>. Run the command once without --json to approve it. rather than opening one. Run status once without --json on a new machine, then script it.

status needs the same artifact:write grant as deploying, which the default device grant carries. A narrowed role sees the API's own 403 sentence instead of the status lines.

Exit codes

| Code | Meaning | |---|---| | 0 | Done. Also whoami when signed out and logout when there was nothing to clear. | | 1 | The command failed — the reason is on the last line of stderr. | | 2 | The command line itself was wrong: unknown command, unknown flag, or a flag missing its value. | | other | The exit code of git or your package manager, which printed its own error. |

Troubleshooting

  • "git is required to clone …" — install git, then rerun.
  • A private repo asks for credentials — that is git's own prompt; set up an SSH agent or a credential helper and rerun.
  • "You approved a different project …" — the consent screen only pre-selects the project; rerun and pick the one in the command.
  • "This project has no deployed source yet." — connect a GitHub repo or publish once.
  • "status needs --project-id , or run bettercms init first." — nothing is cached on this machine yet. Pass the id, or run init.
  • "Sign-in needed for . Run the command once without --json to approve it."--json never opens a browser. Run the same command without --json, approve it, then rerun.
  • "Could not reach https://api.bettercms.ai: …" — DNS, TLS, a proxy, or the 30 s request timeout. Check the network, or point --api somewhere reachable.
  • "Unexpected response from the API." — a 2xx body that was not the JSON expected; usually a captive portal or a proxy answering instead of the API.