Skill: wpank/turborepo (version 1.0.0)

--- model: fast description: | WHAT: Turborepo monorepo build system - caching, task pipelines, and parallel execution for JS/TS projects. WHEN: User configures turbo.json, creates packages, sets up monorepo, shares code between apps, runs --affected builds, debugs cache misses, or has apps/packages directories. KEYWORDS: turborepo, turbo.json, monorepo, dependsOn, task pipeline, caching, remote cache, --filter, --affected, packages, workspace, pnpm workspace, npm workspace, build optimization version: 2.7.6 --- # Turborepo Build system for JavaScript/TypeScript monorepos. Caches task outputs and runs tasks in parallel based on dependency graph. ## Installation ### OpenClaw / Moltbot / Clawbot ```bash npx clawhub@latest install turborepo ``` ## NEVER - **NEVER create root tasks** - Always create package-level tasks in each package's `package.json` - **NEVER use `turbo <task>` shorthand in code** - Use `turbo run <task>` in package.json and CI - **NEVER bypass turbo** - Root scripts must delegate via `turbo run`, not run tasks directly - **NEVER chain turbo tasks with `&&`** - Let turbo orchestrate dependencies via `dependsOn` - **NEVER use `--parallel` flag** - Configure `dependsOn` correctly instead - **NEVER put `.env` at repo root** - Use package-level `.env` files for clarity - **NEVER use `../` in inputs** - Use `$TURBO_ROOT$/path` for repo root files ## IMPORTANT: Package Tasks, Not Root Tasks **DO NOT create Root Tasks. ALWAYS create package tasks.** When creating tasks/scripts/pipelines, you MUST: 1. Add the script to each relevant package's `package.json` 2. Register the task in root `turbo.json` 3. Root `package.json` only delegates via `turbo run <task>` **DO NOT** put task logic in root `package.json`. This defeats Turborepo's parallelization. ```json // DO THIS: Scripts in each package // apps/web/package.json { "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } } // apps/api/package.json { "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } } // packages/ui/package.json { "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } } ``` ```json // turbo.json - register tasks { "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }, "lint": {}, "test": { "dependsOn": ["build"] } } } ``` ```json // Root package.json - ONLY delegates, no task logic { "scripts": { "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test" } } ``` ```json // DO NOT DO THIS - defeats parallelization // Root package.json { "scripts": { "build": "cd apps/web && next build && cd ../api && tsc", "lint": "eslint apps/ packages/", "test": "vitest" } } ``` Root Tasks (`//#taskname`) are ONLY for tasks that truly cannot exist in packages (rare). ## Secondary Rule: `turbo run` vs `turbo` **Always use `turbo run` when the command is written into code:** ```json // package.json - ALWAYS "turbo run" { "scripts": { "build": "turbo run build" } } ``` ```yaml # CI workflows - ALWAYS "turbo run" - run: turbo run build --affected ``` **The shorthand `turbo <tasks>` is ONLY for one-off terminal commands** typed directly by humans or agents. Never write `turbo build` into package.json, CI, or scripts. ## Quick Decision Trees ### "I need to configure a task" ``` Configure a task? ├─ Define task dependencies → references/configuration/tasks.md ├─ Lint/check-types (parallel + caching) → Use Transit Nodes pattern (see below) ├─ Specify build outputs → references/configuration/tasks.md#outputs ├─ Handle environment variables → references/environment/README.md ├─ Set up dev/watch tasks → references/configuration/tasks.md#persistent ├─ Package-specific config → references/configuration/README.md#package-configurations └─ Global settings (cacheDir, daemon) → references/configuration/global-options.md ``` ### "My cache isn't working" ``` Cache problems? ├─ Tasks run but outputs not restored → Missing `outputs` key ├─ Cache misses unexpectedly → references/caching/gotchas.md ├─ Need to debug hash inputs → Use --summarize or --dry ├─ Want to skip cache entirely → Use --force or cache: false ├─ Remote cache not working → references/caching/remote-cache.md └─ Environment causing misses → references/environment/gotchas.md ``` ### "I want to run only changed packages" ``` Run only what changed? ├─ Changed packages + dependents (RECOMMENDED) → turbo run build --affected ├─ Custom base branch → --affected --affected-base=origin/develop ├─ Manual git comparison → --filter=...[origin/main] └─ See all filter options → references/filtering/README.md ``` **`--affected` is the primary way to run only changed packages.** It automatically compares against the default branch and includes dependents. ### "I want to filter packages" ``` Filter packages? ├─ Only changed packages → --affected (see above) ├─ By package name → --filter=web ├─ By directory → --filter=./apps/* ├─ Package + dependencies → --filter=web... ├─ Package + dependents → --filter=...web └─ Complex combinations → references/filtering/patterns.md ``` ### "Environment variables aren't working" ``` Environment issues? ├─ Vars not available at runtime → Strict mode filtering (default) ├─ Cache hits with wrong env → Var not in `env` key ├─ .env changes not causing rebuilds → .env not in `inputs` ├─ CI variables missing → references/environment/gotchas.md └─ Framework vars (NEXT_PUBLIC_*) → Auto-included via inference ``` ### "I need to set up CI" ``` CI setup? ├─ GitHub Actions → references/ci/github-actions.md ├─ Vercel deployment → references/ci/vercel.md ├─ Remote cache in CI → references/caching/remote-cache.md ├─ Only build changed packages → --affected flag ├─ Skip unnecessary builds → turbo-ignore (references/cli/commands.md) └─ Skip container setup when no changes → turbo-ignore ``` ### "I want to watch for changes during development" ``` Watch mode? ├─ Re-run tasks on change → turbo watch (references/watch/README.md) ├─ Dev servers with dependencies → Use `with` key (references/configuration/tasks.md#with) ├─ Restart dev server on dep change → Use `interruptible: true` └─ Persistent dev tasks → Use `persistent: true` ``` ### "I need to create/structure a package" ``` Package creation/structure? ├─ Create an internal package → references/best-practices/packages.md ├─ Repository structure → references/best-practices/structure.md ├─ Dependency management → references/best-practices/dependencies.md ├─ Best practices overview → references/best-practices/README.md ├─ JIT vs Compiled packages → references/best-practices/packages.md#compilation-strategies └─ Sharing code between apps → references/best-practices/README.md#package-types ``` ### "How should I structure my monorepo?" ``` Monorepo structure? ├─ Standard layout (apps/, packages/) → references/best-practices/README.md ├─ Package types (apps vs libraries) → references/best-practices/README.md#package-types ├─ Creating internal packages → references/best-practices/packages.md ├─ TypeScript configuration → references/best-practices/structure.md#typescript-configuration ├─ ESLint configuration → references/best-practices/structure.md#eslint-configuration ├─ Dependency management → references/best-practices/dependencies.md └─ Enforce package boundaries → references/boundaries/README.md ``` ### "I want to enforce architectural boundaries" ``` Enforce boundaries? ├─ Check for violations → turbo boundaries ├─ Tag packages → references/boundaries/README.md#tags ├─ Restrict which packages can import others → references/boundaries/README.md#rule-types └─ Prevent cross-package file imports → references/boundaries/README.md ``` ## Critical Anti-Patterns ### Using `turbo` Shorthand in Code **`turbo run` is recommended in package.json scripts and CI pipelines.** The shorthand `turbo <task>` is intended for interactive terminal use. ```json // WRONG - using shorthand in package.json { "scripts": { "build": "turbo build", "dev": "turbo dev" } } // CORRECT { "scripts": { "build": "turbo run build", "dev": "turbo run dev" } } ``` ```yaml # WRONG - using shorthand in CI - run: turbo build --affected # CORRECT - run: turbo run build --affected ``` ### Root Scripts Bypassing Turbo Root `package.json` scripts MUST delegate to `turbo run`, not run tasks directly. ```json // WRONG - bypasses turbo entirely { "scripts": { "build": "bun build", "dev": "bun dev" } } // CORRECT - delegates to turbo { "scripts": { "build": "turbo run build", "dev": "turbo run dev" } } ``` ### Using `&&` to Chain Turbo Tasks Don't chain turbo tasks with `&&`. Let turbo orchestrate. ```json // WRONG - turbo task not using turbo run { "scripts": { "changeset:publish": "bun build && changeset publish" } } // CORRECT { "scripts": { "changeset:publish": "turbo run build && changeset publish" } } ``` ### `prebuild` Scripts That Manually Build Dependencies Scripts like `prebuild` that manually build other packages bypass Turborepo's dependency graph. ```json // WRONG - manually building dependencies { "scripts": { "prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build", "build": "next build" } } ``` **However, the fix depends on whether workspace dependencies are declared:** 1. **If dependencies ARE declared** (e.g., `"@repo/types": "workspace:*"` in package.json), remove the `prebuild` script. Turbo's `dependsOn: ["^build"]` handles this automatically. 2. **If dependencies are NOT declared**, the `prebuild` exists because `^build` won't trigger without a dependency relationship. The fix is to: - Add the dependency to package.json: `"@repo/types": "workspace:*"` - Then remove the `prebuild` script ```json // CORRECT - declare dependency, let turbo handle build order // package.json { "dependencies": { "@repo/types": "workspace:*", "@repo/utils": "workspace:*" }, "scripts": { "build": "next build" } } // turbo.json { "tasks": { "build": { "dependsOn": ["^build"] } } } ``` **Key insight:** `^build` only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering. ### Overly Broad `globalDependencies` `globalDependencies` affects ALL tasks in ALL packages. Be specific. ```json // WRONG - heavy hammer, affects all hashes { "globalDependencies": ["**/.env.*local"] } // BETTER - move to task-level inputs { "globalDependencies": [".env"], "tasks": { "build": { "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": ["dist/**"] } } } ``` ### Repetitive Task Configuration Look for repeated configuration across tasks that can be collapsed. Turborepo supports shared configuration patterns. ```json // WRONG - repetitive env and inputs across tasks { "tasks": { "build": { "env": ["API_URL", "DATABASE_URL"], "inputs": ["$TURBO_DEFAULT$", ".env*"] }, "test": { "env": ["API_URL", "DATABASE_URL"], "inputs": ["$TURBO_DEFAULT$", ".env*"] }, "dev": { "env": ["API_URL", "DATABASE_URL"], "inputs": ["$TURBO_DEFAULT$", ".env*"], "cache": false, "persistent": true } } } // BETTER - use globalEnv and globalDependencies for shared config { "globalEnv": ["API_URL", "DATABASE_URL"], "globalDependencies": [".env*"], "tasks": { "build": {}, "test": {}, "dev": { "cache": false, "persistent": true } } } ``` **When to use global vs task-level:** - `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config - Task-level `env` / `inputs` - use when only specific tasks need it ### NOT an Anti-Pattern: Large `env` Arrays A large `env` array (even 50+ variables) is **not** a problem. It usually means the user was thorough about declaring their build's environment dependencies. Do not flag this as an issue. ### Using `--parallel` Flag The `--parallel` flag bypasses Turborepo's dependency graph. If tasks need parallel execution, configure `dependsOn` correctly instead. ```bash # WRONG - bypasses dependency graph turbo run lint --parallel # CORRECT - configure tasks to allow parallel execution # In turbo.json, set dependsOn appropriately (or use transit nodes) turbo run lint ``` ### Package-Specific Task Overrides in Root turbo.json When multiple packages need different task configurations, use **Package Configurations** (`turbo.json` in each package) instead of cluttering root `turbo.json` with `package#task` overrides. ```json // WRONG - root turbo.json with many package-specific overrides { "tasks": { "test": { "dependsOn": ["build"] }, "@repo/web#test": { "outputs": ["coverage/**"] }, "@repo/api#test": { "outputs": ["coverage/**"] }, "@repo/utils#test": { "outputs": [] }, "@repo/cli#test": { "outputs": [] }, "@repo/core#test": { "outputs": [] } } } // CORRECT - use Package Configurations // Root turbo.json - base config only { "tasks": { "test": { "dependsOn": ["build"] } } } // packages/web/turbo.json - package-specific override { "extends": ["//"], "tasks": { "test": { "outputs": ["coverage/**"] } } } // packages/api/turbo.json { "extends": ["//"], "tasks": { "test": { "outputs": ["coverage/**"] } } } ``` **Benefits of Package Configurations:** - Keeps configuration close to the code it affects - Root turbo.json stays clean and focused on base patterns - Easier to understand what's special about each package - Works with `$TURBO_EXTENDS$` to inherit + extend arrays **When to use `package#task` in root:** - Single package needs a unique dependency (e.g., `"deploy": { "dependsOn": ["web#build"] }`) - Temporary override while migrating See `references/configuration/README.md#package-configurations` for full details. ### Using `../` to Traverse Out of Package in `inputs` Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` instead. ```json // WRONG - traversing out of package { "tasks": { "build": { "inputs": ["$TURBO_DEFAULT$", "../shared-config.json"] } } } // CORRECT - use $TURBO_ROOT$ for repo root { "tasks": { "build": { "inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"] } } } ``` ### Missing `outputs` for File-Producing Tasks **Before flagging missing `outputs`, check what the task actually produces:** 1. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`) 2. Determine if it writes files to disk or only outputs to stdout 3. Only flag if the task produces files that should be cached ```json // WRONG: build produces files but they're not cached { "tasks": { "build": { "dependsOn": ["^build"] } } } // CORRECT: build outputs are cached { "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] } } } ``` Common outputs by framework: - Next.js: `[".next/**", "!.next/cache/**"]` - Vite/Rollup: `["dist/**"]` - tsc: `["dist/**"]` or custom `outDir` **TypeScript `--noEmit` can still produce cache files:** When `incremental: true` in tsconfig.json, `tsc --noEmit` writes `.tsbuildinfo` files even without emitting JS. Check the tsconfig before assuming no outputs: ```json // If tsconfig has incremental: true, tsc --noEmit produces cache files { "tasks": { "typecheck": { "outputs": ["node_modules/.cache/tsbuildinfo.json"] // or wherever tsBuildInfoFile points } } } ``` To determine correct outputs for TypeScript tasks: 1. Check if `incremental` or `composite` is enabled in tsconfig 2. Check `tsBuildInfoFile` for custom cache location (default: alongside `outDir` or in project root) 3. If no incremental mode, `tsc --noEmit` produces no files ### `^build` vs `build` Confusion ```json { "tasks": { // ^build = run build in DEPENDENCIES first (other packages this one imports) "build": { "dependsOn": ["^build"] }, // build (no ^) = run build in SAME PACKAGE first "test": { "dependsOn": ["build"] }, // pkg#task = specific package's task "deploy": { "dependsOn": ["web#build"] } } } ``` ### Environment Variables Not Hashed ```json // WRONG: API_URL changes won't cause rebuilds { "tasks": { "build": { "outputs": ["dist/**"] } } } // CORRECT: API_URL changes invalidate cache { "tasks": { "build": { "outputs": ["dist/**"], "env": ["API_URL", "API_KEY"] } } } ``` ### `.env` Files Not in Inputs Turbo does NOT load `.env` files - your framework does. But Turbo needs to know about changes: ```json // WRONG: .env changes don't invalidate cache { "tasks": { "build": { "env": ["API_URL"] } } } // CORRECT: .env file changes invalidate cache { "tasks": { "build": { "env": ["API_URL"], "inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"] } } } ``` ### Root `.env` File in Monorepo A `.env` file at the repo root is an anti-pattern — even for small monorepos or starter templates. It creates implicit coupling between packages and makes it unclear which packages depend on which variables. ``` // WRONG - root .env affects all packages implicitly my-monorepo/ ├── .env # Which packages use this? ├── apps/ │ ├── web/ │ └── api/ └── packages/ // CORRECT - .env files in packages that need them my-monorepo/ ├── apps/ │ ├── web/ │ │ └── .env # Clear: web needs DATABASE_URL │ └── api/ │ └── .env # Clear: api needs API_KEY └── packages/ ``` **Problems with root `.env`:** - Unclear which packages consume which variables - All packages get all variables (even ones they don't need) - Cache invalidation is coarse-grained (root .env change invalidates everything) - Security risk: packages may accidentally access sensitive vars meant for others - Bad habits start small — starter templates should model correct patterns **If you must share variables**, use `globalEnv` to be explicit about what's shared, and document why. ### Strict Mode Filtering CI Variables By default, Turborepo filters environment variables to only those in `env`/`globalEnv`. CI variables may be missing: ```json // If CI scripts need GITHUB_TOKEN but it's not in env: { "globalPassThroughEnv": ["GITHUB_TOKEN", "CI"], "tasks": { ... } } ``` Or use `--env-mode=loose` (not recommended for production). ### Shared Code in Apps (Should Be a Package) ``` // WRONG: Shared code inside an app apps/ web/ shared/ # This breaks monorepo principles! utils.ts // CORRECT: Extract to a package packages/ utils/ src/utils.ts ``` ### Accessing Files Across Package Boundaries ```typescript // WRONG: Reaching into another package's internals import { Button } from "../../packages/ui/src/button"; // CORRECT: Install and import properly import { Button } from "@repo/ui/button"; ``` ### Too Many Root Dependencies ```json // WRONG: App dependencies in root { "dependencies": { "react": "^18", "next": "^14" } } // CORRECT: Only repo tools in root { "devDependencies": { "turbo": "latest" } } ``` ## Common Task Configurations ### Standard Build Pipeline ```json { "$schema": "https://turborepo.dev/schema.v2.json", "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "!.next/cache/**"] }, "dev": { "cache": false, "persistent": true } } } ``` Add a `transit` task if you have tasks that need parallel execution with cache invalidation (see below). ### Dev Task with `^dev` Pattern (for `turbo watch`) A `dev` task with `dependsOn: ["^dev"]` and `persistent: false` in root turbo.json may look unusual but is **correct for `turbo watch` workflows**: ```json // Root turbo.json { "tasks": { "dev": { "dependsOn": ["^dev"], "cache": false, "persistent": false // Packages have one-shot dev scripts } } } // Package turbo.json (apps/web/turbo.json) { "extends": ["//"], "tasks": { "dev": { "persistent": true // Apps run long-running dev servers } } } ``` **Why this works:** - **Packages** (e.g., `@acme/db`, `@acme/validators`) have `"dev": "tsc"` — one-shot type generation that completes quickly - **Apps** override with `persistent: true` for actual dev servers (Next.js, etc.) - **`turbo watch`** re-runs the one-shot package `dev` scripts when source files change, keeping types in sync **Intended usage:** Run `turbo watch dev` (not `turbo run dev`). Watch mode re-executes one-shot tasks on file changes while keeping persistent tasks running. **Alternative pattern:** Use a separate task name like `prepare` or `generate` for one-shot dependency builds to make the intent clearer: ```json { "tasks": { "prepare": { "dependsOn": ["^prepare"], "outputs": ["dist/**"] }, "dev": { "dependsOn": ["prepare"], "cache": false, "persistent": true } } } ``` ### Transit Nodes for Parallel Tasks with Cache Invalidation Some tasks can run in parallel (don't need built output from dependencies) but must invalidate cache when dependency source code changes. **The problem with `dependsOn: ["^taskname"]`:** - Forces sequential execution (slow) **The problem with `dependsOn: []` (no dependencies):** - Allows parallel execution (fast) - But cache is INCORRECT - changing dependency source won't invalidate cache **Transit Nodes solve both:** ```json { "tasks": { "transit": { "dependsOn": ["^transit"] }, "my-task": { "dependsOn": ["transit"] } } } ``` The `transit` task creates dependency relationships without matching any actual script, so tasks run in parallel with correct cache invalidation. **How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs. ### With Environment Variables ```json { "globalEnv": ["NODE_ENV"], "globalDependencies": [".env"], "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"], "env": ["API_URL", "DATABASE_URL"] } } } ``` ## Reference Index ### Configuration | File | Purpose | | ------------------------------------------------------------------------------- | -------------------------------------------------------- | | [configuration/README.md](./references/configuration/README.md) | turbo.json overview, Package Configurations | | [configuration/tasks.md](./references/configuration/tasks.md) | dependsOn, outputs, inputs, env, cache, persistent | | [configuration/global-options.md](./references/configuration/global-options.md) | globalEnv, globalDependencies, cacheDir, daemon, envMode | | [configuration/gotchas.md](./references/configuration/gotchas.md) | Common configuration mistakes | ### Caching | File | Purpose | | --------------------------------------------------------------- | -------------------------------------------- | | [caching/README.md](./references/caching/README.md) | How caching works, hash inputs | | [caching/remote-cache.md](./references/caching/remote-cache.md) | Vercel Remote Cache, self-hosted, login/link | | [caching/gotchas.md](./references/caching/gotchas.md) | Debugging cache misses, --summarize, --dry | ### Environment Variables | File | Purpose | | ------------------------------------------------------------- | ----------------------------------------- | | [environment/README.md](./references/environment/README.md) | env, globalEnv, passThroughEnv | | [environment/modes.md](./references/environment/modes.md) | Strict vs Loose mode, framework inference | | [environment/gotchas.md](./references/environment/gotchas.md) | .env files, CI issues | ### Filtering | File | Purpose | | ----------------------------------------------------------- | ------------------------ | | [filtering/README.md](./references/filtering/README.md) | --filter syntax overview | | [filtering/patterns.md](./references/filtering/patterns.md) | Common filter patterns | ### CI/CD | File | Purpose | | --------------------------------------------------------- | ------------------------------- | | [ci/README.md](./references/ci/README.md) | General CI principles | | [ci/github-actions.md](./references/ci/github-actions.md) | Complete GitHub Actions setup | | [ci/vercel.md](./references/ci/vercel.md) | Vercel deployment, turbo-ignore | | [ci/patterns.md](./references/ci/patterns.md) | --affected, caching strategies | ### CLI | File | Purpose | | ----------------------------------------------- | --------------------------------------------- | | [cli/README.md](./references/cli/README.md) | turbo run basics | | [cli/commands.md](./references/cli/commands.md) | turbo run flags, turbo-ignore, other commands | ### Best Practices | File | Purpose | | ----------------------------------------------------------------------------- | --------------------------------------------------------------- | | [best-practices/README.md](./references/best-practices/README.md) | Monorepo best practices overview | | [best-practices/structure.md](./references/best-practices/structure.md) | Repository structure, workspace config, TypeScript/ESLint setup | | [best-practices/packages.md](./references/best-practices/packages.md) | Creating internal packages, JIT vs Compiled, exports | | [best-practices/dependencies.md](./references/best-practices/dependencies.md) | Dependency management, installing, version sync | ### Watch Mode | File | Purpose | | ----------------------------------------------- | ----------------------------------------------- | | [watch/README.md](./references/watch/README.md) | turbo watch, interruptible tasks, dev workflows | ### Boundaries (Experimental) | File | Purpose | | --------------------------------------------------------- | ----------------------------------------------------- | | [boundaries/README.md](./references/boundaries/README.md) | Enforce package isolation, tag-based dependency rules | ## Source Documentation This skill is based on the official Turborepo documentation at: - Source: `docs/site/content/docs/` in the Turborepo repository - Live: https://turborepo.dev/docs

path: README.md
content: # Turborepo

Build system for JavaScript/TypeScript monorepos. Caches task outputs and runs tasks in parallel based on dependency graph. Comprehensive configuration, caching, CI/CD, and anti-pattern prevention.

## What's Inside

- Package tasks vs root tasks (critical distinction)
- Quick decision trees (configure tasks, fix cache, run changed packages, filter, env vars, CI setup, watch mode, package structure, boundaries)
- Critical anti-patterns (root tasks, `turbo` shorthand, `&&` chaining, `prebuild` scripts, overly broad globalDependencies, missing outputs, root `.env`)
- Common task configurations (standard build pipeline, dev with `turbo watch`, transit nodes for parallel tasks)
- Environment variable management (strict mode, `.env` files, framework inference)
- Comprehensive reference index (configuration, caching, environment, filtering, CI/CD, CLI, best practices, watch mode, boundaries)

## When to Use

- Configuring turbo.json task pipelines
- Creating packages and sharing code between apps
- Setting up monorepo structure (apps/, packages/)
- Running `--affected` builds in CI
- Debugging cache misses
- Managing environment variables across packages

## Installation

```bash
npx add https://github.com/wpank/ai/tree/main/skills/devops/turborepo
```

### OpenClaw / Moltbot / Clawbot

