LPM-cli

lpm run

Run package.json scripts — parallel, cached, watchable, workspace-aware.

lpm run <scripts...> [-- script-args...]

Runs one or more scripts entries from package.json. With multiple scripts, runs them sequentially by default; pass -p to run in parallel.

Shorthand: lpm <script> without run

lpm falls through to the same task runner as lpm run <name> when the first argument isn't a known subcommand. That means plain shorthand invocations pick up lpm.json meta-tasks, dependsOn expansion, caching, and the normal script/task resolution rules.

lpm build              # equivalent to lpm run build
lpm typecheck          # equivalent to lpm run typecheck
lpm verify             # equivalent to lpm run verify (including lpm.json meta-tasks)

The lookup only kicks in for unknown subcommand names — built-in commands always win. So lpm test runs the Vitest/Jest forwarder (built-in lpm test), not your "test" script entry. To force the script entry when a built-in conflicts, use lpm run <name> explicitly.

Use lpm run when you need runner flags. lpm build --filter web forwards --filter web to your script; lpm run build --filter web applies LPM's workspace selection.

Hidden package.json scripts

Package scripts whose name starts with . are hidden helper scripts:

package.json
{
  "scripts": {
    "build": "lpm run .build",
    ".build": "tsc -p tsconfig.build.json"
  }
}

lpm run .build and the shorthand lpm .build are rejected when invoked directly from your shell. Hidden scripts are also omitted from missing-script suggestions and script listings.

Visible scripts may call hidden scripts, and lpm.json task dependencies may reference them:

lpm.json
{
  "tasks": {
    "build": {
      "dependsOn": [".build"]
    }
  }
}

Use this for internal building blocks you do not want to expose as top-level project commands.

Examples

lpm run build
lpm run lint test                 # sequential
lpm run -p lint test typecheck    # parallel
lpm run dev -- --port 4000        # forward args to the script
lpm run build --filter web        # workspaces: build only the web member
lpm run build --all               # build every workspace member
lpm run test --affected           # only members affected by recent changes
lpm run test --filter web --no-bail  # keep running selected members after a failure
lpm run build --filter './packages/*' --workspace-concurrency 2
lpm run test --watch              # re-run on file changes
lpm run --env=staging start       # load .env.staging before running

Forwarding arguments

Anything after -- is passed verbatim to the script:

lpm run dev -- --port 4000 --host 0.0.0.0
# runs: <whatever "dev" expands to> --port 4000 --host 0.0.0.0

Parallel execution

lpm run -p lint test typecheck

Runs all three scripts concurrently, respecting any task dependencies declared in lpm.json. Output is buffered per task by default — pass --stream for live prefixed output. With --no-bail, the runner keeps going past failed tasks and reports the failures at the end.

Workspaces

lpm run build --all                    # every member, topological order
lpm run build --filter web             # one member
lpm run build --filter web --filter api # two members (union)
lpm run build --filter-prod ...shared  # prod graph closure only
lpm run test --affected                # members touched since base branch
lpm run test --affected --base develop # change base branch (default: main)
lpm run test --affected --changed-files-ignore-pattern '**/README.md'
lpm run test --affected --test-pattern '**/*.test.js'

