LPM CLI

Publishing your first package

From lpm init to lpm publish, with quality checks and provenance.

This guide walks through publishing a package from scratch. By the end you'll have a package on the LPM.dev Registry that anyone you grant access to can lpm install. We'll cover quality gates, secret scanning, and provenance — the things you can't see from the command name alone.

1. Authenticate

lpm login

Opens your browser, you authenticate against the LPM.dev Registry, LPM CLI captures the redirect token and stores it in your OS keychain. You only do this once per machine.

lpm whoami

Confirms the identity, plan tier, and any orgs you belong to.

2. Create the package

mkdir my-pkg && cd my-pkg
lpm init --lpm

lpm init --lpm walks you through owner / name / version / description. The published name will be @lpm.dev/<owner>.<name> — pick an owner you've claimed on the LPM.dev Registry (or one your org owns). Plain lpm init asks for the target first; choose LPM.dev Registry package for this guide.

For a non-interactive bootstrap (CI scripts):

lpm init --lpm -y --owner <owner> --name <name>

Plain lpm init -y is still valid and defaults to an LPM.dev Registry package, but the explicit flags avoid leaving placeholder owner/name values in automation.

3. Write your code

Build out the package — src/ (or wherever), dist/ (your build output), README, license. The defaults lpm init writes to package.json (substituting the owner/name you supplied):

{
  "name": "@lpm.dev/<owner>.<name>",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "type": "module",
  "license": "MIT",
  "files": ["dist"],
  "packageManager": "lpm@<version>"
}

It also writes an AGENTS.md package-manager hint so coding agents in the repo use lpm install, lpm add, and lpm run.

Tighten files to whatever you actually want shipped. Anything not listed (README, LICENSE, package.json) is included by default per npm convention. If files is absent, LPM CLI excludes project-local .lpm state from the implicit package set while preserving valid direct publisher-authored .lpm/skills/*.md files. Installed dependency skill sets are excluded whether the file set is implicit or explicit.

4. Preview the publish

lpm publish --dry-run

Validates the package configuration and authored skills, packs each target artifact, scans the actual final artifacts for hardcoded secrets, computes the normalized local LPM.dev quality score, resolves real LPM.dev authentication, and performs a non-mutating Registry permission/version preflight. Explicitly included files are scanned even when Git ignores them. It does not reserve or upload a version, create Registry state, generate requested provenance, or claim publication occurred.

lpm publish --check

Runs the network-free preparation path and stops before LPM.dev OIDC, skills staleness, and publish-permission lookups. Both preview modes update only the effective manifest used in memory when authored skills require .lpm/skills; they do not modify package.json. A real publish may persist the entry.

5. Set a quality floor (optional)

lpm publish --min-score 80

Aborts an LPM.dev Registry publish if its normalized local score is below 80. The score is an aggregate, so use the failing rows from lpm publish --check to decide which improvements matter rather than treating the threshold as a substitute for specific project checks.

The local score is computed from readme presence/length, license declaration, types coverage, test signals, and maintenance heuristics, normalized over locally applicable weights. A package passing every local check reaches 100. Server-only checks are reported as not_evaluated, never fabricated as passes or counted as local failures. The Registry computes its separate final server score after upload. See lpm quality for the completed report of an already-published package.

If package.json declares bundledDependencies or bundleDependencies, install first so every requested direct and transitive package is available in the verified LPM layout. Bundled content counts toward tarball limits, secret scanning, and provenance. Missing packages or unsafe symlinks stop the publish.

6. Publish

lpm publish

Pack → scan every final target artifact → quality report → confirmation prompt → upload. Skip the prompt with -y (good in CI).

When CI must wait for public availability, use --wait for a Pool or Marketplace version:

lpm publish --wait

The command keeps upload success separate from the publication wait. If the wait fails, do not publish the same version again.

The package starts in private distribution mode — only you (and anyone you grant access to) can install it. To make it discoverable or paid, flip the distribution mode in the LPM.dev Registry dashboard:

ModeWhat it means
Private (default)Only the publisher and granted users can install. Metadata is not public.
PoolMetadata is public. Installs gated to pool subscribers. Publisher earns a share of pool revenue.
MarketplaceInstalls require a license purchase.

Mode changes are irreversible — going public is a one-way door. See Distribution mode.

7. (Optional) Provenance

In CI with an audience-sigstore OIDC token — GitHub Actions or GitLab CI:

lpm publish --provenance
lpm publish --npm --provenance

Generates a Sigstore-signed attestation that proves which workflow built the tarball and which commit it came from. With --npm, LPM CLI attaches npm's expected {name}-{version}.sigstore bundle in the publish payload. Consumers can verify provenance via the audit pipeline or the registry UI.

--provenance fails loud (non-zero exit) if no usable Sigstore-audience token is found or if Sigstore signing fails — it never silently publishes without provenance. Useful as a hard gate in security-critical packages.

On GitHub Actions, enable permissions: id-token: write on the job. On GitLab CI, mint SIGSTORE_ID_TOKEN via the id_tokens block with aud: sigstore. Other platforms aren't supported.

For npm-compatible targets, provenance requires public access. LPM CLI's npm default is public for scoped and unscoped packages. If repo config enables provenance but a one-off publish should skip it, pass --no-provenance.

8. Publishing elsewhere

lpm publish defaults to the LPM.dev Registry. To target other registries:

lpm publish --npm                                 # registry.npmjs.org
lpm publish --github                              # GitHub Packages
lpm publish --gitlab                              # GitLab Packages
lpm publish --publish-registry https://r.example.com   # custom npm-compatible

You must have auth for the selected target. lpm login --npm opens the browser login for npm.

GitHub and GitLab login can use environment tokens or existing gh and glab sessions. These sources are not copied into LPM storage.

Custom registries use lpm login --login-registry <URL> --token <T>. For multiple targets, set lpm.json > publish.registries. See lpm.json.

Common pitfalls

  • init -y defaults aren't directory-derived. Under -y, the target is the LPM.dev Registry, owner is your lpm whoami profile username (or the literal "username" if offline / not logged in), and name is literally "package" (not your directory's name). The published shape becomes @lpm.dev/<your-username>.package — pass --owner / --name or edit before publishing.
  • Quality score below 80 by default = no readme, no license, no types. Easy fix; usually a 5-minute task.
  • Distribution mode is irreversible. Pool / marketplace are one-way doors. Stay private until you're sure.
  • Generated provenance only works in OIDC-capable CI. Don't pass --provenance from a developer laptop. For npm-compatible targets with a pre-generated bundle, use --provenance-file <PATH>.

See also