```bash
npx clawhub@latest install turborepo
```

### Manual Installation

#### Cursor (per-project)

From your project root:

```bash
mkdir -p .cursor/skills
cp -r ~/.ai-skills/skills/devops/turborepo .cursor/skills/turborepo
```

#### Cursor (global)

```bash
mkdir -p ~/.cursor/skills
cp -r ~/.ai-skills/skills/devops/turborepo ~/.cursor/skills/turborepo
```

#### Claude Code (per-project)

From your project root:

```bash
mkdir -p .claude/skills
cp -r ~/.ai-skills/skills/devops/turborepo .claude/skills/turborepo
```

#### Claude Code (global)

```bash
mkdir -p ~/.claude/skills
cp -r ~/.ai-skills/skills/devops/turborepo ~/.claude/skills/turborepo
```

---

Part of the [DevOps](..) skill category.


path: command/turborepo.md
content: ---
description: Load Turborepo skill for creating workflows, tasks, and pipelines in monorepos. Use when users ask to "create a workflow", "make a task", "generate a pipeline", or set up build orchestration.
---

Load the Turborepo skill and help with monorepo task orchestration: creating workflows, configuring tasks, setting up pipelines, and optimizing builds.

## Workflow

### Step 1: Load turborepo skill

```
skill({ name: 'turborepo' })
```

### Step 2: Identify task type from user request

Analyze $ARGUMENTS to determine:

- **Topic**: configuration, caching, filtering, environment, CI, or CLI
- **Task type**: new setup, debugging, optimization, or implementation

Use decision trees in SKILL.md to select the relevant reference files.

### Step 3: Read relevant reference files

Based on task type, read from `references/<topic>/`:

| Task                 | Files to Read                                             |
| -------------------- | --------------------------------------------------------- |
| Configure turbo.json | `configuration/README.md` + `configuration/tasks.md`      |
| Debug cache issues   | `caching/gotchas.md`                                      |
| Set up remote cache  | `caching/remote-cache.md`                                 |
| Filter packages      | `filtering/README.md` + `filtering/patterns.md`           |
| Environment problems | `environment/gotchas.md` + `environment/modes.md`         |
| Set up CI            | `ci/README.md` + `ci/github-actions.md` or `ci/vercel.md` |
| CLI usage            | `cli/commands.md`                                         |

### Step 4: Execute task

Apply Turborepo-specific patterns from references to complete the user's request.

**CRITICAL - When creating tasks/scripts/pipelines:**

1. **DO NOT create Root Tasks** - Always create package tasks
2. Add scripts to each relevant package's `package.json` (e.g., `apps/web/package.json`, `packages/ui/package.json`)
3. Register the task in root `turbo.json`
4. Root `package.json` only contains `turbo run <task>` - never actual task logic

**Other things to verify:**

- `outputs` defined for cacheable tasks
- `dependsOn` uses correct syntax (`^task` vs `task`)
- Environment variables in `env` key
- `.env` files in `inputs` if used
- Use `turbo run` (not `turbo`) in package.json and CI

### Step 5: Summarize

```
=== Turborepo Task Complete ===

Topic: <configuration|caching|filtering|environment|ci|cli>
Files referenced: <reference files consulted>

<brief summary of what was done>
```

<user-request>
$ARGUMENTS
</user-request>


path: references/best-practices/structure.md
content: # Repository Structure

Detailed guidance on structuring a Turborepo monorepo.

## Workspace Configuration

### pnpm (Recommended)

```yaml
# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"
```

### npm/yarn/bun

```json
// package.json
{
  "workspaces": ["apps/*", "packages/*"]
}
```

## Root package.json

```json
{
  "name": "my-monorepo",
  "private": true,
  "packageManager": "pnpm@9.0.0",
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint",
    "test": "turbo run test"
  },
  "devDependencies": {
    "turbo": "latest"
  }
}
```

Key points:

- `private: true` - Prevents accidental publishing
- `packageManager` - Enforces consistent package manager version
- **Scripts only delegate to `turbo run`** - No actual build logic here!
- Minimal devDependencies (just turbo and repo tools)

## Always Prefer Package Tasks

**Always use package tasks. Only use Root Tasks if you cannot succeed with package tasks.**

```json
// packages/web/package.json
{
  "scripts": {
    "build": "next build",
    "lint": "eslint .",
    "test": "vitest",
    "typecheck": "tsc --noEmit"
  }
}

// packages/api/package.json
{
  "scripts": {
    "build": "tsc",
    "lint": "eslint .",
    "test": "vitest",
    "typecheck": "tsc --noEmit"
  }
}
```

Package tasks enable Turborepo to:

1. **Parallelize** - Run `web#lint` and `api#lint` simultaneously
2. **Cache individually** - Each package's task output is cached separately
3. **Filter precisely** - Run `turbo run test --filter=web` for just one package

**Root Tasks are a fallback** for tasks that truly cannot run per-package:

```json
// AVOID unless necessary - sequential, not parallelized, can't filter
{
  "scripts": {
    "lint": "eslint apps/web && eslint apps/api && eslint packages/ui"
  }
}
```

## Root turbo.json

```json
{
  "$schema": "https://turborepo.dev/schema.v2.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "lint": {},
    "test": {
      "dependsOn": ["build"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
```

## Directory Organization

### Grouping Packages

You can group packages by adding more workspace paths:

```yaml
# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"
  - "packages/config/*"    # Grouped configs
  - "packages/features/*"  # Feature packages
```

This allows:

```
packages/
├── ui/
├── utils/
├── config/
│   ├── eslint/
│   ├── typescript/
│   └── tailwind/
└── features/
    ├── auth/
    └── payments/
```

### What NOT to Do

```yaml
# BAD: Nested wildcards cause ambiguous behavior
packages:
  - "packages/**"  # Don't do this!
```

## Package Anatomy

### Minimum Required Files

```
packages/ui/
├── package.json    # Required: Makes it a package
├── src/            # Source code
│   └── button.tsx
└── tsconfig.json   # TypeScript config (if using TS)
```

### package.json Requirements

```json
{
  "name": "@repo/ui",           // Unique, namespaced name
  "version": "0.0.0",           // Version (can be 0.0.0 for internal)
  "private": true,              // Prevents accidental publishing
  "exports": {                  // Entry points
    "./button": "./src/button.tsx"
  }
}
```

## TypeScript Configuration

### Shared Base Config

Create a shared TypeScript config package:

```
packages/
└── typescript-config/
    ├── package.json
    ├── base.json
    ├── nextjs.json
    └── library.json
```

```json
// packages/typescript-config/base.json
{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "module": "ESNext",
    "target": "ES2022"
  }
}
```

### Extending in Packages

```json
// packages/ui/tsconfig.json
{
  "extends": "@repo/typescript-config/library.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
```

### No Root tsconfig.json

You likely don't need a `tsconfig.json` in the workspace root. Each package should have its own config extending from the shared config package.

## ESLint Configuration

### Shared Config Package

```
packages/
└── eslint-config/
    ├── package.json
    ├── base.js
    ├── next.js
    └── library.js
```

```json
// packages/eslint-config/package.json
{
  "name": "@repo/eslint-config",
  "exports": {
    "./base": "./base.js",
    "./next": "./next.js",
    "./library": "./library.js"
  }
}
```

