Generated banner for Pnpm
Generated logo for Pnpm

Pnpm

Anthony FuCommunityCreated by Anthony Fu

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Low risk

From the author

Node.js package manager with strict dependency resolution. Use when running pnpm specific commands, configuring workspaces via pnpm-workspace.yaml, or managing dependencies with catalogs, patches, overrides, config dependencies, or the global virtual store.
— the description by Anthony Fu

From the skill’s files

Taken from the skill’s own files (its description, section headings and stated usage). Not an AutoGineer summary, and not written by the author as one.

Node.js package manager with strict dependency resolution. Use when running pnpm specific commands, configuring workspaces via pnpm-workspace.yaml, or managing dependencies with catalogs, patches, overrides, config dependencies, or the global virtual store. pnpm is a fast, disk space efficient package manager. It uses a content-addressable store to deduplicate packages across all projects on a machine, and enforces strict dependency resolution by default, preventing phantom dependencies.

What it does

  • Core
  • Features
  • Best Practices

When to use it

  • Use when running pnpm specific commands, configuring workspaces via pnpm-workspace.yaml, or managing dependencies with catalogs, patches, overrides, config dependencies, or the global virtual store.

Advantages

The skill’s files do not state this.

Usage examples

The skill’s files do not state this.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/antfu/pnpm/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip pnpm.zip -d .agents/skills

Documentation by Anthony Fu

pnpm is a fast, disk space efficient package manager. It uses a content-addressable store to deduplicate packages across all projects on a machine, and enforces strict dependency resolution by default, preventing phantom dependencies.

pnpm v12 is a Rust rewrite of v11: stable, and keeps v11's commands, flags, settings, and lockfile format — so most guidance here applies to both. A handful of v12 behaviors differ (git deps resolve via HTTPS, project-aware global bins, other package managers, packageImportMethod: auto hardlinks first on Linux) — see best-practices-migration.

Configuration model (important): pnpm settings live in pnpm-workspace.yaml (and the global config.yaml) using camelCase keys. .npmrc is used only for authentication/registry credentials, and the pnpm field of package.json is no longer read. When working in a pnpm project, check pnpm-workspace.yaml for settings/workspace structure and .npmrc only for auth. Always use --frozen-lockfile (or pnpm ci) in CI.

The skill is based on pnpm 12.18, generated at 2026-11-05. It covers v11+v12 behavior (config split, config includes, isolated global packages, allowBuilds, pmOnFail, global virtual store, native release management, workspace task orchestration, and experimental Python/Cargo support) where current docs describe them.

Core

TopicDescriptionReference
CLI Commandsinstall/add/remove/update, run, dlx/pnx, workspace, runtime, publishing (version, view, sbom, stage)core-cli
Configurationpnpm-workspace.yaml settings (camelCase), global config.yaml, config includes, packageConfigs, auth (.npmrc, auth.ini)core-config
WorkspacesMonorepo support: filtering, workspace protocol, shared lockfile, packageConfigscore-workspaces
StoreContent-addressable store, virtual store, node linker modes (incl. loaded/store loader), frozen/read-only storecore-store

Features

TopicDescriptionReference
CatalogsCentralized dependency versions; catalogMode, catalog: in overridesfeatures-catalogs
OverridesForce versions (incl. transitive & peer deps); packageExtensionsfeatures-overrides
PatchesModify third-party packages; patchedDependencies in pnpm-workspace.yamlfeatures-patches
AliasesInstall under custom names (npm:) and registry aliases (namedRegistries)features-aliases
Hooks.pnpmfile.mjs hooks (readPackage, updateConfig, beforePacking), finders, resolvers/fetchersfeatures-hooks
Peer DependenciesAuto-install, strict mode, rules, dedupePeers, peers checkfeatures-peer-deps
Config DependenciesShare hooks/settings/catalogs/patches across repos via configDependenciesfeatures-config-dependencies
Global Virtual Store & ShimsShared node_modules, git-worktree multi-agent setups, isolated global packages, project-aware bins, other package managersfeatures-global-virtual-store
Supply-Chain SecurityBuild approval (allowBuilds), permissions and agent skills, minimumReleaseAge, trustPolicy, lockfile integrityfeatures-supply-chain-security
Task OrchestrationCross-project task graphs (tasks/dependsOn), concurrency groups, priority, pnpm pipelinefeatures-task-orchestration
Release ManagementNative versioning: pnpm change/version -r/lane, lanes, epics, fixed groupsfeatures-versioning
Multi-EcosystemPython (pypi:) and Cargo (crate:) dependencies alongside npm (experimental)features-multi-ecosystem

Best Practices

