# Context Engineering untuk AI Coding Agent di Monorepo

> Monorepo mematahkan context agent yang naif secara spesifik. Cara men-scope instruction file per directory, mengaktifkan skill lewat glob, dan delegasi fan-out search ke subagent.

URL: https://www.matthewswong.com/id/blog/context-engineering-monorepo-ai-agents/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-09-11
- Updated: 2026-09-11
- Category: AI

> **Key takeaway:** Context engineering di monorepo intinya dua keputusan: instruction file mana yang dimuat bersama directory mana, dan search mana yang dijalankan di luar context window kalian sendiri. Claude Code memuat CLAUDE.md root saat launch dan CLAUDE.md sebuah package hanya ketika ia membaca package itu, dan file read milik subagent tidak pernah masuk ke window utama.

Perubahannya sekitar empat ratus baris dan tersebar di enam package: satu shared type, dua call site yang mengimpornya, satu migration, satu fixture, dan satu generated client yang seharusnya tidak disentuh siapa pun. Saya meminta semuanya dalam satu session, mulai dari root repository, lalu sepuluh menit pertama habis untuk menonton window terisi file yang tidak berhubungan dengan satu pun dari itu.

Itulah bentuk masalah context versi monorepo, dan ia bukan masalah yang sama dengan session yang terlalu panjang. Tulisan ini membahas mekanisme yang menanganinya — instruction file yang di-scope per directory, skill yang aktif lewat glob, search yang didelegasikan, structural lookup, dan daftar singkat hal yang sebaiknya tidak pernah masuk window — plus bagian yang paling sering salah ditebak pembaca untuk masing-masing: kapan ia sebenarnya dimuat. Perilaku pemuatannya diambil dari dokumentasi Claude Code sendiri. Urutannya adalah yang saya pilih setelah sempat melakukannya dengan buruk.

## Kenapa monorepo mematahkan agent yang naif

Monorepo tidak membuat context management lebih sulit secara umum. Ia mematahkan satu asumsi yang spesifik. Di luar monorepo, file yang disentuh sebuah task biasanya berdekatan, jadi membaca sekitar perubahan itu murah dan sebagian besar relevan. Di monorepo, empat ratus baris yang penting tersebar di package yang hanya berbagi satu repository dan nyaris tidak berbagi apa pun lagi, dan cara termurah menemukannya — buka file sampai ada yang kelihatan berhubungan — sekaligus cara tercepat mengisi window dengan hal yang ternyata tidak berhubungan.

Biayanya bukan pada membaca. Biayanya karena semua yang sudah dibaca itu tinggal. Context window menyimpan seluruh percakapan, termasuk setiap message, setiap file yang dibaca agent, dan setiap output command, lalu semuanya dikirim ulang bersama message berikutnya. Jadi file salah yang dibuka di menit ketiga masih kalian bayar di menit keempat puluh, dan di titik itu kalian juga membayar hukuman kedua yang disebut dokumentasi tanpa basa-basi: performa menurun seiring window terisi. Instruksi awal terlupakan, kesalahan bertambah, dan perbaikannya datang setelah penurunan itu, bukan sebelumnya.

## Empat penghuni window, dan dua yang dibayar setiap turn

Sebut dulu empat isi window sebelum mencoba mengecilkan salah satunya. Instruction yang selalu dimuat datang sebelum prompt pertama kalian: system prompt, memory file, nama tool, serta nama dan description setiap skill yang terdeteksi. Conversation history adalah semua yang sudah dibicarakan. Tool result adalah file read, output search, dan output command. Current task adalah prompt kalian. Dua dari empat itu layak diaudit dan dua tidak, dan garis pemisahnya adalah apakah kalian membayarnya sekali atau di setiap request.

Penghuni

Kapan ia masuk

Dikirim ulang setiap turn

Tuas yang kalian punya

Instruction yang selalu dimuat

Sebelum prompt pertama

Ya, dan di-inject ulang setelah compaction

Scope per directory atau per glob

Conversation history

Seiring session berjalan

Ya, seluruhnya, sampai compaction

Clear di antara task yang tidak berkaitan

Tool result

Setiap read, search dan command

Ya, sampai session berakhir

Delegasikan pembacaannya ke subagent

Current task

Saat kalian menekan enter

Tidak, ini yang paling murah

Sebut nama filenya, bukan modulnya

Dokumentasi menyediakan walkthrough interaktif tentang satu session yang terisi penuh, dan sekali mencobanya sudah berguna, dengan catatan yang mereka sebut sendiri: angka tokennya representatif, bukan hasil pengukuran. Bentuknya yang penting. Isi startup kecil dan permanen. Tool result besar dan permanen. Conversation history satu-satunya kategori yang bisa keluar, dan ia keluar dengan cara diringkas, sebuah pertukaran lossy yang syaratnya bukan kalian yang tentukan. Mengaudit bagian yang selalu dimuat itu pekerjaan sekali jadi. Menjaga tool result tetap di luar adalah keputusan yang kalian ambil beberapa kali dalam satu jam.

## Instruction file per directory, dan kapan ia datang

Pecah instruction per directory, dan aturan pemuatannya yang akan melakukan scoping untuk kalian. Claude Code memuat CLAUDE.md dari working directory dan semua directory di atasnya saat launch, lalu memuat file di subdirectory secara on demand ketika ia membaca file di sana. Jadi file root memuat hal yang berlaku di mana saja — konvensi commit, directory mana yang generated, cara menjalankan script sebuah package — sementara file tiap package hanya memuat stack-nya sendiri. Targetkan di bawah dua ratus baris per file; dokumentasi mencatat file yang lebih panjang memakan lebih banyak context sekaligus menurunkan kepatuhan, yang berarti buruk di dua sisi.

Dua detail menentukan apakah ini benar-benar menghemat. Pertama, dari mana kalian memulai session. Dari root repository kalian dapat file root plus file setiap subdirectory begitu session menyentuhnya; dari packages/api kalian dapat package itu dan semua leluhurnya, dan konvensi frontend tidak pernah dimuat sama sekali. Kedua, jebakannya: import tidak menolong. File yang diimpor diekspansi dan dimuat saat launch bersama file yang mereferensikannya, maksimal empat hop, jadi memecah CLAUDE.md yang panjang menjadi import hanya merapikan, bukan menghemat.

> Kalau sebuah task hidup di satu package, mulai session di directory package itu, bukan di root repository. Itu satu-satunya keputusan scoping yang gratis untuk diambil, dan sekali diambil ia berlaku untuk instruction file, project settings, dan skill mana yang masuk scope, semuanya sekaligus. Simpan claudeMdExcludes untuk package yang memang tidak pernah kalian sentuh — ia daftar statis, bukan switch per task.

## Skill yang aktif lewat glob: trik yang sama untuk sebuah prosedur

Konvensi tempatnya di instruction file. Prosedur tempatnya di skill, karena body sebuah skill baru dimuat saat dipakai, dan sebelum itu ia hanya membayar satu nama dan satu description. Di monorepo ada dua cara men-scope-nya. Taruh di dalam package, di mana SKILL.md di packages/api/.claude/skills/ menjadi tersedia begitu Claude membaca file di package itu. Atau simpan di root repository dan beri frontmatter-nya field paths berisi glob pattern, yang membatasi aktivasi otomatis hanya pada file yang cocok. Cara kedua paling pas untuk prosedur yang filenya tersebar — setiap folder migrations di dalam tree, di mana pun folder itu berada.

Kegagalan di sini soal discoverability, bukan biaya. Claude memilih skill dengan membaca nama dan description setiap skill yang terdeteksi, dan dari root repository himpunan itu tumbuh seiring session menyentuh directory; dokumentasi memperingatkan jumlahnya bisa menumpuk sampai ratusan. Description dipendekkan ketika jumlahnya banyak, dan gabungan description dan when_to_use dibatasi 1.536 karakter di listing, jadi skill yang kata pembedanya ada di kalimat kedua bisa kehilangan kata itu tepat ketika ia bersaing dengan paling banyak saudara. Mulai dengan kata yang kemungkinan muncul di permintaan pengguna, dan sebut nama directory-nya di description.