### Using in Packages

```js
// apps/web/.eslintrc.js
module.exports = {
  extends: ["@repo/eslint-config/next"],
};
```

## Lockfile

A lockfile is **required** for:

- Reproducible builds
- Turborepo to understand package dependencies
- Cache correctness

Without a lockfile, you'll see unpredictable behavior.


path: references/best-practices/dependencies.md
content: # Dependency Management

Best practices for managing dependencies in a Turborepo monorepo.

## Core Principle: Install Where Used

Dependencies belong in the package that uses them, not the root.

```bash
# Good: Install in specific package
pnpm add react --filter=@repo/ui
pnpm add next --filter=web

# Avoid: Installing in root
pnpm add react -w  # Only for repo-level tools!
```

## Benefits of Local Installation

### 1. Clarity

Each package's `package.json` lists exactly what it needs:

```json
// packages/ui/package.json
{
  "dependencies": {
    "react": "^18.0.0",
    "class-variance-authority": "^0.7.0"
  }
}
```

### 2. Flexibility

Different packages can use different versions when needed:

```json
// packages/legacy-ui/package.json
{ "dependencies": { "react": "^17.0.0" } }

// packages/ui/package.json
{ "dependencies": { "react": "^18.0.0" } }
```

### 3. Better Caching

Installing in root changes workspace lockfile, invalidating all caches.

### 4. Pruning Support

`turbo prune` can remove unused dependencies for Docker images.

## What Belongs in Root

Only repository-level tools:

```json
// Root package.json
{
  "devDependencies": {
    "turbo": "latest",
    "husky": "^8.0.0",
    "lint-staged": "^15.0.0"
  }
}
```

**NOT** application dependencies:

- react, next, express
- lodash, axios, zod
- Testing libraries (unless truly repo-wide)

## Installing Dependencies

### Single Package

```bash
# pnpm
pnpm add lodash --filter=@repo/utils

# npm
npm install lodash --workspace=@repo/utils

# yarn
yarn workspace @repo/utils add lodash

# bun
cd packages/utils && bun add lodash
```

### Multiple Packages

```bash
# pnpm
pnpm add jest --save-dev --filter=web --filter=@repo/ui

# npm
npm install jest --save-dev --workspace=web --workspace=@repo/ui

# yarn (v2+)
yarn workspaces foreach -R --from '{web,@repo/ui}' add jest --dev
```

### Internal Packages

```bash
# pnpm
pnpm add @repo/ui --filter=web

# This updates package.json:
{
  "dependencies": {
    "@repo/ui": "workspace:*"
  }
}
```

## Keeping Versions in Sync

### Option 1: Tooling

```bash
# syncpack - Check and fix version mismatches
npx syncpack list-mismatches
npx syncpack fix-mismatches

# manypkg - Similar functionality
npx @manypkg/cli check
npx @manypkg/cli fix

# sherif - Rust-based, very fast
npx sherif
```

### Option 2: Package Manager Commands

```bash
# pnpm - Update everywhere
pnpm up --recursive typescript@latest

# npm - Update in all workspaces
npm install typescript@latest --workspaces
```

### Option 3: pnpm Catalogs (pnpm 9.5+)

```yaml
# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"

catalog:
  react: ^18.2.0
  typescript: ^5.3.0
```

```json
// Any package.json
{
  "dependencies": {
    "react": "catalog:"  // Uses version from catalog
  }
}
```

## Internal vs External Dependencies

### Internal (Workspace)

```json
// pnpm/bun
{ "@repo/ui": "workspace:*" }

// npm/yarn
{ "@repo/ui": "*" }
```

Turborepo understands these relationships and orders builds accordingly.

### External (npm Registry)

```json
{ "lodash": "^4.17.21" }
```

Standard semver versioning from npm.

## Peer Dependencies

For library packages that expect the consumer to provide dependencies:

```json
// packages/ui/package.json
{
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "react": "^18.0.0",      // For development/testing
    "react-dom": "^18.0.0"
  }
}
```

## Common Issues

### "Module not found"

1. Check the dependency is installed in the right package
2. Run `pnpm install` / `npm install` to update lockfile
3. Check exports are defined in the package

### Version Conflicts

Packages can use different versions - this is a feature, not a bug. But if you need consistency:

1. Use tooling (syncpack, manypkg)
2. Use pnpm catalogs
3. Create a lint rule

### Hoisting Issues

Some tools expect dependencies in specific locations. Use package manager config:

```yaml
# .npmrc (pnpm)
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
```

## Lockfile

**Required** for:

- Reproducible builds
- Turborepo dependency analysis
- Cache correctness

```bash
# Commit your lockfile!
git add pnpm-lock.yaml  # or package-lock.json, yarn.lock
```


path: references/best-practices/packages.md
content: # Creating Internal Packages

How to create and structure internal packages in your monorepo.

## Package Creation Checklist

1. Create directory in `packages/`
2. Add `package.json` with name and exports
3. Add source code in `src/`
4. Add `tsconfig.json` if using TypeScript
5. Install as dependency in consuming packages
6. Run package manager install to update lockfile

## Package Compilation Strategies

### Just-in-Time (JIT)

Export TypeScript directly. The consuming app's bundler compiles it.

```json
// packages/ui/package.json
{
  "name": "@repo/ui",
  "exports": {
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx"
  },
  "scripts": {
    "lint": "eslint .",
    "check-types": "tsc --noEmit"
  }
}
```

**When to use:**

- Apps use modern bundlers (Turbopack, webpack, Vite)
- You want minimal configuration
- Build times are acceptable without caching

**Limitations:**

- No Turborepo cache for the package itself
- Consumer must support TypeScript compilation
- Can't use TypeScript `paths` (use Node.js subpath imports instead)

### Compiled

Package handles its own compilation.

```json
// packages/ui/package.json
{
  "name": "@repo/ui",
  "exports": {
    "./button": {
      "types": "./src/button.tsx",
      "default": "./dist/button.js"
    }
  },
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}
```

```json
// packages/ui/tsconfig.json
{
  "extends": "@repo/typescript-config/library.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
```

**When to use:**

- You want Turborepo to cache builds
- Package will be used by non-bundler tools
- You need maximum compatibility

**Remember:** Add `dist/**` to turbo.json outputs!

## Defining Exports

### Multiple Entrypoints

```json
{
  "exports": {
    ".": "./src/index.ts",           // @repo/ui
    "./button": "./src/button.tsx",  // @repo/ui/button
    "./card": "./src/card.tsx",      // @repo/ui/card
    "./hooks": "./src/hooks/index.ts" // @repo/ui/hooks
  }
}
```

### Conditional Exports (Compiled)

```json
{
  "exports": {
    "./button": {
      "types": "./src/button.tsx",
      "import": "./dist/button.mjs",
      "require": "./dist/button.cjs",
      "default": "./dist/button.js"
    }
  }
}
```

## Installing Internal Packages

### Add to Consuming Package

```json
// apps/web/package.json
{
  "dependencies": {
    "@repo/ui": "workspace:*"  // pnpm/bun
    // "@repo/ui": "*"         // npm/yarn
  }
}
```

### Run Install

```bash
pnpm install  # Updates lockfile with new dependency
```

### Import and Use

```typescript
// apps/web/src/page.tsx
import { Button } from '@repo/ui/button';

export default function Page() {
  return <Button>Click me</Button>;
}
```

## One Purpose Per Package

### Good Examples

```
packages/
├── ui/                  # Shared UI components
├── utils/               # General utilities
├── auth/                # Authentication logic
├── database/            # Database client/schemas
├── eslint-config/       # ESLint configuration
├── typescript-config/   # TypeScript configuration
└── api-client/          # Generated API client
```

### Avoid Mega-Packages

```
// BAD: One package for everything
packages/
└── shared/
    ├── components/
    ├── utils/
    ├── hooks/
    ├── types/
    └── api/

// GOOD: Separate by purpose
packages/
├── ui/          # Components
├── utils/       # Utilities
├── hooks/       # React hooks
├── types/       # Shared TypeScript types
└── api-client/  # API utilities
```

## Config Packages

### TypeScript Config

```json
// packages/typescript-config/package.json
{
  "name": "@repo/typescript-config",
  "exports": {
    "./base.json": "./base.json",
    "./nextjs.json": "./nextjs.json",
    "./library.json": "./library.json"
  }
}
```

### ESLint Config

```json
// packages/eslint-config/package.json
{
  "name": "@repo/eslint-config",
  "exports": {
    "./base": "./base.js",
    "./next": "./next.js"
  },
  "dependencies": {
    "eslint": "^8.0.0",
    "eslint-config-next": "latest"
  }
}
```

## Common Mistakes

### Forgetting to Export

```json
// BAD: No exports defined
{
  "name": "@repo/ui"
}

// GOOD: Clear exports
{
  "name": "@repo/ui",
  "exports": {
    "./button": "./src/button.tsx"
  }
}
```

### Wrong Workspace Syntax

```json
// pnpm/bun
{ "@repo/ui": "workspace:*" }  // Correct

// npm/yarn
{ "@repo/ui": "*" }            // Correct
{ "@repo/ui": "workspace:*" }  // Wrong for npm/yarn!
```

### Missing from turbo.json Outputs

```json
// Package builds to dist/, but turbo.json doesn't know
{
  "tasks": {
    "build": {
      "outputs": [".next/**"]  // Missing dist/**!
    }
  }
}

// Correct
{
  "tasks": {
    "build": {
      "outputs": [".next/**", "dist/**"]
    }
  }
}
```

## TypeScript Best Practices

### Use Node.js Subpath Imports (Not `paths`)

TypeScript `compilerOptions.paths` breaks with JIT packages. Use Node.js subpath imports instead (TypeScript 5.4+).

**JIT Package:**

```json
// packages/ui/package.json
{
  "imports": {
    "#*": "./src/*"
  }
}
```

```typescript
// packages/ui/button.tsx
import { MY_STRING } from "#utils.ts";  // Uses .ts extension
```

**Compiled Package:**

```json
// packages/ui/package.json
{
  "imports": {
    "#*": "./dist/*"
  }
}
```

```typescript
// packages/ui/button.tsx
import { MY_STRING } from "#utils.js";  // Uses .js extension
```

### Use `tsc` for Internal Packages

For internal packages, prefer `tsc` over bundlers. Bundlers can mangle code before it reaches your app's bundler, causing hard-to-debug issues.

### Enable Go-to-Definition

For Compiled Packages, enable declaration maps:

```json
// tsconfig.json
{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true
  }
}
```

This creates `.d.ts` and `.d.ts.map` files for IDE navigation.

