Port from earendil-works/pi#7681: support AGENTS.override.md context override

AGENTS.override.md now takes priority over AGENTS.md in both startup
project-context loading (prompt_builder) and progressive subdirectory
hint discovery (subdirectory_hints). Lets developers keep a personal,
typically-gitignored override next to committed project instructions
without editing the tracked file.
This commit is contained in:
Teknium 2026-08-06 17:52:54 -07:00
parent 0957277f2f
commit 41efb0ab1b
No known key found for this signature in database
5 changed files with 47 additions and 3 deletions

View File

@ -2059,8 +2059,14 @@ def _load_hermes_md(cwd_path: Path, context_length: Optional[int] = None) -> str
def _load_agents_md(cwd_path: Path, context_length: Optional[int] = None) -> str:
"""AGENTS.md — top-level only (no recursive walk)."""
for name in ["AGENTS.md", "agents.md"]:
"""AGENTS.override.md / AGENTS.md — top-level only (no recursive walk).
``AGENTS.override.md`` wins over ``AGENTS.md`` so a developer can keep a
personal, typically-gitignored override next to the committed project
instructions without editing the tracked file (same convention as
earendil-works/pi#7681).
"""
for name in ["AGENTS.override.md", "AGENTS.md", "agents.md"]:
candidate = cwd_path / name
if candidate.exists():
try:

View File

@ -28,6 +28,7 @@ logger = logging.getLogger(__name__)
# Same filenames as prompt_builder.py but we load ALL found (not first-wins)
# since different subdirectories may use different conventions.
_HINT_FILENAMES = [
"AGENTS.override.md",
"AGENTS.md", "agents.md",
"CLAUDE.md", "claude.md",
".cursorrules",

View File

@ -449,6 +449,27 @@ class TestBuildContextFilesPrompt:
assert "Ruff for linting" in result
assert "Project Context" in result
def test_agents_override_md_wins_over_agents_md(self, tmp_path):
(tmp_path / "AGENTS.md").write_text("Use Ruff for linting.")
(tmp_path / "AGENTS.override.md").write_text("Use Black instead.")
result = build_context_files_prompt(cwd=str(tmp_path))
assert "Use Black instead" in result
assert "Ruff for linting" not in result
assert "AGENTS.override.md" in result
def test_agents_override_md_loads_alone(self, tmp_path):
(tmp_path / "AGENTS.override.md").write_text("Override-only context.")
result = build_context_files_prompt(cwd=str(tmp_path))
assert "Override-only context" in result
assert "Project Context" in result
def test_hermes_md_still_wins_over_agents_override(self, tmp_path):
(tmp_path / ".hermes.md").write_text("Hermes-first context.")
(tmp_path / "AGENTS.override.md").write_text("Override context.")
result = build_context_files_prompt(cwd=str(tmp_path))
assert "Hermes-first context" in result
assert "Override context" not in result
def test_skips_agents_md_in_install_tree_on_fallback(self, monkeypatch, tmp_path):
# A backend that FALLS BACK into the install tree (cwd=None → getcwd,
# the desktop default) must not load that tree's contributor AGENTS.md

View File

@ -284,3 +284,16 @@ class TestExcludedDirectories:
tracker = SubdirectoryHintTracker(working_dir=str(tmp_path))
result = tracker.check_tool_call("read_file", {"path": str(normal / "f.py")})
assert result is not None and "Backend rules" in result
def test_agents_override_md_wins_in_subdirectory(self, tmp_path):
"""AGENTS.override.md takes priority over AGENTS.md per directory."""
sub = tmp_path / "backend"
sub.mkdir()
(sub / "AGENTS.md").write_text("Committed backend rules")
(sub / "AGENTS.override.md").write_text("Personal backend override")
tracker = SubdirectoryHintTracker(working_dir=str(tmp_path))
result = tracker.check_tool_call("read_file", {"path": str(sub / "f.py")})
assert result is not None
assert "Personal backend override" in result
assert "Committed backend rules" not in result

View File

@ -13,6 +13,7 @@ Hermes Agent automatically discovers and loads context files that shape how it b
| File | Purpose | Discovery |
|------|---------|-----------|
| **.hermes.md** / **HERMES.md** | Project instructions (highest priority) | Walks to git root |
| **AGENTS.override.md** | Personal, per-directory override of AGENTS.md (typically gitignored) | CWD at startup + subdirectories progressively |
| **AGENTS.md** | Project instructions, conventions, architecture | CWD at startup + subdirectories progressively |
| **CLAUDE.md** | Claude Code context files (also detected) | CWD at startup + subdirectories progressively |
| **SOUL.md** | Global personality and tone customization for this Hermes instance | `HERMES_HOME/SOUL.md` only |
@ -20,7 +21,9 @@ Hermes Agent automatically discovers and loads context files that shape how it b
| **.cursor/rules/*.mdc** | Cursor IDE rule modules | CWD only |
:::info Priority system
Only **one** project context type is loaded per session (first match wins): `.hermes.md``AGENTS.md``CLAUDE.md``.cursorrules`. **SOUL.md** is always loaded independently as the agent identity (slot #1).
Only **one** project context type is loaded per session (first match wins): `.hermes.md``AGENTS.override.md``AGENTS.md``CLAUDE.md``.cursorrules`. **SOUL.md** is always loaded independently as the agent identity (slot #1).
If an `AGENTS.override.md` exists next to an `AGENTS.md`, the override is loaded **instead of** the committed file — keep a personal (usually gitignored) `AGENTS.override.md` when you want different instructions than the ones checked into the repo, without editing the tracked `AGENTS.md`.
:::
## AGENTS.md