Scoping menentukan apa yang datang bersama file. Ia tidak mengurangi pembacaannya sendiri, yang di monorepo justru separuh tagihan yang lebih besar, dan mekanisme untuk separuh itu adalah subagent.

*Panel tengah yang mengubah tagihan, karena file read milik subagent tidak pernah masuk window utama sehingga tidak pernah dikirim ulang di turn berikutnya.*

## Delegasikan fan-out search-nya, bukan jawabannya

Ini langkah dengan leverage terbesar di monorepo dan yang paling jarang saya lihat dipakai. Subagent mulai dengan context window baru yang terisolasi: ia tidak melihat conversation history kalian, skill yang sudah kalian invoke, maupun file yang sudah dibaca. Ia dapat system prompt sendiri, delegation message yang ditulis session utama, hierarki CLAUDE.md, dan isi penuh skill mana pun yang di-preload oleh definisinya. Setelah itu ia bisa membaca sebanyak apa pun file yang dibutuhkan pertanyaannya, dan hanya final text response-nya yang kembali ke kalian, plus metadata trailer kecil berisi hitungan token dan durasi.

Walkthrough di dokumentasi memberi angka representatif untuk pertukaran ini: research subagent di contoh itu membaca 6.100 token file dan mengembalikan hasil 420 token. Jangan pakai angka itu sebagai benchmark — pakai rasionya sebagai kriteria pemilihan. Pertanyaan dengan search panjang dan jawaban pendek adalah kandidat ideal, dan package mana yang mengimpor type ini serta mana di antaranya yang menganggap nilainya tidak pernah null persis berbentuk seperti itu. Pertanyaan yang jawabannya sendiri panjang bukan kandidat, dan mendelegasikannya hanya memindahkan token satu hop.

Saya salah dengan cara paling jelas lebih dulu: saya mendelegasikan pembacaannya tapi meminta buktinya. Subagent yang mengembalikan empat puluh kutipan file supaya session utama bisa memeriksa hasilnya tidak memindahkan apa pun — kalian membayar pembacaan dua kali dan transkripnya tetap mendarat di window kalian. Minta kesimpulan plus path yang mendukungnya, tulis itu di delegation prompt, dan taruh instruksi yang sama di definisi agent supaya kalian tidak perlu mengingatnya lagi.

## Tanya language server, bukan empat puluh hit grep

Di tempat language server tersedia, structural lookup menggantikan search-nya, bukan memangkasnya. Text search menjawab file mana yang memuat sebuah string. Pertanyaan monorepo hampir selalu di mana sebuah symbol didefinisikan dan siapa yang memanggilnya, dan dua jawaban itu berpisah di setiap re-export, alias, dan type bernama sama di package lain. Plugin code intelligence menyambungkan agent ke language server sehingga ia bisa melompat ke definisi, mendaftar reference, dan memunculkan type error setelah edit alih-alih menyisir tree: satu jawaban presisi, dibanding grep yang menyerahkan empat puluh hit tanpa peringkat apa pun.

Ini tidak gratis, dan biayanya perlu disebut terang-terangan. Plugin-nya butuh binary language server bahasa itu di mesin setiap developer, dan install dari marketplace resmi butuh akses jaringan ke GitHub, tempat marketplace itu di-host; di jaringan yang dibatasi, kalian menambahkan marketplace-nya dari Git host internal atau path lokal. Sebagai gantinya, inilah satu-satunya teknik di daftar ini yang mengurangi pembacaan tanpa menuntut kalian memutuskan apa pun saat itu juga.

> Install code intelligence per bahasa, bukan per repository: jalankan plugin install untuk typescript-lsp dari marketplace resmi di dalam session, atau tambahkan ke project setting enabledPlugins supaya semua orang yang bekerja di repository itu mendapatkannya tanpa perlu install sendiri. Marketplace resmi menerbitkan plugin untuk TypeScript, Python, Go, Rust, dan bahasa umum lainnya.

