LPM CLI

package.json — "lpm" key

Configure dependency layout, peer checks, lifecycle scripts, trust, and version policy in package.json.

The lpm object in package.json holds shared package-management configuration. It works with npm, private registries, and LPM.dev Registry.

package.json
{
  "name": "my-app",
  "private": true,
  "lpm": {
    "strictDeps": "strict",
    "strictPeerDependencies": true,
    "scriptPolicy": "deny"
  }
}
lpm install
lpm config list --json

Use lpm config list to inspect effective configuration and its sources. Edit package.json to change these shared values. The generic lpm config set command writes user configuration instead.

File scope and precedence

Each project manifest defines its dependencies and package policy. Workspace rules differ by field, as described below. There is no single precedence order for the whole lpm object.

FilePurpose
package.json > lpmDependency layout, resolution policy, lifecycle trust, and sandbox requests.
lpm.jsonRuntimes, development services, environments, tasks, and publishing.
lpm.tomlProject defaults for save policy, workspace execution, and related CLI behavior.
~/.lpm/config.tomlUser defaults and machine security policy.
lpm.config.jsonSource-package delivery rules for lpm add.

Security-sensitive project values require authorization if they weaken the approved machine policy. An install or rebuild then reports security_approval_required with a specific recovery command. See temporary security unlocks.

Global installs use their own manifest and user configuration. They do not inherit the current project's lpm object. Global trust lives in ~/.lpm/global/trusted-dependencies.json. See lpm global.

Project and workspace-member manifests have a 16 MiB size limit. Oversized manifests fail before dependency resolution. See local configuration limits.

Top-level keys under "lpm"

All fields are optional. Defaults below apply when no higher-priority configuration supplies a value.

FieldTypeDefault
linker"hoisted" or "isolated"Automatic.
strictDeps"strict", "warn", or "loose""warn".
autoInstallPeersBooleantrue.
strictPeerDependenciesBooleanfalse.
catalogMode"manual", "prefer", or "strict""manual".
cleanupUnusedCatalogsBooleanfalse.
scriptPolicy"deny", "allow", or "triage""deny".
triageAdvisorProvider name"none".
trustedDependenciesName array or binding objectEmpty.
minimumReleaseAgeNon-negative integer, seconds0.
minimumReleaseAgePolicy"direct" or "strict""direct".
minimumReleaseAgeExcludeSelector arrayEmpty.
engineStrictBooleantrue.
overridesSelector-to-range objectEmpty.
patchedDependenciesPackage-to-patch objectEmpty.
peerDependencyRulesPeer-rule objectEmpty.
scriptsLifecycle and sandbox configurationSee field table.
sourceRelative path stringAutomatic source-file selection.

linker

{ "lpm": { "linker": "isolated" } }
ValueLayout
"isolated"Each package sees its declared dependencies through package-local links.
"hoisted"Root direct dependencies appear in project node_modules. Package-local dependency links use shared store entries.

Single-package projects default to hoisted. Workspace projects default to isolated. The hoisted layout does not flatten every transitive dependency into the project root.

A default-hoisted install with incompatible peer requirements switches to isolated and records that decision in lpm.lock. An explicit linker value disables this automatic switch.

Precedence: --linker → user linker → LPM_LINKER → project lpm.linker → automatic default. Unknown values fail installation.

lpm install --linker isolated

See dependency layout and lpm install.

strictDeps

{ "lpm": { "strictDeps": "strict" } }

This field controls source-import checks for the manifest that the install uses. It does not change the node_modules layout.

ValueBehavior
"strict"Fail on undeclared package imports or an incomplete source scan.
"warn"Show advisory warnings after a full online install in human-readable mode.
"loose"Skip these import checks.

Strict checks run before dependency installation, including unchanged, offline, and JSON installs. Source edits trigger another check even when the manifest and lockfile remain unchanged. Unknown mode strings fail installation.

The check scans JavaScript and TypeScript files in src, app, pages, components, and lib. If none of these directories exists, it scans the project directory. It skips nested packages, declaration files, generated directories, and symlinked files.

Declarations in dependencies, devDependencies, optionalDependencies, and peerDependencies satisfy the check. Node built-ins, package self-imports, relative imports, and recognized local aliases do not require another dependency declaration. Aliases can come from TypeScript paths, baseUrl, or lpm.config.json > importAlias.

After a full online install, strict mode also warns about imports whose packages remain uninstalled. This advisory appears only in human-readable output.

This static check does not prove that every import resolves at runtime. It cannot resolve a dynamically constructed package name. If a strict check fails, correct the import or declare the dependency:

lpm install missing-package

autoInstallPeers

{ "lpm": { "autoInstallPeers": false } }

By default, LPM CLI installs missing non-optional peers automatically. Optional peers never trigger automatic installation. With false, missing peers remain diagnostics that you can resolve with explicit dependencies.

Precedence: project autoInstallPeers → user auto-install-peers → true.

strictPeerDependencies

{ "lpm": { "strictPeerDependencies": true } }

With true, missing required peers, incompatible peer versions, and peer conflicts fail installation. With false, these conditions produce warnings. An absent optional peer does not fail the check.

Precedence: --strict-peer-dependencies or --no-strict-peer-dependencies → project value → user strict-peer-dependencies → false.

lpm install --no-strict-peer-dependencies

catalogMode

{ "lpm": { "catalogMode": "prefer" } }

This field controls how lpm install <pkg> saves registry dependencies against the root default catalog.

ValueSave behavior
"manual"Save the normal range or the explicit version specification.
"prefer"Save catalog: when the default catalog matches. Otherwise, warn and save the direct specification.
"strict"Require a matching default-catalog entry before the manifest change can succeed.

Automatic catalog modes take priority over explicit registry versions, save flags, and existing registry ranges. They preserve workspace links, local paths, aliases, Git sources, and tarball URLs.

An explicit catalog request applies to one install:

lpm install react --catalog
lpm install vitest --catalog=testing

Hand-written catalog: and catalog:<name> dependencies resolve in every mode. See lpm catalog and save policy.

cleanupUnusedCatalogs

{ "lpm": { "cleanupUnusedCatalogs": true } }

After a successful install, remove catalog entries with no root or workspace-member reference. LPM CLI checks all four dependency sections, including peers and optional dependencies.

For pnpm workspaces, pnpm-workspace.yaml > cleanupUnusedCatalogs enables the same behavior. An explicit package.json > lpm.cleanupUnusedCatalogs value takes priority.

scriptPolicy

{ "lpm": { "scriptPolicy": "deny" } }

This field controls dependency lifecycle scripts for lpm install and lpm rebuild. Root-project lifecycle scripts have a separate execution contract.

ValueDependency behavior
"deny"Block scripts without individual trust or approval.
"allow"Permit all dependency lifecycle scripts and request automatic execution during install.
"triage"Evaluate scripts with security analysis, trust, provenance, and an optional advisor.

Precedence: CLI policy flag → project scriptPolicy → user script-policy → deny, subject to approved security policy. The user force-security-floor setting can suppress weaker project values and CLI overrides.

Triage approval makes a package eligible to run. An install starts automatic execution if any of these conditions applies:

  • --auto-build is set.
  • scripts.autoBuild is true.
  • All packages with unbuilt scripts are trusted.
  • The effective policy is allow.

The scripts.denyAll switch blocks execution in every policy mode. Invalid policy strings and wrongly typed lifecycle controls fail before execution, including under --json. For example, "denyAll": "true" is invalid. Use the Boolean true.

triageAdvisor

{ "lpm": { "scriptPolicy": "triage", "triageAdvisor": "codex" } }
ValueProvider
"none"No advisor. The other security checks still apply.
"claude-cli"Claude CLI.
"codex"Codex CLI.
"ollama"A local Ollama model.

The advisor is active only under the triage policy. Precedence: --advisor → project triageAdvisor → user triage-advisor → none. An unknown or unavailable provider produces a warning, and installation continues without the advisor.

Advisor verdicts can use a local cache. A cached verdict does not create a persistent trustedDependencies entry. Execution approval belongs to the current install. Packages that do not run remain available for review. Use lpm approve-scripts for durable trust.

See the optional advisor.

trustedDependencies

The legacy array grants trust by package name:

{ "lpm": { "trustedDependencies": ["esbuild", "sharp"] } }

The binding form records the package version, source integrity, and lifecycle-script hash:

{
  "lpm": {
    "trustedDependencies": {
      "esbuild@0.25.1": {
        "integrity": "sha512-...",
        "scriptHash": "sha256-..."
      }
    }
  }
}

A changed binding requires another review. Trust does not bypass sandbox-capability approval or scripts.denyAll. Manage bindings with lpm approve-scripts and inspect changes with lpm trust diff.

A top-level trustedDependencies field outside lpm does not grant lifecycle trust.

minimumReleaseAge

{ "lpm": { "minimumReleaseAge": 86400 } }

The cooldown delays installation of new package versions. The unit is seconds. 0 disables the cooldown. A range can select an older eligible version. An exact pin fails during its cooldown. The selected version does not exceed the current dist-tags.latest target in semantic-version order.

Precedence: CLI → project value → user minimum-release-age-secs → 0, subject to approved security policy.

For an authorized one-run bypass:

lpm install --allow-new

minimumReleaseAgePolicy

{ "lpm": { "minimumReleaseAgePolicy": "strict" } }
ValueScope
"direct"Direct dependencies.
"strict"Direct and transitive dependencies, including lockfile replay.

Strict replay checks use saved publication timestamps when available. Precedence: project value → user release-age-policy → direct, subject to approved security policy.

minimumReleaseAgeExclude

{ "lpm": { "minimumReleaseAgeExclude": ["react", "@company/*", "react@19.1.0"] } }

Accepted selectors are package names, scope wildcards, and exact package versions. Ranges such as react@^19 and protocol values such as npm:react are invalid. Aliases match their canonical package name.

CLI, project, and user lists combine in that order. Duplicate selectors appear once. Manage the project list with lpm trust release-age-exclude.

engineStrict

{ "lpm": { "engineStrict": true } }

The workspace-root value controls engine checks. Member values do not replace it. LPM CLI checks root engines.lpm and engines.node, plus selected dependencies' Node requirements during installation.

A required dependency with an incompatible Node requirement fails installation. A package reachable only through optional dependencies is omitted instead. With false, incompatible dependencies remain installed and human-readable output shows warnings.

Precedence: --no-engine-strict → root project value → user engine-strict → true. See Node version pinning.

overrides

{
  "lpm": {
    "overrides": {
      "lodash": "^4.17.21",
      "react@<18": "18.0.0",
      "baz>foo@1": "2.0.0"
    }
  }
}
SelectorMatches
fooEvery instance of foo.
foo@<1.0.0Natural selections in that range.
baz>foofoo dependencies of baz.
baz>foo@1Matching foo versions reached through baz.

The target replaces the matching consumer's declared range. An exact target pins one version. A range selects the newest eligible version in that range. Security and platform checks still apply.

Root entries apply to every workspace member. Member entries extend the same field, with member values winning on duplicate selectors. Across fields, precedence is lpm.overrides → top-level overrides → resolutions.

Targets must be strings. Nested npm override objects produce warnings and do not apply. Multi-segment selectors such as a>b>c fail installation.

Plain and unqualified parent selectors can rescue a range with no available version. Range-qualified selectors require a natural candidate. They do not match a replacement from another override.

If override selection fails, LPM CLI warns and retains the natural selection. A required dependency fails if neither selection is available. Later engine checks can also fail or omit a selected package.

Summaries include overrides that change a version. A rescued dependency shows unresolved in text and from_version: null in JSON. See resolver overrides.

patchedDependencies

{
  "lpm": {
    "patchedDependencies": {
      "lodash@4.17.21": {
        "path": "patches/lodash@4.17.21.patch",
        "originalIntegrity": "sha512-..."
      }
    }
  }
}

Each key uses an exact package version. path is relative to the manifest that declares it. lpm patch and lpm patch-commit create these entries.

Patches apply after dependency linking and before lifecycle scripts. Installs check the patch checksum from lpm.lock and the selected package's source integrity. A mismatch stops installation. These checks do not detect an upstream republish until an install selects the changed source.

Deliberately omitted dependencies retain their patch registrations. A registration absent from the dependency graph produces an error. Patches cannot change dependency resolution or repair a peer conflict that prevents installation.

peerDependencyRules

{
  "lpm": {
    "peerDependencyRules": {
      "ignoreMissing": ["fsevents"],
      "allowedVersions": { "card>react": "18 || 19" },
      "allowAny": ["@babel/*"]
    }
  }
}
FieldTypeEffect
ignoreMissingName or glob arraySuppress matching missing-peer diagnostics.
allowedVersionsSelector-to-range objectAccept this range in addition to the peer's declared range.
allowAnyName or glob arrayAccept any installed version. Missing-peer diagnostics still apply.

allowedVersions selector grammar

SelectorMatches
reactThe peer named react for any consumer.
@scope/fooA scoped peer for any consumer.
foo>reactThe react peer of foo.
foo@^2>reactThe react peer of a matching foo version.
@scope/foo@^2>reactThe same rule with a scoped consumer.

Multi-segment paths, standalone peer-version qualifiers such as foo@2, and globs are invalid in allowedVersions. Invalid selectors and ranges fail installation. lpm migrate translates supported pnpm peer rules and checks the same selector grammar. lpm doctor --json reports later pnpm/LPM CLI rule differences as pnpm_peer_rules_drift.

The lpm.scripts block

