LPM CLI

Publishing a package

Publish to npm, GitHub Packages, GitLab, a private registry, or LPM.dev Registry.

LPM CLI publishes JavaScript packages to npm-compatible registries and LPM.dev Registry. An LPM.dev Registry account is unnecessary for other targets.

From an existing package directory, this sequence publishes to npm:

lpm publish --npm --check
lpm login --npm
lpm publish --npm --dry-run --ignore-scripts
lpm publish --npm --yes

The final command uploads the package and authorizes its publish scripts. If your build is already complete, use --ignore-scripts on that command.

Choose a registry

RegistryTarget flagPackage name and configuration
npm--npmAn npm-compatible name, such as @acme/widget.
GitHub Packages--githubA scoped name, such as @acme/widget.
GitLab Packages--gitlabAn npm-compatible name and publish.gitlab.projectId.
Private or custom registry--publish-registry <URL>An npm-compatible name and the full HTTPS registry endpoint.
LPM.dev Registry--lpm@lpm.dev/<owner>.<package>, such as @lpm.dev/acme.widget.

npm-compatible targets default to public access. If the registry must restrict access, set the appropriate access field before publication.

A target flag replaces the target list in lpm.json. Without flags or a configured list, publication targets LPM.dev Registry.

.npmrc install routing does not select the publish target. Use lpm.json or a target flag instead of package.json > publishConfig for registry, access, and tag choices.

Create a package

For a new npm-compatible package, use lpm init:

mkdir widget
cd widget
lpm init --npm --yes --name @acme/widget

This creates a manifest and selects npm in lpm.json. It does not create source files or build output.

For a new LPM.dev Registry package:

lpm init --lpm --yes --owner acme --name widget

Replace acme with your account or organization slug. Explicit names avoid placeholder values in non-interactive initialization.

For an existing package, keep its manifest. Check the name, version, license, entrypoints, and published files.

package.json > private: true blocks every publication and preview. Private distribution on LPM.dev Registry is a separate access policy.

Save publication defaults

Add this configuration to the package's own lpm.json:

lpm.json
{
  "publish": {
    "registries": ["npm"],
    "npm": {
      "name": "@acme/widget",
      "access": "public",
      "tag": "latest"
    }
  }
}

For a restricted npm package, change access to restricted. npm requires a scoped name for restricted access.

For GitHub, set registries to ["github"] and add github.name and github.access. GitHub otherwise inherits the npm name and access configuration.

For GitLab, include the project ID:

lpm.json
{
  "publish": {
    "registries": ["gitlab"],
    "gitlab": {
      "projectId": "12345",
      "name": "@acme/widget",
      "access": "restricted"
    }
  }
}

For a custom registry, put its full URL in registries. Its package name, access, and tag come from the npm configuration.

Each workspace package needs its own publish configuration. Publication does not inherit these fields from the workspace root.

Authenticate the selected registry

# npm browser login
lpm login --npm

# GitHub or GitLab CLI session, environment token, or stored token
lpm login --github
lpm login --gitlab

# Custom registry: include the endpoint path
lpm login --login-registry https://registry.example.com/npm --token '<token>'

# LPM.dev Registry browser login
lpm login

Choose the command for your target. See authentication for credential sources, storage, and rotation.

Custom registries need credentials for their endpoint. A public npm credential or NPM_TOKEN does not authorize an arbitrary custom registry.

Prepare the files

For editable components or helpers that consumers copy with lpm add, see Source packages.

A typical compiled package includes its output directory:

package.json (relevant fields)
{
  "name": "@acme/widget",
  "version": "1.0.0",
  "type": "module",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "license": "MIT",
  "files": ["dist"]
}

Create the source, README, and license. Run your project's build before a local check:

lpm install
lpm run build
lpm publish --check

Use your existing build script, or configure lpm pack for a library build. Publication creates its archive from the resulting files.

files limits the archive contents. The manifest, README, license files, and required entrypoints can also enter the archive.

Without files, .npmignore takes precedence over .gitignore. Explicitly included files still enter the secret scan, even if Git ignores them.

Publication never packs .lpm/, lpm.lock, or the other committed LPM project files such as lpm-sources.json and lpm-changes/. Credential files such as .env.local and SSH keys stay out of an archive without files. If files selects one, the publish stops. See package contents for the complete lists.

To publish agent skills with an LPM.dev Registry package, put them in lpm-skills/ at the package root. They ship whatever files lists.

