LPM CLI

lpm add

Copy source files from a package into your project (shadcn-style).

lpm add <package>

lpm add extracts a package's source files into your project rather than installing it as a runtime dependency. Think shadcn-ui style: the code lands in your repo, you own it, you edit it.

lpm install is the runtime-dependency command. lpm add is the source-delivery command. The two are not aliases.

Works with any registry the resolver can reach โ€” LPM.dev Registry, npmjs.org, or a .npmrc-declared private registry. Anything that ships a tarball is fair game.

Try the public npm example package:

lpm add lpm-source-package

lpm-source-package is a minimal configurable source package published to npm, so it exercises the same registry-agnostic lpm add path without requiring an LPM.dev Registry package.

Like lpm install, lpm add rejects a likely typosquatted package name before any registry fetch. Intentional names use the committed policy.typosquat.allow list in lpm.toml.

When npm firewall mode is monitor or enforce, LPM CLI checks the selected source package with LPM Firewall at firewall.lpm.dev when it is a routed public npm package. LPM Firewall is an LPM.dev Registry Pro/Org feature, so active modes send LPM.dev Registry auth; run lpm login locally, or use LPM_TOKEN / registry-audience OIDC in CI. monitor warns and continues when entitlement is denied, while enforce blocks packages whose effective firewall action is block or denied by entitlement before the source tarball is downloaded or copied. In human output, the source-package download line shows ๐Ÿ”ฅ LPM Firewall active when that check is active. LPM.dev Registry packages and other .npmrc-declared private/custom registry packages are not sent to the npm firewall verdict API. Dependencies installed after a config-aware source copy use the normal install pipeline and its firewall checks. The legacy string report is still accepted as a monitor alias.

Examples

lpm add @lpm.dev/owner.ui-kit                      # add an LPM.dev Registry package
lpm add @lpm.dev/owner.ui-kit@1.2.0                # add a specific version
lpm add @lpm.dev/owner.ui-kit?component=dialog     # add only one component
lpm add lodash.merge                               # add an npm package's source
lpm add @my-co/internal --path ./src/vendor        # custom destination
lpm add my-pkg --dry-run                           # preview without writing
lpm add @lpm.dev/owner.ui-kit --no-skills          # skip package skills once

How it works

  1. Resolves the spec against the appropriate registry.
  2. Downloads the tarball into a temporary file and extracts it to a tempdir. Unlike lpm install, the tarball is not persisted to ~/.lpm/store/ โ€” lpm add is a copy-and-discard flow. Re-running lpm add re-downloads.
  3. Picks where to put the files. With --path, honors it. Without --path in a TTY (and not --yes / --json), prompts for an install directory. Non-interactive (--yes, --json, or non-TTY) requires --path for simple-path packages (no lpm.config.json); config-aware packages auto-detect from their declared ecosystem.
  4. Copies the source into the destination, prompting on conflicts.
  5. If the package declares an lpm.config.json, runs the package's configured install steps (component selection, alias rewrites, dep installation). If not, it's a plain source copy and you handle dependencies yourself.

lpm add does not auto-install bare imports it discovers in the copied source. It will surface them at the end of the run so you can add them yourself.

With the default --pm lpm, declared dependencies honor the same minimum release age policy as lpm install. A bare dependency or name@latest selects the newest version old enough to install, then saves the project range from that selected version. An author-written range such as name@^1.11.5 remains unchanged: fallback can only select older versions that still satisfy that range. If no mature version satisfies it, lpm add fails and restores the copied files, package.json, and lockfiles to their pre-add state.

Human output is structured around the source-copy flow:

โ€บ Downloading source package source-pkg@1.0.0
โ€บ Detecting project structure
    Framework:    Vite
    Install path: src/components
    Import alias: @/src/components/
โœ“ Files copied
+ Foo.tsx
โ€บ Installing declared dependencies
+ lucide-react@^0.400.0
โœ“ Done ยท added 1 file and 1 dependency in 486ms

File conflicts use a slim warning line (! File exists: ...) before the interactive choice.

Config-aware vs simple copy

A package becomes "config-aware" by shipping an lpm.config.json at the tarball root. That file declares components, dependencies to auto-install, import aliases, and editor integration hints. Without it, lpm add is a plain source copy โ€” useful for any tarball, not just LPM.dev Registry packages.