### No Root tsconfig.json Needed

Each package should have its own `tsconfig.json`. A root one causes all tasks to miss cache when changed. Only use root `tsconfig.json` for non-package scripts.

### Avoid TypeScript Project References

They add complexity and another caching layer. Turborepo handles dependencies better.


path: references/caching/gotchas.md
content: # Debugging Cache Issues

## Diagnostic Tools

### `--summarize`

Generates a JSON file with all hash inputs. Compare two runs to find differences.

```bash
turbo build --summarize
# Creates .turbo/runs/<run-id>.json
```

The summary includes:

- Global hash and its inputs
- Per-task hashes and their inputs
- Environment variables that affected the hash

**Comparing runs:**

```bash
# Run twice, compare the summaries
diff .turbo/runs/<first-run>.json .turbo/runs/<second-run>.json
```

### `--dry` / `--dry=json`

See what would run without executing anything:

```bash
turbo build --dry
turbo build --dry=json  # machine-readable output
```

Shows cache status for each task without running them.

### `--force`

Skip reading cache, re-execute all tasks:

```bash
turbo build --force
```

Useful to verify tasks actually work (not just cached results).

## Unexpected Cache Misses

**Symptom:** Task runs when you expected a cache hit.

### Environment Variable Changed

Check if an env var in the `env` key changed:

```json
{
  "tasks": {
    "build": {
      "env": ["API_URL", "NODE_ENV"]
    }
  }
}
```

Different `API_URL` between runs = cache miss.

### .env File Changed

`.env` files aren't tracked by default. Add to `inputs`:

```json
{
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", ".env", ".env.local"]
    }
  }
}
```

Or use `globalDependencies` for repo-wide env files:

```json
{
  "globalDependencies": [".env"]
}
```

### Lockfile Changed

Installing/updating packages changes the global hash.

### Source Files Changed

Any file in the package (or in `inputs`) triggers a miss.

### turbo.json Changed

Config changes invalidate the global hash.

## Incorrect Cache Hits

**Symptom:** Cached output is stale/wrong.

### Missing Environment Variable

Task uses an env var not listed in `env`:

```javascript
// build.js
const apiUrl = process.env.API_URL;  // not tracked!
```

Fix: add to task config:

```json
{
  "tasks": {
    "build": {
      "env": ["API_URL"]
    }
  }
}
```

### Missing File in Inputs

Task reads a file outside default inputs:

```json
{
  "tasks": {
    "build": {
      "inputs": [
        "$TURBO_DEFAULT$",
        "../../shared-config.json"  // file outside package
      ]
    }
  }
}
```

## Useful Flags

```bash
# Only show output for cache misses
turbo build --output-logs=new-only

# Show output for everything (debugging)
turbo build --output-logs=full

# See why tasks are running
turbo build --verbosity=2
```

## Quick Checklist

Cache miss when expected hit:

1. Run with `--summarize`, compare with previous run
2. Check env vars with `--dry=json`
3. Look for lockfile/config changes in git

Cache hit when expected miss:

1. Verify env var is in `env` array
2. Verify file is in `inputs` array
3. Check if file is outside package directory


path: references/caching/remote-cache.md
content: # Remote Caching

Share cache artifacts across your team and CI pipelines.

## Benefits

- Team members get cache hits from each other's work
- CI gets cache hits from local development (and vice versa)
- Dramatically faster CI runs after first build
- No more "works on my machine" rebuilds

## Vercel Remote Cache

Free, zero-config when deploying on Vercel. For local dev and other CI:

### Local Development Setup

```bash
# Authenticate with Vercel
npx turbo login

# Link repo to your Vercel team
npx turbo link
```

This creates `.turbo/config.json` with your team info (gitignored by default).

### CI Setup

Set these environment variables:

```bash
TURBO_TOKEN=<your-token>
TURBO_TEAM=<your-team-slug>
```

Get your token from Vercel dashboard → Settings → Tokens.

**GitHub Actions example:**

```yaml
- name: Build
  run: npx turbo build
  env:
    TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
    TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```

## Configuration in turbo.json

```json
{
  "remoteCache": {
    "enabled": true,
    "signature": false
  }
}
```

Options:

- `enabled`: toggle remote cache (default: true when authenticated)
- `signature`: require artifact signing (default: false)

## Artifact Signing

Verify cache artifacts haven't been tampered with:

```bash
# Set a secret key (use same key across all environments)
export TURBO_REMOTE_CACHE_SIGNATURE_KEY="your-secret-key"
```

Enable in config:

```json
{
  "remoteCache": {
    "signature": true
  }
}
```

Signed artifacts can only be restored if the signature matches.

## Self-Hosted Options

Community implementations for running your own cache server:

- **turbo-remote-cache** (Node.js) - supports S3, GCS, Azure
- **turborepo-remote-cache** (Go) - lightweight, S3-compatible
- **ducktape** (Rust) - high-performance option

Configure with environment variables:

```bash
TURBO_API=https://your-cache-server.com
TURBO_TOKEN=your-auth-token
TURBO_TEAM=your-team
```

## Cache Behavior Control

```bash
# Disable remote cache for a run
turbo build --remote-cache-read-only  # read but don't write
turbo build --no-cache                # skip cache entirely

# Environment variable alternative
TURBO_REMOTE_ONLY=true  # only use remote, skip local
```

## Debugging Remote Cache

```bash
# Verbose output shows cache operations
turbo build --verbosity=2

# Check if remote cache is configured
turbo config
```

Look for:

- "Remote caching enabled" in output
- Upload/download messages during runs
- "cache hit, replaying output" with remote cache indicator


path: references/environment/gotchas.md
content: # Environment Variable Gotchas

Common mistakes and how to fix them.

## .env Files Must Be in `inputs`

Turbo does NOT read `.env` files. Your framework (Next.js, Vite, etc.) or `dotenv` loads them. But Turbo needs to know when they change.

**Wrong:**

```json
{
  "tasks": {
    "build": {
      "env": ["DATABASE_URL"]
    }
  }
}
```

**Right:**

```json
{
  "tasks": {
    "build": {
      "env": ["DATABASE_URL"],
      "inputs": ["$TURBO_DEFAULT$", ".env", ".env.local", ".env.production"]
    }
  }
}
```

## Strict Mode Filters CI Variables

In strict mode, CI provider variables (GITHUB_TOKEN, GITLAB_CI, etc.) are filtered unless explicitly listed.

**Symptom:** Task fails with "authentication required" or "permission denied" in CI.

**Solution:**

```json
{
  "globalPassThroughEnv": ["GITHUB_TOKEN", "GITLAB_CI", "CI"]
}
```

## passThroughEnv Doesn't Affect Hash

Variables in `passThroughEnv` are available at runtime but changes WON'T trigger rebuilds.

**Dangerous example:**

```json
{
  "tasks": {
    "build": {
      "passThroughEnv": ["API_URL"]
    }
  }
}
```

If `API_URL` changes from staging to production, Turbo may serve a cached build pointing to the wrong API.

**Use passThroughEnv only for:**

- Auth tokens that don't affect output (SENTRY_AUTH_TOKEN)
- CI metadata (GITHUB_RUN_ID)
- Variables consumed after build (deploy credentials)

## Runtime-Created Variables Are Invisible

Turbo captures env vars at startup. Variables created during execution aren't seen.

**Won't work:**

```bash
# In package.json scripts
"build": "export API_URL=$COMPUTED_VALUE && next build"
```

**Solution:** Set vars before invoking turbo:

```bash
API_URL=$COMPUTED_VALUE turbo run build
```

## Different .env Files for Different Environments

If you use `.env.development` and `.env.production`, both should be in inputs.

```json
{
  "tasks": {
    "build": {
      "inputs": [
        "$TURBO_DEFAULT$",
        ".env",
        ".env.local",
        ".env.development",
        ".env.development.local",
        ".env.production",
        ".env.production.local"
      ]
    }
  }
}
```

## Complete Next.js Example

```json
{
  "$schema": "https://turborepo.dev/schema.v2.json",
  "globalEnv": ["CI", "NODE_ENV", "VERCEL"],
  "globalPassThroughEnv": ["GITHUB_TOKEN", "VERCEL_URL"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "env": [
        "DATABASE_URL",
        "NEXT_PUBLIC_*",
        "!NEXT_PUBLIC_ANALYTICS_ID"
      ],
      "passThroughEnv": ["SENTRY_AUTH_TOKEN"],
      "inputs": [
        "$TURBO_DEFAULT$",
        ".env",
        ".env.local",
        ".env.production",
        ".env.production.local"
      ],
      "outputs": [".next/**", "!.next/cache/**"]
    }
  }
}
```

This config:

- Hashes DATABASE*URL and NEXT_PUBLIC*\* vars (except analytics)
- Passes through SENTRY_AUTH_TOKEN without hashing
- Includes all .env file variants in the hash
- Makes CI tokens available globally


path: references/environment/modes.md
content: # Environment Modes

Turborepo supports different modes for handling environment variables during task execution.

## Strict Mode (Default)

Only explicitly configured variables are available to tasks.

**Behavior:**

- Tasks only see vars listed in `env`, `globalEnv`, `passThroughEnv`, or `globalPassThroughEnv`
- Unlisted vars are filtered out
- Tasks fail if they require unlisted variables

**Benefits:**

- Guarantees cache correctness
- Prevents accidental dependencies on system vars
- Reproducible builds across machines

```bash
# Explicit (though it's the default)
turbo run build --env-mode=strict
```

## Loose Mode

All system environment variables are available to tasks.

```bash
turbo run build --env-mode=loose
```

**Behavior:**

- Every system env var is passed through
- Only vars in `env`/`globalEnv` affect the hash
- Other vars are available but NOT hashed

**Risks:**

- Cache may restore incorrect results if unhashed vars changed
- "Works on my machine" bugs
- CI vs local environment mismatches

**Use case:** Migrating legacy projects or debugging strict mode issues.

## Framework Inference (Automatic)

Turborepo automatically detects frameworks and includes their conventional env vars.

### Inferred Variables by Framework

| Framework        | Pattern             |
| ---------------- | ------------------- |
| Next.js          | `NEXT_PUBLIC_*`     |
| Vite             | `VITE_*`            |
| Create React App | `REACT_APP_*`       |
| Gatsby           | `GATSBY_*`          |
| Nuxt             | `NUXT_*`, `NITRO_*` |
| Expo             | `EXPO_PUBLIC_*`     |
| Astro            | `PUBLIC_*`          |
| SvelteKit        | `PUBLIC_*`          |
| Remix            | `REMIX_*`           |
| Redwood          | `REDWOOD_ENV_*`     |
| Sanity           | `SANITY_STUDIO_*`   |
| Solid            | `VITE_*`            |