## Apa yang sebaiknya tidak pernah masuk window

Content search menghormati .gitignore secara default, jadi node_modules, dist, dan build sudah otomatis di luar hasil tanpa konfigurasi apa pun. Kasus yang mahal adalah yang di-commit orang, plus satu kebiasaan.

Generated code yang di-commit. Generated client itu output turunan, jadi membacanya tidak mengajarkan apa pun yang tidak ada di schema, dan setiap edit padanya dibatalkan oleh run codegen berikutnya.

Dependency yang di-vendor. Vendored SDK itu kode orang lain yang memakai prefix path repository kalian, dan sering menjadi directory terbesar di tree.

Lockfile dan build output yang di-commit. Besar, tidak informatif, dan dikirim ulang setiap turn sampai session berakhir begitu ada yang membukanya.

Satu modul utuh yang diserahkan sebagai context. Ini kebiasaan yang perlu dihentikan: biayanya sama dengan biaya filenya, tidak membawa pertanyaannya sama sekali, dan begitulah session monorepo kehabisan window sebelum kehabisan task.

Blokir kasus yang sudah di-commit dengan Read deny rule, jangan mengandalkan penilaian saat itu juga. Deny rule mencakup file tool bawaan dan command file Bash yang dikenali — cat, head, grep, dan find — ketika path yang diblokir dilewatkan sebagai argumen, dan Claude Code juga berusaha sebaik mungkin menjaga path itu keluar dari hasil Grep dan Glob.

> Deny rule adalah kontrol anggaran, bukan batas penegakan. Path yang diblokir tetap muncul di output search Bash seperti grep -r atau find, dan aturan itu tidak mencakup subprocess yang membuka file sendiri. Ia menghilangkan pembacaan tak sengaja dan godaannya; ia tidak menjamin sebuah file tidak akan pernah terlihat. Kalau sesuatu memang tidak boleh dibaca, tempatnya bukan di dalam repository.

*Baca band paling bawah lebih dulu, karena itulah yang tampak seperti penghematan padahal bukan: import diekspansi saat launch persis seperti file yang mereferensikannya.*

## Lihat sendiri ke mana anggaran kalian pergi

Berhenti menebak separuh mana yang mahal. Jalankan command context setelah session mengerjakan sesuatu yang nyata, bukan saat startup: ia mencetak rincian langsung per kategori beserta saran optimisasi, termasuk file CLAUDE.md dan auto memory mana yang benar-benar dimuat. Startup adalah bagian yang sudah kalian tahu. Yang kalian butuh adalah keadaan setelah dua puluh tool call, ketika session sudah berkeliaran.

Untuk separuh yang selalu dimuat, ada alat yang lebih tajam. Hook InstructionsLoaded menyala ketika sebuah instruction file dimuat, sekali di awal session untuk file yang eager dan kemudian lagi untuk yang lazy, dan input-nya membawa file_path, memory_type, serta load_reason berupa session_start, nested_traversal, path_glob_match, include, atau compact. Karena matcher-nya dicocokkan ke load_reason, kalian bisa mencatat hanya lazy load dan membaca ulang package mana yang benar-benar disentuh sebuah session. Pasangkan dengan logs exporter OpenTelemetry dan flag detail tool yang merekam nama skill secara verbatim, maka event skill_activated juga akan memberi tahu skill mana yang tidak pernah menyala — dan skill yang tidak pernah menyala itu menumpang sewa di listing setiap session.

Aturan yang sekarang saya pegang: monorepo tidak butuh context window yang lebih kecil, ia butuh setiap mekanisme dimuat di momen yang tepat. Instruction tempatnya di sebelah kode yang ia jelaskan. Prosedur tempatnya di belakang sebuah glob. Search tempatnya di window lain. File generated tempatnya di belakang deny rule. Audit separuh yang selalu dimuat satu kali, catat temuannya, lalu belanjakan perhatian yang kalian hemat untuk pembacaan, karena di situlah anggarannya benar-benar pergi.

