Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.devin.ai/llms.txt

Use this file to discover all available pages before exploring further.

Monorepos and multi-package workspaces need extra care in blueprints because different subdirectories may use different languages, package managers, or dependency sets. Devin supports two approaches:

Native workspaces (recommended)

Create a separate blueprint for each subdirectory. Each workspace gets its own initialize, maintenance, and knowledge sections with the working directory automatically set to the subdirectory.

Subshells

Run commands in subdirectories within a single blueprint using (cd dir && command). Simpler for small monorepos with only a few packages.

Native workspaces

Recommended for most monorepos. Native workspaces give each subdirectory its own blueprint with isolated setup, knowledge, and working directory. This is cleaner and easier to maintain than subshells as the number of packages grows.
With native workspaces, each subdirectory gets a dedicated blueprint. Commands in that blueprint run with the working directory already set to the subdirectory — no cd or subshells needed.

The root blueprint

Every repo using native workspaces is expected to have a root blueprint. The root blueprint runs from the repository root and executes before any workspace-scoped blueprints. Use it for shared setup that applies across the entire repo — installing runtimes, global tools, or running root-level dependency installs.
# Root blueprint — runs first, from the repo root
initialize: |
  npm install -g pnpm

maintenance: |
  pnpm install
Workspace blueprints then handle package-specific setup and run after the root blueprint completes.

Creating a workspace

  1. Go to Settings > Environment > Blueprints
  2. Click on the repository
  3. Click Add workspace
  4. Enter the subdirectory path (e.g., packages/frontend)
  5. Write the blueprint for that workspace
The workspace path must correspond to a real directory inside the repository. If the path does not exist when the build runs, the build will fail. Double-check that the path matches your repo structure exactly (e.g., packages/frontend, not pkg/frontend).

Example

A monorepo with a React frontend and a Python backend. The root blueprint installs shared tools, then each workspace handles its own dependencies:
# Root blueprint — shared setup for the entire repo
initialize: |
  npm install -g pnpm
  curl -LsSf https://astral.sh/uv/install.sh | sh

knowledge:
  - name: structure
    contents: |
      Monorepo with two packages:
      - packages/frontend — React app (TypeScript, pnpm)
      - packages/backend — Python API (FastAPI, uv)
Each workspace’s knowledge entries are scoped to that subdirectory. When Devin works in packages/frontend, it sees the frontend lint/test/dev commands — not the backend ones.

When to use native workspaces

  • Subdirectories have different languages or package managers
  • Each package needs its own knowledge entries (lint, test, build commands)
  • You want isolated setup — a broken blueprint for one workspace doesn’t block the others
  • The number of packages is growing and a single blueprint is getting unwieldy

Subshells

For simpler monorepos, you can manage everything in a single blueprint using subshells. Wrap commands in parentheses to run them in a subdirectory without affecting subsequent steps:
maintenance:
  - name: Frontend deps
    run: (cd packages/frontend && pnpm install)
  - name: Backend deps
    run: (cd packages/backend && uv sync)
The parentheses (cd ... && ...) create a subshell. When the subshell exits, the working directory resets to the repo root for the next step.
Without parentheses, cd changes the working directory for all subsequent steps. Always use subshells when switching directories in blueprint steps.

Why subshells matter

Compare these two approaches:
maintenance:
  - name: Frontend deps
    run: (cd packages/frontend && pnpm install)
  - name: Backend deps
    run: (cd packages/backend && uv sync)
Each step runs from the repo root. Both commands find the right packages/ subdirectory.

When to use subshells

  • The monorepo has a few packages with straightforward setup
  • All packages share the same language and package manager
  • You don’t need per-package knowledge entries

Knowledge entries for monorepos

Whether you use native workspaces or subshells, well-structured knowledge entries help Devin navigate the codebase:
knowledge:
  - name: structure
    contents: |
      This is a monorepo with three packages:
      - `packages/frontend` — React app (TypeScript, pnpm)
      - `packages/backend` — Python API (FastAPI, uv)
      - `packages/shared` — Shared TypeScript utilities
  - name: frontend
    contents: |
      cd packages/frontend
      Dev server: pnpm dev
      Lint: pnpm lint
      Test: pnpm test
  - name: backend
    contents: |
      cd packages/backend
      Dev server: uv run uvicorn app.main:app --reload
      Lint: uv run ruff check .
      Test: uv run pytest
