pnpm Skill
Fast, disk-efficient package manager for this pnpm + Turborepo monorepo. pnpm owns the dependency graph, lockfile, and supply-chain policy; Turborepo owns task orchestration (build, lint, dev, --affected).
Division of Labor
| Concern | Tool | Agent action |
|---|---|---|
| Add/remove/update deps | pnpm | pnpm add, pnpm remove, pnpm up |
| Shared version pins | pnpm catalog | Edit pnpm-workspace.yaml catalog: |
| Internal packages | pnpm workspace | "@repo/foo": "workspace:*" |
| Build/lint/dev/deploy | Turborepo | pnpm turbo run <task> or pnpm <task> |
| Affected CI | Turborepo | pnpm turbo run <task> --affected |
Do not put task logic in root package.json when it belongs in packages - see the turborepo skill.
This Repo
| Setting | Value |
|---|---|
| pnpm version | 11.20.0 (pinned via packageManager in root package.json) |
| Workspace globs | apps/*, packages/* in pnpm-workspace.yaml |
| Install (local) | pnpm install |
| Install (CI) | pnpm install --frozen-lockfile |
| Update all deps | pnpm update → pnpm update --recursive --latest (bumps catalog entries in pnpm-workspace.yaml) |
| Lockfile | Single pnpm-lock.yaml (pnpm workspace default) |
| Policy location | pnpm-workspace.yaml only — project .npmrc is auth-only under pnpm 11 |
After adding a new app under apps/, run pnpm install before turbo commands.
Workspace Protocol
Always link internal packages with the workspace protocol:
{
"dependencies": {
"@repo/dtos-common": "workspace:*",
"@repo/enums-common": "workspace:*"
}
}
workspace:*- always use the local workspace version (preferred).- On publish, pnpm rewrites
workspace:to semver ranges automatically.
Never duplicate a shared package version as a registry range in an app - use workspace:*.
Catalogs (Shared Tool Versions)
Shared versions live in the default catalog in pnpm-workspace.yaml with catalogMode: prefer (prefer catalog on pnpm add when listed; one-off deps still allowed):
catalog:
'@cloudflare/vite-plugin': ^1.51.0
'@tailwindcss/vite': ^4.3.3
'@vitejs/plugin-react': ^6.0.5
babel-plugin-react-compiler: ^1.0.0
hono: ^4.13.0
oxfmt: ^0.62.0
oxlint: ^1.77.0
oxlint-tsgolint: ^7.0.2001
react: ^19.2.8
react-dom: ^19.2.8
tailwindcss: ^4.3.3
typescript: ^7.0.2
vite: ^8.2.0
wrangler: ^4.119.0
zod: ^4.5.2
TanStack packages (@tanstack/*) and @types/* are also in the catalog — see pnpm-workspace.yaml for the full list.
Reference in any package.json:
{
"dependencies": {
"hono": "catalog:"
},
"devDependencies": {
"oxfmt": "catalog:",
"oxlint": "catalog:"
}
}
Keep front-app-only utilities (e.g. rollup-plugin-visualizer) out of the catalog. Escalate catalogMode to strict only after catalog coverage is complete.
Workflow for bumping a shared tool:
- All catalog + non-catalog deps:
pnpm update- runspnpm update --recursive --latest, which rewrites catalog ranges inpnpm-workspace.yamland keeps"catalog:"in manifests. - One catalog-managed package:
pnpm update --recursive --latest <pkg>(e.g.oxfmt). - Manual pin: edit
pnpm-workspace.yamlcatalog:, thenpnpm install. - Run
pnpm run ciafter any dependency bump.
Do not bump the same package in multiple package.json files independently - use the catalog.
Workspace-level overrides (e.g. @types/node) stay in pnpm-workspace.yaml, not in individual manifests.
Agent Command Cheat Sheet
# Add runtime dep to an app
pnpm add <pkg> --filter front-app
# Add dev dep to an app
pnpm add -D <pkg> --filter worker-api
# Add tooling dep to workspace root
pnpm add -D <pkg> -w
# Remove from one package
pnpm remove <pkg> --filter front-app
# Run command in one package + its deps
pnpm --filter worker-api... <cmd>
# Run command in one package only
pnpm --filter front-app <cmd>
# Update all deps (including catalog entries in pnpm-workspace.yaml)
pnpm update
# Update one catalog-managed package to latest
pnpm up -r -L oxfmt
# Update within existing ranges only (no catalog bump)
pnpm up -r
# Inspect why a package is installed
pnpm why <pkg>
# Reproducible CI install
pnpm install --frozen-lockfile
Prefer --filter <name> over cd apps/foo && pnpm add - keeps context at repo root.
Package names: front-app, worker-api, @repo/dtos-common, @repo/enums-common, @repo/typescript-config.
Security Settings
Configured in pnpm-workspace.yaml:
| Setting | Purpose |
|---|---|
strictDepBuilds: true |
Block lifecycle scripts unless explicitly allowed |
allowBuilds |
Map of packages allowed (true) or denied (false) to run install scripts |
trustPolicy: no-downgrade |
Reject installs that would downgrade package trust/provenance |
blockExoticSubdeps: true |
Block exotic (non-registry) transitive dependencies |
minimumReleaseAge: 480 |
8-hour cooldown on newly published versions |
minimumReleaseAgeExclude |
Hotfix exceptions (@cloudflare/*, wrangler, miniflare, typescript) |
When pnpm install fails on a blocked build script:
- Decide if the script is necessary (e.g.
esbuild,workerd) or optional. - Add to
allowBuildsinpnpm-workspace.yamlwithtrueorfalse. - Re-run
pnpm install.
Do not disable strictDepBuilds to silence failures.
Hoisting Policy
Use pnpm strict linking defaults. This repository has no hoisting override: workspace dependencies use workspace:*, shared versions use catalog:, and each package declares every dependency it imports. pnpm 11 ignores non-auth project .npmrc settings, so package-manager policy belongs in pnpm-workspace.yaml. Fix a phantom dependency by declaring it in the owning package; do not add broad hoisting.
CI
GitHub Actions uses:
pnpm/setup(pnpm v11+ successor topnpm/action-setup) withruntime: node@24, storecache: true, andinstall: false(default install is not frozen)pnpm install --frozen-lockfile(version comes from rootpackage.jsonpackageManager)pnpm turbo run <task> --affected
Rules:
- Always commit
pnpm-lock.yamlwith manifest changes. - CI uses
--frozen-lockfile- lockfile must be in sync. - Never commit
.env,.dev.vars, or secrets.
Common Mistakes
| Mistake | Correct approach |
|---|---|
Duplicate root-owned tools in each package.json |
Keep OXC, Turbo, and Wrangler tooling at the narrowest actual owner |
pnpm add foo at root without -w |
Use -w for root deps, --filter for packages |
Registry version for @repo/* |
Use workspace:* |
| Registry version for a catalogued package | Use catalog: |
Put install policy in .npmrc |
Put it in pnpm-workspace.yaml |
| Hoist a missing package to mask a phantom dependency | Declare it in the importing package |
Disable strictDepBuilds |
Add entry to allowBuilds |
Run pnpm build for app tasks |
Use pnpm turbo run build or pnpm build |
Hand-edit pnpm-lock.yaml |
Run pnpm install to regenerate |
Scaffolding a New Workspace Package
- Create directory under
apps/orpackages/withpackage.json("private": true). - Add
workspace:*deps on shared packages as needed. - Use
catalog:for shared tools (hono,oxfmt,oxlint,vite, etc.). - Run
pnpm install. - Add a package
turbo.jsonwith"extends": ["//"]and atagsentry. - Add
AGENTS.mdif the package has non-trivial conventions.