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 field | Purpose |
|---|---|
$schema | Editor validation and autocomplete |
type | Optional LPM.dev Registry classification: package or source |
ecosystem | Source project defaults: js or the legacy swift source path |
importAlias | Alias prefix used by imports in the package source |
configSchema | Interactive fields presented to consumers |
defaultConfig | Defaults that override matching field-level defaults |
files | Source selection, destination, and conditional inclusion rules |
dependencies | Dependencies 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
}
}
}
| Property | Accepted value | Behavior |
|---|---|---|
type | string, boolean, or select | Defaults to string |
label | String | Prompt label; defaults to the field key |
default | String, boolean, or number | Initial interactive value |
required | Boolean | Makes --yes fill a missing value from its default |
multiSelect | Boolean | Allows multiple selections for a select field |
options | Strings or { value, label } objects | Choices 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"
}
]
}
| Property | Behavior |
|---|---|
src | Required source path relative to the tarball root |
dest | Destination relative to the consumer's resolved install directory |
include | always by default, when for a condition, or never |
condition | Config key/value pairs used when include is when |
Supported source patterns are:
- Exact paths such as
lib/utils.ts. - A trailing recursive
/**, such ascomponents/dialog/**. - A single-directory
*filename pattern, such asstyles/*.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.
| Limit | Maximum |
|---|---|
| Serialized config | 128 KiB |
| File rules | 1,000 |
| Options per select field | 64 |
src or dest | 512 characters |
importAlias | 200 characters |
| Labels, option values, and dependency entries | 200 characters each |
| Dependencies in one selected branch | 200 |
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
- Source Code Delivery — Understand the consumer workflow.
- Package Types — See how source delivery is detected.
- Publishing Packages — Publish privately or through reviewed public distribution.
- LPM CLI
lpm.config.jsonreference — Complete schema and command behavior. - LPM CLI
addreference — Consumer flags, registry routing, and JSON output.