--filter accepts the filter grammar: exact name (foo), glob (@scope/*), combined name/path (@scope/*{./apps/web}), path glob (./apps/*), forward closure (foo...), reverse closure (...foo), exclusion (!foo).

Full grammar also includes path exact ({./apps/web}), git ref ([origin/main]), and the dependency/dependent-closure variants foo^... and ...^foo.

--filter-prod <EXPR> accepts the same grammar, but closure operators ignore devDependencies edges. Use it when a production deploy/build/test selection should include runtime workspace dependents without pulling in test-only packages.

--all is mutually exclusive with --filter, --filter-prod, and --affected; filters compose with --affected (the affected set is unioned with the filter result).

--all, filters, and --affected require a workspace.

--filter core does not match @babel/core — substring matching is not supported. Use --filter '*/core' for that.

--fail-if-no-match makes a typo'd filter exit non-zero — recommended in CI.

--no-bail keeps running the remaining selected workspace members after a member fails. The command still exits non-zero after the batch if any selected member failed.

--workspace-concurrency <N> caps how many selected workspace members run at once within each topological level. The command reads persistent defaults from lpm.toml > [workspace].concurrency, then ~/.lpm/config.toml > workspace-concurrency, then falls back to the host's available parallelism.

--changed-files-ignore-pattern <glob> drops matching paths before [git-ref] filters or --affected map changed files to workspace members. It also reads project defaults from lpm.toml > [workspace].changed-files-ignore-pattern.

--test-pattern <glob> marks matching changed files as test-only. Directly changed packages still run, but those packages do not seed dependent fan-out when all their changes match the test pattern. It also reads project defaults from lpm.toml > [workspace].test-pattern.

Caching

Task results are cached only when the task opts in via lpm.json (cache: true plus non-empty outputs). A cached task with unchanged inputs replays instantly without re-executing. Bypass the cache with --no-cache.

Single-script human output prints a compact stderr header before the script's own output:

 Running build
    cache    miss
    command  next build

 build · success in 4.82s

On a cache hit, LPM replays the captured stdout/stderr and reports that the task was restored from cache.

The local cache lives under ~/.lpm/cache/tasks/. With lpm.json > remoteCache.enabled, LPM checks the hosted cache after the local cache and uploads successful task outputs after the local cache write. Remote outages, 404s, bad signatures, and corrupt artifacts are misses — they do not fail a successful local build.

Manage cache state with lpm cache. See Task runner — Remote cache for setup.

JSON output

lpm run build --json
lpm run build --filter web --json

lpm run --json emits an LPM metadata envelope, even for a single script in a single package. Single-package task output is captured into the task result instead of being passed through as raw stdout. The envelope includes success, task counts (total, passed, failed), and tasks[] entries with each task name, status, exit code, duration, cache-hit state, and skip reason.

Workspace selections emit the workspace envelope with package counts and per-member task status. If a workspace filter matches nothing and --fail-if-no-match is not set, JSON mode still emits a successful zero-package envelope instead of falling back to a human warning.

Environment files

lpm run start --env=staging

With --env=staging, LPM loads .env, .env.local, .env.staging, then .env.staging.local. Without --env, it loads .env, then .env.local.

If the script name resolves to an env mode through lpm.json, LPM uses that mode automatically unless you override it with --env.

Vault-backed env secrets override file values after dotenv loading.

--no-env-check skips environment-variable schema validation if you've declared a schema.

Watch mode

lpm run dev --watch

Re-runs one script in one package on file changes.

If the task declares inputs globs in lpm.json, only matching file changes retrigger the run. Otherwise, relevant project-file changes retrigger it.

--watch is single-package today: it does not support multiple script names or workspace-selection flags like --all, --filter, --filter-prod, or --affected. Watch runs fresh on every change; the task cache is not used.

Flags

FlagEffect
-p, --parallelRun scripts concurrently (respects task deps from lpm.json)
--no-bailKeep running remaining tasks or selected workspace members after a failure
--streamLive prefixed output instead of buffered per-task in task-graph / parallel runs
--allRun in every workspace member (topological order); mutually exclusive with filters / --affected
--filter <EXPR>Filter workspace members (repeatable; unions)
--filter-prod <EXPR>Filter workspace members with production-only dependency closures
--fail-if-no-matchExit non-zero if the selected workspace set is empty
--workspace-concurrency <N>Limit concurrent workspace members for --all, --filter, or --affected runs
--affectedOnly members affected by changes vs --base
--base <REF>Git base ref for --affected (default: main)
--changed-files-ignore-pattern <glob>Ignore matching git-diff paths for --affected / [git-ref] filters
--test-pattern <glob>Treat matching git-diff paths as test-only for --affected / [git-ref] fan-out decisions
--no-cacheBypass the task cache for cache-enabled tasks; ignored in watch mode
--no-env-checkSkip env-var schema validation
--env=<MODE>Load .env, .env.local, .env.<MODE>, and .env.<MODE>.local
--watchRe-run one script in one package on file changes

Plus the global flags.

See also

  • lpm exec — run a JS/TS file directly (no script needed)
  • lpm dlx — run a package binary without installing
  • Task runner — caching, dependencies, lpm.json task graph
  • Workspaces — filter grammar