Zagnieżdżone pliki AGENTS.md w monorepo: jak wdrożyć
Pojedynczy plik AGENTS.md w monorepo szybko traci aktualność i marnuje tokeny kontekstu. Sprawdź, jak rozdzielić konfigurację na podkatalogi, aby zwiększyć precyzję pracy agenta.
Co oznacza zagnieżdżony plik AGENTS.md w monorepo
Zagnieżdżony plik AGENTS.md w monorepo oznacza jeden mały plik w głównym katalogu repozytorium oraz dodatkowy plik 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 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 nauczyć się tej techniki. Format nie ulegnie zmianie. Problemem jest 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, 600-liniowy plik AGENTS.md w katalogu głównym repozytorium, zawierający opis aplikacji webowej, procesu w tle oraz katalogu Terraform, zawodzi z czterech powodów.
Plik staje się nieaktualny, ponieważ nikt nie czuje się za niego odpowiedzialny. Inżynier zmieniający 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.
Każde zadanie zużywa niepotrzebnie kontekst. Pliki te są ładowane na początku sesji, zanim agent dowie się, o co zostanie zapytany. Dokumentacja Claude Code określa limit: „celuj w poniżej 200 linii na plik CLAUDE.md. Dłuższe pliki zużywają więcej kontekstu i obniżają precyzję działania”. Codex przestaje łączyć pliki instrukcji, gdy ich łączny rozmiar osiągnie 32 KiB, co stanowi domyślny limit project_doc_max_bytes. Plik w katalogu głównym dokumentujący cztery usługi zużywa ten budżet na trzy z nich przy każdym pojedynczym zadaniu.
Instrukcje zaczynają być ze sobą sprzeczne. Katalog webowy wymaga pnpm test. Proces w tle wymaga pytest -q. Zapisane w jednym pliku, każda z tych reguł jest poprawna tylko w określonych sytuacjach, 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 konkretnego katalogu eliminuje zgadywanie, ponieważ w kontekście znajduje się tylko jedna z dwóch reguł.
Plik 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 służy właśnie do usuwania takich treści. „Wycina on zawartość, którą Claude może wywnioskować z bazy kodu, taką jak struktura 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 testem, jaki znam, aby sprawdzić, 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ę źródłową 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 dostarczać dedykowane instrukcje.
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 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 podkatalogu 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 i Claude Code w sierpniu 2026 roku. 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.
Działający 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 jest celowo krótki. Wskazuje on, gdzie szukać informacji i zawiera jedynie 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 przypisany do katalogu zawiera szczegóły 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 ma taką samą strukturę, ale inną zawartość: polecenie instalacji, pytest -q, powód, dla którego konsument musi zachować idempotentność, oraz migrację, która musi zostać wykonana przed przejściem testów. Plik infrastruktury zawiera reguły zapobiegające wyrządzeniu szkód przez agenta. Nigdy nie uruchamiaj terraform apply. Uruchom terraform plan i na tym poprzestań, podając nazwę backendu stanu, który jest już skonfigurowany, aby agent nie próbował inicjować nowego.
Zwróć uwagę, czego nie ma w żadnym z tych plików: opisu przeznaczenia każdej usługi. To należy do ludzi. Upstream wyznacza tę samą granicę, stwierdzając, że "pliki README.md są dla ludzi: zawierają szybki start, opisy projektów i wytyczne dotyczące wkładu", podczas gdy AGENTS.md zawiera "dodatkowy, czasem szczegółowy kontekst potrzebny agentom kodującym: kroki budowania, testy i konwencje". Podział między AGENTS.md a README dla ludzi szczegółowo omawia tę granicę zdanie po zdaniu, a plik DESIGN.md, który rejestruje powody, dla których kod ma taką, a nie inną strukturę obejmuje trzeci plik, wyjaśniający decyzje, a nie polecenia.
Kto aktualizuje plik w przypadku zmiany kodu?
Obowiązuje jedna zasada, której miejsce jest w pliku głównym: osoba wprowadzająca zmiany w kodzie w danym katalogu aktualizuje plik AGENTS.md tego katalogu w tym samym commicie.
Wynika to z przyczyn technicznych, a nie kulturowych. Plik przypisany do katalogu znajduje się w tym samym diffie co kod, dzięki czemu recenzent pull requesta widzi obie zmiany jednocześnie. Plik główny należy do wszystkich, co w praktyce oznacza, że nie należy do nikogo i nigdy nie pojawia się w diffie, który ktokolwiek aktualnie przegląda.
Zasadę tę należy wesprzeć weryfikacją w ramach pull requesta. Mechanizm ten odnajduje najbliższy plik AGENTS.md powyżej każdego zmienionego pliku, a następnie zgłasza sytuację, w której 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 przypadku 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ć formę ostrzeżenia, zamiast blokować proces. Sztywne wymogi uczą użytkowników dodawania pustej linii do pliku tylko po to, aby CI przeszło pomyślnie, a plik edytowany w celu zadowolenia robota jest mniej warty niż brak pliku. Ostrzeżenie stanowi dla recenzenta punkt wyjścia do zadania pytania, co jest elementem, który faktycznie przynosi rezultaty.
Jak rozpoznać nieaktualny plik AGENTS.md?
Obecnie można przeprowadzić dwie kontrole oraz zaobserwować jeden objaw 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 w dokumentacji starsza o sześć miesięcy od daty kodu nie dowodzi, że plik jest błędny. Wskazuje jedynie, który plik należy przeczytać w pierwszej kolejności, a to wszystko, czego potrzeba od kontroli trwającej sekundę.
Szukaj ścieżek, które już nie istnieją. Dokumentacja ulega dezaktualizacji w jeden bardzo konkretny sposób: nadal opisuje kod, który został usunięty. Każda ścieżka w tych plikach jest zapisana w odwrotnych apostrofach, 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"
donePrzeglądaj wyniki ręcznie, zamiast integrować to z CI. Polecenie to oznacza 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 wynika z instrukcji w pliku, a narzędzie zwraca:
No such file or directoryW rezultacie agent postępuje racjonalnie i tworzy własny wrapper fetch. To jest rzeczywisty koszt nieaktualnego pliku. Agent nie ignoruje dokumentacji. Podąża za nią, trafia na ścieżkę usuniętą trzy miesiące temu i przebudowuje kod, który już istnieje. Umiejętność taka jak Ponytail, która ogranicza agenta do najmniejszej działającej zmiany, sprawia, że ten instynkt przebudowy występuje rzadziej, ale nie potrafi odnaleźć pomocnika, którego plik wskazał w niewłaściwym miejscu.
Czy Claude Code odczytuje pliki AGENTS.md?
Nie, i warto to podkreślić, ponieważ zagnieżdżony układ jest od tego zależny. Według stanu na sierpień 2026 dokumentacja podaje: "Claude Code odczytuje CLAUDE.md, a nie AGENTS.md". Wzorzec nadal działa, wystarczy umieścić CLAUDE.md obok każdego AGENTS.md.
Forma importu jest właściwa, gdy wymagane są linie specyficzne dla narzędzia dodane do linii współdzielonych. 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 nic w przypadku powodzenia, więc 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, dlatego należy tam użyć importu @AGENTS.md.
Z tym wiąże się jedna pułapka. Po /compact plik główny (root) jest odczytywany z dysku ponownie, ale zagnieżdżone pliki w podkatalogach nie są wstrzykiwane ponownie. Powracają one przy następnym odczycie pliku w danym katalogu przez agenta. Jeśli reguła dla danego katalogu przestaje obowiązywać w trakcie długiej sesji, zazwyczaj jest to przyczyna, a użycie polecenia touch na dowolnym 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 po osiągnięciu łącznego rozmiaru 32 KiB, co stanowi domyślny limit project_doc_max_bytes; jest to kolejny powód, dla którego należy utrzymywać mały rozmiar pliku głównego.
Aider obsługuje go poprzez .aider.conf.yml z linią read: AGENTS.md.
Gemini CLI obsługuje go poprzez .gemini/settings.json z { "context": { "fileName": "AGENTS.md" } }.
Dokumentacja upstream opisuje wstecznie kompatybilną zmianę nazwy dla repozytoriów wciąż używających starszej, pojedynczej nazwy: mv AGENT.md AGENTS.md && ln -s AGENTS.md AGENT.md.
W bardzo dużym monorepo ustawienie claudeMdExcludes w Claude Code pozwala pominąć pliki nadrzędne na podstawie ś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 wyglądają podobnie, ale zawodzą w zupełnie inny sposób, dlatego warto precyzyjnie określić, po który z nich sięgasz.
Plik AGENTS.md jest tworzony przez użytkownika, zatwierdzany w git, sprawdzany w ramach 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 piszesz Ty, pamięć automatyczna przechowuje „Wnioski i wzorce”, które zapisuje 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 w 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 dokładnie opisuje 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, udostępnij 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”, natomiast Claude Code łączy 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 danym repozytorium nie stanowiło problemu, ponieważ dokładnie tak to działa. Dokumentacja Claude Code sugeruje limit poniżej 200 linii na plik i ostrzega, że dłuższe pliki „zmniejszają skuteczność przestrzegania instrukcji”. Codex domyślnie przestaje łączyć pliki instrukcji po osiągnięciu 32 KiB łącznego rozmiaru. Jeśli plik główny dokumentuje cztery usługi, większość jego treści jest zbędna dla pojedynczego zadania. Szczegółowe informacje należy przenieść do plików w poszczególnych katalogach, pozostawiając w pliku głównym jedynie mapę struktury.
Jak zapobiec dezaktualizacji tych plików?
Należy umieścić w pliku głównym jedną zasadę: osoba zmieniająca kod w danym katalogu aktualizuje plik AGENTS.md w tym samym commicie. 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 niej, oraz okresowo porównywać wynik 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ć plik CLAUDE.md w tym samym katalogu, wpisują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 zastosowanie tylko czasami?
Nie w pliku AGENTS.md. Ten plik jest ładowany w każdej sesji, więc każda linia w nim zawarta 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.