{
  "lpm": {
    "scripts": {
      "autoBuild": true,
      "denyAll": false,
      "trustedScopes": [],
      "passEnv": [],
      "readProject": "narrow",
      "sandboxWriteDirs": ["build/"]
    }
  }
}

These fields control dependency lifecycle scripts and their requested sandbox access.

FieldType and defaultEffect
autoBuildBoolean, falseRequest automatic execution of trusted scripts. A lifecycle failure fails installation.
denyAllBoolean, falseBlock every dependency lifecycle script, including trusted scripts.
trustedScopesString array, emptyTrust every package version and script in the listed lowercase npm scopes.
passEnvString array, emptyRequest additional environment variables. Reserved runtime variables are invalid.
readProject"narrow" or "full", "narrow"Narrow reads permit dependency directories and build metadata. Full reads include project secrets and the project's .lpm directory.
sandboxLimitsObject, emptyRequest resource limits with non-negative integer values.
sandboxWriteDirsString array, emptyRequest additional writable directories.
sandboxReadAllowString array, emptyRequest read access to individual files inside the project.

sandboxLimits accepts RLIMIT_AS, RLIMIT_NPROC, RLIMIT_NOFILE, and RLIMIT_CPU. Limits outside the approved baseline require review. Raw user-configured limits do not grant that authority.

Relative write directories stay inside the canonical project root. External absolute paths require a covering user max-sandbox-write-roots entry and approval. Dangerous roots remain forbidden. Symlinks, junctions, and reparse points below an authorized root are rejected. The project root itself can be a symlink to its canonical directory.

Entries inside the project's .lpm directory or the LPM home, and entries above the project, are always rejected. The project's .lpm directory holds LPM CLI state, such as certificates, env sync state, and recovery backups. Its package directories, .lpm/wrappers/ and .lpm/hoisted/nested/, are readable to scripts, and a script can write its own package's directory there without configuration. With readProject: "narrow", those directories are also the only parts of .lpm that scripts can read.

sandboxReadAllow combines with user script-read-allow entries. Entries inside the project's .lpm directory are rejected. Directories, symlinks, and paths outside the project are invalid. Changes to requested access require another approval through lpm approve-scripts.

Manage scope trust with lpm trust lifecycle-scope. An added scope requires signed trust-scope-widen authorization. Removal also removes that local authorization. For individual packages, prefer version and integrity bindings.

source

{ "lpm": { "source": "src/components" } }

This legacy field selects a file or directory for source-package delivery with lpm add. It applies when lpm.config.json does not provide files rules. The path must stay inside the extracted package. Directory contents keep paths relative to that directory. For new packages, use lpm.config.json.

These fields belong directly in package.json, outside lpm.

workspaces

{ "workspaces": ["packages/*", "apps/*"] }

The object form is also accepted:

{ "workspaces": { "packages": ["packages/*"] } }

See workspaces.

engines

{ "engines": { "node": ">=22.0.0", "lpm": ">=0.78.0" } }

engines.node checks compatibility. It does not select or install Node. Runtime selection uses lpm.json > runtime.node, .nvmrc, or .node-version, then the Node on PATH. A missing required runtime produces an error with a selection hint.

LPM CLI also checks the root engines.lpm value against its own version. Other manager keys, including npm, pnpm, yarn, and bun, produce warnings and are not enforced. Use lpm.json > runtime.bun to select a managed Bun runtime.

overrides / resolutions

These compatibility fields use the supported override rules. Root entries apply to workspace members, and same-field member values take priority.

scripts, bin, dependencies, devDependencies, peerDependencies, optionalDependencies

LPM CLI reads these standard npm fields for tasks, executable links, and dependency resolution. See npm compatibility for supported behavior.

catalogs

{
  "catalogs": {
    "default": { "react": "^19.0.0" },
    "testing": { "vitest": "^3.0.0" }
  }
}

Members reference these ranges with catalog: or catalog:<name>. LPM CLI also reads pnpm-workspace.yaml > catalog and catalogs. On duplicate entries, root package.json > catalogs takes priority.

Catalog values must be package ranges. A catalog cannot reference another catalog. See lpm catalog.

Validation and recovery

lpm doctor --all --json
lpm config list --json
lpm install --json

lpm doctor reports configuration and compatibility problems. The command that consumes a field performs its behavior-specific checks. Invalid lifecycle controls fail instead of inheriting a more permissive policy.

If a strict import check fails, add the missing declaration or correct the import. If a security approval is required, review the requested scope before you use the reported unlock command. If a peer check fails, correct the dependency ranges or define a specific peer rule.

See also