From 72583117103973a55e3e269cb250d92b2fcb1d12 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:30:45 -0700 Subject: [PATCH] fix: stop recursive AGENTS.md walk, load top-level only (#3110) The recursive os.walk for AGENTS.md in subdirectories was undesired. Only load AGENTS.md from the working directory root, matching the behavior of CLAUDE.md and .cursorrules. --- agent/prompt_builder.py | 42 ++++++++---------------------- tests/agent/test_prompt_builder.py | 5 ++-- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/agent/prompt_builder.py b/agent/prompt_builder.py index c47d98aa..6ed6e90a 100644 --- a/agent/prompt_builder.py +++ b/agent/prompt_builder.py @@ -490,39 +490,19 @@ def _load_hermes_md(cwd_path: Path) -> str: def _load_agents_md(cwd_path: Path) -> str: - """AGENTS.md — hierarchical, recursive directory walk.""" - top_level_agents = None + """AGENTS.md — top-level only (no recursive walk).""" for name in ["AGENTS.md", "agents.md"]: candidate = cwd_path / name if candidate.exists(): - top_level_agents = candidate - break - - if not top_level_agents: - return "" - - agents_files = [] - for root, dirs, files in os.walk(cwd_path): - dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ('node_modules', '__pycache__', 'venv', '.venv')] - for f in files: - if f.lower() == "agents.md": - agents_files.append(Path(root) / f) - agents_files.sort(key=lambda p: len(p.parts)) - - total_content = "" - for agents_path in agents_files: - try: - content = agents_path.read_text(encoding="utf-8").strip() - if content: - rel_path = agents_path.relative_to(cwd_path) - content = _scan_context_content(content, str(rel_path)) - total_content += f"## {rel_path}\n\n{content}\n\n" - except Exception as e: - logger.debug("Could not read %s: %s", agents_path, e) - - if not total_content: - return "" - return _truncate_content(total_content, "AGENTS.md") + try: + content = candidate.read_text(encoding="utf-8").strip() + if content: + content = _scan_context_content(content, name) + result = f"## {name}\n\n{content}" + return _truncate_content(result, "AGENTS.md") + except Exception as e: + logger.debug("Could not read %s: %s", candidate, e) + return "" def _load_claude_md(cwd_path: Path) -> str: @@ -576,7 +556,7 @@ def build_context_files_prompt(cwd: Optional[str] = None, skip_soul: bool = Fals Priority (first found wins — only ONE project context type is loaded): 1. .hermes.md / HERMES.md (walk to git root) - 2. AGENTS.md / agents.md (recursive directory walk) + 2. AGENTS.md / agents.md (cwd only) 3. CLAUDE.md / claude.md (cwd only) 4. .cursorrules / .cursor/rules/*.mdc (cwd only) diff --git a/tests/agent/test_prompt_builder.py b/tests/agent/test_prompt_builder.py index 13eaedb3..37fddcc9 100644 --- a/tests/agent/test_prompt_builder.py +++ b/tests/agent/test_prompt_builder.py @@ -464,14 +464,15 @@ class TestBuildContextFilesPrompt: result = build_context_files_prompt(cwd=str(tmp_path)) assert "ESLint" in result - def test_recursive_agents_md(self, tmp_path): + def test_agents_md_top_level_only(self, tmp_path): + """AGENTS.md is loaded from cwd only — subdirectory copies are ignored.""" (tmp_path / "AGENTS.md").write_text("Top level instructions.") sub = tmp_path / "src" sub.mkdir() (sub / "AGENTS.md").write_text("Src-specific instructions.") result = build_context_files_prompt(cwd=str(tmp_path)) assert "Top level" in result - assert "Src-specific" in result + assert "Src-specific" not in result # --- .hermes.md / HERMES.md discovery ---