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

@pplethai/cli

v0.5.1

Published

The Builder's terminal tool: three commands between a PPLE ID and a live app.

Readme

pple CLI

The Builder's terminal tool: three commands between a PPLE ID and a live app.

pple auth login                 # sign in with your PPLE ID (browser, no password in the terminal)
pple init                       # create an app (asks for App Name) and set up its code
pple init --name "Sign-up"      # same, without a prompt (for agents/scripts)
pple init brave-otter           # set up an app that already exists on the platform
pple deploy -m "Add the form"   # send the changes and put them live
pple upgrade                    # update pple itself from npm

Git is an implementation detail, and so is GitHub. A Builder is told what happened to their app — "Sending your changes…", "Volunteer Sign-up is live at https://brave-otter.ppleth.ai" — never what happened to a branch, and never that there is an account somewhere they should go and get. Only a genuine conflict surfaces the tools underneath, and even then with an instruction rather than a stack trace.

Reaching the app's code

App repos are private and the Builder's machine has no credential for them. Before each command that touches code, the CLI asks the Provisioner for a token scoped to that one repo, keeps it in memory, and hands it to git through the child process's environment: GIT_ASKPASS points at askpass/pple-askpass.mjs, which reads the token back out of that environment when git asks for a password. origin stays a plain URL, so the token never reaches argv (world-readable via ps), .git/config, or any other file. Commits are authored as the Builder's PPLE ID identity, which the same response supplies — the pusher is the platform's GitHub App, and would otherwise be the only name in an app's history. The reasoning is ADR-0005.

How the commands work

pple auth login runs the OAuth device authorization grant against PPLE ID: the CLI prints a code, opens the verification page, and polls until the Builder approves it. Tokens land in $XDG_CONFIG_HOME/pple/credentials.json (mode 0600) — never inside an app folder, which is a repo the platform owns. pple whoami renews an expiring token silently via the refresh token.

pple init creates a Builder App (prompt for App Name, or --name) via the same Provisioner POST /apps the Console uses, waits until the app is ready, then scaffolds it — or, given an App Slug, scaffolds an app that already exists. Either way it fetches the app's code, copies the golden-path template under the app's own name, writes .pple/app.json linking the folder to the app, and installs the platform Skill overlay the Builder's AI coding agent can load (ADR-0001, ADR-0006). The App Slug stays platform-generated; bare pple init in a folder that already has an App Link is refused.

pple deploy refreshes that same Skill overlay, records everything in the folder, sends it to the app's repo — merging in a Collaborator's work if there is any — then watches the platform build it and reports the live URL, or the build output that explains why nothing changed. It needs no arguments: the link file says which app. Account-only commands (auth login / logout / whoami) never touch Skills.

pple upgrade asks npm what the latest @pplethai/cli is and, when this copy is one npm's global root holds, installs it — which is also how Skills and the golden-path template move forward (ADR-0006, ADR-0007). Any other install channel is told the manual npm install -g @pplethai/cli line rather than having a second copy installed over the first, and so is a failed install, carrying npm's own reason. The CLI never uses sudo: a global folder the Builder can't write to is theirs to fix, not the CLI's to force.

Staying current

About once a day, before running the command it was given, the CLI asks npm whether there is a newer pple. It runs before the command because that is the only moment an upgrade is free: nothing is half-done, so the new version can simply be handed the same command line. At a terminal the Builder is asked, and on a yes the CLI installs the new version and re-runs the original command on it. With nobody at the keyboard — the AFK agent runs the CLI is built to serve — it prints one line about the new version and gets on with the command; it never asks a question that could hang.

The check is written to fail towards the command. A registry that can't be reached says nothing at all and costs one request, not one per command: update-check.json in the CLI's config directory records that npm was asked, not what it answered. A failed upgrade prints npm's own reason and the manual line, then offers to carry on with the current CLI (unattended: carries on). Two ways to turn the whole thing off:

PPLE_NO_UPDATE_CHECK=1 pple deploy          # this run, or exported in CI
echo '{ "updateCheck": false }' > ~/.config/pple/config.json   # this machine

Layout

| File | What it holds | | ---- | ------------- | | src/cli.ts | argument parsing and the command table | | src/commands/ | one file per command, each taking only Deps | | src/deps.ts | every seam: fetch, clock, subprocesses, output, template/skills dirs | | src/device-flow.ts | RFC 8628 against PPLE ID | | src/api.ts | the Provisioner API, and the only place that knows its error codes | | src/git.ts | git, and the only place that names it or holds the repo token | | src/scaffold.ts | copying the golden-path template | | src/skills.ts | refreshing the platform Skill overlay into an app folder | | src/npm.ts | npm: the registry's latest version, and the install channel | | src/update.ts | the daily update check, its cadence state, and its opt-outs | | askpass/pple-askpass.mjs | what git runs to be told the app's repo token |

Development

npm install
npm test            # drives whole commands against a fake Provisioner and real git
npm run typecheck
npm run build       # bundles the template and Skills, then esbuild → dist/bin.js

Tests run the real command path — real git in a temp directory, a fake platform behind Deps.fetch, and a virtual clock so polling loops finish instantly. test/support/harness.ts is the entry point.

App repos are served in tests by git http-backend behind Basic auth (test/support/git-server.ts), accepting only tokens the fake platform minted. A local path would let every clone and push succeed with no credential at all, and nothing would show that the platform's token is what opens a private repo.

The published package carries its own copies of the golden-path template (npm run build:template copies ../template) and platform Skills (npm run build:skills copies ../skills/pple-platform). Running from a checkout falls back to those repo paths directly. PPLE_TEMPLATE_DIR, PPLE_SKILLS_DIR, PPLE_API_BASE, PPLE_SSO_BASE, PPLE_CLIENT_ID, PPLE_REPO_BASE, and PPLE_REGISTRY_BASE override the defaults for staging work.

CLI release

A full Phase 1 launch includes a CLI release: shipping @pplethai/cli (bin: pple) to the npm latest dist-tag, which is what Builders install and self-update from. This is not part of the Cloudflare Worker deploy — a Worker rollback must never ship or unship a CLI version.

Releases happen through the CLI release workflow (.github/workflows/cli-release.yml): anyone with repository write dispatches it on main, and it re-runs typecheck, test, and build, asks the release gate whether the version in package.json may reach the registry, publishes, and only then tags the commit cli-vX.Y.Z. The one exception is the bootstrap publish that creates the package on npm, which a human runs once — see docs/deploy-phase1.md §3 for both.

The version comes from package.json on main — the workflow takes no version input, and refuses to release a version npm already holds. Nobody edits that version by hand: a PR changing what this package ships carries a Changeset (npx changeset at the repo root, or a skip-changeset label if it ships nothing), and the Version Packages PR spends those Changesets into package.json and CHANGELOG.md. So main may sit ahead of npm — a version here is prepared, not shipped. See .changeset/README.md and ADR-0008.

Past the bootstrap publish that creates the package, there is no supported way to release from a laptop. The release gate is scripts/release-gate.mjs, and it answers locally too:

node scripts/release-gate.mjs release   # may this version reach npm?
npm publish --access public --dry-run   # inspect the tarball, upload nothing

Builders install with npm install -g @pplethai/cli.