agentsclimarketplace

Migrate to pnpm 11

Skill llbbl/pnpm-upgrade-skills/claude/migrate-to-pnpm-11

Auditable, self-contained migration skills for upgrading JS/TS projects to pnpm 11 with supply-chain-hardened defaults. For Claude Code and Codex CLI.

Install
npx -y skills add llbbl/pnpm-upgrade-skills --skill migrate-to-pnpm-11

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Migrate a JavaScript/TypeScript project from npm, yarn, or older pnpm (≤10) to pnpm 11, adopting its supply-chain-hardened defaults (minimumReleaseAge, allowBuilds, blockExoticSubdeps, strictDepBuilds, verifyDepsBeforeRun). Use when the user says "migrate to pnpm 11", "upgrade pnpm", "switch to pnpm", or wants to harden a project against npm supply-chain attacks.

SKILL.md

24.9 KB, as published. Nobody here has run it

/migrate-to-pnpm-11

Migrate a project to pnpm 11 and adopt its supply-chain security defaults.

Why pnpm 11

pnpm 11 (released 2026-04-28) turns on five supply-chain defaults that were opt-in or absent before:

SettingDefault in v11Purpose
minimumReleaseAge1440 (24 h)Refuse to install package versions younger than the cooldown window. Blocks the typical "publish-malware → wait for CI to pull it" window.
blockExoticSubdepstrueTransitive deps cannot come from git URLs or arbitrary tarballs.
strictDepBuildstrueAny dependency with a postinstall/build script that is not on the allowBuilds allowlist fails the install with ERR_PNPM_IGNORED_BUILDS.
verifyDepsBeforeRuninstallpnpm run <script> re-checks the lockfile before running.
optimisticRepeatInstalltrueSkip work when the lockfile is unchanged.

Plus: lockfile entries are re-validated against minimumReleaseAge even if the lockfile was committed under a weaker policy, Git-hosted deps get integrity pinning, and pnpm audit signatures verifies ECDSA registry signatures.

Prerequisites

  • Node.js ≥ 22. pnpm 11 is pure ESM and drops Node 18/20.
  • A clean working tree (or be willing to commit/stash before migrating — the migration touches lockfiles, package.json, .npmrc, and may create pnpm-workspace.yaml).

Git workflow

This skill does not run git commit, git push, or gh pr directly. After the migration is verified, summarize the changes for the user and let them (or a separate commit/PR workflow) handle the commit.

Before editing any files, create a feature branch. The migration touches lockfiles, package.json, .npmrc, pnpm-workspace.yaml, the Dockerfile, and CI configs — too much for a stray commit on main.

git status                       # confirm clean tree
git checkout -b chore/migrate-to-pnpm-11

If the working tree isn't clean, stop and ask the user how to proceed (stash, commit elsewhere, or abort). Do not migrate on top of unrelated in-progress work.

Suggested commit/PR title: chore: migrate to pnpm 11 with supply-chain defaults.

Workflow

Step 1 — Detect the starting point

Run these in parallel and capture each result. Do not assume — check.

node -v
corepack -v 2>/dev/null || echo "no corepack"
cat package.json | grep -E '"packageManager"|"engines"' || true
ls -1 package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null
ls -1 pnpm-workspace.yaml .npmrc 2>/dev/null

From the output, classify the project as one of:

  • npmpackage-lock.json exists, no pnpm/yarn lockfile.
  • yarn (classic / berry)yarn.lock exists. Check .yarnrc.yml to tell berry from classic.
  • pnpm < 10pnpm-lock.yaml with lockfileVersion < 9, or packageManager: pnpm@<10.
  • pnpm 10lockfileVersion: '9.0' and packageManager: [email protected].

