Source configuration

lpm.config.json describes how LPM CLI delivers a source package with lpm add. It lives at the published tarball root, alongside package.json.

my-package/
├── package.json
├── lpm.config.json
├── components/
└── lib/

Publishing a valid config activates the configured lpm add path for that version, so its package page shows lpm add. The optional type field independently controls LPM.dev Registry classification.

Start with the schema

Add the canonical LPM CLI schema for editor validation and autocomplete:

{
  "$schema": "https://cli.lpm.dev/schemas/lpm.config.json",
  "type": "source",
  "ecosystem": "js"
}

The optional top-level type field controls LPM.dev Registry classification. Supported values are package and source; omission retains Registry inference from the published contents. Classification does not disable the config: any valid lpm.config.json still activates LPM CLI's configured lpm add path, including when type is package.

Top-level fieldPurpose
$schemaEditor validation and autocomplete
typeOptional LPM.dev Registry classification: package or source
ecosystemSource project defaults: js or the legacy swift source path
importAliasAlias prefix used by imports in the package source
configSchemaInteractive fields presented to consumers
defaultConfigDefaults that override matching field-level defaults
filesSource selection, destination, and conditional inclusion rules
dependenciesDependencies selected from consumer configuration

The detailed machine-readable schema and CLI-only behavior live in the LPM CLI lpm.config.json reference.

Consumer configuration

An interactive lpm add asks for each configured field that the consumer did not supply:

lpm add @lpm.dev/acme.ui-kit

Consumers can pre-answer fields in the package spec:

lpm add "@lpm.dev/acme.ui-kit?component=dialog,button&styling=panda"

Inline values skip their prompts. Multi-select values are comma-separated.

With --yes, only missing fields marked required receive defaults. Optional fields remain absent, which preserves include-all behavior for optional component filters.

Config fields

Each configSchema key defines a consumer-facing field:

{
  "configSchema": {
    "name": {
      "type": "string",
      "label": "Component name",
      "default": "Example"
    },
    "styling": {
      "type": "select",
      "label": "Styling framework",
      "required": true,
      "default": "panda",
      "options": [
        { "value": "panda", "label": "Panda CSS" },
        { "value": "tailwind", "label": "Tailwind CSS" }
      ]
    },
    "withTests": {
      "type": "boolean",
      "label": "Include tests?",
      "default": false
    }
  }
}
PropertyAccepted valueBehavior
typestring, boolean, or selectDefaults to string
labelStringPrompt label; defaults to the field key
defaultString, boolean, or numberInitial interactive value
requiredBooleanMakes --yes fill a missing value from its default
multiSelectBooleanAllows multiple selections for a select field
optionsStrings or { value, label } objectsChoices for a select field

Give every select field at least one option. An empty or missing option list causes LPM CLI to skip that interactive field.

Pair each required field with a usable default for non-interactive installation.

Default precedence

defaultConfig overrides the default declared inside a matching field:

{
  "configSchema": {
    "styling": {
      "type": "select",
      "required": true,
      "default": "tailwind",
      "options": ["panda", "tailwind"]
    }
  },
  "defaultConfig": {
    "styling": "panda"
  }
}

In this example, the interactive initial value and --yes value are both panda.

File rules

The files array selects source paths and maps them into the consumer's chosen destination:

{
  "files": [
    {
      "src": "components/dialog/**",
      "dest": "components/ui/dialog",
      "include": "when",
      "condition": { "component": "dialog" }
    },
    {
      "src": "styles/*.css",
      "dest": "styles/",
      "include": "always"
    },
    {
      "src": "internal/test-utils.ts",
      "include": "never"
    }
  ]
}
PropertyBehavior
srcRequired source path relative to the tarball root
destDestination relative to the consumer's resolved install directory
includealways by default, when for a condition, or never
conditionConfig key/value pairs used when include is when

