Claude Skill

shared-monorepo-turborepo

Turborepo, workspaces, package architecture, @repo/* naming, exports, tree-shaking

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download agents-inc-skills-dist_plugins_shared-monorepo-turborepo_skills_shared-monorepo-turborepo-3a51ef5.zip · 14 KB
Part of agents-inc/skills — 130 skills

Install

skills CLI npx skills add https://github.com/agents-inc/skills/tree/main/dist/plugins/shared-monorepo-turborepo/skills/shared-monorepo-turborepo
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install agents-inc-skills@llmmart
Git git clone https://github.com/agents-inc/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole agents-inc/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Monorepo Orchestration with Turborepo

Quick Guide: Turborepo 2.x for monorepo orchestration. Task pipelines with dependency ordering. Local + remote caching for massive speed gains. Workspaces for package linking. Syncpack for dependency version consistency. Internal packages use @repo/* naming, explicit exports fields, and workspace:* protocol.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST define task dependencies using dependsOn: ["^build"] in turbo.json to ensure topological ordering)

(You MUST declare all environment variables in the env array of turbo.json tasks for proper cache invalidation)

(You MUST set cache: false for tasks with side effects like dev servers and code generation)

(You MUST use workspace:* protocol for internal package dependencies)

(You MUST use @repo/* naming convention for ALL internal packages)

(You MUST define explicit exports field in package.json - never allow importing internal paths)

(You MUST mark React as peerDependencies NOT dependencies in component packages)

</critical_requirements>


Auto-detection: Turborepo configuration, turbo.json, monorepo setup, workspaces, Bun workspaces, syncpack, task pipelines, @repo/* packages, package.json exports, workspace dependencies, shared configurations

When to use:

  • Configuring Turborepo task pipeline and caching strategies
  • Setting up workspaces for monorepo package linking
  • Enabling remote caching for team/CI cache sharing
  • Synchronizing dependency versions across workspace packages
  • Creating new internal packages in packages/
  • Configuring package.json exports for tree-shaking
  • Setting up shared configuration packages (@repo/eslint-config, @repo/typescript-config)

When NOT to use:

  • Single application projects (use standard build tools directly)
  • Projects without shared packages (no monorepo benefits)
  • Very small projects where setup overhead exceeds caching benefits
  • Polyrepo architecture is preferred over monorepo
  • Projects already using Nx or Lerna (don't mix monorepo tools)
  • App-specific code that won't be shared (keep in app directory)

Key patterns covered:

  • Turborepo 2.x task pipeline (dependsOn, outputs, inputs, cache)
  • Local and remote caching strategies
  • Workspaces for package linking
  • Syncpack for dependency version consistency
  • Environment variable handling in turbo.json
  • Package structure and @repo/* naming conventions
  • package.json exports for tree-shaking
  • Named exports and barrel file patterns
  • Internal dependencies with workspace protocol

Detailed Resources:





<decision_framework>

Decision Framework

New code? → Shared across 2+ apps? → packages/ (else keep in app)
Monorepo? → Builds > 30s or caching matters? → Use Turborepo

For comprehensive decision trees and package creation criteria, see reference.md.

</decision_framework>


<red_flags>

RED FLAGS

High Priority Issues:

  • Missing dependsOn: ["^build"] for build tasks (breaks topological ordering)
  • Missing env array in turbo.json (causes cache misses across environments)
  • Caching dev servers or code generation (incorrect outputs reused)
  • Default exports in library packages (breaks tree-shaking)
  • Missing exports field in package.json (allows internal path imports)

Common Mistakes:

  • Hardcoded versions instead of workspace:* for internal deps
  • React in dependencies instead of peerDependencies
  • Giant barrel files re-exporting everything (negates tree-shaking)
  • Running full test suite without --filter=...[HEAD^] affected detection

Gotchas:

  • dependsOn: ["^task"] runs dependencies' tasks; dependsOn: ["task"] runs same package's task
  • --filter=...[HEAD^] requires fetch-depth: 2 in GitHub Actions
  • Exclude cache directories in outputs: !.next/cache/**

For detailed anti-patterns and checklists, see reference.md.

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST define task dependencies using dependsOn: ["^build"] in turbo.json to ensure topological ordering)

(You MUST declare all environment variables in the env array of turbo.json tasks for proper cache invalidation)

(You MUST set cache: false for tasks with side effects like dev servers and code generation)

(You MUST use workspace:* protocol for internal package dependencies)

(You MUST use @repo/* naming convention for ALL internal packages)

(You MUST define explicit exports field in package.json - never allow importing internal paths)

(You MUST mark React as peerDependencies NOT dependencies in component packages)

Failure to follow these rules will cause incorrect builds, cache misses, broken dependency resolution, and tree-shaking failures.

</critical_reminders>

Files (skills)
  • examples
    • caching.md 4.1 KB
      # Turborepo - Caching Examples
      
      > Remote caching, CI/CD integration, and cache behavior patterns. See [../SKILL.md](../SKILL.md) for core concepts and [core.md](core.md) for essential task pipeline patterns.
      
      **Related Examples:**
      
      - [core.md](core.md) - Essential task pipeline patterns (prerequisite)
      - [workspaces.md](workspaces.md) - Workspace protocol, syncpack
      - [packages.md](packages.md) - Internal package conventions
      
      ---
      
      ## Remote Caching Configuration
      
      ### Good Example - Remote caching with signature verification
      
      ```json
      {
        "remoteCache": {
          "signature": true
        },
        "tasks": {
          "build": {
            "env": ["DATABASE_URL", "NODE_ENV", "API_URL"]
          }
        }
      }
      ```
      
      **Why good:** `signature: true` enables cache verification for security, `env` array declares all environment variables so different values trigger rebuilds, remote cache shares artifacts across team and CI reducing redundant builds
      
      ---
      
      ## Advanced Caching Configuration
      
      ### Good Example - Full turbo.json with advanced caching
      
      ```json
      // turbo.json - Advanced caching configuration (Turborepo 2.x)
      {
        "$schema": "https://turborepo.dev/schema.json",
        "globalDependencies": [".env", "tsconfig.json", "eslint.config.js"],
        "tasks": {
          "build": {
            "dependsOn": ["^build"],
            "outputs": ["dist/**", ".next/**", "build/**", "!.next/cache/**"],
            "cache": true
          },
          "test": {
            "dependsOn": ["^build"],
            "outputs": ["coverage/**"],
            "cache": true,
            "inputs": ["src/**/*.ts", "src/**/*.tsx", "**/*.test.ts", "**/*.test.tsx"]
          },
          "lint": {
            "cache": true,
            "outputs": []
          },
          "dev": {
            "cache": false,
            "persistent": true
          }
        },
        "remoteCache": {
          "signature": true
        }
      }
      ```
      
      **Why good:** `globalDependencies` ensures changes to shared config files invalidate all caches, `inputs` array fine-tunes what triggers test cache invalidation, `outputs: []` for lint means it caches the result without storing files, remote cache with signature verification is secure
      
      ---
      
      ## Cache Hit Examples
      
      ```bash
      # Local development - uses local cache
      turbo run build
      # Cache miss - Building...
      # Packages built: 5
      # Time: 45.2s
      
      # Second run - hits cache
      turbo run build
      # Cache hit - Skipping...
      # Packages restored: 5
      # Time: 1.2s (97% faster)
      
      # Only rebuilds changed packages
      # Edit packages/ui/src/Button.tsx
      turbo run build
      # Cache hit: @repo/types, @repo/config, @repo/api-client
      # Cache miss: @repo/ui (changed)
      # Cache miss: web, admin (depend on @repo/ui)
      # Time: 12.4s (73% faster)
      ```
      
      ---
      
      ## CI/CD Integration Examples
      
      ### Good Example - Remote caching in GitHub Actions
      
      ```yaml
      # .github/workflows/ci.yml - Remote caching in CI
      name: CI
      on: [push, pull_request]
      
      jobs:
        build:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
              with:
                fetch-depth: 2 # Needed for --filter
      
            # Install dependencies with your package manager
            - run: bun install
      
            # Remote cache with Vercel
            - name: Build
              run: turbo run build
              env:
                TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
                TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
      
            # Only run affected tests on PRs
            - name: Test affected
              if: github.event_name == 'pull_request'
              run: turbo run test --filter=...[HEAD^]
      
            # Run all tests on main
            - name: Test all
              if: github.event_name == 'push' && github.ref == 'refs/heads/main'
              run: turbo run test
      ```
      
      **Why good:** `fetch-depth: 2` enables affected detection with `--filter=...[HEAD^]`, remote cache tokens shared via secrets, affected tests run only on PRs to save CI time, full tests run on main for comprehensive coverage
      
      ---
      
      ## Package.json Scripts Examples
      
      ### Good Example - Remote cache setup scripts
      
      ```json
      // package.json - Remote cache setup
      {
        "scripts": {
          "build": "turbo run build",
          "build:fresh": "turbo run build --force",
          "build:affected": "turbo run build --filter=...[HEAD^1]",
          "test:affected": "turbo run test --filter=...[HEAD^1]"
        }
      }
      ```
      
      **Why good:** `:fresh` script bypasses cache when needed, `:affected` scripts only run tasks for changed packages, clear naming convention indicates purpose
      
      ---
      
    • core.md 3.8 KB
      # Turborepo - Core Examples
      
      > Essential task pipeline patterns for Turborepo. See [SKILL.md](../SKILL.md) for core concepts and [reference.md](../reference.md) for decision frameworks.
      
      **Additional Examples:**
      
      - [caching.md](caching.md) - Remote caching, CI/CD integration
      - [workspaces.md](workspaces.md) - Workspace protocol, syncpack, dependency boundaries
      - [packages.md](packages.md) - Internal package conventions, exports, creating packages
      
      ---
      
      ## Task Pipeline Examples
      
      ### Good Example - Proper task configuration with dependencies
      
      ```json
      {
        "tasks": {
          "build": {
            "dependsOn": ["^build"],
            "env": ["DATABASE_URL", "NODE_ENV"],
            "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
          },
          "test": {
            "dependsOn": ["^build"],
            "inputs": [
              "$TURBO_DEFAULT$",
              "src/**/*.tsx",
              "src/**/*.ts",
              "test/**/*.ts",
              "test/**/*.tsx"
            ]
          },
          "dev": {
            "cache": false,
            "persistent": true
          },
          "generate": {
            "dependsOn": ["^generate"],
            "cache": false
          },
          "lint": {}
        }
      }
      ```
      
      **Why good:** `dependsOn: ["^build"]` ensures topological task execution (dependencies build first), `env` array includes all environment variables for proper cache invalidation, `cache: false` prevents caching tasks with side effects (dev servers, code generation), `outputs` specifies cacheable artifacts while excluding cache directories
      
      ### Bad Example - Missing critical configuration
      
      ```json
      {
        "tasks": {
          "build": {
            "outputs": ["dist/**"]
            // BAD: No dependsOn - dependencies may not build first
            // BAD: No env array - environment changes won't invalidate cache
          },
          "dev": {
            "persistent": true
            // BAD: Missing cache: false - dev server output gets cached
          },
          "generate": {
            "dependsOn": ["^generate"]
            // BAD: Missing cache: false - generated files get cached
          }
        }
      }
      ```
      
      **Why bad:** Missing `dependsOn` breaks topological ordering (packages may build before their dependencies), missing `env` array causes stale builds when environment variables change, caching dev servers or code generation tasks causes incorrect cached outputs to be reused
      
      ---
      
      ## Environment Variable Examples
      
      ### Good Example - All env vars declared
      
      ```json
      {
        "tasks": {
          "build": {
            "dependsOn": ["^build"],
            "env": ["API_URL", "NODE_ENV", "DATABASE_URL"],
            "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
          },
          "dev": {
            "cache": false,
            "persistent": true,
            "env": ["API_URL", "NODE_ENV"]
          }
        }
      }
      ```
      
      **Why good:** All environment variables explicitly declared in `env` array, cache invalidates when env values change, ESLint can validate undeclared usage, different environments (dev/staging/prod) properly trigger rebuilds
      
      ### Bad Example - Missing env declarations
      
      ```json
      {
        "tasks": {
          "build": {
            "dependsOn": ["^build"],
            "outputs": ["dist/**"]
            // BAD: No env array - using DATABASE_URL won't invalidate cache
          }
        }
      }
      ```
      
      **Why bad:** Missing `env` array means environment variable changes don't invalidate cache, stale builds with wrong config get reused across environments, ESLint can't catch undeclared variable usage
      
      ---
      
      ## ESLint Integration Example
      
      ### Good Example - Turborepo ESLint plugin
      
      ```javascript
      // packages/eslint-config/base.js
      export const baseConfig = [
        {
          plugins: {
            turbo: turboPlugin,
          },
          rules: {
            "turbo/no-undeclared-env-vars": "warn",
          },
        },
      ];
      ```
      
      **Why good:** ESLint warns when env vars are used but not declared in turbo.json, prevents cache invalidation bugs at development time
      
      **Alternative:** Biome 2.3.10+ includes a native `noUndeclaredEnvVars` rule in its Turborepo domain, eliminating the ESLint dependency for this check.
      
      ---
      
    • packages.md 8 KB
      # Turborepo - Internal Package Examples
      
      > Internal package conventions, exports, and creation patterns. See [../SKILL.md](../SKILL.md) for core concepts and [../reference.md](../reference.md) for decision frameworks.
      
      **Related Examples:**
      
      - [workspaces.md](workspaces.md) - Workspace protocol (prerequisite)
      - [core.md](core.md) - Essential task pipeline patterns
      - [caching.md](caching.md) - Remote caching, CI/CD integration
      
      ---
      
      ## Naming Conventions
      
      ### Package Naming
      
      ```typescript
      // Good Example - Package naming
      // package.json
      {
        "name": "@repo/ui",           // @repo/* prefix, kebab-case
        "name": "@repo/api-client",   // Multi-word: kebab-case
        "name": "@repo/eslint-config" // Config package: kebab-case
      }
      
      // Good Example - File naming
      // button.tsx (NOT Button.tsx)
      // use-auth.ts (NOT useAuth.ts)
      // api-client.ts (NOT apiClient.ts or api_client.ts)
      
      // Good Example - Export naming
      export { Button } from "./button";              // PascalCase for components
      export { useAuth, formatDate } from "./utils";   // camelCase for functions/hooks
      export { API_TIMEOUT_MS } from "./constants";    // SCREAMING_SNAKE_CASE for constants
      ```
      
      **Why good:** Consistent naming enables predictable imports, kebab-case files work across all OS filesystems, @repo prefix prevents namespace collisions with npm packages
      
      ```typescript
      // Bad Example - Inconsistent naming
      {
        "name": "ui",                 // BAD: Missing @repo/ prefix
        "name": "@repo/API-Client",   // BAD: PascalCase package name
        "name": "@mycompany/ui"       // BAD: Custom namespace (use @repo)
      }
      
      // Button.tsx                   // BAD: PascalCase file name
      // useAuth.ts                   // BAD: camelCase file name
      // api_client.ts                // BAD: snake_case file name
      
      export default Button;          // BAD: Default export
      ```
      
      **Why bad:** Missing @repo prefix causes namespace confusion, PascalCase files break on case-sensitive filesystems, default exports prevent tree-shaking and cause naming conflicts
      
      ---
      
      ## package.json Configuration
      
      ### Essential Fields
      
      ```json
      {
        "name": "@repo/ui",
        "version": "0.0.0",
        "private": true,
        "type": "module",
        "exports": {
          "./button": "./src/components/button/button.tsx",
          "./switch": "./src/components/switch/switch.tsx",
          "./hooks": "./src/hooks/index.ts",
          "./styles/*": "./src/styles/*"
        },
        "scripts": {
          "lint": "eslint .",
          "type-check": "tsc --noEmit"
        },
        "peerDependencies": {
          "react": "^19.0.0",
          "react-dom": "^19.0.0"
        },
        "devDependencies": {
          "@repo/eslint-config": "workspace:*",
          "@repo/typescript-config": "workspace:*",
          "typescript": "^5.7.0"
        }
      }
      ```
      
      **Why good:** Explicit exports enable tree-shaking, workspace protocol ensures local versions always used, peerDependencies prevent React version conflicts, private true prevents accidental publishing
      
      ```json
      // Bad Example - Missing exports and wrong dependencies
      {
        "name": "@repo/ui",
        "version": "0.0.0",
        // BAD: No exports field - allows importing internal paths
        "main": "./src/index.ts",
        "dependencies": {
          "react": "^19.0.0", // BAD: Should be peerDependencies
          "@repo/eslint-config": "^1.0.0" // BAD: Should use workspace:*
        }
      }
      ```
      
      **Why bad:** Missing exports allows importing internal paths breaking encapsulation, React in dependencies causes version duplication, hardcoded versions create version conflicts in monorepo
      
      ---
      
      ## Exports Field Pattern
      
      Define explicit exports for each public API to enable tree-shaking and encapsulation.
      
      ```json
      {
        "exports": {
          "./button": "./src/components/button/button.tsx",
          "./switch": "./src/components/switch/switch.tsx",
          "./hooks": "./src/hooks/index.ts"
        }
      }
      ```
      
      **Why good:** Explicit exports enable aggressive tree-shaking, prevents coupling to internal file structure, makes API surface clear to consumers
      
      ```json
      // Bad Example - No exports or barrel file anti-pattern
      {
        // BAD: No exports - allows deep imports
        "main": "./src/index.ts"
      }
      
      // OR worse - barrel file anti-pattern
      {
        "exports": {
          ".": "./src/index.ts"  // BAD: Giant barrel file that re-exports everything
        }
      }
      ```
      
      **Why bad:** No exports allows deep imports like `@repo/ui/src/internal/utils` breaking encapsulation, barrel files bundle all code even if only one component is imported
      
      ---
      
      ## Usage Pattern
      
      ```typescript
      // Good Example - Import from explicit exports
      import { Button } from "@repo/ui/button";
      import { Switch } from "@repo/ui/switch";
      import { useClickOutside } from "@repo/ui/hooks";
      ```
      
      **Why good:** Each import maps to a single file, bundler can tree-shake unused components, clear and predictable import paths
      
      ```typescript
      // Bad Example - Import from internal paths
      import { Button } from "@repo/ui/src/components/button/button";
      import { Switch } from "@repo/ui/src/components/switch/switch";
      ```
      
      **Why bad:** Couples to internal file structure, breaks when package refactors, bypasses intended public API, tree-shaking may fail
      
      ---
      
      ## Barrel Files (Use Sparingly)
      
      Barrel files for small groups only, prefer package.json exports for tree-shaking.
      
      ```typescript
      // Good Example - Small barrel file for related items
      // packages/ui/src/hooks/index.ts
      export { useClickOutside } from "./use-click-outside";
      export { useDebounce } from "./use-debounce";
      export { useMediaQuery } from "./use-media-query";
      export type { DebounceOptions } from "./use-debounce";
      ```
      
      **Why good:** Small barrels (<10 exports) group related items, package.json exports still controls public API, manageable cognitive load
      
      **When to use:** Only for grouping 3-10 tightly related exports (e.g., hooks, utils)
      
      ```typescript
      // Bad Example - Giant barrel file
      // packages/ui/src/index.ts
      export * from "./components/button/button";
      export * from "./components/switch/switch";
      export * from "./components/dialog/dialog";
      export * from "./components/input/input";
      // ... 50 more exports
      ```
      
      **Why bad:** Giant barrels break tree-shaking (bundler loads entire file), slow TypeScript compilation, IDE struggles with autocomplete, defeats purpose of explicit exports
      
      **When not to use:** For large numbers of exports, prefer explicit package.json exports field instead
      
      ---
      
      ## Package Types
      
      ### Component Library Package
      
      ```json
      {
        "name": "@repo/ui",
        "exports": {
          "./button": "./src/components/button/button.tsx",
          "./switch": "./src/components/switch/switch.tsx"
        },
        "peerDependencies": {
          "react": "^19.0.0"
        },
        "sideEffects": ["*.css"]
      }
      ```
      
      ### API Client Package
      
      ```json
      {
        "name": "@repo/api",
        "exports": {
          ".": "./src/client.ts",
          "./types": "./src/types.ts"
        },
        "sideEffects": false
      }
      ```
      
      ### Configuration Package
      
      ```json
      {
        "name": "@repo/eslint-config",
        "exports": {
          "./base": "./base.js",
          "./react": "./react.js"
        },
        "dependencies": {
          "eslint": "^9.0.0",
          "typescript-eslint": "^8.0.0"
        }
      }
      ```
      
      ### TypeScript Config Package
      
      ```json
      {
        "name": "@repo/typescript-config",
        "exports": {
          "./base.json": "./base.json",
          "./react-library.json": "./react-library.json"
        }
      }
      ```
      
      ---
      
      ## Directory Layout
      
      ```
      packages/
      ├── ui/                           # Shared UI components
      │   ├── src/
      │   │   ├── components/
      │   │   │   ├── button/
      │   │   │   │   └── button.tsx
      │   │   │   └── switch/
      │   │   │       └── switch.tsx
      │   │   └── hooks/
      │   │       └── index.ts
      │   ├── package.json
      │   └── tsconfig.json
      │
      ├── api/                          # API client package
      │   ├── src/
      │   │   ├── client.ts
      │   │   └── types.ts
      │   ├── package.json
      │   └── tsconfig.json
      │
      ├── eslint-config/                # Shared ESLint config
      │   ├── base.js
      │   └── package.json
      │
      ├── prettier-config/              # Shared Prettier config
      │   ├── prettier.config.mjs
      │   └── package.json
      │
      └── typescript-config/            # Shared TypeScript config
          ├── base.json
          ├── react-library.json
          └── package.json
      ```
      
      ---
      
    • workspaces.md 3.6 KB
      # Turborepo - Workspace Examples
      
      > Workspace protocol, syncpack, and dependency boundary patterns. See [../SKILL.md](../SKILL.md) for core concepts and [core.md](core.md) for essential task pipeline patterns.
      
      **Related Examples:**
      
      - [core.md](core.md) - Essential task pipeline patterns (prerequisite)
      - [caching.md](caching.md) - Remote caching, CI/CD integration
      - [packages.md](packages.md) - Internal package conventions
      
      ---
      
      ## Workspace Protocol Examples
      
      ### Good Example - Properly configured workspaces
      
      ```json
      {
        "workspaces": ["apps/*", "packages/*"],
        "dependencies": {
          "@repo/ui": "workspace:*",
          "@repo/types": "workspace:*"
        }
      }
      ```
      
      **Why good:** `workspace:*` protocol links local packages automatically, glob patterns `apps/*` and `packages/*` discover all packages dynamically, Bun hoists common dependencies to root reducing duplication
      
      ### Bad Example - Hardcoded versions instead of workspace protocol
      
      ```json
      {
        "workspaces": ["apps/*", "packages/*"],
        "dependencies": {
          "@repo/ui": "1.0.0",
          "@repo/types": "^2.1.0"
        }
      }
      ```
      
      **Why bad:** Hardcoded versions break local package linking (installs from npm instead of linking), version mismatches across packages cause duplicate dependencies, changes to internal packages require manual version updates everywhere
      
      ---
      
      ## Syncpack Examples
      
      ### Good Example - Syncpack configured for version checking
      
      ```json
      // package.json
      {
        "scripts": {
          "deps:check": "syncpack list-mismatches",
          "deps:fix": "syncpack fix-mismatches"
        }
      }
      ```
      
      **Why good:** `deps:check` identifies version mismatches across packages, `deps:fix` auto-updates to consistent versions, runs in CI to prevent version drift
      
      ### Good Example - Syncpack configuration
      
      ```json
      // .syncpackrc.json - Enforce workspace protocol and consistent versions
      {
        "versionGroups": [
          {
            "label": "Use workspace protocol for internal packages",
            "dependencies": ["@repo/*"],
            "dependencyTypes": ["prod", "dev"],
            "pinVersion": "workspace:*"
          }
        ],
        "semverGroups": [
          {
            "range": "^",
            "dependencyTypes": ["prod", "dev"],
            "dependencies": ["**"],
            "packages": ["**"]
          }
        ]
      }
      ```
      
      **Why good:** `versionGroups` enforces workspace protocol for internal packages, `semverGroups` enforces consistent version ranges across all packages
      
      ### Usage Example
      
      ```bash
      # Check for mismatches
      $ bun run deps:check
      react: 18.2.0, 18.3.0, 19.0.0 (3 versions across packages!)
      @types/react: 18.2.0, 18.3.0 (2 versions!)
      
      # Auto-fix to consistent versions
      $ bun run deps:fix
      Updated react to 19.0.0 across all packages
      Updated @types/react to 18.3.0 across all packages
      ```
      
      ---
      
      ## Dependency Boundary Examples
      
      ### Allowed vs Forbidden Dependencies
      
      ```
      ALLOWED:
      apps/web -> @repo/ui -> @repo/types
      apps/admin -> @repo/api-client -> @repo/types
      
      FORBIDDEN:
      @repo/ui -> apps/web  (packages cannot depend on apps)
      @repo/types -> apps/admin  (packages cannot depend on apps)
      @repo/ui -> @repo/api-client -> @repo/ui  (circular dependency)
      ```
      
      ### Circular Dependency Detection
      
      ```bash
      # Using madge to detect circular dependencies
      npx madge --circular --extensions ts,tsx ./packages
      npx madge --circular --extensions ts,tsx ./apps/web/src
      
      # Using dpdm
      npx dpdm --circular ./packages/*/src/index.ts
      ```
      
      ### CI Integration for Dependency Checks
      
      ```json
      // package.json - Add to CI pipeline
      {
        "scripts": {
          "check:circular": "madge --circular --extensions ts,tsx ./packages",
          "check:deps": "bun run deps:check"
        }
      }
      ```
      
      **Why good:** Automated checks prevent circular dependencies from being merged, clear boundary rules enforce clean architecture
      
      ---
      
  • reference.md 11.1 KB
    # Monorepo Reference
    
    > Decision frameworks, anti-patterns, and red flags for Turborepo and monorepo development. See [SKILL.md](SKILL.md) for core concepts and [examples/core.md](examples/core.md) for code examples.
    
    ---
    
    ## Decision Framework
    
    ### When to Create a New Package
    
    ```
    New code to write?
    │
    ├─ Is it a deployable application?
    │  └─ apps/ (web app, API server, admin dashboard)
    │
    ├─ Is it shared across multiple apps?
    │  └─ packages/ (ui, api-client, types)
    │
    ├─ Is it app-specific but significant?
    │  └─ Feature folder within the app (not a package)
    │
    └─ Is it a build tool or generator?
       └─ tools/ (code generators, custom scripts)
    ```
    
    ### Package Creation Criteria
    
    **Create package when:**
    
    - Code is used by 2+ apps
    - Logical boundary exists (UI library, API client)
    - Independent versioning would be valuable
    - Clear ownership/team boundary
    
    **Keep code in app when:**
    
    - Only one app uses it
    - Tightly coupled to app-specific logic
    - Frequently changes with app features
    - No clear reuse potential
    
    ### When to Use Turborepo vs Standard Tools
    
    ```
    Is this a monorepo?
    ├─ NO → Use standard build tools directly
    └─ YES → Are there multiple packages/apps?
        ├─ NO → Use standard build tools
        └─ YES → Do builds take > 30 seconds?
            ├─ YES → Use Turborepo
            └─ NO → Is caching important for your team?
                ├─ YES → Use Turborepo
                └─ NO → Standard tools may be sufficient
    ```
    
    ---
    
    ## RED FLAGS
    
    ### High Priority Issues
    
    - Running full test suite on every PR without affected detection (wastes CI time and money)
    - Not using caching at all (missing `outputs` configuration)
    - Missing `dependsOn: ["^build"]` for tasks that need dependencies built first
    - Forgetting to declare environment variables in `env` array (causes cache misses across environments)
    
    ### Medium Priority Issues
    
    - Not setting `cache: false` for dev servers and code generation tasks
    - Not using remote caching for teams (everyone rebuilds everything locally)
    - Missing `globalDependencies` for shared config files affecting all packages
    - Using `latest` Docker tags in CI (non-deterministic builds)
    
    ### Common Mistakes
    
    - Building dependencies separately instead of letting Turborepo handle topological ordering
    - Rebuilding for each environment instead of building once and deploying many
    - Not setting GitHub Actions concurrency limits (multiple CI runs on same PR)
    - Hardcoding package versions instead of using `workspace:*` protocol
    
    ### Gotchas & Edge Cases
    
    - Cache invalidation requires ALL affected inputs to be declared - missing `env` vars or `inputs` causes stale builds
    - Remote cache requires Vercel account or self-hosted solution - not automatic
    - `dependsOn: ["^task"]` runs dependencies' tasks, `dependsOn: ["task"]` runs same package's task first
    - Excluding cache directories in `outputs` is critical: `!.next/cache/**` prevents caching the cache
    - `--filter=...[HEAD^]` syntax requires fetch-depth: 2 in GitHub Actions checkout
    
    ---
    
    ## Anti-Patterns
    
    ### Missing dependsOn for Build Tasks
    
    ```json
    // ANTI-PATTERN: No dependency ordering
    {
      "tasks": {
        "build": {
          "outputs": ["dist/**"]
          // Missing dependsOn: ["^build"]
        }
      }
    }
    ```
    
    **Why it's wrong:** Dependencies may not build first causing build failures, topological ordering broken.
    
    **What to do instead:** Always use `dependsOn: ["^build"]` for build tasks.
    
    ---
    
    ### Hardcoded Package Versions
    
    ```json
    // ANTI-PATTERN: Hardcoded versions for workspace packages
    {
      "dependencies": {
        "@repo/ui": "1.0.0",
        "@repo/types": "^2.1.0"
      }
    }
    ```
    
    **Why it's wrong:** Breaks local package linking (installs from npm instead), version mismatches cause duplicate dependencies.
    
    **What to do instead:** Use workspace protocol: `"@repo/ui": "workspace:*"`
    
    ---
    
    ### Missing Environment Variable Declarations
    
    ```json
    // ANTI-PATTERN: Env vars not declared
    {
      "tasks": {
        "build": {
          "outputs": ["dist/**"]
          // Missing env array - DATABASE_URL changes won't invalidate cache
        }
      }
    }
    ```
    
    **Why it's wrong:** Environment variable changes don't invalidate cache, stale builds with wrong config get reused.
    
    **What to do instead:** Declare all env vars in the `env` array.
    
    ---
    
    ### Caching Side-Effect Tasks
    
    ```json
    // ANTI-PATTERN: Dev server gets cached
    {
      "tasks": {
        "dev": {
          "persistent": true
          // Missing cache: false
        }
      }
    }
    ```
    
    **Why it's wrong:** Dev servers and code generation should not be cached, causes incorrect cached outputs to be reused.
    
    **What to do instead:** Set `cache: false` for dev servers and code generation tasks.
    
    ---
    
    ## Quick Reference
    
    ### turbo.json Task Checklist
    
    - [ ] `dependsOn: ["^build"]` for tasks needing dependencies built first
    - [ ] `env` array lists all environment variables used
    - [ ] `outputs` array specifies files to cache
    - [ ] `cache: false` for dev servers and code generation
    - [ ] `persistent: true` for long-running tasks like dev servers
    - [ ] `inputs` array fine-tunes cache invalidation triggers
    
    ### Workspace Checklist
    
    - [ ] Root package.json has `workspaces` array
    - [ ] Internal packages use `workspace:*` protocol
    - [ ] Syncpack configured for version consistency
    - [ ] Circular dependency checks in CI
    
    ### Remote Cache Checklist
    
    - [ ] `TURBO_TOKEN` secret configured in CI
    - [ ] `TURBO_TEAM` secret configured in CI
    - [ ] `remoteCache.signature: true` for security
    - [ ] `fetch-depth: 2` in GitHub Actions checkout for affected detection
    
    ---
    
    ## Turborepo 2.x Features
    
    ### Recent Additions
    
    **Devtools (2.7 — Visual Graph Exploration):**
    
    ```bash
    # Launch visual devtools for Package/Task Graph exploration
    turbo devtools
    ```
    
    **`turbo docs` (2.8 — CLI Documentation Search):**
    
    ```bash
    # Search docs from terminal
    turbo docs "package configurations"
    ```
    
    **Composable Configuration (`$TURBO_EXTENDS$`):**
    
    Package configurations can extend and append to inherited arrays instead of overwriting:
    
    ```json
    // packages/web/turbo.json - Extend root config and ADD to arrays
    {
      "extends": ["//"],
      "tasks": {
        "build": {
          "inputs": ["$TURBO_EXTENDS$", "public/**"]
        }
      }
    }
    ```
    
    **Task Options Added in 2.x:**
    
    | Option          | Type       | Purpose                                         |
    | --------------- | ---------- | ----------------------------------------------- |
    | `description`   | `string`   | Human-readable task documentation               |
    | `interruptible` | `boolean`  | Allow `turbo watch` to restart persistent tasks |
    | `with`          | `string[]` | Sibling tasks to run alongside this task        |
    
    **Package Boundaries (Tags) — experimental:**
    
    ```json
    // Root turbo.json - Define boundary rules for tags
    {
      "boundaries": {
        "tags": {
          "ui": {
            "dependencies": {
              "allow": ["shared"],
              "deny": ["api"]
            }
          }
        }
      }
    }
    
    // packages/ui/turbo.json - Assign tags to package
    {
      "tags": ["ui"]
    }
    
    // packages/api/turbo.json
    {
      "tags": ["api"]
    }
    ```
    
    Run `turbo boundaries` to validate dependency rules against tag assignments.
    
    **Special Microsyntax:**
    
    | Syntax            | Purpose                                               |
    | ----------------- | ----------------------------------------------------- |
    | `$TURBO_DEFAULT$` | Restores default input behavior while customizing     |
    | `$TURBO_ROOT$`    | Makes globs relative to repo root (not package)       |
    | `$TURBO_EXTENDS$` | Appends to arrays instead of replacing in pkg configs |
    
    ### Breaking Changes (Turborepo 2.0)
    
    If migrating from Turborepo 1.x, run the codemod:
    
    ```bash
    npx @turbo/codemod migrate
    ```
    
    Key changes:
    
    - `pipeline` renamed to `tasks`
    - `outputMode` renamed to `outputLogs`
    - `globalDotEnv` and `dotEnv` removed (use `inputs` instead)
    - Strict Mode for environment variables is now default
    - `--ignore` removed (use `--filter` instead)
    - `packageManager` field required in root package.json
    
    ---
    
    ## Resources
    
    **Official documentation:**
    
    - Turborepo: https://turborepo.dev/docs
    - Turborepo Configuration: https://turborepo.dev/docs/reference/configuration
    - Turborepo CI/CD: https://turborepo.dev/docs/ci
    - Turborepo Caching: https://turborepo.dev/docs/core-concepts/caching
    - Turborepo Upgrading: https://turborepo.dev/docs/crafting-your-repository/upgrading
    - Bun Workspaces: https://bun.sh/docs/install/workspaces
    
    **Tools:**
    
    - Syncpack: https://github.com/JamieMason/syncpack
    - Turborepo Remote Cache: https://turborepo.dev/docs/core-concepts/remote-caching
    
    ---
    
    ## Internal Package Conventions Reference
    
    The following sections cover decision frameworks and anti-patterns for internal packages.
    
    ---
    
    ## Package Decision Framework
    
    ```
    Creating new code in monorepo?
    ├─ Is it shared across 2+ apps?
    │   ├─ YES → Create internal package
    │   └─ NO → Keep in app directory
    │
    └─ Creating internal package?
        ├─ Component library? → @repo/ui with React peerDeps
        ├─ API client? → @repo/api with sideEffects:false
        ├─ Config (ESLint/TS/Prettier)? → @repo/*-config
        └─ Utils? → @repo/utils with sideEffects:false
    
    Configuring package.json?
    ├─ Set "exports" field → Explicit API surface
    ├─ Set "sideEffects" → false (or ["*.css"] if styles)
    ├─ Internal deps → Use "workspace:*"
    └─ React dependency → Use "peerDependencies"
    
    Importing from packages?
    ├─ Types only? → import type { }
    ├─ Components/functions → import { } from "@repo/*/export-name"
    └─ NEVER → import from internal paths
    ```
    
    ---
    
    ## Package Red Flags
    
    ### High Priority Issues
    
    - **Default exports in library packages** - breaks tree-shaking and naming consistency
    - **Missing `exports` field in package.json** - allows importing internal paths
    - **Hardcoded versions for internal deps instead of `workspace:*`** - version conflicts
    - **React in `dependencies` instead of `peerDependencies`** - version duplication
    
    ### Medium Priority Issues
    
    - Giant barrel files re-exporting everything (negates tree-shaking benefits)
    - Missing `sideEffects` field (prevents aggressive tree-shaking)
    - Importing from internal paths instead of package exports
    - PascalCase file names (breaks on case-sensitive filesystems)
    
    ### Common Mistakes
    
    - Using custom namespace like `@mycompany/*` instead of `@repo/*`
    - Creating internal packages for app-specific code (over-abstraction)
    - Missing `private: true` (can accidentally publish to npm)
    - Using star imports `import *` (breaks tree-shaking)
    
    ### Gotchas & Edge Cases
    
    - `workspace:*` is replaced with actual version on publish (if you ever publish)
    - CSS files must be marked as `sideEffects` even if package is otherwise pure
    - TypeScript `paths` mapping may be needed for some bundlers (some handle it automatically)
    - Barrel files slow down hot module replacement (HMR) in development
    - Package.json `exports` field is strict - missing exports cannot be imported
    
    ---
    
    ## Package Checklist
    
    - [ ] Directory in `packages/`
    - [ ] `package.json` with `@repo/` prefix name
    - [ ] `private: true` set
    - [ ] `exports` field configured
    - [ ] `sideEffects` field set
    - [ ] `workspace:*` for internal dependencies
    - [ ] `peerDependencies` for React (if applicable)
    - [ ] `tsconfig.json` extending shared config
    - [ ] kebab-case file naming
    - [ ] Named exports only
    
  • SKILL.md 9.2 KB
    ---
    name: shared-monorepo-turborepo
    description: Turborepo, workspaces, package architecture, @repo/* naming, exports, tree-shaking
    ---
    
    # Monorepo Orchestration with Turborepo
    
    > **Quick Guide:** Turborepo 2.x for monorepo orchestration. Task pipelines with dependency ordering. Local + remote caching for massive speed gains. Workspaces for package linking. Syncpack for dependency version consistency. Internal packages use `@repo/*` naming, explicit `exports` fields, and `workspace:*` protocol.
    
    ---
    
    <critical_requirements>
    
    ## CRITICAL: Before Using This Skill
    
    > **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
    
    **(You MUST define task dependencies using `dependsOn: ["^build"]` in turbo.json to ensure topological ordering)**
    
    **(You MUST declare all environment variables in the `env` array of turbo.json tasks for proper cache invalidation)**
    
    **(You MUST set `cache: false` for tasks with side effects like dev servers and code generation)**
    
    **(You MUST use `workspace:*` protocol for internal package dependencies)**
    
    **(You MUST use `@repo/*` naming convention for ALL internal packages)**
    
    **(You MUST define explicit `exports` field in package.json - never allow importing internal paths)**
    
    **(You MUST mark React as `peerDependencies` NOT `dependencies` in component packages)**
    
    </critical_requirements>
    
    ---
    
    **Auto-detection:** Turborepo configuration, turbo.json, monorepo setup, workspaces, Bun workspaces, syncpack, task pipelines, @repo/\* packages, package.json exports, workspace dependencies, shared configurations
    
    **When to use:**
    
    - Configuring Turborepo task pipeline and caching strategies
    - Setting up workspaces for monorepo package linking
    - Enabling remote caching for team/CI cache sharing
    - Synchronizing dependency versions across workspace packages
    - Creating new internal packages in `packages/`
    - Configuring package.json exports for tree-shaking
    - Setting up shared configuration packages (@repo/eslint-config, @repo/typescript-config)
    
    **When NOT to use:**
    
    - Single application projects (use standard build tools directly)
    - Projects without shared packages (no monorepo benefits)
    - Very small projects where setup overhead exceeds caching benefits
    - Polyrepo architecture is preferred over monorepo
    - Projects already using Nx or Lerna (don't mix monorepo tools)
    - App-specific code that won't be shared (keep in app directory)
    
    **Key patterns covered:**
    
    - Turborepo 2.x task pipeline (dependsOn, outputs, inputs, cache)
    - Local and remote caching strategies
    - Workspaces for package linking
    - Syncpack for dependency version consistency
    - Environment variable handling in turbo.json
    - Package structure and @repo/\* naming conventions
    - package.json exports for tree-shaking
    - Named exports and barrel file patterns
    - Internal dependencies with workspace protocol
    
    **Detailed Resources:**
    
    - For code examples, see [examples/core.md](examples/core.md) (always start here)
      - [examples/caching.md](examples/caching.md) - Remote caching, CI/CD integration
      - [examples/workspaces.md](examples/workspaces.md) - Workspace protocol, syncpack, dependency boundaries
      - [examples/packages.md](examples/packages.md) - Internal package conventions, exports, creating packages
    - For decision frameworks and anti-patterns, see [reference.md](reference.md)
    
    ---
    
    <philosophy>
    
    ## Philosophy
    
    Turborepo is a high-performance build system designed for JavaScript/TypeScript monorepos. It provides intelligent task scheduling, caching, and remote cache sharing to dramatically reduce build times. Combined with workspaces, it enables efficient package management with automatic dependency linking.
    
    </philosophy>
    
    ---
    
    <patterns>
    
    ## Core Patterns
    
    ### Pattern 1: Turborepo Task Pipeline with Dependency Ordering
    
    Define task dependencies and caching behavior in turbo.json to enable intelligent build orchestration and caching.
    
    #### Key Concepts
    
    - `dependsOn: ["^build"]` - Run dependency tasks first (topological order)
    - `outputs` - Define what files to cache
    - `inputs` - Specify which files trigger cache invalidation
    - `cache: false` - Disable caching for tasks with side effects
    - `persistent: true` - Keep dev servers running
    
    #### Minimal Example
    
    ```json
    {
      "tasks": {
        "build": {
          "dependsOn": ["^build"],
          "env": ["DATABASE_URL", "NODE_ENV"],
          "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
        },
        "dev": { "cache": false, "persistent": true }
      }
    }
    ```
    
    **Key:** `dependsOn: ["^build"]` ensures topological execution, `env` declares variables for cache invalidation, `cache: false` for side-effect tasks.
    
    See [examples/core.md](examples/core.md) for full good/bad comparison examples.
    
    ---
    
    ### Pattern 2: Caching Strategies
    
    Turborepo's caching system dramatically speeds up builds by reusing previous task outputs when inputs haven't changed.
    
    #### What Gets Cached
    
    - Build outputs (`dist/`, `.next/`, framework-specific directories)
    - Test results (when `cache: true`)
    - Lint results
    
    #### What Doesn't Get Cached
    
    - Dev servers (`cache: false`)
    - Code generation (`cache: false` - generates files)
    - Tasks with side effects
    
    #### Cache Invalidation Triggers
    
    - Source file changes
    - Dependency changes
    - Environment variable changes (when in `env` array)
    - Global dependencies changes (`.env`, `tsconfig.json`)
    
    **Setup:** Link a Vercel account (or self-hosted cache), then set `TURBO_TOKEN` and `TURBO_TEAM` environment variables to enable remote cache sharing.
    
    See [examples/caching.md](examples/caching.md) for remote caching configuration and CI integration examples.
    
    ---
    
    ### Pattern 3: Workspaces for Package Management
    
    Configure workspaces to enable package linking and dependency sharing across monorepo packages.
    
    #### Key Concepts
    
    - Root `package.json` declares `"workspaces": ["apps/*", "packages/*"]`
    - Internal deps use `"@repo/ui": "workspace:*"` protocol for automatic linking
    - Standard structure: `apps/` for deployable apps, `packages/` for shared code
    
    See [examples/workspaces.md](examples/workspaces.md) for full good/bad comparison examples and syncpack configuration.
    
    </patterns>
    
    ---
    
    <performance>
    
    ## Performance Optimization
    
    **Cache Hit Metrics:**
    
    - First build: ~45s (5 packages, no cache)
    - Cached build: ~1s (97% faster with local cache)
    - Affected build: ~12s (73% faster, only changed packages rebuild)
    - Team savings: Hours per week with remote cache enabled
    
    **Optimization Strategies:**
    
    - **Set `globalDependencies`** for files affecting all packages (`.env`, `tsconfig.json`) to prevent unnecessary cache invalidation
    - **Use `inputs` array** to fine-tune what triggers cache invalidation for specific tasks
    - **Enable remote caching** to share artifacts across team and CI
    - **Use `--filter` with affected detection** (`--filter=...[HEAD^]`) to only run tasks for changed packages
    - **Set `outputs` carefully** to exclude cache directories (e.g., `!.next/cache/**`)
    
    **Force Cache Bypass:**
    
    ```bash
    # Ignore cache when needed
    bun run build --force
    
    # Only build affected packages
    bun run build --filter=...[HEAD^1]
    ```
    
    </performance>
    
    ---
    
    <decision_framework>
    
    ## Decision Framework
    
    ```
    New code? → Shared across 2+ apps? → packages/ (else keep in app)
    Monorepo? → Builds > 30s or caching matters? → Use Turborepo
    ```
    
    For comprehensive decision trees and package creation criteria, see [reference.md](reference.md).
    
    </decision_framework>
    
    ---
    
    <red_flags>
    
    ## RED FLAGS
    
    **High Priority Issues:**
    
    - Missing `dependsOn: ["^build"]` for build tasks (breaks topological ordering)
    - Missing `env` array in turbo.json (causes cache misses across environments)
    - Caching dev servers or code generation (incorrect outputs reused)
    - Default exports in library packages (breaks tree-shaking)
    - Missing `exports` field in package.json (allows internal path imports)
    
    **Common Mistakes:**
    
    - Hardcoded versions instead of `workspace:*` for internal deps
    - React in `dependencies` instead of `peerDependencies`
    - Giant barrel files re-exporting everything (negates tree-shaking)
    - Running full test suite without `--filter=...[HEAD^]` affected detection
    
    **Gotchas:**
    
    - `dependsOn: ["^task"]` runs dependencies' tasks; `dependsOn: ["task"]` runs same package's task
    - `--filter=...[HEAD^]` requires `fetch-depth: 2` in GitHub Actions
    - Exclude cache directories in outputs: `!.next/cache/**`
    
    For detailed anti-patterns and checklists, see [reference.md](reference.md).
    
    </red_flags>
    
    ---
    
    <critical_reminders>
    
    ## CRITICAL REMINDERS
    
    > **All code must follow project conventions in CLAUDE.md**
    
    **(You MUST define task dependencies using `dependsOn: ["^build"]` in turbo.json to ensure topological ordering)**
    
    **(You MUST declare all environment variables in the `env` array of turbo.json tasks for proper cache invalidation)**
    
    **(You MUST set `cache: false` for tasks with side effects like dev servers and code generation)**
    
    **(You MUST use `workspace:*` protocol for internal package dependencies)**
    
    **(You MUST use `@repo/*` naming convention for ALL internal packages)**
    
    **(You MUST define explicit `exports` field in package.json - never allow importing internal paths)**
    
    **(You MUST mark React as `peerDependencies` NOT `dependencies` in component packages)**
    
    **Failure to follow these rules will cause incorrect builds, cache misses, broken dependency resolution, and tree-shaking failures.**
    
    </critical_reminders>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related