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://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/features-hooks.md


name: pnpm-hooks description: Customize resolution, config, packing, and fetching with .pnpmfile.mjs hooks, finders, and custom resolvers/fetchers

pnpm Hooks (.pnpmfile.mjs)

pnpm hooks customize installation. Declare them in .pnpmfile.mjs (ESM, preferred) or .pnpmfile.cjs (CommonJS), located next to the lockfile (workspace root for a monorepo).

The modern format uses ESM export const hooks = { ... }. The old CommonJS module.exports = { hooks } still works in .pnpmfile.cjs.

Setup

export const hooks = {
  readPackage,
  afterAllResolved,
  updateConfig,
  beforePacking,
}

Hook reference

HookWhenUse
readPackage(pkg, ctx)after a dependency manifest is parsedmutate a dependency's package.json (affects resolution)
afterAllResolved(lockfile, ctx)after resolutionmutate the lockfile before it's written
updateConfig(config)before installmutate pnpm's settings (great with config dependencies)
beforePacking(pkg, dir, context)before pnpm pack/publish tarballcustomize the published manifest only
preResolution(opts, logger)after reading lockfiles, before resolutionmutate opts.wantedLockfile/currentLockfile in place
importPackage(dir, opts)when writing to node_modulesremoved (v12.0.0) - ignored with a warning; use packageImportMethod
filterLog(log)per log entrydeprecated/ignored (v12.0.0) — use loglevel instead

readPackage

Called for every package before resolution. Common uses:

function readPackage(pkg, context) {
  // Add a missing peer dependency
  if (pkg.name === 'some-broken-package') {
    pkg.peerDependencies = { ...pkg.peerDependencies, react: '*' }
  }
  // Pin a transitive version
  if (pkg.dependencies?.lodash) pkg.dependencies.lodash = '^4.17.21'
  // Drop a problematic optional dep
  delete pkg.optionalDependencies?.fsevents
  // Replace a deprecated dep
  if (pkg.dependencies?.['old-pkg']) {
    pkg.dependencies['new-pkg'] = pkg.dependencies['old-pkg']
    delete pkg.dependencies['old-pkg']
  }
  return pkg
}

export const hooks = { readPackage }

Mutations are not written to disk; they only affect resolution. pnpm records a checksum of the pnpmfile in the lockfile and re-resolves affected dependencies when it changes. Guard fields with ?. (e.g. pkg.version?.startsWith). Removing scripts here does not stop a build — use the allowBuilds setting instead. To persist a change to a dependency's files, use pnpm patch.

updateConfig

Modify pnpm's own settings programmatically — most powerful when shipped in a config dependency so settings are shared across repos.

export const hooks = {
  updateConfig(config) {
    return Object.assign(config, {
      enablePrePostScripts: false,
      optimisticRepeatInstall: true,
      resolutionMode: 'lowest-direct',
      verifyDepsBeforeRun: 'install',
    })
  }
}
// Add a catalog entry from a plugin
export const hooks = {
  updateConfig(config) {
    config.catalogs.default ??= {}
    config.catalogs.default['is-odd'] = '1.0.0'
    return config
  }
}

Since v12.4.1, config is the resolved configuration (every setting pnpm will act on, from .npmrc, CLI, and defaults; unset keys are absent, not null). It also carries registriesByScope (scope → registry URL; rewrite to redirect fetches) and configByUri (registry URI → credentials). Since v12.3.0 the pnpmfile is loaded by many more commands (run, exec, rebuild, script shortcuts, link, outdated, import, pack, publish, stage publish), so updateConfig settings like extraEnv/extraBinPaths reach spawned processes and hook-provided catalogs resolve at pack time.

Registry routing and command-line precedence

Returning registriesByScope replaces the routing map: omitted scopes use its default (or the previous main registry); the built-in @jsr route stays unless overridden. Spread ...config.registriesByScope to keep existing routes. A returned registries key is the registries setting (merges per key), not the routing map. Since v12.8, settings given on the command line (--registry, --store-dir, --config.<key>, --no-optional, ...) win: the hook sees them but pnpm restores them after it returns. Rewriting registriesByScope does not affect routes given on the command line.

beforePacking

Customize the manifest that ends up in the published tarball without touching your local package.json.

