Skip to content

Setting Up CodeBuddy Code in Monorepos and Large Repositories

Configure CodeBuddy Code for monorepos and large single-tree repositories using nested CODEBUDDY.md files, sparse worktrees, code intelligence, and per-package skills to keep CodeBuddy focused on the code you're working on.

Large codebases can be a single repository with millions of lines of code or a monorepo containing many packages. CodeBuddy Code can work at any scale, but as the codebase grows, default settings tuned for smaller projects can fill the context window with instructions and file reads irrelevant to the task, wasting tokens and degrading CodeBuddy's performance.

This guide shows individual developers and engineering teams how to limit CodeBuddy's scope to the parts of the codebase that the task involves. Each section notes whether the setup is personal or committed to the repository.

What This Guide Covers

The table below lists each setting and what it does. The file tree after it is the example monorepo referenced by every code example on this page.

Settings on This Page

Each setting below is independent. They stack on top of each other rather than replacing each other, so apply whichever settings suit your repository. Choosing Where to Start CodeBuddy determines where your settings files live, so read it first. Putting It All Together shows the combination of all these settings.

I Want ToUse
Load only the conventions for the code you touch, instead of one root file covering every subsystemPer-directory CODEBUDDY.md files
Prevent CodeBuddy from opening build output, generated code, and vendor dependenciesRead deny rules in permissions.deny
Find symbol definitions or callers through a language server instead of scanning filesCode intelligence plugin
Check out only the directories the task needs when CodeBuddy creates a worktreeworktree.sparsePaths
Read and edit sibling packages or another repository from the same session--add-dir or additionalDirectories
Give CodeBuddy procedures specific to one area, loaded only when relevantPer-directory skills
Replace many per-directory CODEBUDDY.md files with one set of conventions everyone installsA plugin from an internal marketplace

Tip: For workflow techniques to keep context small in any repository, such as running exploration in subagents so file reads don't enter the main conversation, see CodeBuddy Code Best Practices.

Example Monorepo

The examples on this page reference a monorepo with three packages. The same patterns apply to large single-tree repositories: where the examples use packages/api/, substitute your own subsystem directory, such as src/backend/ or lib/core/.

text
monorepo/
  CODEBUDDY.md                # Root instructions
  packages/
    api/
      CODEBUDDY.md              # API-specific instructions
      .codebuddy/skills/
      src/
    web/
      CODEBUDDY.md              # Frontend-specific instructions
      .codebuddy/skills/
      src/
    shared/
      CODEBUDDY.md              # Shared library instructions
      src/

Choosing Where to Start CodeBuddy

Where you start codebuddy determines which files CodeBuddy can read and edit without additional permission grants, which CODEBUDDY.md files load at startup, and which project settings apply.

Started FromFile AccessCODEBUDDY.md Loaded at StartupUse Case
Repository rootEvery fileRoot only; subdirectory files load on demand as CodeBuddy reads thereTask spans multiple packages or subsystems
SubdirectoryOnly that subtree, until you grant moreThat directory's plus each ancestor'sWork scoped to one package or subsystem

Project settings in .codebuddy/settings.json are loaded only from your startup directory, unlike CODEBUDDY.md files which inherit from parent directories: the repository root's .codebuddy/settings.json applies only when you start from the root.

Each section below notes whether its settings file should live at the repository root or in the subdirectory you start from, and whether it's committed or kept local.

Layered CODEBUDDY.md Files by Directory

In large codebases, a single CODEBUDDY.md at the repository root tends to either grow to cover every subsystem's conventions, wasting context on instructions irrelevant to the current task, or stay too generic to be useful. Spreading instructions across per-directory files means CodeBuddy loads repository-wide rules plus only the conventions for the code you're working on.

CodeBuddy Code loads each CODEBUDDY.md file at startup from your working directory and every parent directory, then loads each subdirectory's file on demand when it reads files there. The root file sets repository-wide rules, and each subdirectory adds its own.

A common split is two levels:

  • Root CODEBUDDY.md: Instructions that apply everywhere, such as coding standards, commit conventions, and repository layout
  • Per-subdirectory CODEBUDDY.md: Conventions specific to that area's stack. In a monorepo, this is one per package. In a large single-tree, it's one per subsystem, such as src/db/ or src/api/

Commit these files to the repository so teammates inherit them. Each directory's owner typically maintains its file.

The root CODEBUDDY.md orients CodeBuddy to the repository structure:

markdown
# CODEBUDDY.md

This is a monorepo with three packages under packages/:

- packages/api: Node.js REST API using Express, TypeScript, and PostgreSQL
- packages/web: React frontend using Vite, TypeScript, and TailwindCSS
- packages/shared: Shared TypeScript utilities used by both api and web

Run commands from the package directory, not from the monorepo root.
Each package has its own tsconfig.json, package.json, and test suite.

Each subdirectory's CODEBUDDY.md, here packages/api/CODEBUDDY.md, adds context specific to that area's stack:

markdown
# packages/api/CODEBUDDY.md

This package is the REST API server.

- Run tests: `npm test` (uses Vitest)
- Run dev server: `npm run dev` (port 3001)
- Database migrations: `npm run migrate`
- Environment variables: Copy `.env.example` to `.env`

API routes are in src/routes/. Each route file exports an Express router.
Database queries are in src/db/ using Knex. Never write raw SQL strings in route handlers.

When you start CodeBuddy from packages/api/, it loads packages/api/CODEBUDDY.md and the root CODEBUDDY.md. CodeBuddy sees local instructions alongside repository-wide rules, with no instructions from packages/web/ in context. The same applies to any subdirectory in a non-monorepo tree.

Ways to keep files current as the codebase and models change:

  • Review in pull requests: Treat CODEBUDDY.md edits like any other documentation change so conventions track code
  • Revisit after major model releases: Instructions that worked around older model limitations may become overhead once newer models handle the situation themselves. For example, a rule forcing single-file refactors can be removed once the limitation is gone
  • Add a Stop hook to propose updates: The Stop hook receives the session transcript path when CodeBuddy finishes responding, so a script can review the session and propose CODEBUDDY.md updates while the exposed gaps are still fresh

For more information on how CODEBUDDY.md files load and interact, see Memory and Project Instructions.

Choosing Between Per-Directory CODEBUDDY.md and Path-Scoped Rules

Per-directory CODEBUDDY.md files and path-scoped rules under .codebuddy/rules/ both let you direct instructions to part of the tree. They differ in file location and loading timing.

ApproachFile LocationLoading TimeUse Case
Per-directory CODEBUDDY.mdInside the directory, alongside its codeAt startup when starting from that directory, or on demand when CodeBuddy reads files thereDirectory owners maintain their own conventions; instructions are versioned with the code
Path-scoped rules in .codebuddy/rules/Central .codebuddy/ at the repository rootWhen CodeBuddy processes a file matching the rule's paths: globYou want all conventions in one place, or the same rules apply to many scattered paths

Reducing What CodeBuddy Reads

Instructions are only part of what ends up in CodeBuddy's context. File reads are another cost that grows with the codebase. The settings below prevent reading irrelevant paths and replace exhaustive file scanning with language server lookups.

Prevent Reading Generated and Vendor Code

CodeBuddy's content search respects .gitignore by default, so paths already listed there, such as node_modules/, dist/, and build/, stay out of search results without extra configuration.

For paths that are checked in, such as vendored SDKs or committed generated code, add Read deny rules in permissions.deny to prevent CodeBuddy from opening these files even if search lists them.

To apply these exclusions for everyone working in the repository, commit them to .codebuddy/settings.json. To keep them personal, use .codebuddy/settings.local.json instead. Like other project settings on this page, these files load only from your startup directory. Place them at the repository root if you start CodeBuddy from there, or in each package's .codebuddy/ if you start from a subdirectory.

The example below blocks build artifacts and vendored SDKs:

json
// .codebuddy/settings.json
{
  "permissions": {
    "deny": [
      "Read(./**/dist/**)",
      "Read(./**/build/**)",
      "Read(./**/*.generated.*)",
      "Read(./vendor/**)"
    ]
  }
}

Deny rules cover CodeBuddy's built-in file tools and recognized Bash file commands, including cat, head, grep, and find, when denied paths are passed as arguments. They do not filter denied paths from recursive search output, nor do they cover arbitrary subprocesses that open files on their own. For the complete pattern syntax, see Read and Edit permission rules.

Use Code Intelligence to Reduce File Reads

In large codebases, finding a symbol's definition or usage sites can require many file reads and grep calls. The code intelligence plugin connects CodeBuddy to language servers so it can jump to definition, find references, and surface type errors directly, instead of scanning the tree.

The official marketplace has plugins for TypeScript, Python, Go, Rust, and other common languages. The example below installs the TypeScript plugin:

bash
/plugin install typescript-lsp@claude-plugins-official

To enable a plugin for everyone in the repository rather than installing it yourself, add it to the enabledPlugins project setting.

Code intelligence plugins require the language server binary for each language on each developer's machine. Installing from the official marketplace requires network access to GitHub, where the marketplace is hosted. On restricted networks, add a marketplace from an internal Git host or local path.

This pairs well with the Read deny rules above. Deny rules keep irrelevant content out of context, and code intelligence keeps CodeBuddy from reading the remaining content to locate definitions.

Scoping Worktrees and File Access

These settings control what's on disk in worktrees and which directories beyond the startup point CodeBuddy can read and write.

Check Out Only the Directories You Need

The --worktree flag starts a session in a new git worktree so changes are isolated from the main checkout. By default, it checks out the entire repository. In large repositories, the worktree.sparsePaths setting uses git sparse-checkout to write only the listed directories plus root-level files to disk, so worktrees start faster and use less space.

If everyone working in this directory needs the same paths, commit the setting to .codebuddy/settings.json. To add paths for yourself, use .codebuddy/settings.local.json: lists merge within scope, so a local file can add paths to the committed list but cannot remove them. The example below shows the committed file:

json
// .codebuddy/settings.json
{
  "worktree": {
    "sparsePaths": [
      ".codebuddy",
      "packages/api",
      "packages/shared"
    ]
  }
}

When CodeBuddy creates a worktree, it checks out only .codebuddy/, packages/api/, and packages/shared/ instead of the full tree. Paths in sparsePaths are relative to the repository root, regardless of which subdirectory you start CodeBuddy from. Any directory path works here, not just package roots.

This is particularly useful for subagent worktree isolation. Subagents are parallel CodeBuddy instances spawned for subtasks, each running in a worktree getting a lightweight checkout instead of the full tree. All worktrees in a session share the same sparsePaths, so if one subagent needs packages/api/ and another needs packages/web/, list both.

List directories in sparsePaths, not individual files. Root-level files like package.json, tsconfig.base.json, and lockfiles are always checked out alongside the directories you list. Root-level directories are not, so if you want the repository root's .codebuddy/settings.json, .codebuddy/rules/, or .codebuddy/skills/ available inside the worktree, include .codebuddy in the list.

To avoid duplicating large directories like node_modules in worktrees, pair sparsePaths with symlinkDirectories in the same .codebuddy/settings.json:

json
// .codebuddy/settings.json
{
  "worktree": {
    "sparsePaths": [
      ".codebuddy",
      "packages/api",
      "packages/shared"
    ],
    "symlinkDirectories": [
      "node_modules"
    ]
  }
}

This creates a symlink from each worktree's node_modules/ back to the main repository copy, instead of copying it on disk.

Note: sparsePaths and symlinkDirectories settings are read from your startup directory before a worktree is created. After creation, the session's working directory is the worktree root, not the subdirectory you started from. Therefore, project settings inside the worktree load from the worktree root's .codebuddy/settings.json (the checked-out copy of the repository root file). Put any additional settings you need inside worktrees (such as permission rules or hooks) in the repository root's .codebuddy/settings.json.

For the complete worktree settings reference, see Worktree Settings.

Grant Access Across Packages or Repositories

This section applies when you start CodeBuddy from a subdirectory, or when a task spans multiple checkouts. If you start from the repository root in a single large tree, CodeBuddy already has access to every file, and you can skip this section.

When you start CodeBuddy from packages/api/, it can read and write files within that directory. If the task requires cross-package changes, such as updating a shared type that both api and web import, you need to grant access to sibling directories. The same mechanism grants access to separately checked-out repositories.

The additionalDirectories setting in .codebuddy/settings.json gives CodeBuddy access to directories outside the working directory. The example below grants access to two sibling packages:

json
// .codebuddy/settings.json
{
  "permissions": {
    "additionalDirectories": [
      "../shared",
      "../web"
    ]
  }
}

Relative paths resolve against the directory where you start CodeBuddy. With this configuration, CodeBuddy can read and edit files in packages/shared/ and packages/web/ while working from packages/api/.

You can also grant access at runtime without editing settings, by passing --add-dir when starting CodeBuddy:

bash
codebuddy --add-dir ../shared

Regardless of how you add a directory, CodeBuddy can read and edit files within it. Whether the directory's CODEBUDDY.md, .codebuddy/rules/ files, and skills also load depends on how you add it:

How AddedLoads CODEBUDDY.md and RulesLoads Skills
additionalDirectories settingNeverNever
--add-dir flag or /add-dir commandOnly with the environment variable belowYes

To load CODEBUDDY.md and rules files from a directory added with --add-dir or /add-dir, set the CODEBUDDY_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD environment variable:

bash
CODEBUDDY_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 codebuddy --add-dir ../shared

The environment variable has no effect on directories listed in the additionalDirectories setting. See the memory documentation for details.

For sibling directories that everyone in this area needs, commit additionalDirectories to .codebuddy/settings.json. For personal choices or one-off access, use .codebuddy/settings.local.json or pass --add-dir at startup.

Add Per-Directory Skills

Any subdirectory can define skills scoped to its own stack. Skills load on demand when CodeBuddy determines they're relevant, so API-specific tools don't consume context during frontend work.

Skills live under .codebuddy/skills/ within a directory. Commit them alongside the area's code so anyone who clones the repository gets them. In a monorepo, this can be one set of skills per package. In a large single-tree repository, it's one per subsystem, such as src/db/.codebuddy/skills/.

Create a skill directory within a subdirectory:

bash
mkdir -p packages/api/.codebuddy/skills/api-testing

Then write a SKILL.md inside that directory, here packages/api/.codebuddy/skills/api-testing/SKILL.md. This example teaches CodeBuddy the API package's testing patterns:

markdown
---
name: api-testing
description: Testing patterns for the API package. Use when writing or modifying tests in packages/api/.
---

## Test Structure

Tests are in `src/__tests__/`, mirroring the `src/` directory structure.
Each route file has a corresponding `.test.ts` file.

## Running Tests

- All tests: `npm test`
- Single file: `npm test -- src/__tests__/routes/users.test.ts`
- Watch mode: `npm test -- --watch`

## Test Utilities

- `src/__tests__/helpers/db.ts`: Provides `setupTestDb()` and `teardownTestDb()` for database tests
- `src/__tests__/helpers/auth.ts`: Provides `createTestUser()` and `getAuthToken()` for auth endpoints

## Patterns

- Use `supertest` for HTTP assertions, not raw fetch
- Always wrap database tests in a rolled-back transaction
- Mock external services in `src/__tests__/mocks/`

Different subdirectories hold different skills the same way: packages/web/.codebuddy/skills/component-patterns/ describes the frontend's component conventions instead of testing. When CodeBuddy works on files in packages/api/, it loads the api-testing skill. When it works in packages/web/, it loads component-patterns instead. During a task for the other, neither directory's skills load.

You can also scope a skill by file pattern rather than by location. The paths frontmatter field takes a glob pattern, and CodeBuddy auto-loads the skill only when processing matching files. Use this for a skill in the repository root's .codebuddy/skills/ that applies only to certain files wherever they appear, such as a database migration skill scoped to **/migrations/**.

For more information on creating and organizing skills, see Skills.

Keeping Skills Discoverable

As skills spread across many directories, the list CodeBuddy chooses from can grow large. CodeBuddy selects skills by reading each discovered skill's name and description, and only the selected skill's full content loads into context. This section covers how to keep that list small and how to write descriptions that survive truncation.

Which skills are in scope depends on where you start CodeBuddy:

  • From a subdirectory like packages/api/: Skills from that directory, each parent up to the repository root, and user-level
  • From the repository root: Skills from every subdirectory CodeBuddy touches during the session, potentially accumulating to hundreds
  • After adding a sibling with --add-dir: That sibling's skills also load. The additionalDirectories setting grants file access only, without loading skills

Names always load, but when there are many, descriptions get truncated, which can strip keywords CodeBuddy uses to decide if a skill applies. Keep descriptions short and start with words a request would include, such as "when writing or modifying tests in packages/api/".

For skills shared across many directories, such as PR conventions or deployment checklists, put them in the repository root's .codebuddy/skills/ so they load from any startup directory. When shared skills need their own version history or must work across repositories, package them as a plugin instead. Plugin skills use the plugin-name:skill-name namespace, so they never conflict with per-directory skills. Platform teams can version and update them in one place.

Centralize Conventions When Layering Stops Scaling

As the codebase grows, per-directory CODEBUDDY.md files can become unwieldy. Conventions drift, files go stale, and no one owns the root. Fixing this usually falls on the team maintaining the repository's CodeBuddy Code setup, rather than each developer working in their own area.