Supported source patterns are:

  • Exact paths such as lib/utils.ts.
  • A trailing recursive /**, such as components/dialog/**.
  • A single-directory * filename pattern, such as styles/*.css.

When dest ends in /, LPM CLI preserves each source filename. With multiple matched files and a destination without the trailing slash, it preserves their structure relative to the source-pattern base beneath that destination.

Omitting files uses the package's normal source fallback. An explicit empty files array selects no files and causes lpm add to stop.

Conditional inclusion

Every condition entry must match for a rule to be selected. For multi-select configuration, an expected value matches when it appears in the comma-separated consumer value.

If a condition key is absent from the consumer configuration, that key does not exclude the file. This makes optional component filters include all components by default:

# component absent: include every component rule
lpm add "@lpm.dev/acme.ui-kit?styling=panda"

# include only dialog rules
lpm add "@lpm.dev/acme.ui-kit?component=dialog&styling=panda"

Use a required field with a default when mutually exclusive files would otherwise target the same destination.

Import aliases

importAlias declares the prefix used by the author:

{
  "importAlias": "@/"
}

LPM CLI maps internal author-alias imports to the consumer alias obtained from --alias, tsconfig.json, jsconfig.json, or the interactive prompt:

// Package source
import { cn } from "@/lib/cn"

// Delivered source
import { cn } from "@/components/ui/lib/cn"

Relative imports between delivered files can also be rewritten when the consumer supplies an alias, even if importAlias is omitted. External imports remain unchanged.

Conditional dependencies

The outer key matches a configuration field. The inner key matches a selected value:

{
  "dependencies": {
    "styling": {
      "panda": ["@pandacss/dev"],
      "tailwind": ["tailwindcss", "autoprefixer"]
    },
    "iconLibrary": {
      "lucide": ["lucide-react"],
      "internal": ["@lpm.dev/acme.icons@^2"]
    }
  }
}

LPM CLI installs matching dependencies through the consumer's selected package manager. npm packages, LPM.dev Registry packages, and .npmrc-configured private packages use the same flow.

Bare names and dist-tags resolve against their registry before the consumer manifest is changed. Explicit versions and ranges are preserved. See the LPM CLI schema reference for save-policy details.

If dependencies is omitted, LPM CLI falls back to the source package's package.json > dependencies and peerDependencies. Declaring the field disables that fallback, even when the consumer's choices match no branch.

Complete example

{
  "$schema": "https://cli.lpm.dev/schemas/lpm.config.json",
  "ecosystem": "js",
  "importAlias": "@/",
  "configSchema": {
    "component": {
      "type": "select",
      "label": "Components",
      "multiSelect": true,
      "options": ["dialog", "button"]
    },
    "styling": {
      "type": "select",
      "label": "Styling framework",
      "required": true,
      "options": ["panda", "tailwind"]
    },
    "withTests": {
      "type": "boolean",
      "label": "Include tests?",
      "required": true
    }
  },
  "defaultConfig": {
    "styling": "panda",
    "withTests": false
  },
  "files": [
    {
      "src": "components/dialog/**",
      "dest": "components/ui/dialog",
      "include": "when",
      "condition": { "component": "dialog" }
    },
    {
      "src": "components/button/**",
      "dest": "components/ui/button",
      "include": "when",
      "condition": { "component": "button" }
    },
    {
      "src": "styles/panda.css",
      "dest": "styles/theme.css",
      "include": "when",
      "condition": { "styling": "panda" }
    },
    {
      "src": "styles/tailwind.css",
      "dest": "styles/theme.css",
      "include": "when",
      "condition": { "styling": "tailwind" }
    },
    {
      "src": "tests/**",
      "dest": "__tests__",
      "include": "when",
      "condition": { "withTests": true }
    }
  ],
  "dependencies": {
    "styling": {
      "panda": ["@pandacss/dev"],
      "tailwind": ["tailwindcss"]
    },
    "withTests": {
      "true": ["vitest"]
    }
  }
}

With no component value, both component rules are included. styling and withTests receive defaultConfig values under --yes.

LPM.dev Registry validation

LPM.dev Registry validates lpm.config.json before accepting a publish. The serialized configuration has a 128 KiB limit.

LimitMaximum
Serialized config128 KiB
File rules1,000
Options per select field64
src or dest512 characters
importAlias200 characters
Labels, option values, and dependency entries200 characters each
Dependencies in one selected branch200

The Registry rejects malformed field and rule shapes. On the consumer side, LPM CLI also refuses destination paths that are absolute, contain parent-directory traversal, or escape through existing symlinks.

Use the canonical schema during authoring. A local publish preview remains useful for the package's other checks:

lpm publish --check

lpm publish --check does not reproduce LPM.dev Registry's config size and schema validation. The Registry performs those authoritative checks during upload.

Optional authoring help

The LPM Guide skill can draft lpm.config.json from a package source tree:

npx skills add lpm-dev/lpm-guide

Review generated output against the canonical schema and the delivery behavior described above before publishing.

See also