Source Configuration

The lpm.config.json file makes source packages configurable. It controls how lpm add filters and delivers source files to consumers.

Tip: The LPM Guide skill can generate lpm.config.json for you. Install it with npx skills add lpm-dev/lpm-guide, then ask your AI coding agent to "generate lpm config" - it will analyze your package structure and create the right configuration automatically.

Place it alongside your package.json:

my-package/
├── package.json
├── lpm.config.json
├── components/
│   ├── dialog/
│   └── button/
├── styles/
│   ├── panda.config.js
│   └── tailwind.config.js
└── lib/
    └── utils.js

Consumer Experience

When someone runs lpm add, the CLI reads your config and prompts interactively:

# Interactive — prompts for each option
lpm add @lpm.dev/acme.ui-kit

# URL params — pre-configured, skips prompts
lpm add "@lpm.dev/acme.ui-kit?component=dialog,button&styling=panda"

# Use defaults, skip all prompts
lpm add @lpm.dev/acme.ui-kit --yes

Config Schema

Define interactive prompts with configSchema. Each key becomes a config parameter.

{
  "configSchema": {
    "component": {
      "type": "select",
      "multiSelect": true,
      "label": "Which components do you want?",
      "options": [
        { "value": "dialog", "label": "Dialog" },
        { "value": "button", "label": "Button" },
        { "value": "tabs", "label": "Tabs" }
      ]
    },
    "styling": {
      "type": "select",
      "required": true,
      "label": "Styling framework",
      "options": [
        { "value": "panda", "label": "Panda CSS" },
        { "value": "tailwind", "label": "Tailwind CSS" }
      ],
      "default": "panda"
    },
    "darkMode": {
      "type": "boolean",
      "label": "Include dark mode styles?",
      "default": true
    }
  }
}

Field Types

TypeUIValue format
selectSingle-choice dropdown"panda"
select + multiSelect: trueCheckbox list"dialog,button"
booleanYes/No"true" / "false"

Schema Properties

PropertyTypeDescription
type"select" or "boolean"Field type
labelstringDisplay label for the prompt
optionsarrayFor select type. Array of { value, label } objects or plain strings
multiSelectbooleanShow checkbox UI instead of radio
requiredbooleanConsumer must provide a value. Prevents include-all behavior
defaultanyDefault value when consumer doesn't choose

File Rules

The files array controls which files get copied and where.

{
  "files": [
    {
      "src": "components/dialog/**",
      "dest": "components/ui/dialog/",
      "include": "when",
      "condition": { "component": "dialog" }
    },
    {
      "src": "lib/utils.js",
      "dest": "lib/utils.js",
      "include": "always"
    },
    {
      "src": "internal/test-utils.js",
      "include": "never"
    }
  ]
}

File Rule Properties

PropertyTypeDescription
srcstringSource path relative to package root. Supports ** glob
deststringDestination path. If omitted, uses src. If ends with /, preserves filename
includestring"always" (default), "never", or "when"
conditionobjectRequired when include is "when". Key-value pairs with AND logic

Include Behavior

ValueBehavior
alwaysAlways included (default)
neverNever included - exclude internal/test files
whenIncluded only when condition matches

Include-All by Default

When a condition key is not provided by the consumer (not in URL params, not answered in prompts), the file is included regardless of the condition value:

# No component filter → ALL components installed
lpm add "@lpm.dev/acme.kit?styling=panda"

# Only dialog installed
lpm add "@lpm.dev/acme.kit?component=dialog&styling=panda"

# Dialog + button installed
lpm add "@lpm.dev/acme.kit?component=dialog,button&styling=panda"

Use required: true on fields where include-all doesn't make sense (e.g., styling where panda and tailwind configs would conflict at the same destination path).

Import Alias

The importAlias field enables smart import rewriting. Declare the alias prefix you use during development:

{
  "importAlias": "@/"
}

How It Works

When you develop your package, you write imports naturally - relative or with your configured alias:

// Author writes (with @/ alias configured in tsconfig):
import { cn } from "@/lib/cn"
import Icon from "@/atoms/Icon"

When a consumer runs lpm add, the CLI prompts for their import alias:

? Import alias for this directory? @/components/design-system

The CLI rewrites every internal import to use the consumer's alias:

// Consumer gets:
import { cn } from "@/components/design-system/lib/cn"
import Icon from "@/components/design-system/atoms/Icon"

External imports (react, next/link, @radix-ui/dialog) are never touched.

Without importAlias

If you don't set importAlias, the CLI still asks the consumer for their alias. Relative imports between internal files are rewritten to use the consumer's alias when provided. If the consumer skips the alias prompt, relative imports are left as-is.

When to Use

  • Use when your package has multiple files that import each other
  • Use when you use alias imports (like @/) during development
  • Not needed for single-file packages or packages with no internal cross-references

Conditional Dependencies

Declare npm dependencies that should be installed based on config choices:

{
  "dependencies": {
    "styling": {
      "panda": ["@pandacss/dev"],
      "tailwind": ["tailwindcss", "autoprefixer"]
    },
    "iconLibrary": {
      "lucide": ["lucide-react"],
      "heroicons": ["@heroicons/react"]
    }
  }
}

Regular npm packages are installed via the detected package manager. @lpm.dev/ packages are shown as manual install commands.

Default Config

Provide defaults so the --yes flag works without prompts:

{
  "defaultConfig": {
    "styling": "panda",
    "baseColor": "neutral"
  }
}

Full Example

A complete lpm.config.json:

{
  "importAlias": "@/",
  "configSchema": {
    "component": {
      "type": "select",
      "multiSelect": true,
      "label": "Components",
      "options": [
        { "value": "dialog", "label": "Dialog" },
        { "value": "button", "label": "Button" }
      ]
    },
    "styling": {
      "type": "select",
      "required": true,
      "label": "Styling Framework",
      "options": [
        { "value": "panda", "label": "Panda CSS" },
        { "value": "tailwind", "label": "Tailwind CSS" }
      ],
      "default": "panda"
    }
  },
  "defaultConfig": {
    "styling": "panda"
  },
  "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": "lib/tokens.js",
      "dest": "lib/tokens.js",
      "include": "always"
    },
    {
      "src": "styles/panda.config.js",
      "dest": "styles/config.js",
      "include": "when",
      "condition": { "styling": "panda" }
    },
    {
      "src": "styles/tailwind.config.js",
      "dest": "styles/config.js",
      "include": "when",
      "condition": { "styling": "tailwind" }
    }
  ],
  "dependencies": {
    "styling": {
      "panda": ["@pandacss/dev"],
      "tailwind": ["tailwindcss"]
    }
  }
}

Validation Rules

The CLI validates your config and rejects invalid files:

RuleConstraint
File sizeMax 1 MB
File rules countMax 1000
src / dest pathsMust be relative, no path traversal (..)
configSchema typesMust be "select" or "boolean"
select fieldsMust have non-empty options array
include valuesMust be "always", "never", or "when"
include: "when"Must have a condition object
importAliasMust be a string ending with /