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

@formeui/cli

v0.1.2

Published

The forme CLI. Installs Free and Pro Forme source into a project.

Readme

@formeui/cli

The forme CLI, published to npm as @formeui/cli. It installs Forme source into a project and then gets out of the way: after an install nothing from @forme/* is needed at runtime. The CLI is a delivery mechanism, not a dependency.

npx @formeui/cli add button                    # Free, public, MIT
npx @formeui/cli login                         # sign in, once
npx @formeui/cli add analytics-dashboard       # Pro, needs a licence

Installed globally (npm i -g @formeui/cli) the command is plain forme, which is what the rest of this file writes. The npm name forme belongs to an unrelated package, so never document npx forme.

Why the CLI splits in two

Forme has two tiers and they are delivered differently, on purpose.

Free components are public and already install with the shadcn CLI. So the CLI hands them over rather than reimplementing the installer: it writes a temporary components.json with a registries entry and runs npx shadcn@latest add @forme/<name>, then puts your components.json back byte for byte. Owning prompts, diffs, --overwrite and the Tailwind check is shadcn's job, and delegating means Forme is not on the hook when shadcn changes.

Pro components are the product, so they come from the authenticated API and are written by this CLI. Nothing is public, and there is no npm package to pull into your app.

forme add button
  -> writes a temporary components.json with "@forme"
  -> npx shadcn@latest add @forme/button --yes
  -> restores components.json exactly as it was

forme add analytics-dashboard
  -> POSTs your bearer token for the Pro item
  -> installs forme-foundation first, then the item
  -> writes each file to its @components/ target
  -> prints the npm packages to install

Commands

forme add <name...>

| Option | Meaning | | ---------------------- | ----------------------------------------------------------------------------------------------- | | --dry-run | print the plan and write nothing | | --registry-url <url> | where Free components come from. Default https://formeui.com, or FORME_REGISTRY_URL | | --api-url <url> | where the Pro registry and CLI auth live. Default https://api.formeui.com, or FORME_API_URL |

Names may be given as button or @forme/button. An unknown name is reported with the closest names in the index, and the names that were understood still install; the exit code is 1 when anything was missing.

Pro installs write to the @components/ alias from your components.json, with src/ layouts, custom aliases and tsconfig paths all resolved. Dependencies declared in a registry item are installed first, so a file is never written before the file it imports. npm packages are printed for you to install rather than added to package.json: Forme never adds a runtime dependency.

forme login

The browser hand-off, end to end:

  1. The CLI listens on 127.0.0.1 on an ephemeral port.
  2. It generates a random state and opens <siteUrl>/cli/auth?port=<port>&state=<state>. The URL is printed either way, so a machine with no browser to open still works.
  3. You sign in on the site and approve the CLI. The site writes a single-use code that expires in five minutes.
  4. The site sends the browser to http://127.0.0.1:<port>/callback?code=<code>&state=<state>.
  5. The CLI checks state, POSTs the code to /api/cli/authorize, saves the token, prints a small page and exits.

Checking state is what stops another website from handing your browser a code it did not ask for. The local server binds to loopback only and is closed on every exit path, including Ctrl-C. Five minutes is the timeout.

| Option | Meaning | | ------------------ | -------------------------------------------------------------------------------- | | --site-url <url> | where the browser goes to sign in. Default the registry URL, or FORME_SITE_URL | | --api-url <url> | where the token exchange happens |

forme logout

Deletes the local token, and revokes it server-side when it can reach the API. The local file goes first, so a network failure cannot strand a credential on disk; if the revoke did not happen, the CLI says so and tells you to run it again when you are online.

forme whoami

The signed-in email and entitlements. --json prints them raw.

forme list

Every component, marked free or pro. --tier free|pro filters, --json prints raw. Pro names only appear when you are signed in, because a Pro item must not be discoverable from the public registry, not even by name.

forme search <query...>

Matches name, title, description and category. All words must match.

forme init

Adds the @forme registry to an existing components.json, permanently, and prints the two Tailwind v4 steps:

@import "tailwindcss";
@import "../components/forme/foundation.css"; /* adjust to your globals.css */

forme add forme-foundation writes that CSS file for you.

