AI Coding in a Monorepo: A Per-Package Workflow That Stays in Its Lane

AI agents in monorepos either ship clean PRs or melt down across 6 packages. Here is the AGENTS.md + scoping + two-phase commit workflow that keeps them contained, as of June 2026.

You point Cursor at a 12-package monorepo and ask it to add a feature in apps/web. It opens 47 files across 6 packages, “refactors” packages/utils for no reason, and leaves an unrelated package failing to compile. That is the default failure mode of AI agents in a monorepo: too much context, unclear ownership, hidden coupling. This tutorial gives engineers in Turborepo, Nx, pnpm-workspace, or Yarn-workspace setups a reliable per-package workflow that keeps agents inside their lane while still letting them do cross-package work safely.

TL;DR

  • Give every package its own AGENTS.md. The nearest-file-wins convention is now the cross-tool standard (Cursor, Claude Code via import, Codex, Copilot). OpenAI’s own monorepo ships roughly 88 nested AGENTS.md files — one per significant package.
  • Scope every prompt to a target package and an explicit exclusion list. “Work in packages/api only; do not touch packages/ui or apps/web.”
  • Do cross-package changes in two commits: consumer first against a contract that does not exist yet, provider second. Each commit reviews as one concern.
  • Validate with the affected graph, not the world. turbo run build test --affected -- --base=origin/main (Turborepo 2.1+) or nx affected -t build test runs only changed packages plus their dependents.
  • This pays off at 3+ packages with cross-boundary coupling. Below that, default tooling is fine.

Why monorepos break AI agents

A single-app repo gives an agent a clear blast radius: every file is fair game and the build is one command. A monorepo inverts that. The agent sees dozens of packages, no signal about which one owns a contract, and a dependency graph it cannot infer from folder names. Without structure, it does one of two things: ignores the package that actually needs changing, or “improves” sibling packages it was never asked to touch. Both produce PRs no human wants to review.

The fix is not a smarter model. It is three pieces of structure that scope the agent’s context and constrain its output: per-package instruction files, explicit prompt scoping, and a two-phase commit pattern for anything that crosses a boundary.

Who this is for

Developers in Turborepo, Nx, pnpm-workspace, or Yarn-workspace setups, plus large single repos with clear package boundaries. Engineering leads writing AI guidelines for a team also benefit — these conventions are what make AI-generated PRs reviewable at scale.

Reach for this workflow on any non-trivial change in a monorepo with three or more packages, especially when the change might cross boundaries: a new API endpoint consumed by web, a new shared component, an update to a generated client. Single-package edits in a monorepo still work fine with default tooling; structure matters when the agent needs to know not to touch sibling packages.

It is overkill for tiny monorepos (2-3 packages that share everything) and for repos still being carved into a monorepo — let the structure stabilize first. For a single-app codebase, see using AI to audit a React Native project instead.

The instruction file is AGENTS.md now

In 2026, the project-level instruction file has consolidated on AGENTS.md — the same plain-Markdown file is read by Cursor, Codex, GitHub Copilot, and (via import) Claude Code. The behavior that makes it work in a monorepo is nearest-file-wins: an agent editing packages/api/src/route.ts reads the AGENTS.md chain from the git root down to that directory, with the closest file taking precedence. That is exactly the per-package scoping a monorepo needs.

How the major tools consume it, as of June 2026:

ToolInstruction filePer-package behaviorScoping handle
Cursor.cursor/rules/*.mdc (or AGENTS.md)Nested .cursor/rules/ under a package overrides root; rules are glob-scopedFolder scope + @-mention files
Claude CodeCLAUDE.md (import AGENTS.md)Walks up from CWD, merges additively; sibling package files are not loadedSet the working directory; @file references
Codex CLIAGENTS.mdConcatenates global + every AGENTS.md from git root to CWDPass only target files
GitHub CopilotAGENTS.mdNearest-file-wins on editFile/selection scope

Two practical notes. Claude Code does not read AGENTS.md directly — either add @AGENTS.md inside your CLAUDE.md or symlink it with ln -s AGENTS.md CLAUDE.md. And keep each file small: a healthy root file is roughly 200-300 lines, and individual files should be split into nested ones once they pass 150-200 lines, both for readability and token budget. The honest 2026 default is: start with AGENTS.md, add Cursor .mdc rules only when you hit a real scoping limit, and add CLAUDE.md only if your team standardizes on Claude Code.

Before you start

  • Inventory packages and document boundaries. Which package owns the API contract? Which owns shared types? Which is internal-only? The “must NOT depend on” rule is often more useful to the agent than the “depends on” rule.
  • Pin one tool for the task. Cursor, Claude Code, Codex, and Aider all work; switching mid-task is how agents lose context.
  • Decide a test strategy. Per-package tests are not enough for cross-package work. You need at least one cross-package integration test and a working full-build command.
  • Confirm you can build a single package or the affected set. turbo run build --filter=@acme/api for one package, or --affected for the changed graph. The agent uses this to validate without rebuilding the world.

Step by step

  1. Maintain a root AGENTS.md plus a short one per package. Each package entry: name, one-line purpose, public API surface, ownership, and what it must NOT depend on. The agent’s context loader picks up the nearest package’s file automatically.
  2. Scope every prompt explicitly. State both the target and the exclusions: “Work in packages/api only. Do NOT touch packages/ui, apps/web, or any tests outside packages/api.”
  3. Limit retrieval to the target package. In Cursor use folder scope; in Claude Code set the working directory; in Codex/Aider pass only the target files. This is the single biggest lever against the agent reading the wrong package.
  4. Do cross-package changes in two passes. First pass: write the consuming package’s call site against a contract that does not yet exist. Commit (the build fails on purpose). Second pass: implement the providing package. Commit. Each commit is one concern, so review is trivial.
  5. Ask the agent about downstream consumers, explicitly. After any cross-package edit: “Did this change a public API surface? If yes, list every package that consumes this API and what each one needs to change.” Models forget downstream consumers by default.
  6. Validate with the affected graph before committing. turbo run build test --affected -- --base=origin/main (Turborepo 2.1+, Aug 2024) or nx affected -t build test. Nx reads the actual dependency graph, so it catches dependents that changed files alone would not flag. Cross-package contract breaks only surface in the integrated build, never in a single package’s tests.

A real cross-package example

Adding a new GET /usage endpoint in packages/api that the dashboard in apps/web will call:

  1. Scope to apps/web. Write the call site (fetchUsage() and the component that renders it) against the response shape you want. Commit. The build fails — packages/api has no such endpoint yet, and that is the point: the consumer now defines the contract.
  2. Scope to packages/api. Implement GET /usage to match the shape the consumer expects. Regenerate the typed client if you have one. Commit.
  3. Run turbo run build test --affected -- --base=origin/main, then your e2e suite, which is the only thing that exercises both packages together.
  4. Open the PR with two clean commits. A reviewer reads the contract in commit one and the implementation in commit two, in order, without bisecting a 6-package diff.

Common mistakes

  • Running the agent at the repo root with no scope. It retrieves random files from the wrong package.
  • Editing across boundaries in one prompt. Review becomes painful and the diff is impossible to bisect.
  • Omitting per-package conventions (test framework, linter rules) from AGENTS.md, so the agent uses the wrong ones.
  • Skipping the integrated build. Single-package tests pass while the integrated system breaks; only --affected or a full build catches it.
  • Letting the agent regenerate code into the wrong package. Always name which package owns generated artifacts (GraphQL types, protobuf, Prisma client) and the command that regenerates them.
  • Treating AGENTS.md as write-once. Packages get added and split; an outdated boundary doc misleads the agent into the same trap as no doc at all. Re-audit quarterly.

Advanced tips

  • One short AGENTS.md per package beats one giant root file. It keeps each file under the ~150-200 line split threshold and loads less irrelevant context.
  • Use @-references for conditional context. In Claude Code, @docs/database-conventions.md pulls a doc in only when needed instead of bloating every prompt.
  • Document the regeneration command in the file, e.g. “after changing the schema, run pnpm gen:types from the repo root,” so the agent knows to run it and where.
  • Keep a one-line module map for very large monorepos: per package, its inputs, outputs, and the one file the agent should read first.
  • Pre-load recent cross-package changes. New work tends to follow recent patterns; pointing the agent at the last few similar PRs improves its contract design.

Quality check

  • Each commit in the PR touches one package (or two, if you intentionally used the two-phase pattern).
  • The affected build passes — --affected or a full turbo run build && turbo run test, not just the package the agent edited.
  • The PR description names every package touched. If it does not, your prompt did not ask for it.
  • Generated code is regenerated and committed. If the agent forgot, your AGENTS.md is missing the regeneration command.

FAQ

  • Should I use AGENTS.md or CLAUDE.md?: Start with AGENTS.md — it is the cross-tool standard read by Cursor, Codex, and Copilot. Add CLAUDE.md only if your team standardizes on Claude Code, and even then import the same file with @AGENTS.md or a symlink so you maintain one source of truth.
  • One instruction file or many?: Many. One short file per package plus a small root index. Nearest-file-wins means the agent reads the closest one, and small files keep you under the ~150-200 line split threshold and inside the token budget.
  • Cursor vs Claude Code in a monorepo?: Both work. Cursor scopes by folder and glob-scoped .mdc rules; Claude Code walks up from the working directory and merges additively, so it never loads sibling package files into a session. Pick the UX you already use.
  • How do I run only the changed packages?: turbo run build test --affected -- --base=origin/main in Turborepo (2.1+) or nx affected -t build test in Nx. Nx understands the dependency graph, so it also runs dependents of changed packages, not just the changed ones.
  • How do I stop the agent from “improving” unrelated code?: Explicit exclusion in the prompt: “Do not modify files outside packages/api. Do not run linters that auto-format other packages.”
  • What if my packages do not have clear boundaries?: Fix that first. Without boundaries, AI cannot help in a monorepo — and neither can a human engineer reviewing the diff.

Tags: #AI coding #Tutorial #Workflow