Also detect: workspace (pnpm-workspace.yaml with packages:, or package.json workspaces), monorepo tooling (Turborepo, Nx, Changesets), and CI files that pin a package manager (.github/workflows/*.yml, Dockerfile, .tool-versions, .nvmrc).

If Node < 22: stop and tell the user. Suggest nvm install 22 && nvm use 22 (or mise/asdf equivalents). Update .nvmrc / .tool-versions / engines.node as part of the migration.

Step 2 — Install pnpm 11 via corepack

Prefer corepack so the version is pinned per-project rather than globally.

corepack enable
corepack prepare pnpm@latest --activate
pnpm -v   # confirm 11.x

Then pin in package.json:

{
  "packageManager": "[email protected]",
  "engines": { "node": ">=22" }
}

Use the exact installed version (pnpm -v) rather than latest.

Step 3 — Convert the lockfile (path-specific)

Run from the repo root. For workspaces, run at the workspace root only.

From npm:

pnpm import   # reads package-lock.json → writes pnpm-lock.yaml
rm package-lock.json
rm -rf node_modules

From yarn (classic or berry):

pnpm import   # reads yarn.lock → writes pnpm-lock.yaml
rm yarn.lock .yarnrc .yarnrc.yml 2>/dev/null
rm -rf node_modules .yarn .pnp.* 2>/dev/null

From pnpm < 10: delete the old lockfile so pnpm 11 regenerates at lockfile v9. Do not carry forward a v6/v7 lockfile.

rm pnpm-lock.yaml
rm -rf node_modules

From pnpm 10: keep the lockfile as-is. Lockfile v9 is unchanged between 10 and 11.

Step 4 — Migrate configuration files

This is where the most subtle breakage happens. .npmrc is auth/registry only in pnpm 11. Every pnpm-specific setting moves into pnpm-workspace.yaml (even for non-workspace projects — the file is required as the settings home). The pnpm field in package.json is also no longer read.

Read the project's current .npmrc, pnpm-workspace.yaml, and package.json's pnpm field. Move every non-auth, non-registry key into pnpm-workspace.yaml.

Old locationOld keyNew locationNew key
.npmrc or package.json#pnpmonly-built-dependencies / onlyBuiltDependenciespnpm-workspace.yamlallowBuilds (allowlist map)
sameonlyBuiltDependenciesFilesameallowBuilds (inline the file's contents)
sameneverBuiltDependenciessameallowBuilds (omit entries, or set to false)
sameignoredBuiltDependenciessameallowBuilds (set to false)
sameignoreDepScriptssameallowBuilds: {} (empty allowlist = block all)
sameallowNonAppliedPatchessameallowUnusedPatches
sameignorePatchFailuressameremoved — no replacement; fix the patch
package.json#pnpm.auditConfigignoreCvespnpm-workspace.yamlauditConfig.ignoreGhsas (translate CVE→GHSA via GitHub Advisory DB)
Env varsnpm_config_*env / CIpnpm_config_*

Stay in .npmrc (do not move): registry=, //registry.npmjs.org/:_authToken=..., @scope:registry=, network proxy keys, engine-strict. Most other things move.

Step 5 — Write a hardened pnpm-workspace.yaml

Create or update the file. Keep defaults explicit so they survive future pnpm changes and so the security posture is visible in code review.

# Supply-chain hardening (defaults shown explicitly)
minimumReleaseAge: 1440         # block packages < 24 h old
blockExoticSubdeps: true        # no git/tarball transitive deps
strictDepBuilds: true           # fail install on unapproved postinstalls
verifyDepsBeforeRun: install    # re-check lockfile before `pnpm run`

# Postinstall allowlist — explicitly approve every dep with a build script.
# Empty map = block all. Add entries only after auditing the script.
allowBuilds:
  # esbuild: true
  # \"@swc/core\": true

# If this is a workspace:
# packages:
#   - 'packages/*'
#   - 'apps/*'

For projects that legitimately need fresher releases (e.g. internal monorepo packages published moments before install), prefer raising minimumReleaseAge for the registry and using a per-package override rather than disabling it globally.

Step 6 — First install, expect ERR_PNPM_IGNORED_BUILDS

pnpm install

With strictDepBuilds: true and an empty allowBuilds, the install will fail listing every dependency that wants to run a build script. For each one:

  1. Look up the package on npm — what does its postinstall actually do?
  2. If it is a known legitimate native build (e.g. esbuild, @swc/core, sharp, better-sqlite3, node-gyp-based deps the project actually uses), add it to allowBuilds as true.
  3. If it looks unnecessary or suspicious, leave it blocked (false or omitted) — the package will still install, just without running its script. Verify the app still works.

Re-run pnpm install until it succeeds.

Step 7 — Verify

pnpm install --frozen-lockfile   # reproducibility check
pnpm audit                       # known advisories
pnpm audit signatures            # ECDSA registry signatures (new in v11)
pnpm run build && pnpm test      # or whatever the project uses

If pnpm audit signatures reports unsigned packages, surface them to the user but do not auto-pin around them.

Step 8 — Update CI, Docker, and tooling

Every CI provider has its own Node-install task and its own cache idiom. Don't generalize from the GitHub Actions example to GitLab or vice versa — follow the matching subsection below. Do not invent CI config from scratch; copy the snippet, adapt the names.

Three things to keep in mind across all providers:

  • Node ≥ 22 in every place Node is pinned (CI config, Dockerfile, .nvmrc, engines.node).
  • Cache the pnpm store, not node_modules. pnpm uses a content-addressed store; node_modules is just symlinks into it, so caching node_modules caches the symlinks rather than the content. Key the cache on pnpm-lock.yaml's hash.
  • Install pnpm before using it. Corepack (shipped with Node since 16) is the simplest path — corepack enable && corepack prepare pnpm@11 --activate. The packageManager field in package.json then pins the exact version.

8a. Detect what's in use

# CI provider files
ls -1 \
  .github/workflows \
  .gitlab-ci.yml \
  .circleci/config.yml \
  bitbucket-pipelines.yml \
  azure-pipelines.yml \
  Jenkinsfile \
  .drone.yml \
  .buildkite/pipeline.yml .buildkite/pipeline.yaml \
  .travis.yml \
  2>/dev/null

# Container / dev-env files
ls -1 \
  Dockerfile Dockerfile.* \
  docker-compose.yml docker-compose.yaml compose.yml compose.yaml \
  .dockerignore \
  .devcontainer/devcontainer.json .devcontainer.json \
  2>/dev/null

# Anything still referencing npm / yarn / pre-v11 pnpm / pre-22 Node
grep -RnE "npm (ci|install|run)\b|yarn (install|add|run)\b|pnpm@(7|8|9|10)\b|node:(16|18|20)\b|setup-node@v[0-3]\b" \
  .github .gitlab-ci.yml .circleci bitbucket-pipelines.yml azure-pipelines.yml \
  Jenkinsfile .drone.yml .buildkite .travis.yml \
  Dockerfile* docker-compose*.yml compose*.yml \
  Makefile justfile .tool-versions .nvmrc package.json \
  2>/dev/null

8b. GitHub Actions

Replace actions/setup-node blocks with the following. pnpm/action-setup must run before actions/setup-node so cache: pnpm can find the binary.

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 11
          run_install: false

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm audit signatures
      - run: pnpm test

Corepack-only alternative (no third-party action): replace the pnpm/action-setup step with - run: corepack enable before setup-node. The packageManager field in package.json then dictates the version.

8c. GitLab CI

Cache the store, not node_modules. The cache key auto-invalidates on lockfile changes.

# .gitlab-ci.yml
image: node:22

variables:
  PNPM_HOME: "${CI_PROJECT_DIR}/.pnpm-store"

cache:
  key:
    files:
      - pnpm-lock.yaml
  paths:
    - .pnpm-store

before_script:
  - corepack enable
  - corepack prepare pnpm@11 --activate
  - pnpm config set store-dir "$PNPM_HOME"

test:
  script:
    - pnpm install --frozen-lockfile
    - pnpm audit signatures
    - pnpm test

8d. CircleCI

# .circleci/config.yml
version: 2.1
jobs:
  test:
    docker:
      - image: cimg/node:22.13
    steps:
      - checkout
      - run:
          name: Enable corepack + pnpm
          command: |
            sudo corepack enable
            corepack prepare pnpm@11 --activate
            pnpm config set store-dir ~/.pnpm-store
      - restore_cache:
          keys:
            - v1-pnpm-store-{{ checksum "pnpm-lock.yaml" }}
            - v1-pnpm-store-
      - run: pnpm install --frozen-lockfile
      - save_cache:
          key: v1-pnpm-store-{{ checksum "pnpm-lock.yaml" }}
          paths:
            - ~/.pnpm-store
      - run: pnpm audit signatures
      - run: pnpm test

The fallback cache key (v1-pnpm-store- with no checksum) restores the most recent store when the exact lockfile hash isn't cached; pnpm still verifies content hashes.

8e. Bitbucket Pipelines

The built-in node cache targets node_modules (wrong for pnpm). Define a custom store cache.

# bitbucket-pipelines.yml
image: node:22

definitions:
  caches:
    pnpm-store: $HOME/.pnpm-store

pipelines:
  default:
    - step:
        caches: [pnpm-store]
        script:
          - corepack enable
          - corepack prepare pnpm@11 --activate
          - pnpm config set store-dir $HOME/.pnpm-store
          - pnpm install --frozen-lockfile
          - pnpm audit signatures
          - pnpm test

8f. Azure Pipelines

# azure-pipelines.yml
trigger: [main]
pool:
  vmImage: ubuntu-latest

variables:
  PNPM_STORE_PATH: $(Pipeline.Workspace)/.pnpm-store

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '22.x'

  - script: |
      corepack enable
      corepack prepare pnpm@11 --activate
      pnpm config set store-dir $(PNPM_STORE_PATH)
    displayName: Install pnpm 11

  - task: Cache@2
    inputs:
      key: 'pnpm | "$(Agent.OS)" | pnpm-lock.yaml'
      path: $(PNPM_STORE_PATH)
    displayName: Cache pnpm store

  - script: pnpm install --frozen-lockfile
  - script: pnpm audit signatures
  - script: pnpm test

8g. Jenkinsfile (declarative)

The named Docker volume persists the store across builds on the same agent.

pipeline {
  agent {
    docker {
      image 'node:22'
      args  '-v pnpm-store:/root/.local/share/pnpm/store'
    }
  }
  environment {
    COREPACK_ENABLE_DOWNLOAD_PROMPT = '0'
  }
  stages {
    stage('Setup pnpm') {
      steps {
        sh '''
          corepack enable
          corepack prepare pnpm@11 --activate
          pnpm -v
        '''
      }
    }
    stage('Install') { steps { sh 'pnpm install --frozen-lockfile' } }
    stage('Verify')  { steps { sh 'pnpm audit signatures' } }
    stage('Test')    { steps { sh 'pnpm test' } }
  }
}

8h. Drone CI

# .drone.yml
kind: pipeline
type: docker
name: default

steps:
  - name: test
    image: node:22
    volumes:
      - name: pnpm-store
        path: /root/.local/share/pnpm/store
    commands:
      - corepack enable
      - corepack prepare pnpm@11 --activate
      - pnpm install --frozen-lockfile
      - pnpm audit signatures
      - pnpm test

volumes:
  - name: pnpm-store
    host: { path: /tmp/pnpm-store }

8i. Buildkite

# .buildkite/pipeline.yml
steps:
  - label: ":pnpm: test"
    plugins:
      - docker#v5.11.0:
          image: node:22
          mount-buildkite-agent: false
          volumes:
            - "buildkite-pnpm-store:/root/.local/share/pnpm/store"
    commands:
      - corepack enable
      - corepack prepare pnpm@11 --activate
      - pnpm install --frozen-lockfile
      - pnpm audit signatures
      - pnpm test

8j. Travis CI

Travis is in maintenance mode for most projects; if this migration is also a good moment to move to another provider, take it. If staying:

# .travis.yml
language: node_js
node_js: 22

cache:
  directories:
    - $HOME/.pnpm-store

before_install:
  - corepack enable
  - corepack prepare pnpm@11 --activate
  - pnpm config set store-dir $HOME/.pnpm-store

install: pnpm install --frozen-lockfile

script:
  - pnpm audit signatures
  - pnpm test

8k. Dockerfile — patch existing first, then consider the patterns

Patch existing Dockerfiles before adding new ones. The most common post-migration Docker failure looks like this:

[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: <pkg>@x.y.z, ...
Build Failed: ... exit code: 1

Root cause: pnpm-workspace.yaml is where pnpm 11's allowBuilds allowlist lives, and existing Dockerfiles typically only copy package.json and the lockfile into the build context. Without the workspace file, pnpm 11 sees every dep with a build script as "unaddressed" and fails (new v11 default behavior, not configurable per-stage).

Action — find and fix every install stage:

grep -nE '^COPY .*(package\.json|pnpm-lock\.yaml)' Dockerfile Dockerfile.* 2>/dev/null

For each COPY package.json pnpm-lock.yaml ./ (or similar) line in a stage that runs pnpm install / pnpm fetch, add pnpm-workspace.yaml:

- COPY package.json pnpm-lock.yaml ./
+ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./

If you have separate deps and deps_prod (or build and prod) stages, both need the workspace file — the allowlist applies to --prod installs too. Also bump any corepack prepare pnpm@<old> lines to [email protected] so the pin matches the packageManager field (the field overrides the pin at install time, but a stale pin is confusing in review).

If the existing Dockerfile is broadly fine after that two-line patch, stop here — don't rewrite it from one of the patterns below. Only use the patterns when there's no existing Dockerfile or it needs a structural rewrite.


Pattern menu for new Dockerfiles:

--mount=type=cache requires BuildKit (default on Docker 23+). Declare it explicitly with # syntax=docker/dockerfile:1.7. Prefer node:22-slim over node:22-alpine unless you specifically need musl — Alpine + node-gyp + native deps adds friction.

Pattern 1 — single-stage (dev images, simple apps):

# syntax=docker/dockerfile:1.7
FROM node:22-slim

ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0

RUN corepack enable

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
    pnpm install --frozen-lockfile

COPY . .
CMD ["pnpm", "start"]

Pattern 2 — multi-stage with pnpm fetch (production):

pnpm fetch populates the store from the lockfile alone, so the dependency layer rebuilds only when pnpm-lock.yaml changes — not when source changes.

# syntax=docker/dockerfile:1.7
FROM node:22-slim AS base
ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN corepack enable
WORKDIR /app

FROM base AS deps
COPY pnpm-lock.yaml pnpm-workspace.yaml* ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
    pnpm fetch --prod

FROM base AS build
COPY . .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
    pnpm install --frozen-lockfile --offline && \
    pnpm run build

FROM node:22-slim AS runtime
ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
RUN corepack enable
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./package.json
USER node
CMD ["node", "dist/index.js"]

Pattern 3 — monorepo deploy with pnpm deploy:

Extracts a self-contained, hoisted node_modules for one workspace package, with no workspace symlinks. Ready to copy into a slim runtime stage.

# syntax=docker/dockerfile:1.7
FROM node:22-slim AS base
ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
RUN corepack enable
WORKDIR /repo

FROM base AS build
COPY . .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
    pnpm install --frozen-lockfile
RUN pnpm --filter=@my-org/api build
RUN pnpm deploy --filter=@my-org/api --prod /out

FROM node:22-slim AS runtime
WORKDIR /app
COPY --from=build /out .
USER node
CMD ["node", "dist/index.js"]

8l. docker-compose

The named pnpm-store volume persists the store across docker compose up cycles so local dev installs stay fast. Use docker compose (the v2 plugin), not legacy docker-compose.

services:
  app:
    image: node:22
    working_dir: /app
    environment:
      COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
      PNPM_HOME: /pnpm
      PATH: /pnpm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    volumes:
      - .:/app
      - node_modules:/app/node_modules
      - pnpm-store:/pnpm/store
    command: sh -c "corepack enable && pnpm install && pnpm run dev"

volumes:
  node_modules:
  pnpm-store:

8m. .dockerignore

The lockfile and workspace config must stay in the build context. If a .dockerignore exists, read it and verify none of these patterns are excluded — bare * rules and broad globs are the usual offenders:

grep -E '(^|/)(pnpm-workspace\.yaml|pnpm-lock\.yaml|package\.json|\*\.yaml|\*\.json|\*$)' .dockerignore 2>/dev/null

If any of those files would be excluded, add explicit un-ignore rules (!pnpm-workspace.yaml, etc.) or narrow the offending glob. The Dockerfile patch in 8k is useless if .dockerignore strips the file out of the build context before COPY ever sees it.

Reference .dockerignore for a new project — exclude outputs and local-only files, keep dep manifests:

# Always exclude
node_modules
.git
.pnpm-store
dist
build
coverage
.next
.turbo
.cache

# Local-only files
.env
.env.local
.env.*.local
*.log

# Don't exclude — these MUST be in the build context:
# package.json
# pnpm-lock.yaml
# pnpm-workspace.yaml

If .npmrc holds an auth token, don't copy it into the image. Mount as a BuildKit secret:

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    --mount=type=cache,id=pnpm,target=/pnpm/store \
    pnpm install --frozen-lockfile

Build with docker build --secret id=npmrc,src=$HOME/.npmrc ..

8n. devcontainer.json

{
  "name": "App",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:22",
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "22" }
  },
  "postCreateCommand": "corepack enable && corepack prepare pnpm@11 --activate && pnpm install",
  "containerEnv": {
    "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0"
  },
  "mounts": [
    "source=pnpm-store-${devcontainerId},target=/home/node/.local/share/pnpm/store,type=volume"
  ]
}

8o. Cross-cutting updates regardless of provider

  • .nvmrc / .tool-versions — bump to Node 22.
  • engines.node in package.json">=22".
  • CI env vars — rename every npm_config_* to pnpm_config_*.
  • Scripts that call npx — prefer pnpm dlx. dlx honors allowBuilds and strictDepBuilds, so a dlx command needing build scripts will fail unless allowlisted.
  • Makefile / justfile targets calling npm/yarn — switch to pnpm.

8p. Sanity check after pushing

The first post-migration CI run should show all of:

  1. Node 22.x or newer.
  2. pnpm 11.x via corepack or pnpm/action-setup.
  3. pnpm install --frozen-lockfile succeeding with no ERR_PNPM_IGNORED_BUILDS.
  4. pnpm audit signatures running and either passing or returning a known list.
  5. Meaningful cache hit on the second run.

If any are missing, re-run the detection grep — the pipeline got partially upgraded.

Step 9 — Summarize and stop

Summarize the changes for the user — files touched, packages added to allowBuilds, any findings from pnpm audit signatures, and any Dockerfile / CI edits. Do not run git commit, git push, or gh pr yourself. Let the user decide how to commit (their own workflow, a separate agent, or by hand).

Gotchas

  • pnpm install -g with no arguments is removed. Per-global-package isolation now — each pnpm add -g <pkg> gets its own dir under PNPM_HOME/bin.
  • pnpm server is gone. If the project's tooling used it, find an alternative.
  • Removed commands: access, bugs, edit, issues, owner, prefix, profile, pkg, repo, set-script, team, token. Most have native equivalents or are no longer needed.
  • useNodeVersion / executionEnv.nodeVersion removed. Use devEngines.runtime or external version managers (nvm/mise/asdf).
  • managePackageManagerVersions, packageManagerStrict, packageManagerStrictVersion all replaced by pmOnFail.
  • Git-hosted deps now get integrity pinning. The first install after migration writes gitHosted: true entries — subsequent installs detect tampering. Don't be alarmed by the lockfile diff.
  • minimumReleaseAge and CI cache: if CI restores a lockfile from cache that was resolved before v11's policy, pnpm re-validates and may refuse to install. This is intended.

Quick reference: file diff after a typical migration

  • package.jsonpackageManager set, engines.node bumped, pnpm field removed.
  • .npmrc — trimmed to auth/registry lines only (or deleted if it only held pnpm settings).
  • pnpm-workspace.yaml — created or expanded with hardening keys and allowBuilds.
  • pnpm-lock.yaml — regenerated at lockfile v9 (no change if already on pnpm 10).
  • package-lock.json / yarn.lock — deleted.
  • .nvmrc / .tool-versions — Node 22.
  • CI files — package manager and Node version updated.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.