LPM CLI

lpm <file> and lpm exec

Run JS/TS source files directly, or run project-local binaries from node_modules/.bin.

lpm <file> [--env <mode>] [--watch] [--plain-node] [args...]
lpm exec [--env <mode>] [--no-env-check] <bin> [args...]

LPM CLI has two execution lanes:

  • lpm <file> runs a JavaScript or TypeScript source file directly.
  • lpm exec <bin> runs a project-local binary from node_modules/.bin, matching npm/pnpm muscle memory.

lpm exec does not run source files. Use lpm scripts/seed.ts, not lpm exec scripts/seed.ts.

There is also a shorthand: lpm <bin> tries a project-local binary only after the name does not match a built-in command, a source-file/path invocation, a package.json script, or an lpm.json task. Use lpm exec <bin> when you want the local-bin lane explicitly.

Source Files

lpm scripts/seed.ts
lpm scripts/view.tsx
lpm scripts/migrate.js
lpm scripts/seed.ts --env staging
lpm scripts/seed.ts --watch
lpm scripts/seed.ts --plain-node
lpm scripts/seed.ts -- --env=staging

Runs a single JavaScript or TypeScript file without adding a scripts entry to package.json. .js / .mjs / .cjs go through plain node; .ts / .tsx / .mts / .cts use LPM CLI's OXC-backed TypeScript runtime on supported Node versions. If no safe runtime is available, LPM CLI fails with a fix-it message instead of downloading tsx through npx.

Human progress goes to stderr and names the selected runtime before the file starts:

 Executing scripts/seed.ts with Node.js v22.18.0 + LPM CLI TS runtime
 Done · exited 0 in 412ms

Local Binaries

lpm exec eslint --fix src
lpm exec vitest run
lpm exec --env staging prisma migrate deploy
lpm jest --watch  # shorthand when no script/task named "jest" exists

lpm exec <bin> resolves <bin> only from project node_modules/.bin directories, walking upward through the workspace. It loads project env, injects the same local-bin PATH used by lpm run, strips inherited runtime-hook env like NODE_OPTIONS and LD_PRELOAD, then spawns the binary directly without a shell.

It does not fetch packages and does not fall back to system PATH. The bare shorthand uses the same local-bin lookup, but only after scripts/tasks miss. Use lpm dlx when you want to run a package binary without adding it to the project.

Runtime Selection

ExtensionRuntime
.js, .mjs, .cjsnode - direct spawn, no shell, no LPM CLI TS preload
.ts, .mts, .ctsNode 22.18+ / 23.6+ / 24+ with the LPM CLI TS runtime -> Node 22.6-22.17 / 23.0-23.5 with --experimental-strip-types -> project-local tsx
.tsxNode 22.18+ / 23.6+ / 24+ with the LPM CLI TS runtime's OXC JSX transform -> project-local tsx when the Node version cannot load the LPM CLI runtime

The effective PATH includes project node_modules/.bin, any matching managed Node that LPM CLI resolved for the project, and then your existing system PATH.

LPM CLI source-file execution does not fall back to npx tsx. That fallback can download and execute npm code outside LPM CLI's install-policy/security model. If TypeScript execution cannot use the LPM CLI runtime, Node's built-in strip-types path, or a project-local tsx, use lpm use node@22.18+ or add and pin tsx in the project explicitly.

The LPM CLI TS runtime is LPM CLI-owned: it writes LPM CLI's bundled loader into the LPM CLI cache, calls LPM CLI's Rust/OXC transformer without fetching npm packages, executes .ts, .tsx, .mts, and .cts, reads tsconfig.json baseUrl / paths mappings for project-local imports, emits source maps, and caches transforms by source content, runtime version, Node version, platform, architecture, file path, tsconfig fingerprint, and transform options. Cache entries are validated before reuse.

TSX uses OXC's JSX transform. By default it emits the React automatic JSX runtime (react/jsx-runtime). jsx: "react" or explicit jsxFactory / jsxFragmentFactory selects the classic runtime, jsx: "react-jsxdev" enables development JSX output, and jsxImportSource changes the automatic-runtime import source. JSX runtime packages still need to be present in the project or otherwise resolvable by Node; LPM CLI does not download them during execution.

Plain Node Mode

--plain-node disables the LPM CLI TS runtime preload and child Node propagation for lpm <file>. --no-augment is an alias.

Use it when you need to check what Node itself can run without LPM CLI augmentation:

lpm scripts/seed.ts --plain-node

In plain mode, .ts / .mts / .cts use Node's built-in TypeScript support when available. .tsx is refused because plain Node does not support TSX.

Child Node Processes

When a TypeScript entrypoint runs with the LPM CLI TS runtime, child processes spawned as node child.ts inherit a controlled LPM CLI NODE_OPTIONS preload. That lets TypeScript child scripts use the same loader and tsconfig.json path mappings without invoking npx or a project-local wrapper.

Inherited parent NODE_OPTIONS is stripped before execution, so a developer shell or CI environment cannot silently replace the LPM CLI loader. --plain-node / --no-augment disables this child propagation.

Environment Loading

Both execution lanes load the project's env files and env secrets before spawn. Pass --env <mode> to load the same mode-specific files as lpm run, such as .env.staging and .env.staging.local.

--no-env-check skips env-schema validation only; it does not disable env loading.

Before spawning, LPM CLI strips inherited runtime-hook env like NODE_OPTIONS, LD_PRELOAD, LD_AUDIT, BASH_ENV, and DYLD_INSERT_LIBRARIES. Project env files, env secrets, and env-schema defaults are filtered for the same runtime-hook names before they are applied, so those keys are ignored when they come from project env. LPM CLI-controlled runtime env is added after that filtering.

Watch Mode

--watch re-runs the same source-file execution plan when the target file changes:

lpm scripts/seed.ts --watch

It watches the requested file only; dependency-graph watching is not part of direct source-file execution yet.

Argument Forwarding

For source files, put LPM CLI flags after the file and use -- when the script arg starts with -:

lpm scripts/seed.ts --env staging -- --verbose --env=prod

For local binaries, flags for LPM CLI belong before the binary name. Everything after the binary name is passed to the binary:

lpm exec --env staging eslint --fix src/index.ts

Flags

LaneFlagEffect
Both--env <mode>Load env files and secrets for the selected mode
Both--no-env-checkSkip env-var schema validation before execution
Source files--watchRe-run the same plan when the target file changes
Source files--plain-nodeDisable the LPM CLI TS runtime preload and child Node propagation
Source files--no-augmentAlias for --plain-node

Plus the global flags.

See Also

  • lpm run - run named scripts from package.json
  • lpm dlx - run a binary from a registry package without installing it
  • lpm dev - full dev server with HTTPS, tunnel, services