### Disabling Framework Inference

Globally via CLI:

```bash
turbo run build --framework-inference=false
```

Or exclude specific patterns in config:

```json
{
  "tasks": {
    "build": {
      "env": ["!NEXT_PUBLIC_*"]
    }
  }
}
```

### Why Disable?

- You want explicit control over all env vars
- Framework vars shouldn't bust the cache (e.g., analytics IDs)
- Debugging unexpected cache misses

## Checking Environment Mode

Use `--dry` to see which vars affect each task:

```bash
turbo run build --dry=json | jq '.tasks[].environmentVariables'
```


path: references/cli/commands.md
content: # turbo run Flags Reference

Full docs: https://turborepo.dev/docs/reference/run

## Package Selection

### `--filter` / `-F`

Select specific packages to run tasks in.

```bash
turbo build --filter=web
turbo build -F=@repo/ui -F=@repo/utils
turbo test --filter=./apps/*
```

See `filtering/` for complete syntax (globs, dependencies, git ranges).

### Task Identifier Syntax (v2.2.4+)

Run specific package tasks directly:

```bash
turbo run web#build              # Build web package
turbo run web#build docs#lint    # Multiple specific tasks
```

### `--affected`

Run only in packages changed since the base branch.

```bash
turbo build --affected
turbo test --affected --filter=./apps/*  # combine with filter
```

**How it works:**

- Default: compares `main...HEAD`
- In GitHub Actions: auto-detects `GITHUB_BASE_REF`
- Override base: `TURBO_SCM_BASE=development turbo build --affected`
- Override head: `TURBO_SCM_HEAD=your-branch turbo build --affected`

**Requires git history** - shallow clones may fall back to running all tasks.

## Execution Control

### `--dry` / `--dry=json`

Preview what would run without executing.

```bash
turbo build --dry          # human-readable
turbo build --dry=json     # machine-readable
```

### `--force`

Ignore all cached artifacts, re-run everything.

```bash
turbo build --force
```

### `--concurrency`

Limit parallel task execution.

```bash
turbo build --concurrency=4      # max 4 tasks
turbo build --concurrency=50%    # 50% of CPU cores
```

### `--continue`

Keep running other tasks when one fails.

```bash
turbo build test --continue
```

### `--only`

Run only the specified task, skip its dependencies.

```bash
turbo build --only  # skip running dependsOn tasks
```

### `--parallel` (Discouraged)

Ignores task graph dependencies, runs all tasks simultaneously. **Avoid using this flag**—if tasks need to run in parallel, configure `dependsOn` correctly instead. Using `--parallel` bypasses Turborepo's dependency graph, which can cause race conditions and incorrect builds.

## Cache Control

### `--cache`

Fine-grained cache behavior control.

```bash
# Default: read/write both local and remote
turbo build --cache=local:rw,remote:rw

# Read-only local, no remote
turbo build --cache=local:r,remote:

# Disable local, read-only remote
turbo build --cache=local:,remote:r

# Disable all caching
turbo build --cache=local:,remote:
```

## Output & Debugging

### `--graph`

Generate task graph visualization.

```bash
turbo build --graph                # opens in browser
turbo build --graph=graph.svg      # SVG file
turbo build --graph=graph.png      # PNG file
turbo build --graph=graph.json     # JSON data
turbo build --graph=graph.mermaid  # Mermaid diagram
```

### `--summarize`

Generate JSON run summary for debugging.

```bash
turbo build --summarize
# creates .turbo/runs/<run-id>.json
```

### `--output-logs`

Control log output verbosity.

```bash
turbo build --output-logs=full        # all logs (default)
turbo build --output-logs=new-only    # only cache misses
turbo build --output-logs=errors-only # only failures
turbo build --output-logs=none        # silent
```

### `--profile`

Generate Chrome tracing profile for performance analysis.

```bash
turbo build --profile=profile.json
# open chrome://tracing and load the file
```

### `--verbosity` / `-v`

Control turbo's own log level.

```bash
turbo build -v      # verbose
turbo build -vv     # more verbose
turbo build -vvv    # maximum verbosity
```

## Environment

### `--env-mode`

Control environment variable handling.

```bash
turbo build --env-mode=strict  # only declared env vars (default)
turbo build --env-mode=loose   # include all env vars in hash
```

## UI

### `--ui`

Select output interface.

```bash
turbo build --ui=tui     # interactive terminal UI (default in TTY)
turbo build --ui=stream  # streaming logs (default in CI)
```

---

# turbo-ignore

Full docs: https://turborepo.dev/docs/reference/turbo-ignore

Skip CI work when nothing relevant changed. Useful for skipping container setup.

## Basic Usage

```bash
# Check if build is needed for current package (uses Automatic Package Scoping)
npx turbo-ignore

# Check specific package
npx turbo-ignore web

# Check specific task
npx turbo-ignore --task=test
```

## Exit Codes

- `0`: No changes detected - skip CI work
- `1`: Changes detected - proceed with CI

## CI Integration Example

```yaml
# GitHub Actions
- name: Check for changes
  id: turbo-ignore
  run: npx turbo-ignore web
  continue-on-error: true

- name: Build
  if: steps.turbo-ignore.outcome == 'failure'  # changes detected
  run: pnpm build
```

## Comparison Depth

Default: compares to parent commit (`HEAD^1`).

```bash
# Compare to specific commit
npx turbo-ignore --fallback=abc123

# Compare to branch
npx turbo-ignore --fallback=main
```

---

# Other Commands

## turbo boundaries

Check workspace violations (experimental).

```bash
turbo boundaries
```

See `references/boundaries/` for configuration.

## turbo watch

Re-run tasks on file changes.

```bash
turbo watch build test
```

See `references/watch/` for details.

## turbo prune

Create sparse checkout for Docker.

```bash
turbo prune web --docker
```

## turbo link / unlink

Connect/disconnect Remote Cache.

```bash
turbo link    # connect to Vercel Remote Cache
turbo unlink  # disconnect
```

## turbo login / logout

Authenticate with Remote Cache provider.

```bash
turbo login   # authenticate
turbo logout  # log out
```

## turbo generate

Scaffold new packages.

```bash
turbo generate
```


path: references/ci/patterns.md
content: # CI Optimization Patterns

Strategies for efficient CI/CD with Turborepo.

## PR vs Main Branch Builds

### PR Builds: Only Affected

Test only what changed in the PR:

```yaml
- name: Test (PR)
  if: github.event_name == 'pull_request'
  run: turbo run build test --affected
```

### Main Branch: Full Build

Ensure complete validation on merge:

```yaml
- name: Test (Main)
  if: github.ref == 'refs/heads/main'
  run: turbo run build test
```

## Custom Git Ranges with --filter

For advanced scenarios, use `--filter` with git refs:

```bash
# Changes since specific commit
turbo run test --filter="...[abc123]"

# Changes between refs
turbo run test --filter="...[main...HEAD]"

# Changes in last 3 commits
turbo run test --filter="...[HEAD~3]"
```

## Caching Strategies

### Remote Cache (Recommended)

Best performance - shared across all CI runs and developers:

```yaml
env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```

### actions/cache Fallback

When remote cache isn't available:

```yaml
- uses: actions/cache@v4
  with:
    path: .turbo
    key: turbo-${{ runner.os }}-${{ github.sha }}
    restore-keys: |
      turbo-${{ runner.os }}-${{ github.ref }}-
      turbo-${{ runner.os }}-
```

Limitations:

- Cache is branch-scoped
- PRs restore from base branch cache
- Less efficient than remote cache

## Matrix Builds

Test across Node versions:

```yaml
strategy:
  matrix:
    node: [18, 20, 22]

steps:
  - uses: actions/setup-node@v4
    with:
      node-version: ${{ matrix.node }}

  - run: turbo run test
```

## Parallelizing Across Jobs

Split tasks into separate jobs:

```yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: turbo run lint --affected

  test:
    runs-on: ubuntu-latest
    steps:
      - run: turbo run test --affected

  build:
    runs-on: ubuntu-latest
    needs: [lint, test]
    steps:
      - run: turbo run build
```

### Cache Considerations

When parallelizing:

- Each job has separate cache writes
- Remote cache handles this automatically
- With actions/cache, use unique keys per job to avoid conflicts

```yaml
- uses: actions/cache@v4
  with:
    path: .turbo
    key: turbo-${{ runner.os }}-${{ github.job }}-${{ github.sha }}
```

## Conditional Tasks

Skip expensive tasks on draft PRs:

```yaml
- name: E2E Tests
  if: github.event.pull_request.draft == false
  run: turbo run test:e2e --affected
```

Or require label for full test:

```yaml
- name: Full Test Suite
  if: contains(github.event.pull_request.labels.*.name, 'full-test')
  run: turbo run test
```


path: references/ci/vercel.md
content: # Vercel Deployment

Turborepo integrates seamlessly with Vercel for monorepo deployments.

## Remote Cache

Remote caching is **automatically enabled** when deploying to Vercel. No configuration needed - Vercel detects Turborepo and enables caching.

This means:

- No `TURBO_TOKEN` or `TURBO_TEAM` setup required on Vercel
- Cache is shared across all deployments
- Preview and production builds benefit from cache

## turbo-ignore

Skip unnecessary builds when a package hasn't changed using `turbo-ignore`.

### Installation

```bash
npx turbo-ignore
```

Or install globally in your project:

```bash
pnpm add -D turbo-ignore
```

### Setup in Vercel

1. Go to your project in Vercel Dashboard
2. Navigate to Settings > Git > Ignored Build Step
3. Select "Custom" and enter:

```bash
npx turbo-ignore
```

### How It Works

`turbo-ignore` checks if the current package (or its dependencies) changed since the last successful deployment:

1. Compares current commit to last deployed commit
2. Uses Turborepo's dependency graph
3. Returns exit code 0 (skip) if no changes
4. Returns exit code 1 (build) if changes detected

### Options

```bash
# Check specific package
npx turbo-ignore web

# Use specific comparison ref
npx turbo-ignore --fallback=HEAD~1

# Verbose output
npx turbo-ignore --verbose
```

## Environment Variables

Set environment variables in Vercel Dashboard:

1. Go to Project Settings > Environment Variables
2. Add variables for each environment (Production, Preview, Development)

Common variables:

- `DATABASE_URL`
- `API_KEY`
- Package-specific config

## Monorepo Root Directory

For monorepos, set the root directory in Vercel:

1. Project Settings > General > Root Directory
2. Set to the package path (e.g., `apps/web`)