If the package declares bundled dependencies, install them before publication. Missing dependencies and unsafe symlinks stop preparation.

Check before uploading

CommandWhat happens
lpm publish --checkBuilds the publication archive, lists its files, and checks it locally. No lifecycle scripts, registry requests, or uploads.
lpm publish --dry-run --ignore-scriptsChecks the selected targets and credentials without lifecycle scripts or uploads.
lpm publish --dry-run --yesRuns publish lifecycle scripts and checks the selected targets without uploads.

Local checks include package configuration, final archive secret scanning, authored skills, supplied provenance files, and applicable quality checks. Review the listed files before the first upload. JSON output lists them in tarball_files.

Real publication and dry-run can run these scripts in order: prepublishOnly, prepack, prepare, postpack, publish, and postpublish.

These scripts can change local files or contact external services. --yes grants script consent as well as skipping the publication prompt.

For LPM.dev Registry, dry-run can exchange a CI identity for temporary credentials and write authentication audit records. Use --check for checks without that exchange.

Publish once

After the checks pass:

lpm publish --yes

The command uses the configured targets. For one invocation, select a target explicitly:

lpm publish --github --yes

For an LPM.dev Registry quality floor, use these commands instead of the ordinary check and publish:

lpm publish --lpm --check --min-score 80
lpm publish --lpm --yes --min-score 80 --wait

There is no default minimum score. --min-score applies only to that invocation. The Registry calculates its final report after upload.

--wait waits for the uploaded release under its current distribution rules. It does not select a distribution mode.

Publish to multiple registries

Use a name for each registry:

lpm.json
{
  "publish": {
    "registries": ["npm", "lpm"],
    "npm": { "name": "@acme/widget", "access": "public" },
    "lpm": { "name": "@lpm.dev/acme.widget" }
  }
}

Authenticate both targets, then run the check and publication sequence. Each archive receives its target name. The source manifest keeps its original name.

Publish a prerelease

Set publish.npm.tag to next before an npm-compatible prerelease:

lpm version 2.0.0-rc.1 --no-git-tag-version
lpm publish --npm --check
lpm publish --npm --yes

Ordinary lpm publish has no --tag flag. Use lpm release publish for publication across selected workspace packages.

Publish from CI

For npm, configure Trusted Publishing for the package, or provide a publish-capable NPM_TOKEN through the CI secret store.

GitHub Actions needs permissions: id-token: write. Other supported CI providers can supply NPM_ID_TOKEN with audience npm:registry.npmjs.org.

LPM CLI attempts the npm identity exchange first. If that exchange fails, it can use an available npm token.

After the build:

lpm publish --npm --check --json
lpm publish --npm --ignore-scripts --yes --json

For LPM.dev Registry, configure a trusted publisher for an existing package. GitHub Actions needs permissions: id-token: write.

lpm publish --lpm --check --json
lpm publish --lpm --ignore-scripts --yes --wait --json

GitLab CI supplies LPM_OIDC_TOKEN with audience https://lpm.dev. A dry-run consumes that supplied assertion, so a later publication needs a fresh assertion.

Add provenance

Replace the final publish command with:

lpm publish --npm --ignore-scripts --yes --provenance

Generated provenance requires a separate OIDC assertion with the Sigstore audience. GitHub Actions uses id-token: write. GitLab supplies SIGSTORE_ID_TOKEN with audience sigstore.

A provenance identity does not authenticate publication to the registry. npm-compatible provenance also requires public access.

If provenance cannot be generated, publication fails. Use --no-provenance to disable configured provenance for one run.

Choose LPM.dev distribution

New packages start in Private distribution. The dashboard can request Free, Pool, or Marketplace distribution when the account meets the requirements.

Public activation requires review. The package stays private until approval. Activated public modes cannot switch to another mode or return to Private.

See package distribution for access, eligibility, dependency restrictions, and review states.

Recover from a failed publication

  • Invalid name or target: Check the resolved names and registry configuration before another upload.
  • Secret scan failure: Remove the secret from the published files and rotate an exposed credential.
  • Version already exists: Inspect that version before choosing a new version with lpm version.
  • One target failed: Retry only that target. Successful uploads do not roll back across registries.
  • A post-publish script or publication wait failed: The upload can already exist. Inspect the registry before retrying.
  • Trusted Publisher exchange failed: Check the package's trust configuration, workflow identity, and token audience.

See also