Skip to content

GitHub Copilot Workspace for a TypeScript Monorepo

Configuring Copilot Workspace for a Yarn Monorepo

GitHub Copilot Workspace loses context fast in a large monorepo without the right setup. Here's the config that keeps its suggestions scoped correctly.

· · 4 min read

Quick Take

My first week with Copilot Workspace in a Yarn monorepo, it kept suggesting fixes that referenced files from a completely unrelated package. The default setup assumes a single-package repo, and a monorepo needs a few explicit nudges.

The specific failure that prompted this setup: Copilot Workspace suggested a fix for a failing test in blogs/coding-dunia, and the suggested code referenced a component that only existed in blogs/real-smart-home, a completely different Astro project sharing the same monorepo. The two packages had similarly-named files, and without an explicit scoping hint, Copilot's retrieval treated the whole repo as one undifferentiated codebase.

TL;DR: Copilot Workspace's context retrieval isn't monorepo-aware by default, similarly-named files across unrelated packages can bleed into its suggestions. A .github/copilot-instructions.md describing the monorepo's package boundaries, shared conventions, and per-package differences is the single most effective fix, read on every session rather than something you have to repeat in every prompt.

The Instructions File That Fixed It

<!-- .github/copilot-instructions.md -->
# Repository Structure

This is a Yarn 4 workspace monorepo. Packages live under two roots:

- `packages/*`: shared TypeScript libraries, consumed by blog sites via
  `@local/blog-toolkit`. Changes here affect every blog.
- `blogs/*`: individual Astro sites, each with its own `package.json`,
  `astro.config.mjs`, and `src/content/` collection. Each blog is
  independent; code in one blog's `src/` never imports from another
  blog's `src/` directly.

# When Suggesting a Fix

- Check which package the file under discussion belongs to before
  suggesting a related file, changes to `blogs/coding-dunia/src/`
  should never reference `blogs/real-smart-home/src/` or vice versa.
- Shared logic belongs in `packages/shared/src/`, not duplicated across
  blog packages. If a fix looks like it should be shared, check
  `packages/shared/src/` for an existing utility first.

# Conventions

- TypeScript strict mode is enabled everywhere; don't suggest `any`.
- Tests use Vitest, files are `*.test.ts` under `tests/`.

This is read automatically on every Copilot Workspace session in the repo, it's the equivalent of a project's CLAUDE.md for Claude Code, giving the same persistent context without needing to restate the monorepo's structure in every individual prompt.

Scoping a Session to One Package

Beyond the persistent instructions file, Copilot Workspace sessions themselves benefit from an explicit scope statement at the start of a task description, rather than relying on file selection alone to communicate intent:

Task: Fix the failing test in blogs/coding-dunia/tests/unit/validator.test.ts

Scope: This task only touches blogs/coding-dunia/. Do not modify files
in packages/shared/ or any other blogs/* package, even if a similar
pattern exists there.

Being explicit about what's out of scope, not just what's in scope, cuts down on a specific failure mode: Copilot proposing a "consistency" fix that touches a sibling package's file because it found a similar pattern there, when the actual task only needed the one file changed.

Handling Shared Package Version Drift

Monorepos often have version drift between packages, a dependency pinned differently in blogs/coding-dunia/package.json than in blogs/real-smart-home/package.json, sometimes deliberately. Without guidance, Copilot's suggestions can assume a version's API that isn't actually what's installed in the specific package being edited:

# In .github/copilot-instructions.md

# Dependency Versions

Package versions can differ between `blogs/*` sites. Before suggesting
an API from a dependency (React, Astro, etc.), check that package's own
`package.json` for its actual installed version, don't assume the
version used in a different blogs/* package applies here.

This single paragraph fixed a recurring issue where a Copilot suggestion used a React 19-only API (use()) in a package still on React 18, because a sibling package in the same monorepo happened to be on React 19 and Copilot's retrieval picked up that context instead.

What Directory-Scoped Instructions Add

For monorepos with genuinely divergent package conventions, not just different dependency versions, a nested .github/copilot-instructions.md inside a specific package directory can layer additional, package-specific guidance on top of the root file, where the platform supports it:

<!-- blogs/coding-dunia/.github/copilot-instructions.md -->
# Coding Dunia Specific Conventions

This blog uses Astro Content Collections with a strict `category` enum
(see src/content.config.ts). Never suggest a category value outside:
react, typescript, javascript, nodejs, css, performance, tooling.

The root file covers repo-wide structure; a package-scoped file covers the specific conventions that only make sense in the context of that one package, like a closed content-category enum that would be meaningless guidance to apply repo-wide.

Conclusion

The gap between "Copilot works fine on a small project" and "Copilot keeps suggesting the wrong package's file" is almost always a missing instructions file, not a limitation of the tool itself. A .github/copilot-instructions.md that states the monorepo's package boundaries, shared-vs-independent code locations, and per-package version differences fixes the cross-contamination failure mode directly, and it only needs to be written once per repo, not repeated in every prompt.

Frequently Asked Questions

Why does Copilot Workspace struggle with monorepos by default?
Copilot's context-gathering is optimized for a single coherent codebase, and a Yarn/npm/pnpm workspace monorepo often has multiple package.json files, overlapping dependency versions across packages, and code that looks similar across packages but belongs to entirely separate applications. Without a scoping hint, Copilot's retrieval can pull in a file from an unrelated package that happens to share a similar name or pattern, producing a suggestion that references the wrong package's conventions.
Does a CLAUDE.md or AGENTS.md style file help Copilot Workspace the same way it helps Claude Code?
Yes, GitHub Copilot reads repository-level instruction files (a .github/copilot-instructions.md file specifically), which serve a similar purpose to a CLAUDE.md, giving the tool persistent context about repo structure, conventions, and constraints it wouldn't otherwise infer correctly from code alone. In a monorepo, this file is the single highest-value thing to write well, since it's read on every session, not just once.
Should each package in a monorepo have its own Copilot configuration?
Where a package has genuinely different conventions from the rest of the repo, a different framework, a different testing approach, a nested .github/copilot-instructions.md scoped to that package's directory (if your Copilot Workspace version supports directory-scoped instructions) is worth it. For packages that follow the shared repo conventions, the root-level instructions file is sufficient and avoids duplicating the same guidance across a dozen package-specific files.