SSD Nodes Learn Hosting plans →
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-31

ניהול קובצי AGENTS.md מקוננים ב-monorepo

קובץ AGENTS.md יחיד בשורש המאגר הופך למיושן וגוזל נפח הקשר מיותר. למדו כיצד לפצל את ההגדרות לקבצים מקוננים לפי תיקיות שירות כדי לשמור על דיוק הסוכן ועל ביצועי המערכת.

מה המשמעות של קובצי AGENTS.md מקוננים בתוך monorepo

קובצי AGENTS.md מקוננים בתוך monorepo משמעותם קובץ קטן אחד בשורש המאגר ועוד קובץ אחד בתוך כל תיקיית שירות. הקובץ בשורש מכיל את הכללים המעטים שתקפים בכל מקום, בתוספת מיפוי של מיקומי הקבצים האחרים. כל קובץ שירות מכיל את הפקודות והמוסכמות עבור אותה תיקייה בלבד. סוכן (agent) שעורך את services/worker/queue.py קורא את קובץ השורש ואת קובץ העבודה, ואינו מבזבז כלל הקשר (context) על ה-front end שבו הוא לעולם לא יגע.

אין שום דבר להתקין. AGENTS.md הוא מוסכמה, ופרויקט המקור מצהיר על כך בבירור:

AGENTS.md הוא פשוט Markdown סטנדרטי. השתמשו בכל כותרות שתרצו; הסוכן פשוט מנתח את הטקסט שאתם מספקים.

זו הסיבה שהטכניקה הזו ראויה ללמידה יסודית. הפורמט לא ישתנה תחת ידיכם. מה שעלול להשתבש הוא המיקום והתחזוקה, ושניהם באחריותכם.

מדוע קובץ AGENTS.md אחד גדול בשורש הפרויקט מפסיק לעבוד?

קובץ AGENTS.md בודד באורך 600 שורות בשורש של מאגר המכיל אפליקציית אינטרנט, worker רקע וספריית Terraform נכשל בארבע דרכים שונות.

הוא מתיישן, כי אף אחד לא אחראי עליו. המהנדס שמשנה שם של סקריפט בדיקה ב-apps/web עורך קבצים תחת apps/web. קובץ ה-AGENTS.md שבשורש אינו חלק מה-diff, לכן אף סוקר לא רואה את חוסר ההתאמה. שישה שבועות לאחר מכן, הקובץ מתאר שלב בנייה שכבר לא קיים, והאדם שגרם לתקלה כבר שכח מהשינוי.

