feat(agent): cascading context file discovery with walk-up - #10482
Conversation
Add three new config options for context file discovery: - context.compose: compose all found files instead of first-match-wins (default: true) - context.walk_limit: how far to walk up parents: git_root/home/unlimited/absolute path (default: home) - context.show_loaded: print discovered context files at startup (default: true) Add migration v17→v18 to seed defaults for existing users.
🤖 Automated PR ReviewSecurity Scan✓ No hardcoded secrets Code Quality✓ Well-structured new feature SummaryStatus: COMMENT Good feature for cascading context discovery. Security looks OK (uses resolve()). Consider simplifying the walk parent logic for maintainability. Reviewed by Hermes Agent |
Response to automated reviewPath traversal: walk_limit="unlimited": Intentional — matches the original
Lines changed: The 601 lines include 338 lines for Note: #10503 builds on this PR and includes further simplifications based on testing feedback. |
|
Implements #10299 — cascading context file discovery with walk-up and /context command. |
|
Implements #10299 — cascading context file discovery with walk-up and /context command. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the detailed proposal. The startup limitation is real, but current main has deliberately moved toward a different context-loading model.
Problems
agent/prompt_builder.py:1004uses string-prefix containment. With stop path/home/alice, a CWD under/home/alice2is treated as in-bounds and the loop can append/homeand/. Use path ancestry (relative_to/is_relative_to) and stop at the exact resolved boundary. This is especially important because main commit306b6615cf00a77e39fb27b69653a2a5d24ce5f8hardened parent discovery against context files outside a safe project boundary.cli.py:6205never parsescmd; all advertised/context add,remove,walk, andcomposeinvocations produce the same listing.context.show_loadedis read atcli.py:6221but unused, andrun_agent.py:3274-3289does not consume it, so the promised startup display is absent.- Current main intentionally keeps startup context CWD-only (
agent/prompt_builder.py:1876-1984) and progressively loads workspace-local hints with an explicit outside-workspace rejection (agent/subdirectory_hints.py:169-196). The current docs explain this avoids prompt bloat and preserves a stable system prompt (website/docs/user-guide/features/context-files.md:32-49).
Suggested changes
- Rework the boundary check and add regression tests for sibling-prefix paths and CWDs outside the configured root.
- Resolve the eager-home-composition design against the existing progressive, workspace-bounded mechanism before salvaging; then implement or narrow the
/contextandshow_loadedcontract consistently.
Automated hermes-sweeper review.
| dirs.append(parent) | ||
| break | ||
| # Check if we've gone past the stop path | ||
| if str(parent_resolved).startswith(str(stop_path)): |
There was a problem hiding this comment.
String prefixes do not establish path ancestry: with stop /home/alice and CWD /home/alice2/project, this branch treats the CWD as in-bounds and later appends /home and /. Use Path.is_relative_to()/relative_to() on resolved paths, reject a CWD outside the bound, and stop exactly at the bound.
| # "git_root" | "home" | "unlimited" | absolute path | ||
| "walk_limit": "home", | ||
| # Print discovered context files at startup. | ||
| "show_loaded": True, |
There was a problem hiding this comment.
show_loaded is persisted but not used: run_agent.py forwards only compose and walk_limit, and the /context handler reads this value without branching on it. The advertised startup display will never occur until this is wired into startup presentation.
| } | ||
| _cprint(labels.get(self.tool_progress_mode, "")) | ||
|
|
||
| def _handle_context_command(self, cmd: str): |
There was a problem hiding this comment.
This handler never parses cmd, so the body-promised /context add, remove, walk, and compose subcommands all execute the same listing. Either implement the durable config mutations/subcommand dispatch or narrow the command contract.
Problem
Context file discovery (
AGENTS.md,CLAUDE.md) is currently CWD-only at startup. Only.hermes.mdwalks up parent directories (to git root). Users with cascading context files at multiple levels (home, workspace, project) miss instructions from parent directories.Two separate problems:
1. No walk-up for AGENTS.md and CLAUDE.md
_load_agents_md()and_load_claude_md()check onlycwd_path / name. Users lose workspace-level and home-level instructions.2. Hard-coded first-match-wins
The
orchain inbuild_context_files_prompt()means only ONE context file type is loaded.Solution
Config-driven cascading discovery
New functions in
agent/prompt_builder.py_resolve_walk_limit()— resolves limit config to a concrete directory_walk_parents()— yields CWD + parents up to the configured limit_load_all_of_type()— collects ALL matching files with inode dedup (macOS APFS)discover_context_files()— returns file list for/contextcommand and verbose display/context command
Key changes
_load_agents_mdand_load_claude_mdnow walk parents and compose all found filesbuild_context_files_prompt()acceptscomposeandwalk_limitparamsrun_agent.pyreads config and passes settings throughTesting
Files changed
agent/prompt_builder.pytests/agent/test_prompt_builder.pyhermes_cli/config.pyrun_agent.pycli.pyhermes_cli/commands.pyTotal: 6 files, +601/-25
Relevant Issues