## Sumber dan bacaan lanjutan

- Dokumentasi Claude Code — Menyiapkan Claude Code di monorepo atau codebase besar
- Dokumentasi Claude Code — Cara Claude mengingat project: hierarki CLAUDE.md dan rule ber-scope path
- Dokumentasi Claude Code — Memperluas Claude dengan skills, termasuk field frontmatter paths
- Dokumentasi Claude Code — Subagents, dan apa yang dimuat di dalam context window terisolasi
- Dokumentasi Claude Code — Menjelajahi context window, walkthrough session interaktif

## Source links

- https://code.claude.com/docs/en/large-codebases
- https://code.claude.com/docs/en/memory
- https://code.claude.com/docs/en/skills
- https://code.claude.com/docs/en/sub-agents
- https://code.claude.com/docs/en/context-window

## FAQ

### Kenapa monorepo mengisi context window AI coding agent secepat itu?

Karena baris yang disentuh sebuah task tersebar di package yang nyaris tidak berbagi apa pun selain satu repository, jadi cara termurah menemukannya adalah membuka file sampai ada yang kelihatan berhubungan. Semua yang sudah dibaca lalu tinggal di window dan dikirim ulang bersama setiap message berikutnya, sehingga file salah yang dibuka di menit pertama masih kalian bayar satu jam kemudian. Dokumentasi Claude Code menyebut context window sebagai sumber daya terpenting untuk dikelola justru karena itu.

### Apakah CLAUDE.md di subdirectory monorepo dimuat saat startup?

Tidak. Claude Code memuat CLAUDE.md dari working directory dan semua directory di atasnya saat launch, tetapi file di subdirectory dimuat on demand, yaitu ketika Claude membaca file di directory itu. Itulah yang membuat file per package murah, karena konvensi package lain tidak pernah masuk ke session yang tidak menyentuhnya. Memecah file panjang menjadi import tidak memberi efek yang sama, karena import diekspansi saat launch.

### Bagaimana cara men-scope skill Claude Code ke satu bagian monorepo?

Ada dua cara. Taruh SKILL.md di dalam package, misalnya di packages/api/.claude/skills/, supaya ia tersedia begitu Claude membaca file di sana. Atau simpan di root repository dan tambahkan field paths berisi glob pattern di frontmatter, yang membatasi aktivasi otomatis hanya pada file yang cocok. Cara kedua lebih baik ketika filenya tersebar, seperti setiap folder migrations di dalam tree.

### Apakah mendelegasikan search ke subagent benar-benar menghemat context?

Ya, karena subagent berjalan di context window terisolasi miliknya sendiri dan hanya final text response-nya yang kembali ke percakapan utama, plus metadata trailer kecil berisi hitungan token dan durasi. Walkthrough interaktif di dokumentasi menunjukkan research subagent membaca 6.100 token file dan mengembalikan hasil 420 token, dengan angka yang representatif dan bukan hasil pengukuran. Penghematannya hilang kalau kalian meminta ia mengembalikan kutipan filenya, bukan kesimpulan.

### Bagaimana cara melihat instruction file mana yang dimuat sebuah session?

Jalankan command context setelah session mengerjakan sesuatu yang nyata, bukan saat startup: ia mencetak rincian langsung per kategori dan mendaftar file CLAUDE.md serta auto memory mana yang dimuat. Untuk log berjalan, tambahkan hook InstructionsLoaded. Input-nya membawa file_path, memory_type, dan load_reason seperti session_start, nested_traversal, atau path_glob_match, dan matcher-nya dicocokkan ke load_reason sehingga kalian bisa mencatat hanya lazy load.

---

- HTML version: https://www.matthewswong.com/id/blog/context-engineering-monorepo-ai-agents/
- English: https://www.matthewswong.com/en/blog/context-engineering-monorepo-ai-agents/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
