Zagnieżdżone pliki AGENTS.md w monorepo - jak wdrożyć
Pojedynczy plik AGENTS.md w monorepo szybko staje się nieaktualny i marnuje tokeny kontekstu. Sprawdź, jak wdrożyć strukturę zagnieżdżoną, aby ograniczyć instrukcje do katalogów.
Co oznacza zagnieżdżony AGENTS.md w monorepo
Zagnieżdżony plik AGENTS.md w monorepo oznacza jeden mały plik w katalogu głównym repozytorium oraz po jednym dodatkowym pliku wewnątrz każdego katalogu usługi. Plik główny zawiera kilka reguł obowiązujących wszędzie oraz mapę lokalizacji pozostałych plików. Każdy plik wewnątrz usługi zawiera polecenia i konwencje dotyczące wyłącznie tego katalogu. Agent edytujący services/worker/queue.py odczytuje plik główny oraz plik roboczy, nie zużywając przy tym kontekstu na interfejs użytkownika, którego nigdy nie będzie modyfikował.
Nie ma nic do zainstalowania. AGENTS.md to konwencja, co projekt nadrzędny określa wprost:
AGENTS.md to zwykły plik Markdown. Można używać dowolnych nagłówków; agent po prostu analizuje dostarczony tekst.
Dlatego warto dobrze opanować tę technikę. Format nie ulegnie zmianie. Problemem może być jedynie rozmieszczenie i utrzymanie plików, a za oba te aspekty odpowiada użytkownik.
Dlaczego jeden duży plik AGENTS.md w katalogu głównym przestaje działać?
Pojedynczy plik AGENTS.md o długości 600 linii, znajdujący się w katalogu głównym repozytorium zawierającego aplikację internetową, proces w tle oraz katalog Terraform, zawodzi z czterech powodów.
Deaktualizuje się, ponieważ nikt nie czuje się za niego odpowiedzialny. Inżynier, który zmienia nazwę skryptu testowego w apps/web, edytuje pliki w apps/web. Plik AGENTS.md w katalogu głównym nie znajduje się w tym diffie, więc żaden recenzent nie zauważy rozbieżności. Sześć tygodni później plik opisuje krok budowania, który już nie istnieje, a osoba, która wprowadziła zmianę, już o niej zapomniała.
Pochłania kontekst przy każdym zadaniu. Te pliki są ładowane na początku sesji, zanim agent dowie się, o co zostanie zapytany. Dokumentacja Claude Code określa to jasno: „celuj w mniej niż 200 linii na plik CLAUDE.md. Dłuższe pliki zużywają więcej kontekstu i zmniejszają skuteczność stosowania się do instrukcji”. Codex przestaje łączyć pliki instrukcji, gdy ich łączny rozmiar osiągnie 32 KiB, czyli domyślną wartość project_doc_max_bytes. Plik w katalogu głównym, który dokumentuje cztery usługi, marnuje ten budżet na trzy z nich przy każdym pojedynczym zadaniu.
Instrukcje zaczynają być ze sobą sprzeczne. Katalog web wymaga pnpm test. Proces w tle wymaga pytest -q. Zapisane w jednym pliku, każda z reguł jest poprawna tylko w niektórych przypadkach, więc agent musi zgadywać, która z nich ma zastosowanie. Dokumentacja Claude Code opisuje rezultat: „jeśli dwie reguły są ze sobą sprzeczne, Claude może wybrać jedną z nich arbitralnie”. Plik przypisany do katalogu eliminuje zgadywanie, ponieważ w kontekście znajduje się tylko jedna z dwóch reguł. Gdy reguła, co do której masz pewność, że została jasno sformułowana, jest mimo wszystko pomijana, analiza przyczyn, dla których instrukcja nie jest uwzględniana jest skuteczniejsza niż czwarta próba przeredagowania tekstu.
Wypełnia się faktami, które agent mógłby odczytać z kodu. Drzewo katalogów, lista zależności, podsumowanie działania każdego pakietu. Mechanizm /doctor w Claude Code istnieje właśnie po to, aby usuwać takie treści. „Wycina on zawartość, którą Claude może wywnioskować z bazy kodu, taką jak układy katalogów, listy zależności i przeglądy architektury”, pozostawiając jedynie „pułapki, uzasadnienia i konwencje, które odbiegają od domyślnych ustawień narzędzi”. To zdanie jest najlepszym znanym mi testem sprawdzającym, czy dana linia w ogóle powinna znaleźć się w pliku.
Czy agent odczytuje plik z katalogu głównego, czy tylko najbliższy?
W tym miejscu większość osób błędnie interpretuje działanie modelu, dlatego warto przytoczyć konwencję nadrzędną zamiast ją parafrazować:
Umieść kolejny plik AGENTS.md wewnątrz każdego pakietu. Agenci automatycznie odczytują najbliższy plik w drzewie katalogów, więc to ten najbliższy ma pierwszeństwo, co pozwala każdemu podprojektowi dostarczyć własne, dopasowane instrukcje.
A w kwestii konfliktów:
Plik AGENTS.md znajdujący się najbliżej edytowanego pliku ma pierwszeństwo; jawne polecenia użytkownika na czacie nadpisują wszystko inne.
Dla wielu osób sformułowanie „ma pierwszeństwo” oznacza „plik z katalogu głównego jest ignorowany”. Tak nie jest. W narzędziach implementujących tę konwencję każdy plik znajdujący się na ścieżce od katalogu głównego repozytorium aż do katalogu roboczego jest odczytywany i łączony w całość. Najbliższy plik wygrywa tylko wtedy, gdy dwa pliki zawierają sprzeczne informacje na ten sam temat.
Codex jasno określa ten mechanizm: „Codex łączy pliki od katalogu głównego w dół, rozdzielając je pustymi liniami. Pliki znajdujące się bliżej bieżącego katalogu nadpisują wcześniejsze wytyczne”. Claude Code postępuje analogicznie w przypadku własnego pliku. Pliki w hierarchii katalogów powyżej katalogu roboczego „są ładowane w całości przy uruchomieniu”, a „wszystkie wykryte pliki są łączone w kontekst, zamiast wzajemnie się nadpisywać”. Katalogi poniżej katalogu roboczego zachowują się inaczej: Claude Code ładuje te pliki na żądanie, „gdy Claude odczytuje pliki w tych katalogach”.
Wynikają z tego dwie praktyczne konsekwencje. Plik z katalogu głównego stanowi prefiks każdej sesji w repozytorium, więc każdą linię w nim zawartą należy traktować jako kosztowną, ponieważ jest ona przetwarzana setki razy w tygodniu. Plik w konkretnym katalogu nie generuje kosztów, gdy agent pracuje w innym miejscu, co oznacza, że szczegółowe instrukcje są tam tanie i właśnie tam powinny się znajdować.
To zachowanie zostało zweryfikowane z dokumentacją Codex oraz Claude Code w sierpniu 2026. Narzędzia implementują tę konwencję w nieco odmienny sposób i podlegają zmianom, dlatego należy potwierdzić zasady ładowania dla konkretnego agenta używanego w zespole.
Gotowy układ repozytorium dla trzech usług
repo/
AGENTS.md rules true everywhere, plus the map
apps/web/AGENTS.md TypeScript client, Vite, Vitest
services/worker/AGENTS.md Python queue consumer, pytest
infra/AGENTS.md Terraform and the deploy scriptsPlik główny (root) jest celowo krótki. Wskazuje on lokalizacje plików i zawiera wyłącznie reguły obowiązujące w każdym katalogu.
# AGENTS.md
This is a monorepo. Each top-level directory ships its own AGENTS.md.
Read this file and the AGENTS.md nearest the code you are editing
before you change anything.
- `apps/web` browser client
- `services/worker` queue consumer
- `infra` Terraform and deploy scripts
## Rules for the whole repository
- The package manager is `pnpm`. `npm install` writes a second lockfile
that CI ignores, so the install you tested is not the install that ships.
- Any `generated/` directory is build output. Edit the schema in
`schemas/` and run `pnpm codegen` instead.
- `.env.local` holds real credentials. Do not read it and do not print it.
- If you change code in a directory, update that directory's AGENTS.md
in the same commit.Plik wewnątrz każdego katalogu zawiera szczegółowe informacje i może być tak długi, jak wymaga tego dany katalog.
# apps/web
Browser client. Vite and React, TypeScript with `strict` on.
## Commands
- `pnpm dev` serves on port 5173.
- `pnpm test` runs Vitest once and exits.
- `pnpm typecheck` runs `tsc --noEmit`.
## Conventions
- One component per file under `src/components/`.
- All HTTP goes through `src/api/client.ts`. Do not call `fetch` directly,
because the client attaches the auth header and retries on 429.
## Traps
- `pnpm build` does not type check. Vite strips the types instead of
checking them, so a broken type still produces a green build.
Run `pnpm typecheck` as a separate step.Plik roboczy (worker) ma taką samą strukturę, ale inną zawartość: polecenie instalacji, pytest -q, powód, dla którego konsument musi zachowywać idempotentność, oraz migrację, która musi zostać wykonana, zanim testy zakończą się powodzeniem. Plik infrastruktury (infra) zawiera reguły zapobiegające wyrządzeniu szkód przez agenta. Nigdy nie należy uruchamiać terraform apply. Należy wykonać terraform plan i na tym poprzestać, wskazując backend stanu, który jest już skonfigurowany, aby agent nie próbował inicjować nowego.
Warto zwrócić uwagę na to, czego nie ma w żadnym z tych plików: opisu przeznaczenia poszczególnych usług. To należy do domeny ludzi. Upstream wyznacza tę samą granicę, stwierdzając, że "pliki README.md są przeznaczone dla ludzi: zawierają szybki start, opisy projektów i wytyczne dotyczące współtworzenia", podczas gdy AGENTS.md zawiera "dodatkowy, czasem szczegółowy kontekst potrzebny agentom programistycznym: kroki budowania, testy i konwencje". Artykuł podział między AGENTS.md a README dla ludzi szczegółowo omawia tę granicę zdanie po zdaniu, a plik DESIGN.md, który dokumentuje powody przyjętej architektury kodu opisuje trzeci rodzaj pliku, wyjaśniający podjęte decyzje, a nie wydawane polecenia.
Kto aktualizuje plik, gdy zmienia się kod?
Obowiązuje jedna zasada, której miejsce jest w pliku głównym: każdy, kto zmienia kod w danym katalogu, aktualizuje plik AGENTS.md tego katalogu w tym samym commicie.
Działa to z powodów technicznych, a nie kulturowych. Plik znajdujący się w danym katalogu jest częścią tego samego diffa co kod, więc osoba sprawdzająca pull request widzi obie zmiany jednocześnie. Plik główny należy do wszystkich, co oznacza, że nie należy do nikogo i nigdy nie pojawia się w diffie, który ktoś aktualnie przegląda.
Zasadę tę należy poprzeć weryfikacją w ramach pull requesta. Mechanizm ten znajduje najbliższy plik AGENTS.md powyżej każdego zmienionego pliku, a następnie zgłasza, jeśli plik ten nie został zmodyfikowany.
#!/usr/bin/env bash
# Warn when code changed but the nearest AGENTS.md above it did not.
changed=$(git diff --name-only origin/main...HEAD)
nearest_doc() {
d=$(dirname "$1")
while [ "$d" != "." ]; do
if [ -f "$d/AGENTS.md" ]; then echo "$d/AGENTS.md"; return; fi
d=$(dirname "$d")
done
echo "AGENTS.md"
}
printf '%s\n' "$changed" | while read -r f; do
[ -n "$f" ] || continue
case "$f" in AGENTS.md|*/AGENTS.md) continue ;; esac
doc=$(nearest_doc "$f")
printf '%s\n' "$changed" | grep -Fqx "$doc" && continue
echo "note: $f changed but $doc was not updated"
doneW gałęzi, w której przebudowano API client bez aktualizacji dokumentacji, wynik wygląda następująco:
note: apps/web/src/api/client.ts changed but apps/web/AGENTS.md was not updatedNależy zachować to jako ostrzeżenie, a nie jako błąd blokujący. Sztywna blokada uczy ludzi dodawania pustej linii do pliku tylko po to, aby CI przeszło pomyślnie, a plik edytowany w celu zadowolenia robota jest mniej wart niż brak pliku. Ostrzeżenie daje recenzentowi powód do zadania pytania, co jest elementem, który faktycznie przynosi efekty.
Jak rozpoznać nieaktualny plik AGENTS.md?
Można wykonać dwa testy oraz zwrócić uwagę na jeden objaw występujący podczas sesji.
Porównaj wiek każdego pliku z wiekiem kodu, który opisuje. %cs wyświetla datę commitu jako YYYY-MM-DD.
for f in $(git ls-files '*AGENTS.md'); do
d=$(dirname "$f")
printf '%s doc:%s code:%s\n' "$f" \
"$(git log -1 --format=%cs -- "$f")" \
"$(git log -1 --format=%cs -- "$d")"
doneapps/web/AGENTS.md doc:2026-02-11 code:2026-08-07
services/worker/AGENTS.md doc:2026-07-29 code:2026-08-09
infra/AGENTS.md doc:2026-08-01 code:2026-08-01Data dokumentacji starsza o sześć miesięcy od daty kodu nie jest dowodem na to, że plik jest błędny. Wskazuje jedynie, który plik należy przeczytać w pierwszej kolejności, a to jedyna informacja potrzebna z testu trwającego sekundę.
Wyszukaj ścieżki, które już nie istnieją. Dokumentacja ulega dezaktualizacji w jeden konkretny sposób: nadal opisuje kod, który został usunięty. Każda ścieżka w tych plikach jest zapisana w backtickach, więc łatwo je wyodrębnić i przetestować.
grep -o '`[^`]*`' apps/web/AGENTS.md | tr -d '`' | grep '/' | while read -r p; do
[ -e "$p" ] || [ -e "apps/web/$p" ] || echo "missing: $p"
doneNależy analizować wynik ręcznie, zamiast integrować to polecenie z CI. Wskazuje ono również wzorce typu glob, takie jak src/**/*.ts, oraz każdy zacytowany adres URL, ponieważ oba zawierają ukośnik i żaden z nich nie jest plikiem na dysku.
Objaw podczas sesji. Agent odczytuje plik, próbuje otworzyć src/api/client.ts, ponieważ tak nakazuje plik, a narzędzie zwraca:
No such file or directoryW rezultacie agent postępuje w sposób logiczny i tworzy własną nakładkę fetch. To jest rzeczywisty koszt nieaktualnego pliku. Agent nie ignoruje dokumentacji. Postępuje zgodnie z nią, trafia na ścieżkę usuniętą trzy miesiące temu i odtwarza kod, który już istnieje. Umiejętność taka jak Ponytail, która ogranicza agenta do najmniejszej działającej zmiany, sprawia, że instynkt odtwarzania kodu występuje rzadziej, ale nie jest w stanie odnaleźć pomocnika, którego plik wskazał w nieprawidłowym miejscu.
Czy Claude Code odczytuje pliki AGENTS.md?
Nie, i warto to wyraźnie podkreślić, ponieważ zagnieżdżony układ jest od tego uzależniony. Według stanu na sierpień 2026 dokumentacja podaje: „Claude Code odczytuje CLAUDE.md, a nie AGENTS.md”. Ten wzorzec nadal działa, wystarczy umieścić CLAUDE.md obok każdego AGENTS.md.
Forma importu jest właściwa, gdy wymagane jest dodanie linii specyficznych dla narzędzia ponad liniami wspólnymi. Należy umieścić to w services/worker/CLAUDE.md:
@AGENTS.md
## Claude Code
Use plan mode for changes under `services/worker/migrations/`.Forma dowiązania symbolicznego (symlink) jest właściwa, gdy nie ma żadnych specyficznych dla narzędzia elementów do dodania.
git ls-files '*AGENTS.md' | while read -r f; do
ln -s AGENTS.md "$(dirname "$f")/CLAUDE.md"
done
ls -l apps/web/CLAUDE.mdln nie wyświetla żadnych komunikatów w przypadku powodzenia, dlatego należy sprawdzić listę: apps/web/CLAUDE.md -> AGENTS.md. Następnie należy rozpocząć sesję i uruchomić /context, gdzie załadowane pliki pojawią się w sekcji Memory files. W systemie Windows dowiązanie symboliczne wymaga uprawnień administratora lub włączonego trybu deweloperskiego (Developer Mode), dlatego należy tam użyć importu @AGENTS.md.
Z tym zagadnieniem wiąże się jedna pułapka. Po wykonaniu /compact plik główny (root) jest ponownie odczytywany z dysku, ale zagnieżdżone pliki w podkatalogach nie są ponownie wstrzykiwane. Powracają one przy następnym odczycie dowolnego pliku w danym katalogu przez agenta. Jeśli reguła dla danego katalogu przestaje działać w trakcie długiej sesji, zazwyczaj jest to przyczyna, a dotknięcie (touch) dowolnego pliku w tym katalogu przywraca jej działanie.
Ustawienia wskazujące innym agentom plik AGENTS.md
Codex odczytuje AGENTS.md natywnie. Na każdym poziomie sprawdza najpierw AGENTS.override.md, co pozwala na lokalne nadpisanie ustawień dla jednego katalogu bez edycji pliku współdzielonego. Łączenie jest przerywane, gdy łączny rozmiar osiągnie 32 KiB, co stanowi domyślny limit project_doc_max_bytes; jest to kolejny powód, dla którego plik główny powinien być niewielki.
Aider obsługuje to poprzez .aider.conf.yml z linią read: AGENTS.md.
Gemini CLI obsługuje to poprzez .gemini/settings.json z { "context": { "fileName": "AGENTS.md" } }.
Dokumentacja nadrzędna opisuje wstecznie kompatybilną zmianę nazwy dla repozytoriów nadal używających starszej nazwy w liczbie pojedynczej: mv AGENT.md AGENTS.md && ln -s AGENTS.md AGENT.md.
W bardzo dużych monorepozytoriach ustawienie claudeMdExcludes w Claude Code pozwala pominąć pliki nadrzędne według ścieżki lub wzorca glob, co jest przydatne, gdy katalog innego zespołu znajduje się powyżej własnego.
Czym różni się to od pamięci agenta lub umiejętności?
Mechanizmy te wydają się podobne, lecz zawodzą w zupełnie inny sposób, dlatego warto precyzyjnie określić, z którego z nich należy skorzystać.
Plik AGENTS.md jest tworzony przez użytkownika, zatwierdzany w git, sprawdzany w pull request i identyczny dla każdego, kto sklonuje repozytorium. Pamięć agenta jest zapisywana przez agenta, przechowywana poza repozytorium i lokalna dla jednej maszyny. Dokumentacja Claude Code wyznacza tę samą granicę: CLAUDE.md zawiera „Instrukcje i zasady”, które tworzy użytkownik, pamięć automatyczna zawiera „Wnioski i wzorce”, które tworzy Claude, a katalog pamięci nie jest współdzielony między maszynami. Test jest prosty. Jeśli dany fakt musi być prawdziwy dla współpracownika korzystającego ze świeżego klona, nie może on znajdować się w pamięci. Jak pamięć agenta jest utrwalana między sesjami omawia tę część zagadnienia.
Umiejętność (skill) to trzeci element. AGENTS.md to kontekst ładowany podczas każdej sesji; umiejętność to procedura ładowana wtedy, gdy jest potrzebna. Dokumentacja Claude Code podaje użyteczną zasadę: „Jeśli wpis jest wieloetapową procedurą lub dotyczy tylko jednej części bazy kodu, przenieś go do umiejętności lub zasady o zasięgu ścieżki”. Druga połowa tego zdania precyzyjnie określa problem, który rozwiązuje zagnieżdżony plik AGENTS.md. Pierwsza połowa dotyczy tego, do czego służą umiejętności agenta, a gdy ta sama procedura jest potrzebna w więcej niż jednym repozytorium, należy udostępnić umiejętność między repozytoriami, zamiast wklejać te same akapity do dziesięciu różnych plików AGENTS.md.
Upstream zauważa, że „w momencie pisania tego tekstu główne repozytorium OpenAI zawiera 88 plików AGENTS.md”. Ta liczba stanowi cały argument. Duże repozytorium nie potrzebuje większego pliku. Potrzebuje więcej małych plików, z których każdy znajduje się obok opisywanego kodu i jest zarządzany przez osobę, która ostatnio ten kod modyfikowała.
FAQ
Czy zagnieżdżony plik AGENTS.md zastępuje plik główny, czy go uzupełnia?
Uzupełnia go. Dokumentacja upstream podaje, że „najbliższy plik ma pierwszeństwo”, co opisuje zachowanie w przypadku konfliktu, a nie proces ładowania. Codex „łączy pliki od katalogu głównego w dół, rozdzielając je pustymi liniami”, a Claude Code scala wszystkie pliki znalezione podczas przeszukiwania ścieżki od katalogu roboczego w górę, zamiast je nadpisywać. Najbliższy plik wygrywa tylko wtedy, gdy dwa pliki zawierają sprzeczne instrukcje dotyczące tego samego zagadnienia. Wspólne reguły należy zapisać raz w katalogu głównym i nie powtarzać ich w każdym podkatalogu.
Jak duży powinien być główny plik AGENTS.md?
Na tyle mały, aby jego dołączenie do każdego zapytania w tym repozytorium nie stanowiło problemu, ponieważ dokładnie to się dzieje. Dokumentacja Claude Code sugeruje ograniczenie długości do 200 linii na plik i ostrzega, że dłuższe pliki „obniżają skuteczność przestrzegania instrukcji”. Codex domyślnie przestaje scalać pliki instrukcji po osiągnięciu 32 KiB łącznej wielkości. Jeśli plik główny dokumentuje cztery usługi, większość jego treści jest zbędna dla pojedynczego zadania. Szczegóły warto przenieść do plików w poszczególnych katalogach, pozostawiając w głównym pliku jedynie mapę.
Jak zapobiec dezaktualizacji tych plików?
Należy dodać jedną regułę w pliku głównym: osoba zmieniająca kod w danym katalogu aktualizuje plik AGENTS.md w tym samym katalogu w ramach tego samego commita. Umieszczenie pliku obok kodu zwiększa szansę na przestrzeganie tej zasady, ponieważ zmiana trafia do tego samego pull requesta, który jest przeglądany przez człowieka. Warto dodać ostrzeżenie w CI, które mapuje każdą zmienioną ścieżkę do najbliższego pliku AGENTS.md powyżej, oraz okresowo porównywać git log -1 --format=%cs dla każdego pliku z tym samym poleceniem uruchomionym dla dokumentowanego katalogu.
Czy Claude Code odczytuje pliki AGENTS.md?
Nie. Według stanu na sierpień 2026 dokumentacja stwierdza: „Claude Code odczytuje CLAUDE.md, a nie AGENTS.md”. Należy utworzyć CLAUDE.md w tym samym katalogu, umieszczając @AGENTS.md w pierwszej linii, co załaduje plik współdzielony i pozwoli dodać instrukcje specyficzne dla Claude poniżej. Dowiązanie symboliczne utworzone za pomocą ln -s AGENTS.md CLAUDE.md działa, gdy nie ma potrzeby dodawania dodatkowych treści, choć w systemie Windows wymaga uprawnień administratora lub włączonego trybu deweloperskiego. Należy uruchomić /context w sesji i potwierdzić, że plik pojawia się w sekcji Memory files.
Gdzie umieścić regułę, która ma znaczenie tylko czasami?
Nie w AGENTS.md. Ten plik ładuje się w każdej sesji, więc każda linia w nim konkuruje o uwagę z właściwym zapytaniem. Procedura składająca się z kilku kroków, potrzebna tylko okazjonalnie, powinna znaleźć się w umiejętności (skill), która jest ładowana na żądanie. Reguła dotycząca jednego katalogu powinna znaleźć się w pliku AGENTS.md tego katalogu. Fakty, które agent może odczytać bezpośrednio z kodu, takie jak drzewo katalogów czy lista zależności, nie powinny znajdować się w żadnym z tych plików.