src/skills/shared-monorepo-pnpm-workspaces/SKILL.md
pnpm workspace protocol, filtering, catalogs, shared dependencies, publishing, and CI/CD for monorepo management
npx skillsauth add agents-inc/skills shared-monorepo-pnpm-workspacesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: pnpm 10.x workspaces for monorepo management.
pnpm-workspace.yamldefines workspace packages.workspace:*protocol for internal linking.catalog:protocol for dependency version synchronization.--filterfor targeted commands. Sharedtsconfigand tooling config across packages. Changesets for versioning and publishing. Strict dependency isolation by default.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use workspace:* protocol for ALL internal package dependencies -- never hardcode versions)
(You MUST use --frozen-lockfile in CI -- pnpm enables this by default in CI environments)
(You MUST define workspace packages in pnpm-workspace.yaml at the repository root)
(You MUST put pnpm-specific settings in pnpm-workspace.yaml -- NOT .npmrc (pnpm v10 change))
(You MUST use catalog: protocol when sharing dependency versions across 3+ packages)
(You MUST use --filter for targeted commands instead of pnpm -r when only specific packages changed)
</critical_requirements>
Auto-detection: pnpm-workspace.yaml, pnpm workspaces, workspace protocol, workspace:*, catalog:, pnpm filter, pnpm recursive, pnpm monorepo, pnpm-lock.yaml, .npmrc pnpm, pnpm catalogs, pnpm publish
When to use:
pnpm-workspace.yaml for workspace package discoveryworkspace:* protocolcatalog: protocol--filter or -rWhen NOT to use:
Key patterns covered:
pnpm-workspace.yaml setup and workspace package discoveryworkspace:*, workspace:^, workspace:~)--filter, -F, glob patterns, dependency selectors)-r, --parallel, --workspace-concurrency)pnpm-workspace.yaml (v10: settings moved from .npmrc)publishConfig and changesets--frozen-lockfileDetailed resources:
pnpm workspaces provide strict, efficient monorepo management with content-addressable storage. Unlike npm/yarn, pnpm creates a non-flat node_modules where packages can only access their declared dependencies -- this strictness catches missing dependency declarations early. The workspace protocol (workspace:*) ensures internal packages always link locally, while catalogs (catalog:) centralize version management to eliminate version drift.
Core principles:
pnpm-lock.yaml at the workspace root for all packagesWhen to use pnpm workspaces:
When NOT to use:
pnpm-workspace.yaml)The pnpm-workspace.yaml file at the repository root defines which directories contain workspace packages. Every pnpm workspace MUST have this file.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
In pnpm v10, settings moved from .npmrc to this file:
# pnpm-workspace.yaml (v10+)
packages:
- "apps/*"
- "packages/*"
linkWorkspacePackages: true
saveWorkspaceProtocol: rolling
disallowWorkspaceCycles: true
Why good: Single source of truth for workspace definition and settings
See examples/core.md for full workspace setup, directory structure, and all settings.
workspace:*)The workspace protocol ensures internal packages always resolve to the local workspace version, never from the registry.
| Protocol | During Development | After pnpm publish |
| ------------- | ---------------------- | ------------------------------------------- |
| workspace:* | Links to local package | Replaced with exact version (e.g., 1.5.0) |
| workspace:^ | Links to local package | Replaced with caret range (e.g., ^1.5.0) |
| workspace:~ | Links to local package | Replaced with tilde range (e.g., ~1.5.0) |
{
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/types": "workspace:*"
}
}
Why good: Guarantees local linking, pnpm refuses to resolve externally, version conversion on publish ensures correct semver
{
"dependencies": {
"@repo/ui": "^1.0.0"
}
}
Why bad: Hardcoded versions may install from npm registry instead of local workspace, version mismatches across packages
See examples/packages.md for protocol variants, aliasing, and internal package setup.
Catalogs define dependency versions once in pnpm-workspace.yaml and reference them across all packages with catalog:.
# pnpm-workspace.yaml
catalog:
react: ^19.0.0
react-dom: ^19.0.0
typescript: ^5.7.0
{
"dependencies": {
"react": "catalog:",
"react-dom": "catalog:"
}
}
Why good: Single version source of truth, updating one line updates all packages, eliminates merge conflicts
Named catalogs support version migration:
catalogs:
react18:
react: ^18.3.1
react19:
react: ^19.0.0
See examples/packages.md for named catalogs, strict enforcement, and full examples.
--filter (or -F) restricts commands to specific packages instead of running across the entire workspace.
# Exact package
pnpm --filter @repo/web-app build
# Package and ALL its dependencies
pnpm --filter "web-app..." build
# Changed packages since main
pnpm --filter "...[origin/main]" test
# Exclude a package
pnpm --filter "!@repo/docs" build
Why good: Targeted execution saves CI time, dependency-aware filtering ensures correct build order
# BAD: Running everything when only one package changed
pnpm -r build
Why bad: Wastes CI time rebuilding all packages, no change detection
See examples/scripts.md for all filter variants, CI-optimized patterns, and graph exploration.
# Topological order (respects dependency graph) -- use for build
pnpm -r build
# Parallel (ignores dependency graph) -- use for test, lint
pnpm -r --parallel test
# Controlled concurrency
pnpm -r --workspace-concurrency 4 build
# BAD: Building in parallel when packages depend on each other
pnpm -r --parallel build
Why bad: Packages may build before their dependencies finish, causing missing module failures
See examples/scripts.md for dependency management and adding dependencies.
Share TypeScript compiler options across all workspace packages using a configuration package.
{
"name": "@repo/config-typescript",
"private": true,
"exports": {
"./base": "./tsconfig.base.json",
"./react": "./tsconfig.react.json",
"./node": "./tsconfig.node.json"
}
}
Consumer usage:
{
"extends": "@repo/config-typescript/react",
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
Why good: Single source of truth for TypeScript settings, changes propagate to all packages
See examples/packages.md for full base/react/node configs and ESLint sharing.
Use publishConfig to control what gets published and changesets for versioning.
{
"name": "@repo/ui",
"main": "./src/index.ts",
"publishConfig": {
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"access": "public"
}
}
Why good: Source during development (fast HMR), built output on publish
pnpm changeset # Create changeset
pnpm changeset version # Bump versions + changelogs
pnpm publish -r # Publish to npm
See examples/publishing.md for changesets config, Docker deployment, and migration.
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install
- run: pnpm --filter "...[origin/main]" build
- run: pnpm --filter "...[origin/main]" test
Why good: pnpm/action-setup@v4 handles installation, cache: "pnpm" caches the store, --frozen-lockfile is automatic in CI, change-based filtering only builds affected packages
See examples/ci.md for complete workflows including automated release with changesets.
</patterns>Install Performance:
--frozen-lockfile (default in CI) skips resolution for fastest installsWorkspace Performance:
--filter to run commands only on affected packages--parallel for tasks without cross-package dependencies (test, lint)--workspace-concurrency to control parallelism on resource-constrained CI--filter "...[origin/main]") skips unchanged packagesDisk Savings:
pnpm store path)CI Caching:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
This caches the pnpm content-addressable store between runs, making subsequent installs near-instant.
</performance><decision_framework>
Does 3+ packages use this dependency?
YES -> Use catalog: protocol
NO -> Direct version is fine
Will this dependency version be updated frequently?
YES -> Use catalog: (one-line update)
NO -> Direct version is acceptable
Are you publishing packages to npm?
NO -> Use workspace:* (exact local linking, version irrelevant)
YES -> Do consumers need flexible version ranges?
YES -> workspace:^ (caret range on publish)
NO -> workspace:* (exact version on publish)
Running a command in CI?
YES -> Use --filter "...[origin/main]" (only affected packages)
NO -> Running locally?
ALL packages -> pnpm -r <cmd>
ONE package -> pnpm --filter <name> <cmd>
Is the command order-dependent (build)?
YES -> Use -r (topological order) or --filter with ...
NO -> Use --parallel for speed (lint, test)
Need strict dependency isolation?
YES -> pnpm (non-flat node_modules by default)
NO -> Any works
Need disk efficiency?
YES -> pnpm (content-addressable store)
NO -> Any works
Need zero-config PnP (no node_modules)?
YES -> Yarn Berry with PnP
NO -> pnpm or npm
</decision_framework>
<red_flags>
High Priority Issues:
workspace:* (breaks local linking, installs from registry).npmrc that should be in pnpm-workspace.yaml (silently ignored in pnpm v10)pnpm-workspace.yaml at repo root (pnpm will not recognize workspace packages)pnpm install without --frozen-lockfile in CI (lockfile can mutate silently)--parallel for build commands when packages depend on each other (race conditions)Medium Priority Issues:
catalog: when 3+ packages share the same dependency version (version drift)pnpm -r build in CI instead of --filter "...[origin/main]" (wastes time)private: true on internal packages (risk of accidental npm publish)allowBuilds (or the older onlyBuiltDependencies) allowlist (blocks all install scripts in v10)Common Mistakes:
shamefullyHoist: true as a quick fix instead of declaring missing dependencies properlyfetch-depth: 0 in GitHub Actions checkout (breaks git-based change detection)Gotchas & Edge Cases:
workspace:* is replaced with the actual version on pnpm publish -- this is expected behavior, not a bugcatalog: entries must match the dependency name exactly -- typos silently fall through--filter "...[origin/main]" requires git history -- use fetch-depth: 0 or at minimum fetch-depth: 2pnpm approve-builds to allowlist packages that need postinstall (like esbuild, sharp), or configure allowBuilds in pnpm-workspace.yamlinjectWorkspacePackages: true is required for pnpm deploy (or use pnpm deploy --legacy to bypass)disallowWorkspaceCycles: truesaveWorkspaceProtocol: rolling (default) means pnpm add auto-saves with workspace: -- this is correct behavior</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use workspace:* protocol for ALL internal package dependencies -- never hardcode versions)
(You MUST use --frozen-lockfile in CI -- pnpm enables this by default in CI environments)
(You MUST define workspace packages in pnpm-workspace.yaml at the repository root)
(You MUST put pnpm-specific settings in pnpm-workspace.yaml -- NOT .npmrc (pnpm v10 change))
(You MUST use catalog: protocol when sharing dependency versions across 3+ packages)
(You MUST use --filter for targeted commands instead of pnpm -r when only specific packages changed)
Failure to follow these rules will cause broken dependency resolution, version drift, missed CI caching, and security vulnerabilities.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety