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 nestedAGENTS.mdfiles — one per significant package. - Scope every prompt to a target package and an explicit exclusion list. “Work in
packages/apionly; do not touchpackages/uiorapps/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+) ornx affected -t build testruns 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:
| Tool | Instruction file | Per-package behavior | Scoping handle |
|---|---|---|---|
| Cursor | .cursor/rules/*.mdc (or AGENTS.md) | Nested .cursor/rules/ under a package overrides root; rules are glob-scoped | Folder scope + @-mention files |
| Claude Code | CLAUDE.md (import AGENTS.md) | Walks up from CWD, merges additively; sibling package files are not loaded | Set the working directory; @file references |
| Codex CLI | AGENTS.md | Concatenates global + every AGENTS.md from git root to CWD | Pass only target files |
| GitHub Copilot | AGENTS.md | Nearest-file-wins on edit | File/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/apifor one package, or--affectedfor the changed graph. The agent uses this to validate without rebuilding the world.
Step by step
- Maintain a root
AGENTS.mdplus 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. - Scope every prompt explicitly. State both the target and the exclusions: “Work in
packages/apionly. Do NOT touchpackages/ui,apps/web, or any tests outsidepackages/api.” - 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.
- 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.
- 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.
- Validate with the affected graph before committing.
turbo run build test --affected -- --base=origin/main(Turborepo 2.1+, Aug 2024) ornx 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:
- 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/apihas no such endpoint yet, and that is the point: the consumer now defines the contract. - Scope to
packages/api. ImplementGET /usageto match the shape the consumer expects. Regenerate the typed client if you have one. Commit. - Run
turbo run build test --affected -- --base=origin/main, then your e2e suite, which is the only thing that exercises both packages together. - 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
--affectedor 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.mdas 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.mdper 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.mdpulls 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:typesfrom 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 —
--affectedor a fullturbo 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.mdis missing the regeneration command.
FAQ
- Should I use
AGENTS.mdorCLAUDE.md?: Start withAGENTS.md— it is the cross-tool standard read by Cursor, Codex, and Copilot. AddCLAUDE.mdonly if your team standardizes on Claude Code, and even then import the same file with@AGENTS.mdor 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
.mdcrules; 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/mainin Turborepo (2.1+) ornx affected -t build testin 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.
Related
Tags: #AI coding #Tutorial #Workflow