Vercel automatically:

- Installs dependencies from monorepo root
- Runs build from the package directory
- Detects framework settings

## Build Command

Vercel auto-detects `turbo run build` when `turbo.json` exists at root.

Override if needed:

```bash
turbo run build --filter=web
```

Or for production-only optimizations:

```bash
turbo run build --filter=web --env-mode=strict
```


path: references/ci/github-actions.md
content: # GitHub Actions

Complete setup guide for Turborepo with GitHub Actions.

## Basic Workflow Structure

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Build and Test
        run: turbo run build test lint
```

## Package Manager Setup

### pnpm

```yaml
- uses: pnpm/action-setup@v3
  with:
    version: 9

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'pnpm'

- run: pnpm install --frozen-lockfile
```

### Yarn

```yaml
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'yarn'

- run: yarn install --frozen-lockfile
```

### Bun

```yaml
- uses: oven-sh/setup-bun@v1
  with:
    bun-version: latest

- run: bun install --frozen-lockfile
```

## Remote Cache Setup

### 1. Create Vercel Access Token

1. Go to [Vercel Dashboard](https://vercel.com/account/tokens)
2. Create a new token with appropriate scope
3. Copy the token value

### 2. Add Secrets and Variables

In your GitHub repository settings:

**Secrets** (Settings > Secrets and variables > Actions > Secrets):

- `TURBO_TOKEN`: Your Vercel access token

**Variables** (Settings > Secrets and variables > Actions > Variables):

- `TURBO_TEAM`: Your Vercel team slug

### 3. Add to Workflow

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```

## Alternative: actions/cache

If you can't use remote cache, cache Turborepo's local cache directory:

```yaml
- uses: actions/cache@v4
  with:
    path: .turbo
    key: turbo-${{ runner.os }}-${{ hashFiles('**/turbo.json', '**/package-lock.json') }}
    restore-keys: |
      turbo-${{ runner.os }}-
```

Note: This is less effective than remote cache since it's per-branch.

## Complete Example

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: pnpm/action-setup@v3
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build
        run: turbo run build --affected

      - name: Test
        run: turbo run test --affected

      - name: Lint
        run: turbo run lint --affected
```


path: references/filtering/patterns.md
content: # Common Filter Patterns

Practical examples for typical monorepo scenarios.

## Single Package

Run task in one package:

```bash
turbo run build --filter=web
turbo run test --filter=@acme/api
```

## Package with Dependencies

Build a package and everything it depends on:

```bash
turbo run build --filter=web...
```

Useful for: ensuring all dependencies are built before the target.

## Package Dependents

Run in all packages that depend on a library:

```bash
turbo run test --filter=...ui
```

Useful for: testing consumers after changing a shared package.

## Dependents Only (Exclude Target)

Test packages that depend on ui, but not ui itself:

```bash
turbo run test --filter=...^ui
```

## Changed Packages

Run only in packages with file changes since last commit:

```bash
turbo run lint --filter=[HEAD^1]
```

Since a specific branch point:

```bash
turbo run lint --filter=[main...HEAD]
```

## Changed + Dependents (PR Builds)

Run in changed packages AND packages that depend on them:

```bash
turbo run build test --filter=...[HEAD^1]
```

Or use the shortcut:

```bash
turbo run build test --affected
```

## Directory-Based

Run in all apps:

```bash
turbo run build --filter=./apps/*
```

Run in specific directories:

```bash
turbo run build --filter=./apps/web --filter=./apps/api
```

## Scope-Based

Run in all packages under a scope:

```bash
turbo run build --filter=@acme/*
```

## Exclusions

Run in all apps except admin:

```bash
turbo run build --filter=./apps/* --filter=!admin
```

Run everywhere except specific packages:

```bash
turbo run lint --filter=!legacy-app --filter=!deprecated-pkg
```

## Complex Combinations

Apps that changed, plus their dependents:

```bash
turbo run build --filter=...[HEAD^1] --filter=./apps/*
```

All packages except docs, but only if changed:

```bash
turbo run build --filter=[main...HEAD] --filter=!docs
```

## Debugging Filters

Use `--dry` to see what would run without executing:

```bash
turbo run build --filter=web... --dry
```

Use `--dry=json` for machine-readable output:

```bash
turbo run build --filter=...[HEAD^1] --dry=json
```

## CI/CD Patterns

PR validation (most common):

```bash
turbo run build test lint --affected
```

Deploy only changed apps:

```bash
turbo run deploy --filter=./apps/* --filter=[main...HEAD]
```

Full rebuild of specific app and deps:

```bash
turbo run build --filter=production-app...
```


path: references/configuration/gotchas.md
content: # Configuration Gotchas

Common mistakes and how to fix them.

## #1 Root Scripts Not Using `turbo run`

Root `package.json` scripts for turbo tasks MUST use `turbo run`, not direct commands.

```json
// WRONG - bypasses turbo, no parallelization or caching
{
  "scripts": {
    "build": "bun build",
    "dev": "bun dev"
  }
}

// CORRECT - delegates to turbo
{
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev"
  }
}
```

**Why this matters:** Running `bun build` or `npm run build` at root bypasses Turborepo entirely - no parallelization, no caching, no dependency graph awareness.

## #2 Using `&&` to Chain Turbo Tasks

Don't use `&&` to chain tasks that turbo should orchestrate.

```json
// WRONG - changeset:publish chains turbo task with non-turbo command
{
  "scripts": {
    "changeset:publish": "bun build && changeset publish"
  }
}

// CORRECT - use turbo run, let turbo handle dependencies
{
  "scripts": {
    "changeset:publish": "turbo run build && changeset publish"
  }
}
```

If the second command (`changeset publish`) depends on build outputs, the turbo task should run through turbo to get caching and parallelization benefits.

## #3 Overly Broad globalDependencies

`globalDependencies` affects hash for ALL tasks in ALL packages. Be specific.

```json
// WRONG - affects all hashes
{
  "globalDependencies": ["**/.env.*local"]
}

// CORRECT - move to specific tasks that need it
{
  "globalDependencies": [".env"],
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": ["dist/**"]
    }
  }
}
```

**Why this matters:** `**/.env.*local` matches .env files in ALL packages, causing unnecessary cache invalidation. Instead:

- Use `globalDependencies` only for truly global files (root `.env`)
- Use task-level `inputs` for package-specific .env files with `$TURBO_DEFAULT$` to preserve default behavior

## #4 Repetitive Task Configuration

Look for repeated configuration across tasks that can be collapsed.

```json
// WRONG - repetitive env and inputs across tasks
{
  "tasks": {
    "build": {
      "env": ["API_URL", "DATABASE_URL"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"]
    },
    "test": {
      "env": ["API_URL", "DATABASE_URL"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"]
    }
  }
}

// BETTER - use globalEnv and globalDependencies
{
  "globalEnv": ["API_URL", "DATABASE_URL"],
  "globalDependencies": [".env*"],
  "tasks": {
    "build": {},
    "test": {}
  }
}
```

**When to use global vs task-level:**

- `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config
- Task-level `env` / `inputs` - use when only specific tasks need it

## #5 Using `../` to Traverse Out of Package in `inputs`

Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` instead.

```json
// WRONG - traversing out of package
{
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]
    }
  }
}

// CORRECT - use $TURBO_ROOT$ for repo root
{
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]
    }
  }
}
```

## #6 MOST COMMON MISTAKE: Creating Root Tasks

**DO NOT create Root Tasks. ALWAYS create package tasks.**

When you need to create a task (build, lint, test, typecheck, etc.):

1. Add the script to **each relevant package's** `package.json`
2. Register the task in root `turbo.json`
3. Root `package.json` only contains `turbo run <task>`

```json
// WRONG - DO NOT DO THIS
// Root package.json with task logic
{
  "scripts": {
    "build": "cd apps/web && next build && cd ../api && tsc",
    "lint": "eslint apps/ packages/",
    "test": "vitest"
  }
}

// CORRECT - DO THIS
// apps/web/package.json
{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }

// apps/api/package.json
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }

// packages/ui/package.json
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }

// Root package.json - ONLY delegates
{ "scripts": { "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test" } }

// turbo.json - register tasks
{
  "tasks": {
    "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
    "lint": {},
    "test": {}
  }
}
```

**Why this matters:**

- Package tasks run in **parallel** across all packages
- Each package's output is cached **individually**
- You can **filter** to specific packages: `turbo run test --filter=web`

Root Tasks (`//#taskname`) defeat all these benefits. Only use them for tasks that truly cannot exist in any package (extremely rare).

## #7 Tasks That Need Parallel Execution + Cache Invalidation

