ניהול קובצי AGENTS.md מקוננים ב-monorepo
קובץ AGENTS.md יחיד בשורש המאגר הופך ללא רלוונטי וגוזל הקשר יקר. למדו כיצד לפצל את ההגדרות לתיקיות משנה כדי לשפר את דיוק הסוכן ולמנוע שגיאות בתחזוקת הפרויקט.
מה המשמעות של קובצי AGENTS.md מקוננים בתוך monorepo
קובצי AGENTS.md מקוננים בתוך monorepo משמעותם קובץ קטן אחד בשורש המאגר ועוד קובץ אחד בתוך כל תיקיית שירות. הקובץ בשורש מכיל את הכללים המעטים שתקפים בכל מקום, בתוספת מיפוי של מיקומי הקבצים האחרים. כל קובץ שירות מכיל את הפקודות והמוסכמות עבור אותה תיקייה בלבד. סוכן (agent) שעורך את services/worker/queue.py קורא את קובץ השורש ואת קובץ העבודה, ואינו מבזבז כלל הקשר (context) על ה-front end שבו הוא לעולם לא יגע.
אין שום דבר להתקין. AGENTS.md הוא מוסכמה, והפרויקט במעלה הזרם (upstream) מצהיר על כך בבירור:
AGENTS.md הוא פשוט Markdown סטנדרטי. השתמשו בכל כותרת שתרצו; הסוכן פשוט מנתח את הטקסט שאתם מספקים.
זו הסיבה שהטכניקה הזו ראויה ללימוד מעמיק. הפורמט לא ישתנה תחת ידיכם. מה שעלול להשתבש הוא המיקום והתחזוקה, ושניהם באחריותכם.
מדוע קובץ AGENTS.md אחד גדול בשורש הפרויקט מפסיק לעבוד?
קובץ AGENTS.md יחיד באורך 600 שורות בשורש של מאגר המכיל יישום אינטרנט, worker רקע וספריית Terraform נכשל בארבע דרכים שונות.
הוא מתיישן, כי אף אחד לא אחראי עליו. המהנדס שמשנה שם של סקריפט בדיקה ב-apps/web עורך קבצים תחת apps/web. הקובץ AGENTS.md שבשורש אינו חלק מה-diff, לכן אף סוקר לא רואה את חוסר ההתאמה. שישה שבועות לאחר מכן, הקובץ מתאר שלב build שכבר לא קיים, והאדם שגרם לתקלה כבר שכח מהשינוי.
הוא מבזבז הקשר (context) בכל משימה. קבצים אלו נטענים בתחילת הסשן, לפני שהסוכן יודע מה עומדים לשאול אותו. התיעוד של Claude Code מציין זאת במפורש: "יש לשאוף לפחות מ-200 שורות לכל קובץ CLAUDE.md. קבצים ארוכים יותר צורכים יותר הקשר ומפחיתים את רמת הציות להנחיות". Codex מפסיק למזג קובצי הנחיות ברגע שהגודל המשולב שלהם מגיע ל-32 KiB, ה-project_doc_max_bytes המוגדר כברירת מחדל. קובץ שורש שמתעד ארבעה שירותים מבזבז את המכסה הזו על שלושה מהם בכל משימה ומשימה.
הנחיות מתחילות לסתור זו את זו. ספריית ה-web דורשת pnpm test. ה-worker דורש pytest -q. כאשר כל כלל כתוב באותו קובץ, הוא נכון רק בחלק מהזמן, ולכן הסוכן נאלץ לנחש איזה כלל תקף. התיעוד של Claude Code מתאר את התוצאה: "אם שני כללים סותרים זה את זה, Claude עשוי לבחור אחד מהם באופן שרירותי". קובץ לכל ספרייה מסיר את הצורך בניחוש, כיוון שרק אחד משני הכללים נמצא בהקשר בכל רגע נתון.
הוא מתמלא בעובדות שהסוכן יכול לקרוא מהקוד. עץ ספריות, רשימת תלויות, סיכום של מה שכל חבילה עושה. הבדיקה /doctor של Claude Code קיימת בדיוק כדי להסיר תוכן כזה. היא "מחסירה תוכן ש-Claude יכול להסיק מתוך בסיס הקוד, כגון מבנה ספריות, רשימות תלויות וסקירות ארכיטקטורה" ושומרת "מלכודות, רציונל ומוסכמות השונים מברירות המחדל של הכלים". משפט זה הוא המבחן הטוב ביותר שאני מכיר כדי לקבוע אם שורה מסוימת בכלל שייכת לקובץ.
האם ה-agent קורא את קובץ ה-root, או רק את הקרוב ביותר?
כאן רוב האנשים טועים בהבנת המודל, ולכן כדאי לצטט את המוסכמה של ה-upstream במקום לנסח אותה מחדש:
יש להציב קובץ AGENTS.md נוסף בתוך כל חבילה. ה-agents קוראים אוטומטית את הקובץ הקרוב ביותר בעץ הספריות, כך שהקובץ הקרוב ביותר הוא הקובע וכל תת-פרויקט יכול לספק הנחיות מותאמות אישית.
ובנוגע להתנגשויות:
קובץ ה-AGENTS.md הקרוב ביותר לקובץ הנערך הוא הקובע; הנחיות מפורשות של המשתמש בצ'אט גוברות על הכל.
עבור אנשים רבים, "הקובץ הקרוב ביותר הוא הקובע" מתפרש כ-"קובץ ה-root מתעלם". זה לא המצב. בכלים המממשים את המוסכמה הזו, כל קובץ שנמצא בנתיב משורש ה-repository ועד לספריית העבודה נקרא ומצורף לשאר. הקובץ הקרוב ביותר קובע רק במקרים שבהם שני קבצים מציגים הנחיות סותרות בנוגע לאותו נושא.
התיעוד של Codex מפרט את המנגנון: "Codex משרשרת קבצים מה-root ומטה, ומחברת ביניהם באמצעות שורות ריקות. קבצים הקרובים יותר לספרייה הנוכחית שלך גוברים על הנחיות קודמות". Claude Code פועלת באותו אופן עבור שם הקובץ שלה. קבצים בהיררכיית הספריות שמעל ספריית העבודה "נטענים במלואם בעת ההפעלה", ו-"כל הקבצים שזוהו משורשרים לתוך ה-context במקום לדרוס זה את זה". ספריות מתחת לספריית העבודה מתנהגות אחרת: Claude Code טוענת את הקבצים הללו לפי דרישה, "כאשר Claude קוראת קבצים באותן ספריות".
מכאן נובעות שתי השלכות מעשיות. קובץ ה-root מהווה תחילית (prefix) לכל session ב-repository, לכן יש להתייחס לכל שורה בו כאל שורה שאתה משלם עליה מאה פעמים בשבוע. קובץ ברמת הספרייה אינו עולה דבר כאשר ה-agent עובד במקום אחר, מה שאומר שפירוט הוא זול שם ושייך לשם.
התנהגות זו נבדקה מול התיעוד של Codex ו-Claude Code באוגוסט 2026. כלים מממשים את המוסכמה בשינויים קלים והם אכן משתנים, לכן יש לוודא את כללי הטעינה עבור ה-agent הספציפי שהצוות שלך מריץ.
מבנה עבודה עבור מאגר עם שלושה שירותים
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 scriptsקובץ השורש קצר במכוון. הוא מציין היכן לחפש וכולל רק את הכללים התקפים בכל ספריה.
# 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.הקובץ הייעודי לכל ספריה הוא המקום בו מפורטים הפרטים, והוא יכול להיות ארוך ככל שהספריה דורשת.
# 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.קובץ ה-worker הוא בעל מבנה זהה אך תוכן שונה: פקודת ההתקנה, pytest -q, הסיבה לכך שהצרכן חייב להישאר אידמפוטנטי, והמיגרציה שחייבת לרוץ לפני שהבדיקות עוברות. קובץ ה-infra הוא המקום בו כותבים את הכללים שמונעים מסוכן (agent) לגרום נזק. לעולם אל תריצו terraform apply. הריצו terraform plan ועצרו שם, וציינו את ה-state backend שכבר מוגדר כדי שהסוכן לא ינסה לאתחל אחד חדש.
שימו לב למה שאינו מופיע באף אחד מהקבצים הללו: תיאור המטרה של כל שירות. זה שייך לבני האדם. Upstream מותח את אותו קו, וקובע כי "קבצי README.md מיועדים לבני אדם: התחלות מהירות, תיאורי פרויקט והנחיות תרומה", בעוד ש-AGENTS.md מכיל "את ההקשר הנוסף, לעיתים מפורט, שסוכני קידוד צריכים: שלבי בנייה, בדיקות ומוסכמות". ה-הפרדה בין AGENTS.md לבין README המיועד לבני אדם עוברת משפט אחר משפט על הגבול הזה, ו-קובץ DESIGN.md המתעד מדוע הקוד מעוצב כפי שהוא מכסה את הקובץ השלישי, זה שמסביר החלטות במקום פקודות.
מי מעדכן את הקובץ כאשר הקוד משתנה?
כלל אחד בלבד, והוא מוחל על קובץ ה-root: מי שמשנה קוד בתיקייה מסוימת מעדכן את הקובץ AGENTS.md של אותה תיקייה באותו commit.
זה עובד מסיבות טכניות, לא תרבותיות. הקובץ המקומי לכל תיקייה מופיע באותו diff של הקוד, כך שהבודק של ה-pull request רואה את שניהם בבת אחת. קובץ root שייך לכולם, מה שאומר שהוא לא שייך לאף אחד, והוא לעולם לא מופיע ב-diff שאותו מישהו כבר קורא.
גבו את הכלל באמצעות בדיקה ב-pull request. היא מאתרת את קובץ ה-AGENTS.md הקרוב ביותר מעל כל קובץ שהשתנה, ומדווחת כאשר הקובץ הזה לא עבר שינוי.
#!/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"
doneב-branch שבוצע בו שינוי ב-API client מבלי לגעת בתיעוד, הפלט נראה כך:
note: apps/web/src/api/client.ts changed but apps/web/AGENTS.md was not updatedהשאירו זאת כאזהרה ולא ככשל. חסימה קשיחה גורמת לאנשים להוסיף שורה ריקה לקובץ רק כדי שה-CI יציג אישור ירוק, וקובץ שנערך כדי לספק רובוט שווה פחות מקובץ שלא קיים כלל. האזהרה מעניקה לבודק שאלה לשאול, וזה החלק שבאמת עובד.
כיצד ניתן לזהות קובץ AGENTS.md שהתיישן?
קיימות כיום שתי בדיקות שניתן להריץ, ותסמין אחד שניתן להבחין בו במהלך סשן.
השוו את גיל הקובץ לגיל הקוד שהוא מתאר. %cs מדפיס את תאריך ה-commit בתור 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-01תיעוד שתאריכו מוקדם בשישה חודשים מתאריך הקוד אינו מוכיח בהכרח שהקובץ שגוי. הוא רק מצביע על הקובץ שמומלץ לקרוא ראשון, וזה כל מה שנדרש מבדיקה שאורכת שנייה אחת.
חפשו נתיבים שאינם קיימים עוד. תיעוד מתיישן בדרך ספציפית מאוד: הוא ממשיך לתאר קוד שנמחק. כל נתיב בקבצים אלו כתוב בתוך backticks, ולכן קל לשלוף אותם ולבדוק אותם.
grep -o '`[^`]*`' apps/web/AGENTS.md | tr -d '`' | grep '/' | while read -r p; do
[ -e "$p" ] || [ -e "apps/web/$p" ] || echo "missing: $p"
doneקראו את הפלט במקום להטמיע בדיקה זו ב-CI. הפקודה תסמן גם תבניות glob כמו src/**/*.ts וכל URL שצוטט, כיוון ששניהם מכילים לוכסן (slash) ואף אחד מהם אינו קובץ על הדיסק.
התסמין במהלך סשן. הסוכן קורא את הקובץ, מנסה לפתוח את src/api/client.ts כיוון שהקובץ הורה לו לעשות זאת, והכלי מחזיר:
No such file or directoryלכן, הסוכן פועל בצורה הגיונית וכותב לעצמו wrapper מסוג fetch. זהו המחיר האמיתי של קובץ מיושן. הסוכן אינו מתעלם מהתיעוד שלכם. הוא עוקב אחר התיעוד, מגיע לנתיב שנמחק לפני שלושה חודשים, ובונה מחדש קוד שכבר קיים אצלכם. מיומנות כגון Ponytail, המגבילה את הסוכן לשינוי הקטן ביותר שעובד, הופכת את אינסטינקט הבנייה מחדש הזה לנדיר יותר, אך היא אינה יכולה למצוא כלי עזר שהקובץ שלכם הפנה למקום שגוי.
האם Claude Code קורא קובצי AGENTS.md?
לא, וחשוב לציין זאת במפורש כיוון שהמבנה המקונן מסתמך על כך. נכון לאוגוסט 2026, התיעוד מציין: "Claude Code קורא את CLAUDE.md, לא את AGENTS.md." התבנית עדיין עובדת, פשוט יש להוסיף CLAUDE.md לצד כל AGENTS.md.
צורת ה-import נכונה כאשר רוצים שורות ספציפיות לכלי מסוים מעל השורות המשותפות. יש להוסיף זאת ל-services/worker/CLAUDE.md:
@AGENTS.md
## Claude Code
Use plan mode for changes under `services/worker/migrations/`.צורת ה-symlink נכונה כאשר אין תוכן ספציפי לכלי להוסיף.
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 לא מדפיס דבר בעת הצלחה, לכן יש לבדוק את הרשימה באמצעות: apps/web/CLAUDE.md -> AGENTS.md. לאחר מכן, התחילו סשן והריצו את /context, שם הקבצים שנטענו יופיעו תחת Memory files. ב-Windows, יצירת symlink דורשת הרשאות Administrator או מצב Developer Mode, לכן השתמשו שם ב-import מסוג @AGENTS.md.
מלכודת אחת קשורה לכך. לאחר /compact, קובץ השורש נקרא מחדש מהדיסק, אך קבצים מקוננים בתתי-תיקיות אינם מוזרקים מחדש. הם יחזרו בפעם הבאה שהסוכן יקרא קובץ באותה תיקייה. אם נראה שכלל לפי-תיקייה מפסיק לעבוד באמצע סשן ארוך, זו בדרך כלל הסיבה, ונגיעה בכל קובץ בתיקייה תחזיר אותו לפעולה.
הגדרות המפנות סוכנים אחרים ל-AGENTS.md
Codex קורא את AGENTS.md באופן טבעי. בכל רמה הוא בודק תחילה את AGENTS.override.md, מה שמאפשר לתיקייה אחת דריסה מקומית ללא עריכת הקובץ המשותף. הוא מפסיק למזג ברגע שהגודל המשולב מגיע ל-32 KiB, ה-project_doc_max_bytes המוגדר כברירת מחדל, וזו סיבה נוספת לשמור על קובץ השורש קטן.
Aider טוען אותו דרך .aider.conf.yml עם השורה read: AGENTS.md.
Gemini CLI טוען אותו דרך .gemini/settings.json עם { "context": { "fileName": "AGENTS.md" } }.
התיעוד הרשמי מציע שינוי שם התואם לאחור עבור מאגרים שעדיין משתמשים בשם היחיד הישן: mv AGENT.md AGENTS.md && ln -s AGENTS.md AGENT.md.
ב-monorepo גדול מאוד, ההגדרה claudeMdExcludes של Claude Code מדלגת על קובצי אב לפי נתיב או glob, דבר שימושי כאשר תיקייה של צוות אחר נמצאת מעל התיקייה שלכם.
במה זה שונה מזיכרון של סוכן (agent memory) או ממיומנות (skill)?
מנגנונים אלו נראים דומים אך נכשלים בדרכים שונות לחלוטין, לכן חשוב לדייק בבחירת הכלי המתאים.
הקובץ AGENTS.md נכתב על ידך, מבוצע לו commit ל-git, הוא עובר סקירה ב-pull request, והוא זהה עבור כל מי שמשכפל (clone) את המאגר. זיכרון של סוכן נכתב על ידי הסוכן עצמו, נשמר מחוץ למאגר, והוא מקומי למכונה אחת בלבד. התיעוד של Claude Code משרטט את אותו קו הפרדה: CLAUDE.md מכיל "הנחיות וכללים" שאתה כותב, זיכרון אוטומטי מכיל "למידות ודפוסים" ש-Claude כותב, וספריית הזיכרון אינה משותפת בין מכונות. המבחן פשוט: אם עובדה חייבת להיות נכונה עבור עמית שביצע clone טרי, היא לא יכולה להימצא בזיכרון. המאמר כיצד זיכרון סוכן נשמר בין סשנים מכסה את החלק הזה של התמונה.
מיומנות (skill) היא הדבר השלישי. AGENTS.md הוא הקשר (context) שנטען בכל סשן; מיומנות היא הליך שנטען כאשר יש בו צורך. התיעוד של Claude Code מציע כלל שימושי: "אם רשומה היא הליך רב-שלבי או שהיא רלוונטית רק לחלק אחד של בסיס הקוד, העבר אותה למיומנות או לכלל בעל טווח (scope) מוגדר לפי נתיב". החלק השני של המשפט הזה הוא בדיוק מה שקובץ AGENTS.md מקונן פותר. החלק הראשון הוא מה ש-מיומנויות סוכן נועדו עבורו, וכאשר יש צורך באותו הליך ביותר ממאגר אחד, שתף את המיומנות בין מאגרים במקום להדביק את אותן פסקאות בעשרה קובצי AGENTS.md שונים.
התיעוד במעלה הזרם (upstream) מציין כי "בזמן כתיבת שורות אלו, במאגר הראשי של OpenAI יש 88 קובצי AGENTS.md". המספר הזה הוא כל הטיעון. מאגר גדול לא זקוק לקובץ גדול יותר. הוא זקוק ליותר קבצים קטנים, שכל אחד מהם ממוקם לצד הקוד שהוא מתאר, וכל אחד מהם נמצא בבעלות מי שביצע את השינוי האחרון באותו קוד.
FAQ
האם קובץ AGENTS.md מקונן מחליף את קובץ השורש או מתווסף אליו?
הוא מתווסף אליו. לפי התיעוד של המפתחים, "הקובץ הקרוב ביותר מקבל עדיפות", מה שמתאר מה קורה במקרה של התנגשות, ולא מה נטען בפועל. Codex "משרשר קבצים מהשורש ומטה, ומפריד ביניהם בשורות ריקות", ו-Claude Code משרשר כל קובץ שהוא מוצא בזמן סריקת העץ מתיקיית העבודה כלפי מעלה, במקום לדרוס אותם. הקובץ הקרוב ביותר קובע רק כאשר שני קבצים נותנים הנחיות שונות על אותו נושא. כתבו חוקים משותפים בשורש פעם אחת, ואל תחזרו עליהם בכל תיקייה.
מה צריך להיות הגודל של קובץ AGENTS.md בשורש?
קטן מספיק כדי שלא יפריע לכם שהוא מודבק בראש כל בקשה שאתם מבצעים באותו מאגר, כי זה בדיוק מה שקורה. התיעוד של Claude Code מציע לשאוף לפחות מ-200 שורות לקובץ ומזהיר שקבצים ארוכים יותר "מפחיתים את רמת הציות". כברירת מחדל, Codex מפסיק למזג קובצי הנחיות בהגיעם ל-32 KiB במצטבר. אם קובץ השורש שלכם מתעד ארבעה שירותים, רובו מהווה עומס מיותר עבור כל משימה בודדת. העבירו את הפירוט לקבצים מקומיים בכל תיקייה והשאירו בשורש רק מפת התמצאות.
איך מונעים מהקבצים האלו להפוך ללא רלוונטיים?
הוסיפו חוק אחד בקובץ השורש: מי שמשנה קוד בתיקייה מסוימת, מעדכן את קובץ ה-AGENTS.md של אותה תיקייה באותו commit. הצבת הקובץ לצד הקוד היא מה שגורם לחוק להישמר, כיוון שהשינוי מופיע באותו pull request diff שאדם כבר קורא. הוסיפו התרעת CI שממפה כל נתיב שהשתנה לקובץ ה-AGENTS.md הקרוב ביותר מעליו, ומדי פעם השוו את git log -1 --format=%cs של כל קובץ מול אותה פקודה המורצת על התיקייה שהוא מתעד.
האם Claude Code קורא קובצי AGENTS.md?
לא. נכון לאוגוסט 2026, התיעוד מציין ש-"Claude Code קורא את CLAUDE.md, לא את AGENTS.md." צרו CLAUDE.md באותה תיקייה עם @AGENTS.md בשורה הראשונה, מה שיטען את הקובץ המשותף ויאפשר לכם להוסיף הנחיות ספציפיות ל-Claude מתחתיו. קישור סימבולי (symlink) שנוצר באמצעות ln -s AGENTS.md CLAUDE.md עובד כאשר אין צורך להוסיף דבר נוסף, אם כי ב-Windows הוא דורש הרשאות Administrator או מצב מפתח (Developer Mode). הריצו את /context בסשן וודאו שהקובץ מופיע תחת Memory files.
איפה שמים חוק שרלוונטי רק לעיתים?
לא ב-AGENTS.md. הקובץ הזה נטען בכל סשן, לכן כל שורה בו מתחרה על תשומת הלב מול הבקשה שהקלדתם בפועל. נוהל עם כמה שלבים שנדרש לעיתים רחוקות שייך ל-skill, שנטען לפי דרישה. חוק שחל על תיקייה אחת שייך ל-AGENTS.md של אותה תיקייה. עובדה שהסוכן יכול לקרוא ישירות מהקוד, כמו עץ התיקיות או רשימת התלויות, לא שייכת לאף אחד מהם.