Skip to docs content

For pnpm users

scpm should be a drop-in replacement for pnpm projects. There are only minor differences in behavior.

Behavior differences

A handful of commands behave differently in a way that's worth knowing before you ship an scpm-based workflow:

CommandDifference
scpm run <script>Checks install staleness and auto-installs before running. pnpm run does not.
scpm testAuto-installs first, then runs the test script — equivalent to pnpm install-test in one command.
scpm exec <bin>Auto-installs on stale state before running. pnpm exec does not install.
scpm install (new project)Creates scpm-lock.yaml if there's no existing lockfile. pnpm creates pnpm-lock.yaml. In an existing pnpm project, scpm reads and writes pnpm-lock.yaml in place.

Everything else — add, remove, update, dlx, list, why, pack, publish, approve-builds — matches pnpm's behavior.

Command map

Do not translate every pnpm install && pnpm run ... habit literally. scpmr <script>, scpm test, and scpm exec <bin> check install freshness and install first only when needed. Use scpmx <pkg> for one-off tools.

pnpmscpmNotes
pnpm installscpm installReads and updates an existing pnpm-lock.yaml in place. Only new projects (no supported lockfile on disk yet) default to scpm-lock.yaml.
pnpm add reactscpm add reactSupports dependency sections, exact pins, peer deps, workspace root adds, and globals.
pnpm remove reactscpm remove reactRemoves from the manifest and relinks.
pnpm updatescpm updateUpdates all or named direct dependencies.
pnpm run buildscpm run buildRuns scripts with an auto-install staleness check first.
pnpm testscpm testShortcut for the test script; scpm auto-installs first (equivalent to pnpm install-test).
pnpm exec vitestscpm exec vitestRuns local binaries with project node_modules/.bin on PATH.
pnpm dlx cowsay hiscpmx cowsay hiInstalls into a throwaway environment and runs the binary.
pnpm listscpm listSupports depth, JSON, parseable, long, prod/dev, and global modes.
pnpm why debugscpm why debugShows reverse dependency paths.
pnpm packscpm packCreates a publishable tarball with npm-style file selection.
pnpm publishscpm publishPublishes to the configured registry; workspace fanout is available via filters.
pnpm approve-buildsscpm approve-buildsRecords packages allowed to run lifecycle build scripts.

Files and directories

Conceptpnpmscpm
Default lockfile (new projects)pnpm-lock.yamlscpm-lock.yaml
Virtual storenode_modules/.pnpm/node_modules/.scpm/
Global content-addressable store~/.pnpm-store/$XDG_DATA_HOME/scpm/store/v1/ (defaulting to ~/.local/share/scpm/store/v1/). Run scpm store path to see the resolved location.
Install statenode_modules/.modules.yamlnode_modules/.scpm-state
Workspace manifestpnpm-workspace.yamlscpm-workspace.yaml

scpm reads pnpm v11 YAML files for compatibility. scpm-lock.yaml and scpm-workspace.yaml use pnpm-compatible shapes today but are the long-term contract and may diverge over time.

scpm never touches pnpm's node_modules/.pnpm/ or ~/.pnpm-store/. The two virtual stores can coexist under node_modules. For the lockfile and workspace YAML, scpm reads and writes whichever file already exists on disk — pnpm-lock.yaml keeps getting updates in place, and an existing pnpm-workspace.yaml is mutated in place (scpm does not spawn a parallel scpm-workspace.yaml alongside it). When neither workspace yaml exists, scpm creates scpm-workspace.yaml.

What's different

  • Separate install locations. Installs go into node_modules/.scpm/ and $XDG_DATA_HOME/scpm/store/ (defaulting to ~/.local/share/scpm/store/) instead of pnpm's .pnpm/ and ~/.pnpm-store/. If a project already has a pnpm-built node_modules, scpm installs alongside — the two virtual stores live side by side.
  • Default YAML filenames for new projects. A project with no lockfile yet gets scpm-lock.yaml. If it already has pnpm-lock.yaml (or any other supported lockfile — package-lock.json, npm-shrinkwrap.json, yarn.lock, bun.lock), scpm reads and writes that file in place. Install auto-adds unreviewed dependency builds to the workspace yaml's allowBuilds map with false; scpm approve-builds flips reviewed entries to true (matching pnpm v11). When no workspace yaml exists, scpm creates scpm-workspace.yaml; an existing pnpm-workspace.yaml is mutated in place.
  • Build approvals. Dependency lifecycle script approval follows pnpm v11's allowlist model. Use explicit policy fields in package.json or scpm-workspace.yaml to opt in. scpm can also run approved dependency builds in a jail with package glob permissions for env, path, and network exceptions.
  • Speed. See the benchmarks.

Supported pnpm lockfile versions

scpm reads and writes pnpm-lock.yaml at lockfile version 9 — the format shipped by pnpm v9 and later. Older pnpm lockfiles (versions 5, 6, 7, and 8, used by pnpm 7.x and 8.x) are not supported and will cause scpm to refuse the install.

To upgrade an older pnpm lockfile, run a modern pnpm once to convert it:

npx pnpm@latest install

That rewrites pnpm-lock.yaml at v9. Commit the result, then switch to scpm install.

Out of scope

scpm does not manage Node.js itself. Runtime-management commands like pnpm env, pnpm runtime, pnpm setup, and pnpm self-update are intentionally not implemented — use mise to install and switch Node versions:

mise use node@22