pnpm 12 went stable on August 26, 2026, and the headline is the same shape as every other Rust-rewrite release this year: the CLI, config, and lockfile format all carry over from pnpm 11, install times drop by up to 90%, and the team explicitly designed it so upgrading isn’t a migration project. Mostly true. But four behavior changes underneath that “nothing to migrate” framing aren’t backward-compatible, and the one that bites first is silent — pnpm 12 stops recording SSH git dependency URLs, and an old lockfile with a [email protected]: entry just sits there unresolved until something forces a re-resolve.
None of that is visible to Claude Code unless CLAUDE.md says it. An agent troubleshooting a failed install has no way to know whether pnpm-lock.yaml needs a targeted pnpm update <package> or a full pnpm install --force, and reaching for the wrong one either doesn’t fix the problem or rewrites more of the lockfile than the situation calls for.
Browse more real-world CLAUDE.md and AGENTS.md examples in our gallery.
What Actually Changed in pnpm 12
The four changes worth stating explicitly in a CLAUDE.md, because pnpm --version alone won’t surface any of them:
- Git dependencies resolve through HTTPS only — GitHub, GitLab, and Bitbucket git dependencies now resolve through canonical HTTPS URLs regardless of how they were specified. SSH URLs (
[email protected]:owner/repo.git) are no longer written into new lockfile entries. Lockfiles generated under pnpm 11 that still contain SSH-form entries keep working until something touches that dependency — then it re-resolves under the new HTTPS scheme. engineStrictcloses an escape hatch — withengineStricton, an incompatible package used to be tolerable if it landed inoptionalDependenciesinstead ofdependencies. pnpm 12 fails the install either way. Code that relied on the optional-dependency wrapper as a workaround for an engines mismatch will start failing on a version bump that has nothing to do with the actual change being made.- Global bins for
node/deno/bunbecame project-aware —pnpm add -g node(ordeno,bun) now installs the actual runtime release, and invoking it inside a project with a pinned version runs that project’s pinned version, not whatever was installed globally. This is gated by theglobalShimssetting, but the default behavior changed. --resolution-onlyis gone —pnpm install --resolution-onlywas removed outright. Running it now returnserror: unexpected argument '--resolution-only' foundinstead of doing anything.pnpm peers checkis the replacement for what that flag used to do.
The Git Dependency Change That Breaks Silently
This is the one that costs the most debugging time, because nothing errors at upgrade time. A project pins a private git dependency with an SSH URL, upgrades to pnpm 12, and the existing lockfile entry is untouched — pnpm doesn’t rewrite lockfiles it doesn’t have to touch. The break shows up later, on whatever machine or CI runner first needs to re-resolve that dependency (a fresh clone, a pnpm update, a cache miss): the resolution now goes through HTTPS, and if that environment authenticates to the git host over SSH keys and has no HTTPS credential configured, the install fails with a generic auth error that doesn’t mention pnpm 12 anywhere.
An agent debugging that failure, reading a CLAUDE.md that documents “git dependencies use SSH, keys are in ~/.ssh/,” will spend time checking SSH config that’s no longer the relevant credential path. The fix — configure HTTPS token auth for the git host and let pnpm re-resolve, or explicitly run pnpm update <package> to force the rewrite — isn’t something an agent should guess at mid-debugging session.
# What actually needs to happen once, after upgrading to pnpm 12,
# for any private git dependency previously pinned via SSH:
pnpm update <package-name> # forces re-resolution to the new HTTPS scheme
git diff pnpm-lock.yaml # confirms the entry moved off git@host:owner/repo.git
A CLAUDE.md Template for pnpm 12
# CLAUDE.md — Package Manager
## pnpm Version
- Pinned via `packageManager` in `package.json`: `[email protected]`. This project
upgraded from pnpm 11 on {date} — the CLI and lockfile format carried over,
but git dependency resolution and `engineStrict` behavior did not.
## Git Dependencies
- All git dependencies resolve over HTTPS as of pnpm 12, regardless of how the
URL is written in `package.json`. Do not assume SSH keys are the relevant
credential path when a git-dependency install fails — check HTTPS token
auth for the git host first.
- If a git-dependency install fails after a fresh clone or cache miss, run
`pnpm update <package>` to force re-resolution before assuming the
dependency itself is broken.
## engineStrict
- `engineStrict` is {on/off}. If on: packages with an incompatible `engines`
field now fail the install even from `optionalDependencies` — there is no
wrapper-based workaround anymore. Fix the actual engines mismatch rather
than moving the package between dependency types.
## Global Tooling
- Do not run `pnpm add -g node`, `pnpm add -g deno`, or `pnpm add -g bun` to
"fix" a version-related error without checking `globalShims` first — as of
pnpm 12 these install the actual runtime and a project with a pinned
version will run that pinned version over whatever was just installed
globally.
{
"permissions": {
"allow": [
"Bash(pnpm install)",
"Bash(pnpm run *)",
"Bash(pnpm peers check*)"
],
"deny": [
"Bash(pnpm add -g node*)",
"Bash(pnpm add -g deno*)",
"Bash(pnpm add -g bun*)",
"Bash(pnpm install --resolution-only*)"
]
}
}
The --resolution-only deny isn’t there because it’s dangerous — it’s there because it no longer does anything, and a stale habit or an older troubleshooting script that still calls it will hard-error instead of silently no-op-ing. Denying it turns a confusing CI failure into an immediate, legible one.
pnpm 11 vs pnpm 12: What an Agent Needs to Know
| Behavior | pnpm 11 | pnpm 12 |
|---|---|---|
| Git dependency URLs | SSH or HTTPS, as specified | HTTPS only, on re-resolution |
optionalDependencies + engineStrict | Bypassed engine mismatch | Fails install like a regular dependency |
pnpm add -g node | Installed npm’s node wrapper package | Installs the actual Node.js runtime |
--resolution-only flag | Supported | Removed — use pnpm peers check |
Common Mistakes to Watch For
Debugging a git-dependency install failure by checking SSH keys. As of pnpm 12, that’s the wrong credential path once a dependency re-resolves — check HTTPS auth for the git host instead.
Moving a package into optionalDependencies to route around an engineStrict failure. That workaround stopped working in pnpm 12; the actual engines mismatch needs fixing.
Running pnpm add -g node as a quick fix for a version mismatch error. It now installs the real Node.js runtime and won’t override a project’s pinned version anyway — it’s more likely to add confusion than resolve anything.
Assuming a lockfile is fully migrated to pnpm 12 behavior right after the upgrade. pnpm doesn’t rewrite lockfile entries it doesn’t have to touch, so SSH-form git dependency entries can sit unchanged until something forces a re-resolve — “we upgraded pnpm” and “our lockfile reflects pnpm 12 behavior” aren’t the same statement.
The Rust rewrite itself is close to a non-event for most projects — same commands, same lockfile, dramatically faster installs. The part worth writing into CLAUDE.md isn’t the rewrite; it’s the handful of behavior changes riding along with it that only surface when something re-resolves, and that an agent has no way to distinguish from an unrelated failure unless the project says so.
Browse more real-world CLAUDE.md and AGENTS.md examples in our gallery.
FAQ
Does upgrading to pnpm 12 require migrating my lockfile?
No — pnpm 12 keeps the lockfile format byte-compatible with pnpm 11 and the team designed the release specifically so existing workflows carry over. The exception is entries that get re-resolved after the upgrade (via pnpm update, a fresh clone, or a cache miss), which pick up pnpm 12’s new resolution behavior.
Why did my private git dependency stop installing after upgrading to pnpm 12?
pnpm 12 resolves git dependencies through HTTPS only; SSH URLs are no longer recorded in new lockfile entries. If your environment authenticates to the git host over SSH keys and has no HTTPS credential configured, re-resolution fails with a generic auth error. Configure HTTPS token auth or run pnpm update <package> to force the rewrite.
Does engineStrict still allow incompatible packages in optionalDependencies?
No. In pnpm 11, wrapping a package with an incompatible engines field in optionalDependencies was a common way to bypass engineStrict. pnpm 12 closes that — the install fails the same way it would for a regular dependency.
What replaced the --resolution-only flag?
pnpm install --resolution-only was removed in pnpm 12 and returns an “unexpected argument” error if called. pnpm peers check covers the same use case.
What does pnpm add -g node do differently in pnpm 12?
It installs the actual Node.js runtime release rather than npm’s node wrapper package, and — depending on the globalShims setting — a project with a pinned Node version will run that pinned version instead of whatever was just installed globally.