export const hooks = {
  beforePacking(pkg, dir, context) {   // dir: project being packed; context.log(msg) for debug log
    delete pkg.devDependencies
    pkg.main = './dist/index.js'
    return pkg
  }
}

preResolution

Only in-place changes to options.wantedLockfile/currentLockfile count; the return value is ignored, and a hook that throws or leaves an invalid lockfile fails the install. With --frozen-lockfile the changed wanted lockfile is checked against manifests. logger.info(msg)/logger.warn(msg) print messages.

export const hooks = {
  preResolution({ wantedLockfile }, logger) {
    delete wantedLockfile.importers?.['.']   // re-resolve root deps
    logger.info('Dropped locked root dependencies')
  },
}

afterAllResolved

export const hooks = {
  afterAllResolved(lockfile, context) {
    context.log(`Resolved ${Object.keys(lockfile.packages || {}).length} packages`)
    return lockfile
  }
}

Finders (pnpm list / why)

Custom predicates used via --find-by:

export const finders = {
  react17: (ctx) => ctx.readManifest().peerDependencies?.react === '^17.0.0'
}
pnpm why --find-by=react17
pnpm list --find-by=react17 --find-by=other   # repeatable; matches if any finder matches

Custom resolvers & fetchers (advanced)

Register top-level resolvers/fetchers to support new package schemes (e.g. my-protocol:pkg). Each is an object with cheap canResolve/canFetch guards plus resolve/fetch. Custom resolvers run before built-ins; custom resolution type fields must use the custom: prefix.

const resolver = {
  canResolve: (dep) => dep.alias.startsWith('@company/'),
  resolve: async (dep) => ({
    id: `${dep.alias}@${dep.bareSpecifier}`,
    resolution: { type: 'custom:cdn', cdnUrl: '...' },
  }),
}
const fetcher = {
  canFetch: (id, res) => res.type === 'custom:cdn',
  fetch: (cafs, res, opts, fetchers) =>
    fetchers.remoteTarball(cafs, { tarball: res.cdnUrl, integrity: res.integrity }, opts),
}
module.exports = { resolvers: [resolver], fetchers: [fetcher] }

Built-in fetchers: localTarball, remoteTarball, gitHostedTarball, git, directory, binary (runtime archives like Node.js). A result from a built-in fetcher must be returned unchanged (changed filesMap is rejected; changed manifest/requiresBuild ignored) - return a { delegate } envelope to install different files.

hooks.fetchers was removed in v11 — use the top-level fetchers export instead.

Delegating to built-in fetchers

Instead of fetching itself, a custom fetcher can return a { delegate } envelope naming a complete, fetchable resolution for pnpm to fetch with its built-in path (single-step; a custom-typed delegate is rejected):

fetch: (cafs, resolution) => ({
  delegate: { tarball: resolution.customUrl, integrity: resolution.integrity },
})

The envelope works for every resolution in every pnpm version. In pnpm 12, cafs is a handle (with storeDir and tempDir()) to pass to the built-in fetchers.

ignorePnpmfile: false                  # ignore the pnpmfile entirely
pnpmfile: ['.pnpmfile.mjs']            # local pnpmfile location(s); default falls back to .pnpmfile.cjs if no .mjs
globalPnpmfile: ~/.pnpm/global_pnpmfile.mjs

Hooks vs Overrides

Hooks (.pnpmfile)Overrides (pnpm-workspace.yaml)
LogicJavaScriptdeclarative
Scopeany manifest field, config, lockfile, packingversions
Use whenconditional/complex fixessimple version pins

Prefer overrides/packageExtensions for simple cases; use hooks for conditional logic, config sharing, or packing tweaks.

Key Points

  • Prefer .pnpmfile.mjs with export const hooks/finders/resolvers/fetchers.

  • Hooks: updateConfig (mutate settings), beforePacking (published manifest), preResolution. importPackage was removed in v12.

  • With sharedWorkspaceLockfile: false, each project runs its own pnpmfile hooks, then the root's.

  • Pair updateConfig with config dependencies to share settings/catalogs across repos.

  • --ignore-scripts does not disable the pnpmfile; use ignorePnpmfile.

<!-- Source references: - https://pnpm.io/pnpmfile - https://pnpm.io/finders - https://pnpm.io/config-dependencies -->

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 or Anthony Fu.

Download ZIP

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