A structure knowledge entry that maps each directory to its language and toolchain helps Devin navigate the repo quickly. With native workspaces, each workspace has its own knowledge entries, so the structure entry is most useful in the root blueprint or for subshell-based setups.

Examples

Turborepo / Nx workspace

For workspaces managed by a monorepo build tool like Turborepo or Nx, install dependencies at the root and let the tool handle per-package orchestration:
initialize: |
  npm install -g pnpm turbo

maintenance: |
  pnpm install

knowledge:
  - name: structure
    contents: |
      Turborepo monorepo. Use `turbo` for building and testing:
      - `apps/web` — Next.js app
      - `apps/api` — Express API
      - `packages/ui` — Shared component library
      - `packages/config` — Shared configuration
  - name: build
    contents: turbo run build
  - name: test
    contents: turbo run test
  - name: lint
    contents: turbo run lint
  - name: dev
    contents: turbo run dev

Multiple JDK versions

A Java monorepo where different services require different JDK versions:
initialize:
  - name: Install JDK 17 (primary)
    run: |
      sudo apt-get update -qq
      sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-17-jdk-headless
      echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' \
        | sudo tee /etc/profile.d/java.sh > /dev/null

  - name: Install JDK 11 (legacy service)
    run: |
      sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-11-jdk-headless

maintenance:
  - name: Warm dependency caches
    run: |
      export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
      (cd services/api && ./gradlew dependencies --refresh-dependencies)

      export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
      (cd services/legacy && ./gradlew dependencies --refresh-dependencies)

knowledge:
  - name: build_api
    contents: |
      Build the API service (JDK 17):
        JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
        cd services/api && ./gradlew clean build
  - name: build_legacy
    contents: |
      Build the legacy service (JDK 11):
        JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
        cd services/legacy && ./gradlew clean build
  - name: test_all
    contents: |
      Run tests for all services:
        JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
        (cd services/api && ./gradlew test)

        JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
        (cd services/legacy && ./gradlew test)

Org blueprint for shared tools

When multiple monorepo packages share the same tools, install them once in the org-wide blueprint:
# Org-wide blueprint (Settings > Environment > Blueprints > Org-wide setup)
initialize:
  - name: Install pnpm
    run: npm install -g pnpm
  - name: Install uv
    run: curl -LsSf https://astral.sh/uv/install.sh | sh
  - name: Install shared build tools
    run: npm install -g turbo typescript
Then each repo blueprint only needs project-specific commands:
# Repo blueprint (uses pnpm and uv from the org blueprint)
maintenance:
  - name: Install all workspace deps
    run: pnpm install
  - name: Install Python service deps
    run: (cd services/ml-pipeline && uv sync)

Best practices

When each subdirectory has its own language, package manager, or build process, native workspaces keep each blueprint focused and independent. Reserve subshells for simple cases with a few packages.
When using the subshell approach, wrap cd commands in parentheses: (cd dir && command). This prevents one step’s directory change from affecting the next step.
Language runtimes and package managers used across multiple repos belong in the org-wide blueprint. This avoids duplication and keeps repo blueprints focused on project-specific setup.
If package A depends on package B being built first, list B’s build step before A’s install step in maintenance. Blueprint steps run sequentially in the order listed.
A knowledge entry named structure that maps directories to their languages and tools helps Devin navigate the codebase. Include which package manager each subdirectory uses and any cross-package dependencies.
Instead of one large knowledge entry, create separate entries for each package (e.g., frontend, backend, ml-pipeline). With native workspaces, each workspace has its own knowledge section built in.
Use pnpm install (not pnpm install --force) and uv sync (not rm -rf .venv && uv sync). Incremental commands are faster during periodic rebuilds.