Some tasks can run in parallel (don't need built output from dependencies) but must still invalidate cache when dependency source code changes. Using `dependsOn: ["^taskname"]` forces sequential execution. Using no dependencies breaks cache invalidation.

**Use Transit Nodes for these tasks:**

```json
// WRONG - forces sequential execution (SLOW)
"my-task": {
  "dependsOn": ["^my-task"]
}

// ALSO WRONG - no dependency awareness (INCORRECT CACHING)
"my-task": {}

// CORRECT - use Transit Nodes for parallel + correct caching
{
  "tasks": {
    "transit": { "dependsOn": ["^transit"] },
    "my-task": { "dependsOn": ["transit"] }
  }
}
```

**Why Transit Nodes work:**

- `transit` creates dependency relationships without matching any actual script
- Tasks that depend on `transit` gain dependency awareness
- Since `transit` completes instantly (no script), tasks run in parallel
- Cache correctly invalidates when dependency source code changes

**How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs.

## Missing outputs for File-Producing Tasks

**Before flagging missing `outputs`, check what the task actually produces:**

1. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`)
2. Determine if it writes files to disk or only outputs to stdout
3. Only flag if the task produces files that should be cached

```json
// WRONG - build produces files but they're not cached
"build": {
  "dependsOn": ["^build"]
}

// CORRECT - outputs are cached
"build": {
  "dependsOn": ["^build"],
  "outputs": ["dist/**"]
}
```

No `outputs` key is fine for stdout-only tasks. For file-producing tasks, missing `outputs` means Turbo has nothing to cache.

## Forgetting ^ in dependsOn

```json
// WRONG - looks for "build" in SAME package (infinite loop or missing)
"build": {
  "dependsOn": ["build"]
}

// CORRECT - runs dependencies' build first
"build": {
  "dependsOn": ["^build"]
}
```

The `^` means "in dependency packages", not "in this package".

## Missing persistent on Dev Tasks

```json
// WRONG - dependent tasks hang waiting for dev to "finish"
"dev": {
  "cache": false
}

// CORRECT
"dev": {
  "cache": false,
  "persistent": true
}
```

## Package Config Missing extends

```json
// WRONG - packages/web/turbo.json
{
  "tasks": {
    "build": { "outputs": [".next/**"] }
  }
}

// CORRECT
{
  "extends": ["//"],
  "tasks": {
    "build": { "outputs": [".next/**"] }
  }
}
```

Without `"extends": ["//"]`, Package Configurations are invalid.

## Root Tasks Need Special Syntax

To run a task defined only in root `package.json`:

```bash
# WRONG
turbo run format

# CORRECT
turbo run //#format
```

And in dependsOn:

```json
"build": {
  "dependsOn": ["//#codegen"]  // Root package's codegen
}
```

## Overwriting Default Inputs

```json
// WRONG - only watches test files, ignores source changes
"test": {
  "inputs": ["tests/**"]
}

// CORRECT - extends defaults, adds test files
"test": {
  "inputs": ["$TURBO_DEFAULT$", "tests/**"]
}
```

Without `$TURBO_DEFAULT$`, you replace all default file watching.

## Caching Tasks with Side Effects

```json
// WRONG - deploy might be skipped on cache hit
"deploy": {
  "dependsOn": ["build"]
}

// CORRECT
"deploy": {
  "dependsOn": ["build"],
  "cache": false
}
```

Always disable cache for deploy, publish, or mutation tasks.


path: references/configuration/tasks.md
content: # Task Configuration Reference

Full docs: https://turborepo.dev/docs/reference/configuration#tasks

## dependsOn

Controls task execution order.

```json
{
  "tasks": {
    "build": {
      "dependsOn": [
        "^build",        // Dependencies' build tasks first
        "codegen",       // Same package's codegen task first
        "shared#build"   // Specific package's build task
      ]
    }
  }
}
```

| Syntax     | Meaning                              |
| ---------- | ------------------------------------ |
| `^task`    | Run `task` in all dependencies first |
| `task`     | Run `task` in same package first     |
| `pkg#task` | Run specific package's task first    |

The `^` prefix is crucial - without it, you're referencing the same package.

### Transit Nodes for Parallel Tasks

For tasks like `lint` and `check-types` that can run in parallel but need dependency-aware caching:

```json
{
  "tasks": {
    "transit": { "dependsOn": ["^transit"] },
    "lint": { "dependsOn": ["transit"] },
    "check-types": { "dependsOn": ["transit"] }
  }
}
```

**DO NOT use `dependsOn: ["^lint"]`** - this forces sequential execution.
**DO NOT use `dependsOn: []`** - this breaks cache invalidation.

The `transit` task creates dependency relationships without running anything (no matching script), so tasks run in parallel with correct caching.

## outputs

Glob patterns for files to cache. **If omitted, nothing is cached.**

```json
{
  "tasks": {
    "build": {
      "outputs": ["dist/**", "build/**"]
    }
  }
}
```

**Framework examples:**

```json
// Next.js
"outputs": [".next/**", "!.next/cache/**"]

// Vite
"outputs": ["dist/**"]

// TypeScript (tsc)
"outputs": ["dist/**", "*.tsbuildinfo"]

// No file outputs (lint, typecheck)
"outputs": []
```

Use `!` prefix to exclude patterns from caching.

## inputs

Files considered when calculating task hash. Defaults to all tracked files in package.

```json
{
  "tasks": {
    "test": {
      "inputs": ["src/**", "tests/**", "vitest.config.ts"]
    }
  }
}
```

**Special values:**

| Value                 | Meaning                                 |
| --------------------- | --------------------------------------- |
| `$TURBO_DEFAULT$`     | Include default inputs, then add/remove |
| `$TURBO_ROOT$/<path>` | Reference files from repo root          |

```json
{
  "tasks": {
    "build": {
      "inputs": [
        "$TURBO_DEFAULT$",
        "!README.md",
        "$TURBO_ROOT$/tsconfig.base.json"
      ]
    }
  }
}
```

## env

Environment variables to include in task hash.

```json
{
  "tasks": {
    "build": {
      "env": [
        "API_URL",
        "NEXT_PUBLIC_*",  // Wildcard matching
        "!DEBUG"          // Exclude from hash
      ]
    }
  }
}
```

Variables listed here affect cache hits - changing the value invalidates cache.

## cache

Enable/disable caching for a task. Default: `true`.

```json
{
  "tasks": {
    "dev": { "cache": false },
    "deploy": { "cache": false }
  }
}
```

Disable for: dev servers, deploy commands, tasks with side effects.

## persistent

Mark long-running tasks that don't exit. Default: `false`.

```json
{
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
```

Required for dev servers - without it, dependent tasks wait forever.

## interactive

Allow task to receive stdin input. Default: `false`.

```json
{
  "tasks": {
    "login": {
      "cache": false,
      "interactive": true
    }
  }
}
```

## outputLogs

Control when logs are shown. Options: `full`, `hash-only`, `new-only`, `errors-only`, `none`.

```json
{
  "tasks": {
    "build": {
      "outputLogs": "new-only"  // Only show logs on cache miss
    }
  }
}
```

## with

Run tasks alongside this task. For long-running tasks that need runtime dependencies.

```json
{
  "tasks": {
    "dev": {
      "with": ["api#dev"],
      "persistent": true,
      "cache": false
    }
  }
}
```

Unlike `dependsOn`, `with` runs tasks concurrently (not sequentially). Use for dev servers that need other services running.

## interruptible

Allow `turbo watch` to restart the task on changes. Default: `false`.

```json
{
  "tasks": {
    "dev": {
      "persistent": true,
      "interruptible": true,
      "cache": false
    }
  }
}
```

Use for dev servers that don't automatically detect dependency changes.

## description (Pre-release)

Human-readable description of the task.

```json
{
  "tasks": {
    "build": {
      "description": "Compiles the application for production deployment"
    }
  }
}
```

For documentation only - doesn't affect execution or caching.

## passThroughEnv

Environment variables available at runtime but NOT included in cache hash.

```json
{
  "tasks": {
    "build": {
      "passThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
    }
  }
}
```

**Warning**: Changes to these vars won't cause cache misses. Use `env` if changes should invalidate cache.

## extends (Package Configuration only)

Control task inheritance in Package Configurations.

```json
// packages/ui/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "lint": {
      "extends": false  // Exclude from this package
    }
  }
}
```

| Value            | Behavior                                                       |
| ---------------- | -------------------------------------------------------------- |
| `true` (default) | Inherit from root turbo.json                                   |
| `false`          | Exclude task from package, or define fresh without inheritance |


path: references/configuration/global-options.md
content: # Global Options Reference

Options that affect all tasks. Full docs: https://turborepo.dev/docs/reference/configuration

## globalEnv

Environment variables affecting all task hashes.

```json
{
  "globalEnv": ["CI", "NODE_ENV", "VERCEL_*"]
}
```

Use for variables that should invalidate all caches when changed.

## globalDependencies

Files that affect all task hashes.

```json
{
  "globalDependencies": [
    "tsconfig.json",
    ".env",
    "pnpm-lock.yaml"
  ]
}
```

Lockfile is included by default. Add shared configs here.

## globalPassThroughEnv

Variables available to tasks but not included in hash.

```json
{
  "globalPassThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
}
```

Use for credentials that shouldn't affect cache keys.

## cacheDir

Custom cache location. Default: `node_modules/.cache/turbo`.

```json
{
  "cacheDir": ".turbo/cache"
}
```

## daemon

Background process for faster subsequent runs. Default: `true`.

```json
{
  "daemon": false
}
```

Disable in CI or when debugging.

## envMode

How unspecified env vars are handled. Default: `"strict"`.

```json
{
  "envMode": "strict"  // Only specified vars available
  // or
  "envMode": "loose"   // All vars pass through
}
```

Strict mode catches missing env declarations.

## ui

Terminal UI mode. Default: `"stream"`.

```json
{
  "ui": "tui"     // Interactive terminal UI
  // or
  "ui": "stream"  // Traditional streaming logs
}
```

TUI provides better UX for parallel tasks.

## remoteCache

Configure remote caching.

```json
{
  "remoteCache": {
    "enabled": true,
    "signature": true,
    "timeout": 30,
    "uploadTimeout": 60
  }
}
```

| Option          | Default                | Description                                            |
| --------------- | ---------------------- | ------------------------------------------------------ |
| `enabled`       | `true`                 | Enable/disable remote caching                          |
| `signature`     | `false`                | Sign artifacts with `TURBO_REMOTE_CACHE_SIGNATURE_KEY` |
| `preflight`     | `false`                | Send OPTIONS request before cache requests             |
| `timeout`       | `30`                   | Timeout in seconds for cache operations                |
| `uploadTimeout` | `60`                   | Timeout in seconds for uploads                         |
| `apiUrl`        | `"https://vercel.com"` | Remote cache API endpoint                              |
| `loginUrl`      | `"https://vercel.com"` | Login endpoint                                         |
| `teamId`        | -                      | Team ID (must start with `team_`)                      |
| `teamSlug`      | -                      | Team slug for querystring                              |

See https://turborepo.dev/docs/core-concepts/remote-caching for setup.

## concurrency

Default: `"10"`

Limit parallel task execution.

```json
{
  "concurrency": "4"     // Max 4 tasks at once
  // or
  "concurrency": "50%"   // 50% of available CPUs
}
```

## futureFlags

Enable experimental features that will become default in future versions.

```json
{
  "futureFlags": {
    "errorsOnlyShowHash": true
  }
}
```

### `errorsOnlyShowHash`

When using `outputLogs: "errors-only"`, show task hashes on start/completion:

- Cache miss: `cache miss, executing <hash> (only logging errors)`
- Cache hit: `cache hit, replaying logs (no errors) <hash>`

## noUpdateNotifier

Disable update notifications when new turbo versions are available.

```json
{
  "noUpdateNotifier": true
}
```

## dangerouslyDisablePackageManagerCheck

Bypass the `packageManager` field requirement. Use for incremental migration.

```json
{
  "dangerouslyDisablePackageManagerCheck": true
}
```

**Warning**: Unstable lockfiles can cause unpredictable behavior.

## Git Worktree Cache Sharing (Pre-release)

When working in Git worktrees, Turborepo automatically shares local cache between the main worktree and linked worktrees.

**How it works:**

- Detects worktree configuration
- Redirects cache to main worktree's `.turbo/cache`
- Works alongside Remote Cache

**Benefits:**

- Cache hits across branches
- Reduced disk usage
- Faster branch switching

**Disabled by**: Setting explicit `cacheDir` in turbo.json.


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)