Config

~/.config/forme/config.json, mode 0600 on POSIX because it holds a credential.

{ "token": "...", "expiresAt": "2026-12-01T00:00:00.000Z", "email": "[email protected]" }

FORME_CONFIG_DIR points it somewhere else, which is how you keep two accounts or run the CLI in a test. XDG_CONFIG_HOME is honoured when it is set.

Environment

| Variable | Default | What it does | | -------------------- | ------------------------- | ------------------------------------------------------- | | FORME_CONFIG_DIR | ~/.config/forme | Where the token is stored | | XDG_CONFIG_HOME | unset | Used to find the config directory | | FORME_REGISTRY_URL | https://formeui.com | Where Free components come from | | FORME_API_URL | https://api.formeui.com | Where the Pro registry and CLI auth live | | FORME_SITE_URL | the registry URL | Where the browser goes to sign in | | FORME_INDEX_PATH | unset | Read the index from a local file instead of the network |

FORME_INDEX_PATH

Points forme list, forme search and the tier lookup in forme add at a local index file instead of fetching one. The file is the shadcn registry index shape that packages/registry/dist/registry.json is written in:

{
  "name": "forme",
  "homepage": "https://formeui.com",
  "items": [
    {
      "name": "button",
      "title": "Button",
      "description": "The primary action element.",
      "type": "registry:ui",
      "tier": "free",
      "category": "buttons",
      "dependencies": ["clsx"],
      "registryDependencies": ["forme-foundation"]
    }
  ]
}

So forme list works with no network at all:

FORME_INDEX_PATH=packages/registry/dist/registry.json forme list

Only the index is read from that file. forme add <pro-name> still fetches the item itself from the API, because source must never sit in a file you can commit.

How it runs

In this repo, bin/forme.mjs imports ../src/cli.ts and lets Node strip the types, so working on the CLI needs no build step. On a Node too old to strip types it re-executes itself with --experimental-strip-types, passing arguments and the exit code through.

Published, it cannot do that: Node refuses to strip types from files under node_modules. So prepack compiles src/ to dist/ (tsconfig.build.json) and the package ships bin/ and dist/ only. The shim runs the source when it exists and the build otherwise, so a stale local dist/ never shadows src/.

Everything the CLI receives from outside is validated with zod before it is used: the registry index, the registry item, the authorize and whoami replies, and your components.json. A malformed registry item is a bad gateway, not a file to install.

Layout

bin/forme.mjs     the shim: the TS source here, the compiled dist/ when published
src/cli.ts        commander wiring, one function per command
src/catalog.ts    the index: Free from the site, Pro from the API, or FORME_INDEX_PATH
src/api.ts        fetch + zod schemas for every Forme response
src/install.ts    resolve the item graph, write files, print npm packages
src/free.ts       the shadcn CLI hand-off, and `init`
src/login.ts      the local server, the state check and the token exchange
src/project.ts    components.json and tsconfig alias resolution
src/config.ts     ~/.config/forme/config.json and the URL defaults
src/output.ts     plain text output and FormeError

Releasing

Bumping the version is the release. Nothing else publishes.

pnpm cli:version patch      # or minor, major, or prerelease --preid=beta
git commit -am "CLI: 0.1.1" && git push

On main, .github/workflows/publish-cli.yml runs whenever this package.json changes. It publishes only if npm does not have that version yet: it type-checks, tests, packs (which builds dist/), installs the tarball in an empty project and runs it, then publishes and tags cli-v<version>. A prerelease version goes out under the next dist-tag, never latest. To retry a failed publish, run the workflow by hand.

Check a package before you release it:

cd packages/cli && pnpm pack    # then npm i ./formeui-cli-<version>.tgz somewhere

Publishing is token-free. The workflow authenticates with npm trusted publishing (OIDC): @formeui/cli trusts GitHub Actions in dvaJi/forme running publish-cli.yml, and nothing else. No npm token is stored anywhere. It is set up on npmjs.com under the package's Settings → Trusted publishing, and npm accepts it before a package's first publish, so 0.1.0 went out through the workflow too. If the workflow is renamed or moved, update that setting or the next release fails with an auth error.