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
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/antfu/pnpm/downloadUnzip 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
| Topic | Description | Reference |
|---|---|---|
| CLI Commands | install/add/remove/update, run, dlx/pnx, workspace, runtime, publishing (version, view, sbom, stage) | core-cli |
| Configuration | pnpm-workspace.yaml settings (camelCase), global config.yaml, config includes, packageConfigs, auth (.npmrc, auth.ini) | core-config |
| Workspaces | Monorepo support: filtering, workspace protocol, shared lockfile, packageConfigs | core-workspaces |
| Store | Content-addressable store, virtual store, node linker modes (incl. loaded/store loader), frozen/read-only store | core-store |
Features
| Topic | Description | Reference |
|---|---|---|
| Catalogs | Centralized dependency versions; catalogMode, catalog: in overrides | features-catalogs |
| Overrides | Force versions (incl. transitive & peer deps); packageExtensions | features-overrides |
| Patches | Modify third-party packages; patchedDependencies in pnpm-workspace.yaml | features-patches |
| Aliases | Install under custom names (npm:) and registry aliases (namedRegistries) | features-aliases |
| Hooks | .pnpmfile.mjs hooks (readPackage, updateConfig, beforePacking), finders, resolvers/fetchers | features-hooks |
| Peer Dependencies | Auto-install, strict mode, rules, dedupePeers, peers check | features-peer-deps |
| Config Dependencies | Share hooks/settings/catalogs/patches across repos via configDependencies | features-config-dependencies |
| Global Virtual Store & Shims | Shared node_modules, git-worktree multi-agent setups, isolated global packages, project-aware bins, other package managers | features-global-virtual-store |
| Supply-Chain Security | Build approval (allowBuilds), permissions and agent skills, minimumReleaseAge, trustPolicy, lockfile integrity | features-supply-chain-security |
| Task Orchestration | Cross-project task graphs (tasks/dependsOn), concurrency groups, priority, pnpm pipeline | features-task-orchestration |
| Release Management | Native versioning: pnpm change/version -r/lane, lanes, epics, fixed groups | features-versioning |
| Multi-Ecosystem | Python (pypi:) and Cargo (crate:) dependencies alongside npm (experimental) | features-multi-ecosystem |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| CI/CD Setup | GitHub Actions, GitLab, Docker, pnpm ci, store caching, frozen lockfiles | best-practices-ci |
| Migration | npm/Yarn → pnpm, phantom deps, and pnpm v10 → v11 → v12 upgrade notes | best-practices-migration |
| Performance | Install optimizations, allowBuilds, global virtual store, workspace parallelization | best-practices-performance |
Files
- ATTRIBUTION.mdAdded by AutoGineer889 B
- GENERATION.md134 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md5.1 KiB
- references/best-practices-ci.md9.7 KiB
- references/best-practices-migration.md9.9 KiB
- references/best-practices-performance.md6.0 KiB
- references/core-cli.md11.3 KiB
- references/core-config.md12.8 KiB
- references/core-store.md10.7 KiB
- references/core-workspaces.md6.6 KiB
- references/features-aliases.md4.4 KiB
- references/features-catalogs.md5.0 KiB
- references/features-config-dependencies.md4.2 KiB
- references/features-global-virtual-store.md10.0 KiB
- references/features-hooks.md8.9 KiB
- references/features-multi-ecosystem.md6.2 KiB
- references/features-overrides.md5.5 KiB
- references/features-patches.md5.0 KiB
- references/features-peer-deps.md5.9 KiB
- references/features-supply-chain-security.md7.7 KiB
- references/features-task-orchestration.md6.8 KiB
- references/features-versioning.md5.5 KiB
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 CommonJSmodule.exports = { hooks }still works in.pnpmfile.cjs.
Setup
export const hooks = {
readPackage,
afterAllResolved,
updateConfig,
beforePacking,
}Hook reference
| Hook | When | Use |
|---|---|---|
readPackage(pkg, ctx) | after a dependency manifest is parsed | mutate a dependency's package.json (affects resolution) |
afterAllResolved(lockfile, ctx) | after resolution | mutate the lockfile before it's written |
updateConfig(config) | before install | mutate pnpm's settings (great with config dependencies) |
beforePacking(pkg, dir, context) | before pnpm pack/publish tarball | customize the published manifest only |
preResolution(opts, logger) | after reading lockfiles, before resolution | mutate opts.wantedLockfile/currentLockfile in place |
importPackage(dir, opts) | when writing to node_modules | removed (v12.0.0) - ignored with a warning; use packageImportMethod |
filterLog(log) | per log entry | deprecated/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). Removingscriptshere does not stop a build — use theallowBuildssetting instead. To persist a change to a dependency's files, usepnpm 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,
configis the resolved configuration (every setting pnpm will act on, from.npmrc, CLI, and defaults; unset keys are absent, notnull). It also carriesregistriesByScope(scope → registry URL; rewrite to redirect fetches) andconfigByUri(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), soupdateConfigsettings likeextraEnv/extraBinPathsreach 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 matchesCustom 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.fetcherswas removed in v11 — use the top-levelfetchersexport 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,
cafsis a handle (withstoreDirandtempDir()) to pass to the built-infetchers.
Related settings
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.mjsHooks vs Overrides
| Hooks (.pnpmfile) | Overrides (pnpm-workspace.yaml) | |
|---|---|---|
| Logic | JavaScript | declarative |
| Scope | any manifest field, config, lockfile, packing | versions |
| Use when | conditional/complex fixes | simple version pins |
Prefer overrides/packageExtensions for simple cases; use hooks for conditional logic, config sharing, or packing tweaks.
Key Points
Prefer
.pnpmfile.mjswithexport const hooks/finders/resolvers/fetchers.Hooks:
updateConfig(mutate settings),beforePacking(published manifest),preResolution.importPackagewas removed in v12.With
sharedWorkspaceLockfile: false, each project runs its own pnpmfile hooks, then the root's.Pair
updateConfigwith config dependencies to share settings/catalogs across repos.--ignore-scriptsdoes not disable the pnpmfile; useignorePnpmfile.
<!-- 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.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download pnpm.zip