TopicDescriptionReference
CI/CD SetupGitHub Actions, GitLab, Docker, pnpm ci, store caching, frozen lockfilesbest-practices-ci
Migrationnpm/Yarn → pnpm, phantom deps, and pnpm v10 → v11 → v12 upgrade notesbest-practices-migration
PerformanceInstall optimizations, allowBuilds, global virtual store, workspace parallelizationbest-practices-performance

Files

references/best-practices-ci.md


name: pnpm-ci-cd-setup description: Optimizing pnpm for continuous integration and deployment workflows

pnpm CI/CD Setup

Best practices for using pnpm in CI/CD environments for fast, reliable builds.

CI auto-behaviors: When pnpm detects a CI environment it switches to frozen-lockfile mode automatically (opt out with --no-frozen-lockfile; force or disable detection with the ci setting) and fails on an incompatible lockfile written by a newer pnpm major instead of rewriting it — keep the CI pnpm version in sync with the one that generated the lockfile. The global virtual store is auto-disabled in CI (no warm cache).

Installing pnpm in CI

On CI systems that run jobs in a container image (GitLab CI, Bitbucket Pipelines), use the official image ghcr.io/pnpm/pnpm:<major> as the job image: pnpm is already on PATH, and pnpm runtime set node lts -g installs Node.js. For your own Dockerfile, copy pnpm in: COPY --from=ghcr.io/pnpm/pnpm:12 /opt/pnpm /opt/pnpm (use the -alpine tag for musl bases), then symlink pnpm pn pnpx pnx from /opt/pnpm into /usr/local/bin.

Elsewhere outside GitHub Actions (which has its own action), use the standalone install script, never Corepack. Save it to a file first, with a fallback download so a failed download never runs a partial script:

{ curl -fsSL https://get.pnpm.io/ -o /tmp/pnpm-install.sh \
  || curl -fsSL https://github.com/pnpm/get.pnpm.io/releases/latest/download/install.sh -o /tmp/pnpm-install.sh; } \
  && sh /tmp/pnpm-install.sh
  • No Node.js needed — pnpm is self-contained and can install a runtime afterwards (pnpm runtime set node lts -g).

  • Follows the project's version — after bootstrap, pnpm switches to the packageManager/devEngines.packageManager pin on first use, so you don't pin twice.

  • The script sets PNPM_HOME and edits a shell profile CI never reloads — export PNPM_HOME and put $PNPM_HOME/bin on PATH yourself.

Corepack installs a JS shim, so every pnpm call starts Node.js to run the shim first — a cost paid on each of CI's many invocations. Installing pnpm itself avoids it.

Cache the metadata cache too

pnpm cache path prints pnpm's metadata cache directory. Caching it (alongside the store) also caches the lockfile verification log, letting a job skip re-checking an unchanged lockfile against supply-chain policies — the dominant install cost once the store is warm.

GitHub Actions

Basic Setup
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10

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

      - run: pnpm install --frozen-lockfile   # or: pnpm ci
      - run: pnpm test
      - run: pnpm build

pnpm ci (aliases clean-install, install-clean) = pnpm clean + pnpm install --frozen-lockfile, ideal for fully reproducible CI builds.

With Store Caching

For larger projects, cache the pnpm store:

- uses: pnpm/action-setup@v4
  with:
    version: 10

- name: Get pnpm store directory
  shell: bash
  run: |
    echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v4
  name: Setup pnpm cache
  with:
    path: ${{ env.STORE_PATH }}
    key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
    restore-keys: |
      ${{ runner.os }}-pnpm-store-

- run: pnpm install --frozen-lockfile

Trust: only cache/restore the pnpm store and cache dir between trusted jobs. A store an untrusted job can write to must not be reused by trusted jobs — it is part of pnpm's trust domain.

Matrix Testing
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: 'pnpm'
      - run: pnpm install --frozen-lockfile
      - run: pnpm test

GitLab CI

image: node:20

stages:
  - install
  - test
  - build

variables:
  PNPM_HOME: /root/.local/share/pnpm
  PATH: $PNPM_HOME:$PATH

before_script:
  - corepack enable
  - corepack prepare pnpm@latest --activate

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - .pnpm-store

install:
  stage: install
  script:
    - pnpm config set store-dir .pnpm-store
    - pnpm install --frozen-lockfile

test:
  stage: test
  script:
    - pnpm test

build:
  stage: build
  script:
    - pnpm build

Docker

PATH: global pnpm binaries live in $PNPM_HOME/bin. In Docker set ENV PATH="$PNPM_HOME/bin:$PATH" (not $PNPM_HOME). The official image ghcr.io/pnpm/pnpm:<version|major|latest> (Debian slim, or -alpine variants; pnpm + bundled node-gyp, no Node.js) already sets PNPM_HOME=/pnpm and PATH; choose Node via pnpm runtime set node <ver> -g or devEngines.runtime (pnpm install downloads it).

Always copy pnpm-workspace.yaml (when present) before pnpm install/pnpm fetch: it holds project settings and workspace patterns, and omitting it can change installs or fail frozen installs. Also copy patches and files named by include.

BuildKit cache mounts: if the image has a runtime from pnpm runtime set node -g, don't mount over /pnpm/store; mount /var/cache/pnpm and pass --store-dir /var/cache/pnpm.

pnpm fetch recipe: COPY pnpm-lock.yaml pnpm-workspace.yaml /app/, RUN pnpm fetch, then after COPY . /app run pnpm install --offline --frozen-lockfile (fetch only fills the virtual store); use a separate stage with pnpm fetch --prod + install --offline --frozen-lockfile --prod for production deps.

Multi-Stage Build
# Build stage
FROM node:24-slim AS builder

# Enable corepack for pnpm
RUN corepack enable

WORKDIR /app

# Copy package files first for layer caching
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages/*/package.json ./packages/

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source and build
COPY . .
RUN pnpm build

# Production stage
FROM node:20-slim AS runner

RUN corepack enable
WORKDIR /app

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
COPY --from=builder /app/pnpm-lock.yaml /app/pnpm-workspace.yaml ./

# Production install
RUN pnpm install --frozen-lockfile --prod

CMD ["node", "dist/index.js"]
Optimized for Monorepos
FROM node:20-slim AS builder
RUN corepack enable
WORKDIR /app

# Copy workspace config
COPY pnpm-lock.yaml pnpm-workspace.yaml ./

# Copy all package.json files maintaining structure
COPY packages/core/package.json ./packages/core/
COPY packages/api/package.json ./packages/api/

# Install all dependencies
RUN pnpm install --frozen-lockfile

# Copy source
COPY . .

# Build specific package
RUN pnpm --filter @myorg/api build

Key CI Flags

--frozen-lockfile

Always use in CI. Fails if pnpm-lock.yaml needs updates:

pnpm install --frozen-lockfile
--prefer-offline

Use cached packages when available:

pnpm install --frozen-lockfile --prefer-offline
--ignore-scripts

Skip lifecycle scripts for faster installs (use cautiously):

pnpm install --frozen-lockfile --ignore-scripts

Pinning the pnpm version

Pin with packageManager (exact) or devEngines.packageManager (range, resolved version stored in the lockfile):

// package.json
{ "packageManager": "[email protected]" }

The standalone install script and pnpm/action-setup both honor this pin. To skip the pin check when version management is external (asdf/mise/Volta), set pmOnFail: ignore in pnpm-workspace.yaml, or run a one-off with pnpm with current <cmd>.

Corepack still works but is no longer the recommended CI path (it adds a Node.js shim to every pnpm call). Prefer the standalone script above.

Monorepo CI Strategies

Build Changed Packages Only
- name: Build changed packages
  run: |
    pnpm --filter "...[origin/main]" build
Parallel Jobs per Package
jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      packages: ${{ steps.changes.outputs.packages }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - id: changes
        run: |
          echo "packages=$(pnpm --filter '...[origin/main]' list --json | jq -c '[.[].name]')" >> $GITHUB_OUTPUT

  test:
    needs: detect-changes
    if: needs.detect-changes.outputs.packages != '[]'
    runs-on: ubuntu-latest
    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - run: pnpm install --frozen-lockfile
      - run: pnpm --filter ${{ matrix.package }} test

Best Practices Summary

  1. Use pnpm ci or --frozen-lockfile in CI

  2. Install via the standalone script (get.pnpm.io/install.sh) rather than Corepack

  3. Cache the pnpm store and metadata cache (pnpm cache path), only across trusted jobs

  4. Match the CI pnpm major to the one that wrote the lockfile (CI fails on incompatible lockfiles)

  5. Pin packageManager (or devEngines.packageManager) in package.json

  6. Use --filter in monorepos to build only what changed

  7. Multi-stage Docker builds; set PATH=$PNPM_HOME/bin:$PATH

<!-- Source references: - https://pnpm.io/continuous-integration - https://pnpm.io/docker - https://pnpm.io/cli/ci - https://pnpm.io/settings/other#ci - https://pnpm.io/cli/cache-path - https://github.com/pnpm/action-setup -->

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Anthony Fu
Publisher
Anthony Fu (Community)
Source
antfu/skills · skills/pnpm · commit f63e9ec
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Anthony Fu.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download pnpm.zip