הוא מבזבז הקשר (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, או רק את הקרוב ביותר?

כאן רוב האנשים טועים בהבנת המודל, לכן כדאי לצטט את המוסכמה הרשמית במקום לנסח אותה מחדש:

יש להציב קובץ AGENTS.md נוסף בתוך כל חבילה. ה-agents קוראים אוטומטית את הקובץ הקרוב ביותר בעץ הספריות, כך שהקובץ הקרוב ביותר מקבל עדיפות וכל תת-פרויקט יכול להפיץ הנחיות מותאמות אישית.

ובנוגע להתנגשויות:

קובץ ה-AGENTS.md הקרוב ביותר לקובץ הנערך הוא הקובע; הנחיות מפורשות של המשתמש בצ'אט גוברות על הכל.

עבור אנשים רבים, "מקבל עדיפות" מתפרש כ"קובץ ה-root מתעלם". זה לא המצב. בכלים המממשים את המוסכמה, כל קובץ שנמצא בנתיב מ-root המאגר ועד לספריית העבודה נקרא ומצורף לשאר הקבצים. הקובץ הקרוב ביותר קובע רק במקרים שבהם שני קבצים מציגים הנחיות סותרות בנוגע לאותו נושא.

Codex מפרטת את המנגנון: "Codex משרשרת קבצים מה-root ומטה, ומחברת ביניהם באמצעות שורות ריקות. קבצים הקרובים יותר לספרייה הנוכחית שלך גוברים על הנחיות קודמות". Claude Code פועלת באותו אופן עבור שם הקובץ שלה. קבצים בהיררכיית הספריות שמעל ספריית העבודה "נטענים במלואם בעת ההפעלה", ו-"כל הקבצים שנמצאו משורשרים להקשר במקום להחליף זה את זה". ספריות מתחת לספריית העבודה מתנהגות אחרת: Claude Code טוענת את הקבצים הללו לפי דרישה, "כאשר Claude קוראת קבצים באותן ספריות".

נובעות מכך שתי השלכות מעשיות. קובץ ה-root מהווה תחילית לכל סשן במאגר, לכן יש להתייחס לכל שורה בו כאל שורה שאתה משלם עליה מאה פעמים בשבוע. קובץ ברמת ספרייה אינו עולה דבר כאשר ה-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 של הקוד, כך שהבודק (reviewer) של ה-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

השאירו זאת כאזהרה ולא כחסימה (failure). חסימה קשיחה גורמת לאנשים להוסיף שורה ריקה לקובץ רק כדי שה-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")"
done
apps/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 שצוטט, כיוון ששניהם מכילים לוכסן ואינם קבצים על הדיסק.

התסמין במהלך סשן. הסוכן קורא את הקובץ, מנסה לפתוח את 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.md

ln אינו מדפיס דבר בעת הצלחה, לכן יש לבדוק את הרשימה באמצעות: apps/web/CLAUDE.md -> AGENTS.md. לאחר מכן יש להתחיל סשן ולהריץ את /context, שם הקבצים שנטענו יופיעו תחת Memory files. ב-Windows, יצירת symlink דורשת הרשאות Administrator או מצב Developer Mode, לכן יש להשתמש שם ב-import מסוג @AGENTS.md.

מלכודת אחת קשורה לכך. לאחר /compact, קובץ ה-root נקרא מחדש מהדיסק, אך קבצים מקוננים בתתי-תיקיות אינם מוזרקים מחדש. הם חוזרים לפעול בפעם הבאה שהסוכן קורא קובץ באותה תיקייה. אם נראה שכלל ברמת התיקייה מפסיק לעבוד באמצע סשן ארוך, זו בדרך כלל הסיבה, ונגיעה (touch) בכל קובץ בתיקייה תחזיר אותו לפעולה.

הגדרות המפנות סוכנים אחרים אל AGENTS.md

Codex קורא את AGENTS.md באופן טבעי. בכל רמה הוא בודק תחילה אם קיים AGENTS.override.md, מה שמאפשר לתיקייה אחת לקבל דריסת הגדרות מקומית ללא עריכת הקובץ המשותף. הוא מפסיק למזג ברגע שהגודל המשולב מגיע ל-32 KiB, שהוא ערך ברירת המחדל של project_doc_max_bytes, וזו סיבה נוספת לשמור על קובץ ה-root קטן.

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 שונים.

התיעוד המקורי מציין כי "בזמן כתיבת שורות אלו, במאגר הראשי של OpenAI יש 88 קובצי AGENTS.md". המספר הזה הוא כל הטיעון. מאגר גדול לא צריך קובץ גדול יותר. הוא צריך יותר קובצי קטנים, שכל אחד מהם ממוקם לצד הקוד שהוא מתאר, וכל אחד מהם נמצא בבעלות מי שביצע את השינוי האחרון באותו קוד.

FAQ

האם קובץ AGENTS.md מקונן מחליף את קובץ השורש או מתווסף אליו?

הוא מתווסף אליו. לפי התיעוד של ה־upstream, "הקובץ הקרוב ביותר מקבל עדיפות", מה שמתאר מה קורה במקרה של התנגשות, ולא מה נטען בפועל. 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 של אותה תיקייה. עובדה שהסוכן יכול לקרוא ישירות מהקוד, כמו עץ התיקיות או רשימת התלויות, לא שייכת לאף אחד מהם.