feat: read global AGENTS.md from ~/.agents/ as vendor-neutral fallback#2236
feat: read global AGENTS.md from ~/.agents/ as vendor-neutral fallback#2236mvanhorn wants to merge 1 commit into
Conversation
|
|
||
| /// User-level project instructions loaded as a fallback when the workspace and | ||
| /// its parents do not define project context. `.codewhale/` takes priority | ||
| /// over `.deepseek/` for both WHALE.md and AGENTS.md. | ||
| /// over vendor-neutral `.agents/`, which takes priority over legacy | ||
| /// `.deepseek/`, for both WHALE.md and AGENTS.md. | ||
| const GLOBAL_AGENTS_RELATIVE_PATH: &[&str] = &[".codewhale", "AGENTS.md"]; | ||
| const GLOBAL_AGENTS_VENDOR_NEUTRAL_PATH: &[&str] = &[".agents", "AGENTS.md"]; |
There was a problem hiding this comment.
WHALE.md in a vendor-neutral directory is self-contradictory
~/.agents/ is introduced as a vendor-neutral location, but GLOBAL_WHALE_VENDOR_NEUTRAL_PATH adds ~/.agents/WHALE.md at priority 3, ahead of ~/.agents/AGENTS.md at priority 4. A user who creates ~/.agents/AGENTS.md to share config across multiple tools could have it silently shadowed if they also happen to have ~/.agents/WHALE.md, without any warning. Lookups of WHALE.md there also only benefit codewhale, which undermines the vendor-neutral intent. Consider restricting ~/.agents/ to AGENTS.md only, or document the WHALE.md precedence explicitly.
| #[test] | ||
| fn test_load_global_agents_falls_back_to_vendor_neutral_path() { | ||
| let workspace = tempdir().expect("workspace tempdir"); | ||
| let home = tempdir().expect("home tempdir"); | ||
| let global_dir = home.path().join(".agents"); | ||
| fs::create_dir(&global_dir).expect("mkdir .agents"); | ||
| let global_agents = global_dir.join("AGENTS.md"); | ||
| fs::write(&global_agents, "Vendor-neutral instructions").expect("write global agents"); | ||
|
|
||
| let ctx = load_project_context_with_parents_and_home(workspace.path(), Some(home.path())); | ||
|
|
||
| assert!(ctx.has_instructions()); | ||
| assert!( | ||
| ctx.instructions | ||
| .as_ref() | ||
| .unwrap() | ||
| .contains("Vendor-neutral instructions") | ||
| ); | ||
| assert_eq!(ctx.source_path, Some(global_agents)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_codewhale_specific_path_wins_over_agents_path() { | ||
| let workspace = tempdir().expect("workspace tempdir"); | ||
| let home = tempdir().expect("home tempdir"); | ||
|
|
||
| let codewhale_dir = home.path().join(".codewhale"); | ||
| fs::create_dir(&codewhale_dir).expect("mkdir .codewhale"); | ||
| let codewhale_agents = codewhale_dir.join("AGENTS.md"); | ||
| fs::write(&codewhale_agents, "CodeWhale-specific instructions") | ||
| .expect("write codewhale agents"); | ||
|
|
||
| let agents_dir = home.path().join(".agents"); | ||
| fs::create_dir(&agents_dir).expect("mkdir .agents"); | ||
| fs::write(agents_dir.join("AGENTS.md"), "Vendor-neutral instructions") | ||
| .expect("write vendor-neutral agents"); | ||
|
|
||
| let ctx = load_project_context_with_parents_and_home(workspace.path(), Some(home.path())); | ||
|
|
||
| assert!(ctx.has_instructions()); | ||
| let instructions = ctx.instructions.as_ref().unwrap(); | ||
| assert!( | ||
| instructions.contains("CodeWhale-specific instructions"), | ||
| "CodeWhale-specific global file should win:\n{instructions}" | ||
| ); | ||
| assert!( | ||
| !instructions.contains("Vendor-neutral instructions"), | ||
| "lower-priority .agents file should be skipped:\n{instructions}" | ||
| ); | ||
| assert_eq!(ctx.source_path, Some(codewhale_agents)); | ||
| } |
There was a problem hiding this comment.
~/.agents/WHALE.md path has no test coverage
GLOBAL_WHALE_VENDOR_NEUTRAL_PATH (~/.agents/WHALE.md) is a new constant added at priority 3, but neither new test exercises it. Both new tests only write to ~/.agents/AGENTS.md. If the path-building loop for WHALE.md had a typo or the constant were mis-ordered, nothing would catch it. A test confirming ~/.agents/WHALE.md is found when that file exists (and wins over ~/.agents/AGENTS.md in the same directory) would close this gap.
There was a problem hiding this comment.
Code Review
This pull request introduces support for vendor-neutral global configuration paths (.agents/WHALE.md and .agents/AGENTS.md) as fallback options. The resolution priority has been updated so that .codewhale/ takes priority over .agents/, which in turn takes priority over the legacy .deepseek/ paths. Additionally, unit tests have been added to verify the fallback behavior and priority resolution. There are no review comments, and I have no additional feedback to provide.
- Workspace version: 0.8.46 → 0.8.47 - All inter-crate dependency versions updated - Merged PR #2236: global AGENTS.md from ~/.agents/
Hmbown
left a comment
There was a problem hiding this comment.
The core idea of reading as a vendor-neutral fallback is good. But the PR also introduces at priority 3, above at priority 4 — which contradicts the vendor-neutral intent and creates a silent shadowing hazard.
Requested changes:
- Remove from the candidate list entirely. If CodeWhale-specific global config is needed, it should go in (which is already higher priority at tier 1-2).
- Keep only in the vendor-neutral tier.
- Add a test verifying that is read but is NOT (to prevent regression).
The two new tests writing to are well-structured — those are fine to keep.
Hmbown
left a comment
There was a problem hiding this comment.
The core idea of reading ~/.agents/AGENTS.md as a vendor-neutral fallback is good. But the PR also introduces ~/.agents/WHALE.md at priority 3, above ~/.agents/AGENTS.md at priority 4 — which contradicts the vendor-neutral intent and creates a silent shadowing hazard.
Requested changes:
- Remove
~/.agents/WHALE.mdfrom the candidate list. If CodeWhale-specific global config is needed, use~/.codewhale/(already higher priority at tiers 1-2). - Keep only
~/.agents/AGENTS.mdin the vendor-neutral tier. - Add a test verifying that
~/.agents/AGENTS.mdis read but~/.agents/WHALE.mdis NOT.
The two new tests writing to ~/.agents/AGENTS.md are well-structured — those are fine to keep.
Summary
~/.agents/AGENTS.mdas a vendor-neutral fallback when~/.claude/CLAUDE.mdis absent.linzhiqin2003) for global AGENTS.md handling, extended to cover the home-dir fallback path.crates/tui/src/project_context.rs; default behavior unchanged for users who already have~/.claude/CLAUDE.md.Closes #2156.
AI was used for assistance.
Greptile Summary
This PR inserts
~/.agents/as a new vendor-neutral fallback tier (priority 3–4) between the existing~/.codewhale/(priority 1–2) and~/.deepseek/(priority 5–6) global instruction directories, with two new tests covering theAGENTS.mdcase and priority ordering.GLOBAL_AGENTS_VENDOR_NEUTRAL_PATH(~/.agents/AGENTS.md) andGLOBAL_WHALE_VENDOR_NEUTRAL_PATH(~/.agents/WHALE.md) are added to the candidate list inload_global_agents_context; the first matching file wins and the rest are skipped.~/.agents/AGENTS.mdis loaded when no codewhale-specific file exists, and one confirming~/.codewhale/AGENTS.mdtakes priority over~/.agents/AGENTS.md.~/.agents/WHALE.mdpath (priority 3) has no corresponding test and sits above~/.agents/AGENTS.md(priority 4) in the same directory, which could silently shadow a user's vendor-neutral config.Confidence Score: 4/5
Safe to merge with minor cleanup; default behavior for existing users is unchanged.
The fallback logic is additive and only activates when neither ~/.codewhale/ nor ~/.agents/ files exist for current users, so there is no regression risk for anyone with an existing setup. The main gap is that ~/.agents/WHALE.md (priority 3) is added without a test and can silently shadow ~/.agents/AGENTS.md in the same directory — a user who relies on the vendor-neutral path could be surprised if they also have a WHALE.md there. The WHALE.md addition also sits oddly in a directory marketed as vendor-neutral.
crates/tui/src/project_context.rs — specifically the untested GLOBAL_WHALE_VENDOR_NEUTRAL_PATH constant and its interaction with GLOBAL_AGENTS_VENDOR_NEUTRAL_PATH.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[load_global_agents_context] --> B{~/.codewhale/WHALE.md?} B -- exists --> Z[return context] B -- missing --> C{~/.codewhale/AGENTS.md?} C -- exists --> Z C -- missing --> D{~/.agents/WHALE.md?} D -- exists --> Z D -- missing --> E{~/.agents/AGENTS.md?} E -- exists --> Z E -- missing --> F{~/.deepseek/WHALE.md?} F -- exists --> Z F -- missing --> G{~/.deepseek/AGENTS.md?} G -- exists --> Z G -- missing --> H[return None] style D fill:#ffe0b2,stroke:#f57c00 style E fill:#ffe0b2,stroke:#f57c00Reviews (1): Last reviewed commit: "feat: read global AGENTS.md from ~/.agen..." | Re-trigger Greptile