# npm compatibility (/docs/packages/npm-compatibility)



LPM CLI is **drop-in compatible** with the npm ecosystem. Reads `package.json`. Resolves npm-flavored semver. Materializes `node_modules/`. Installs from `registry.npmjs.org` by default. Honors `.npmrc` for routing and auth. Forwards subprocess exit codes. Almost any npm-shaped project works without conversion — `lpm install` produces a runnable tree.

This page is the explicit scope: what carries over verbatim, what's different, and what LPM CLI adds.

## Carries over verbatim [#carries-over-verbatim]

| Surface                                                                                         | Status                                                                                                                                                                                                                                                                                                                                                                                                                            |   |                                                                                 |
| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | ------------------------------------------------------------------------------- |
| `package.json > dependencies` / `devDependencies` / `peerDependencies` / `optionalDependencies` | Read identically                                                                                                                                                                                                                                                                                                                                                                                                                  |   |                                                                                 |
| `package.json > scripts`                                                                        | Run via `lpm run <name>` (or bare `lpm <name>` as shorthand)                                                                                                                                                                                                                                                                                                                                                                      |   |                                                                                 |
| `package.json > bin` (string or map form)                                                       | Both shapes accepted                                                                                                                                                                                                                                                                                                                                                                                                              |   |                                                                                 |
| `package.json > files`                                                                          | Honored at publish time                                                                                                                                                                                                                                                                                                                                                                                                           |   |                                                                                 |
| `package.json > engines.node`                                                                   | Honored as a root pin source and enforced for both the root and selected dependency versions; required mismatches abort and optional-only incompatible packages are skipped                                                                                                                                                                                                                                                       |   |                                                                                 |
| `package.json > engines.lpm`                                                                    | Enforced against the running CLI version (npm only enforces with `engine-strict=true`; LPM CLI enforces by default)                                                                                                                                                                                                                                                                                                               |   |                                                                                 |
| `package.json > workspaces` (array or object form)                                              | Both shapes accepted                                                                                                                                                                                                                                                                                                                                                                                                              |   |                                                                                 |
| `package.json > overrides`                                                                      | npm-style overrides honored (lower precedence than `lpm.overrides`)                                                                                                                                                                                                                                                                                                                                                               |   |                                                                                 |
| `package.json > resolutions`                                                                    | yarn-style resolutions honored                                                                                                                                                                                                                                                                                                                                                                                                    |   |                                                                                 |
| `package.json > catalogs` and `pnpm-workspace.yaml > catalog` / `catalogs`                      | Centralized version catalogs                                                                                                                                                                                                                                                                                                                                                                                                      |   |                                                                                 |
| `.npmrc`                                                                                        | Read for `registry=`, `@scope:registry=`, and `//host/:_authToken=` lines                                                                                                                                                                                                                                                                                                                                                         |   |                                                                                 |
| Semver dialect                                                                                  | `^`, `~`, \`                                                                                                                                                                                                                                                                                                                                                                                                                      |   | `, `\*`, `x`, hyphen ranges, prereleases, dist-tags (`latest`, `beta`, `next\`) |
| Lifecycle script names                                                                          | Bare root installs run `pnpm:devPreinstall`, then `preinstall`, `install`, `postinstall`, `preprepare`, `prepare`, and `postprepare`. Dependency packages execute only `preinstall`, `install`, and `postinstall` through the trust-gated rebuild path (see [Differences](#differences)); dependency `prepare`, `prepublishOnly`, `preuninstall`, `uninstall`, and `postuninstall` are recognized for detection but not executed. |   |                                                                                 |

If a project's only contract with npm is "I have a `package.json` with deps and scripts," that project works unchanged.

## Differences [#differences]

The differences are deliberate — LPM CLI defaults differ where npm's defaults aren't safe, and a handful of legacy npm behaviors are simplified.

### Dependency lifecycle scripts: deny by default [#dependency-lifecycle-scripts-deny-by-default]

npm runs `preinstall` / `install` / `postinstall` for every dependency package by default. LPM CLI **doesn't**. Bare `lpm install` still runs the root project's install lifecycle, including `prepare`; dependency package scripts stay behind the approval gate.

```bash
lpm install        # root lifecycle + downloads + links; dependency scripts blocked
lpm rebuild        # manually runs scripts for trusted packages
```

To run dependency scripts during install:

```bash
lpm install --policy=allow      # or --yolo
```

To approve packages for the default deny path: [`lpm approve-scripts`](/docs/packages/approve-scripts) walks the blocked set interactively.

This is the single biggest behavioral difference. See [Security & audit](/docs/packages/security-audit) for why.

### `node_modules` layout: hoisted by default, isolated for workspaces and peer conflicts [#node_modules-layout-hoisted-by-default-isolated-for-workspaces-and-peer-conflicts]

LPM CLI starts single-package projects in its v2 hoisted virtual-store layout: declared root deps are exposed at project `node_modules/<dep>`, and package-local dependency links live inside shared store link entries. This is compatible with normal Node resolution, but it is not npm-v3-style "flatten every transitive to the project root." **Workspaces auto-flip to isolated** (pnpm-style symlinks into the global virtual store) — phantom-dep bugs are most expensive in monorepos, so the safer layout becomes the default there. If a default-hoisted resolve finds incompatible peer requirements, LPM CLI also auto-switches that project to isolated and records the decision in `lpm.lock`. Force a specific mode:

```bash
lpm install --linker=isolated      # opt into pnpm-style for non-workspaces
lpm install --linker=hoisted       # use LPM CLI's hoisted virtual-store layout
```

Or persist it in `package.json > lpm > linker` / `~/.lpm/config.toml > linker`. Explicit linker settings opt out of peer-conflict auto-switching.

Under isolated layout, code that imports a dep it didn't declare in `dependencies` (phantom-dep access) breaks. npm's hoisting silently allowed it; isolated doesn't. Add the missing entries to `dependencies` — the fix is one-line per package.

### Lockfile: `lpm.lock` + optional `lpm.lockb` [#lockfile-lpmlock--optional-lpmlockb]

npm produces `package-lock.json`. LPM CLI always produces `lpm.lock` (TOML, git-diffable, authoritative). It also produces `lpm.lockb` when the current binary wire format can represent the graph. Commit `lpm.lock` always; commit `lpm.lockb` when it exists.

Convert with:

```bash
lpm migrate
```

See [Migrating from npm](/docs/guides/migrating-from-npm).

### Save policy: never `*` [#save-policy-never-]

npm's `npm install <pkg>` saves `^resolvedVersion`. LPM CLI does the same — but LPM CLI also enforces that `*` can never become a default. Wildcards must be requested per-package via `pkg@*`. See [Save policy](/docs/packages/save-policy).

### Cooldown on recent versions [#cooldown-on-recent-versions]

LPM CLI blocks direct/root dependency versions published less than 24h ago by default. npm doesn't. Override with `lpm install --allow-new`, or opt into strict transitive cooldown with `minimumReleaseAgePolicy: "strict"`. See [Recently published packages](/docs/packages/install#recently-published-packages).

### `engines` enforced by default [#engines-enforced-by-default]

npm reads `engines.node` / `engines.npm` and only enforces them when `engine-strict=true` is set in `.npmrc`. LPM CLI enforces by default: workspace-root `engines.lpm` and `engines.node` mismatches abort, as do incompatible required dependencies. An incompatible package reachable only through optional edges is skipped. Dependency Node ranges are stored in `lpm.lock` and revalidated on warm, frozen, and offline installs.

```bash
lpm install --no-engine-strict     # warning-only for this invocation
```

Or persistent in `package.json > lpm > engineStrict = false` (per-project) or `~/.lpm/config.toml > engine-strict = false` (per-user). See [`lpm install` § engines enforcement](/docs/packages/install#engines-enforcement).

### `npm publish --otp` → `lpm publish` with `otpRequired` [#npm-publish---otp--lpm-publish-with-otprequired]

npm prompts for OTP at publish time when 2FA is enabled. LPM CLI mirrors this; configure via `lpm.json > publish.npm.otpRequired = true` to prompt up-front (saves a round-trip). See [`lpm.json` publish.npm](/docs/reference/lpm-json#publishnpm).

## What LPM CLI adds [#what-lpm-cli-adds]

Things LPM CLI offers on top of the npm-compatible baseline:

| Feature                                                                               | Where                                                                                                                           |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Strict trust binding for lifecycle scripts (`{name, version, integrity, scriptHash}`) | [`lpm approve-scripts`](/docs/packages/approve-scripts)                                                                         |
| Behavioral analysis tags                                                              | [`lpm audit`](/docs/packages/audit), [`lpm query`](/docs/packages/query)                                                        |
| Registry signature audit and optional install-time verification                       | [`lpm audit signatures`](/docs/packages/audit#registry-signatures), [`lpm config signatures`](/docs/infra/config#setup-wizards) |
| npm publish-trust and verified-provenance no-downgrade policy                         | [`lpm config trust-policy`](/docs/infra/config#setup-wizards)                                                                   |
| Provenance drift detection                                                            | [Security & audit § Layer 4](/docs/packages/security-audit#layer-4-provenance--cooldown)                                        |
| Triage gate                                                                           | [Security & audit § Layer 5](/docs/packages/security-audit#layer-5-triage)                                                      |
| Local patches with integrity binding                                                  | [`lpm patch`](/docs/packages/patch)                                                                                             |
| Dual-format lockfile                                                                  | [Lockfile](/docs/packages/lockfile)                                                                                             |
| Workspace `--affected` + topology-aware caching                                       | [Workspaces](/docs/packages/workspaces), [Task runner](/docs/dev/task-runner)                                                   |
| `workspace:*` protocol                                                                | [Workspaces](/docs/packages/workspaces#workspace-protocol)                                                                      |
| Catalog protocol                                                                      | [Workspaces](/docs/packages/workspaces#catalogs)                                                                                |
| Native `jsr:` dependency specs                                                        | [`lpm install`](/docs/packages/install#jsr-dependencies), [Registries](/docs/registries#jsr-packages)                           |
| Path-selector overrides (`baz>foo`)                                                   | [`package.json` lpm.overrides](/docs/reference/package-json-lpm#overrides)                                                      |
| Multi-registry routing via `.npmrc` (LPM.dev Registry + npm + private in one project) | [Registries](/docs/registries)                                                                                                  |
| Source delivery (`lpm add`)                                                           | [`lpm add`](/docs/packages/add)                                                                                                 |
| Built-in lazy-downloaded tools (oxlint, biome)                                        | [`lpm lint`](/docs/dev/lint), [`lpm fmt`](/docs/dev/fmt)                                                                        |
| Zero-config dev server with HTTPS, tunnel, services                                   | [`lpm dev`](/docs/dev/dev)                                                                                                      |
| SE-0292 Swift Package Registry support                                                | [Swift Package Registry](/docs/packages/swift-package-registry)                                                                 |

## What LPM CLI does not do [#what-lpm-cli-does-not-do]

Honest scope:

* **Lockfile-only PR review** — no first-class diff tool yet beyond `git diff lpm.lock`. Use the deterministic format and your PR review tool of choice.
* **Reading the `pnpm.*` namespace at install time** — plain `lpm install` does not consume `pnpm.overrides`, `pnpm.patchedDependencies`, or `pnpm.peerDependencyRules`. `lpm migrate` translates all three into their `lpm.*` equivalents in one pass; install-time warnings (and stable `lpm doctor --json` codes `pnpm_overrides_drift`, `pnpm_patches_drift`, `pnpm_peer_rules_drift`) flag any post-migration drift. See [Migrating from pnpm](/docs/guides/migrating-from-pnpm).
* **Yarn berry's `.pnp.cjs`** — no Plug'n'Play support. LPM CLI materializes `node_modules/` (isolated or hoisted).
* **GUI** — there's no `lpm-vault`-style desktop app shipped today. The CLI is the surface.

## Compatibility with the broader ecosystem [#compatibility-with-the-broader-ecosystem]

* **Tools that read `package.json`** (TypeScript, ESLint, build tools): work unchanged.
* **Tools that walk `node_modules/`** (most bundlers, test runners): work under both isolated and hoisted layouts.
* **Tools that hard-code `package-lock.json`**: don't see LPM CLI's lockfile. Either run `lpm migrate` to add `lpm.lock` alongside, or use `lpm install --linker=hoisted` for a closer-to-npm shape.

## See also [#see-also]

* [`lpm migrate`](/docs/packages/migrate) — convert an npm/pnpm/yarn/bun project
* [Migrating from npm](/docs/guides/migrating-from-npm) — walkthrough
* [Registries](/docs/registries) — `.npmrc` routing
* [Save policy](/docs/packages/save-policy) — npm-compatible defaults with stricter rails
* [Security & audit](/docs/packages/security-audit) — what's different about LPM CLI's trust model