Move conventions and reference material out of always-loaded CODEBUDDY.md into mechanisms that load on demand:

  • Skills: Reference material that CodeBuddy loads only when relevant to the task
  • Plugins: Versioned packages of skills, hooks, and commands centrally owned by platform teams
  • MCP servers: If your organization already runs code search or RAG indexing over the repository, expose it as an MCP tool so CodeBuddy queries it instead of reading files directly

Recommending the Right Plugin at Session Start

Once conventions live in plugins, a teammate starting CodeBuddy in an unfamiliar part of the tree has no signal about which plugin the area's owner maintains. The SessionStart hook can bridge this gap, because anything the hook prints to stdout is added to CodeBuddy's context before the first prompt.

For example, you can write a script that reads the startup directory from hook input, looks it up in a path-to-plugin mapping committed to the repository, and prints a suggestion for CodeBuddy to relay in its first response. See Automating Actions with Hooks for writing and registering hooks.

Putting It All Together

The combined configuration below uses a monorepo layout. The same files apply to any subdirectory in a large single-tree. Project settings load only from the directory where you start CodeBuddy, so each subdirectory's .codebuddy/settings.json must be self-contained rather than layered on top of the root file.

The example commits worktree, additionalDirectories, and Read deny rules in .codebuddy/settings.json so every developer in packages/api/ gets the same sibling access, sparse paths, and exclusions. The file below is packages/api/'s committed per-area settings:

json
// packages/api/.codebuddy/settings.json
{
  "worktree": {
    "sparsePaths": [
      ".codebuddy",
      "packages/api",
      "packages/shared"
    ],
    "symlinkDirectories": [
      "node_modules"
    ]
  },
  "permissions": {
    "additionalDirectories": [
      "../shared"
    ],
    "deny": [
      "Read(./**/dist/**)",
      "Read(./**/build/**)"
    ]
  }
}

Because this session starts from packages/api/, the sibling package's CODEBUDDY.md file is out of scope. If you also start sessions from the root, you can add exclusion rules to the repository root's .codebuddy/settings.local.json.

The additionalDirectories entry applies when you start CodeBuddy directly from packages/api/. Inside a worktree created from this session, the working directory is the worktree root, so this settings file doesn't load. The sibling package is already reachable inside the worktree without it, but the deny rules need a second copy in the repository root's .codebuddy/settings.json so worktree sessions pick them up, as noted in the worktree settings note:

json
// .codebuddy/settings.json
{
  "permissions": {
    "deny": [
      "Read(./**/dist/**)",
      "Read(./**/build/**)"
    ]
  }
}

With this set up, the repository has this layout:

text
monorepo/
  CODEBUDDY.md
  .codebuddy/settings.json                # Deny rules for worktree sessions
  packages/
    api/
      CODEBUDDY.md
      .codebuddy/settings.json                    # worktree, additionalDirectories, deny rules
      .codebuddy/skills/api-testing/SKILL.md
    web/
      CODEBUDDY.md
      .codebuddy/skills/component-patterns/SKILL.md
    shared/
      CODEBUDDY.md

With this setup, starting CodeBuddy from packages/api/:

  • Loads the root CODEBUDDY.md and packages/api/CODEBUDDY.md, skipping packages/web/CODEBUDDY.md
  • Can read and edit files in packages/api/ and packages/shared/
  • Skips reads of build output under dist/ and build/ in packages/api/
  • Has the api-testing skill available on demand
  • Creates worktrees containing .codebuddy/, packages/api/, packages/shared/, and root-level files, with deny rules from the root settings file applied to the entire worktree

Scope and Plan Cross-Package Changes

The configuration above controls what CodeBuddy sees. When a single change involves multiple packages, such as updating a shared type along with every call site that uses it, how you scope and sequence the task also affects the outcome.

Two techniques help keep cross-package changes consistent:

  • Give CodeBuddy the entire change in one session: Delivering the shared edit and its call sites together keeps the decisions behind each edit consistent, rather than re-deriving them per package
  • Save a plan to a file before editing: Plan first and ask CodeBuddy to write the plan to a markdown file in the repository. Long cross-package sessions compress their context mid-flight, and a saved plan survives where conversation history may not

Next Steps

Once this configuration is in place, you can refine it:

  • Use hooks to run per-directory linters or type checkers after CodeBuddy edits files
  • Review Managing Costs Effectively to understand how codebase size affects token usage and how to set spending limits before a broader rollout