For @lpm.dev/* source packages, lpm add also reconciles package-published agent guidance into .lpm/skills/<package>/ by default. Use --no-skills to skip that step once, or lpm config lpm-skills --set false to persist the preference. --skills overrides the persistent opt-out for one run. The setting does not delete existing package skills or affect explicit lpm skills add commands; see AI agent skills.

Swift packages

For Swift packages, prefer lpm install โ€” it's the right command for SPM dependencies (resolves through the SE-0292 registry and edits Package.swift). lpm add still works for Swift sources today and respects --target for Xcode-target wiring, but consider it the legacy path for that ecosystem.

Authoring config-aware packages: lpm.config.json

Ship lpm.config.json at the root of your tarball to control how lpm add extracts your package. With the file present, lpm add becomes a configurable installer โ€” interactive prompts, conditional file copying, conditional dependency injection, import-path rewriting.

Install the lpm-guide skill if you want an agent to draft the config from your source tree:

npx skills add lpm-dev/lpm-guide

Then paste a prompt like this into your agent:

Use the lpm-guide skill's Source Config workflow. Inspect this package's source tree and generate an lpm.config.json for a configurable source package.

Identify the options consumers should choose, such as components, styling framework, variants, or optional examples. Use required fields when choices would conflict, add defaultConfig so lpm add --yes works, map files[] from the package root to the consumer install path, declare conditional dependencies, and include example lpm add commands with query params.
lpm.config.json (sketch)
{
  "$schema": "https://cli.lpm.dev/schemas/lpm.config.json",
  "ecosystem": "js",
  "importAlias": "@/",
  "configSchema": {
    "component": {
      "type": "select",
      "label": "Which component?",
      "options": ["dialog", "popover"],
      "default": "dialog",
      "required": true
    },
    "withTests": { "type": "boolean", "label": "Include tests?", "default": false }
  },
  "files": [
    { "src": "src/dialog/**", "dest": "dialog", "include": "when",
      "condition": { "component": "dialog" } },
    { "src": "tests/**", "dest": "__tests__", "include": "when",
      "condition": { "withTests": true } }
  ],
  "dependencies": {
    "withTests": { "true": ["vitest"] }
  }
}

The full schema reference โ€” every field, every value type, every default โ€” lives at lpm.config.json. The published JSON Schema at https://cli.lpm.dev/schemas/lpm.config.json drives editor autocomplete (add $schema as shown above).

Pre-answering prompts

Prompts can be pre-answered from the CLI by appending ?key=value&key=value to the package spec:

lpm add @lpm.dev/owner.ui-kit?component=dialog&styling=panda

Inline values bypass the prompt entirely. With --yes, prompts are skipped and defaultConfig / configSchema.<field>.default fill in any required fields the user didn't supply.

Behavior matrix

StateWhat lpm add does
No lpm.config.jsonPlain copy of the package's source. No prompts. No auto-install. Surfaces bare imports for you to install yourself.
lpm.config.json present, TTYPrompts for every configSchema key not already inlined; copies per files[] rules; installs dependencies matching the chosen values.
lpm.config.json present, --yes / --json / non-TTYSkips prompts; uses defaults for required fields; copies per matching rules; installs deps.

Prerequisites

If the source package declares dependencies (config-driven or via its own package.json), your project must already have a package.json. lpm add runs a preflight check before copying any files and exits early with a remediation hint if the manifest is missing โ€” there's nowhere for the dep entries to land otherwise, and we'd rather fail loudly than copy source files you can't import from.

$ lpm add @author/ui-kit
error: this source package declares dependencies, but the project has no
       `package.json` to record them in.
       Run `lpm init` (or `npm init -y`) first to create a manifest, then
       re-run `lpm add`.
       To copy the source files without installing the declared dependencies,
       pass `--no-install-deps` and resolve the imports yourself.

If you only want the source files (and you'll handle the imports yourself), pass --no-install-deps to skip the preflight and let the copy proceed without touching package.json.

Flags

FlagEffect
--path <DIR>Target directory (overrides interactive prompt and auto-detection)
-y, --yesSkip interactive prompts, use defaults
--forceOverwrite existing files without prompting
--dry-runShow what would be done without writing anything
--no-install-depsSkip dependency installation after copy (for config-aware packages)
--skillsInstall package-published LPM.dev skills for this invocation, overriding user config
--no-skillsSkip package-published LPM.dev skill auto-install for this invocation
--no-editor-setupCompatibility flag. Package skills do not create editor integrations.
--pm <NAME>Package manager for dep installation (lpm, npm, pnpm, yarn, bun, auto; default lpm)
--alias <ALIAS>Import alias prefix (e.g., @/components) โ€” overrides auto-detection
--target <NAME>Swift SPM target name (Swift packages only)
--no-engine-strictUse warning-only root checks and, when LPM installs dependencies, dependency checks (see lpm install). Root preflight runs before any manifest mutation, so a constraint violation can't leave the project half-modified.

Plus the global flags.

See also