Subchapter 15.7
examples/npm-to-pnpm.mdMarkdown4 KBView on GitHub
A worked campaign showing an agentic recipe applied to a non-trivial per-repo migration. Read it for shape, then generalize.
This example shows campaign only. The production npm→pnpm migration lives in — Corepack pinning, integrity-hash format, and the VULN-545 acceptance conditions stay in the ticket, not in the skill.
Also bundled
EvalsRecipe type: agentic (a scoped sub-agent runs the migration; the validation gate proves the result). Signal type: file-existence (package-lock.json present).
intent: "Migrate each Node repo from npm to pnpm: replace package-lock.json with pnpm-lock.yaml and update CI to use pnpm."
scope: multi-repo
target_selector:
enumerate: "gh repo list bitwarden --no-archived --limit 1000 --json name,defaultBranchRef"
applicability_filter:
signal: "package-lock.json present at repo root"
detect: "gh api repos/bitwarden/<repo>/contents/package-lock.json --jq .sha"
recipe:
type: agentic
body: |
a scoped sub-agent, given only this repo: run `pnpm import`, delete package-lock.json,
update the CI workflow to pnpm, and fix any scripts that hardcode `npm`
idempotency: "pnpm-lock.yaml present AND package-lock.json absent → already migrated, no-op"
validation: "pnpm install --frozen-lockfile + repo build/test (from its CLAUDE.md) + Skill(perform-preflight)"
pr_spec:
branch: "force-multiplier/npm-to-pnpm"
title: "[PM-XXXXX] deps: Migrate from npm to pnpm"
body: "<filled from the repo's PULL_REQUEST_TEMPLATE.md>"
labels: ["ai-review"]
draft: true
safety_policy:
max_targets_per_run: 10
destructive: false # replaces a lockfile; the old one is recoverable via git history
dry_run: false
pilot: requiredWorkspace repos: If
package.jsondeclares"workspaces", the sub-agent must createpnpm-workspace.yamldeclaring the workspace packages before runningpnpm import— pnpm requires it, and workspace packages are silently skipped without it.
package-lock.json. Show the list; a repo with no lockfile, or one already on pnpm, is skipped-not-applicable.pnpm install --frozen-lockfile and the build for real. Lock the PR title/body/label. Confirm, then fan out.force-multiplier/npm-to-pnpm, hand the scoped sub-agent the migration, second-pass the diff (does its shape match the pilot — roughly a lockfile + a CI file + maybe a script?), validate, secrets-scan, commit, open the draft PR.pnpm install failed is failed with the error in Notes — not hidden.