diff --git a/crates/tui/src/project_context.rs b/crates/tui/src/project_context.rs index 03cdc575..b474582e 100644 --- a/crates/tui/src/project_context.rs +++ b/crates/tui/src/project_context.rs @@ -53,9 +53,10 @@ const REPO_CONSTITUTION_RELATIVE_PATH: &[&str] = &[".codewhale", "constitution.j const SUPPORTED_CONSTITUTION_SCHEMA: u32 = 1; /// User-level project instructions loaded as a fallback when the workspace and -/// its parents do not define project context. `.codewhale/` takes priority -/// over vendor-neutral `.agents/`, which takes priority over legacy -/// `.deepseek/`, for both WHALE.md and AGENTS.md. +/// its parents do not define project context. Any global AGENTS.md takes +/// priority over any deprecated global WHALE.md; within each file name, +/// `.codewhale/` takes priority over vendor-neutral `.agents/`, which takes +/// priority over legacy `.deepseek/`. const GLOBAL_AGENTS_RELATIVE_PATH: &[&str] = &[".codewhale", "AGENTS.md"]; const GLOBAL_AGENTS_VENDOR_NEUTRAL_PATH: &[&str] = &[".agents", "AGENTS.md"]; const GLOBAL_AGENTS_LEGACY_PATH: &[&str] = &[".deepseek", "AGENTS.md"]; @@ -759,17 +760,17 @@ fn load_global_agents_context(workspace: &Path, home_dir: Option<&Path>) -> Opti // Priority order (AGENTS.md preferred over the now-deprecated WHALE.md): // 1. ~/.codewhale/AGENTS.md (canonical) - // 2. ~/.codewhale/WHALE.md (deprecated, legacy fallback) - // 3. ~/.agents/AGENTS.md (vendor-neutral fallback) - // 4. ~/.agents/WHALE.md (deprecated, vendor-neutral legacy) - // 5. ~/.deepseek/AGENTS.md (legacy fallback) + // 2. ~/.agents/AGENTS.md (vendor-neutral fallback) + // 3. ~/.deepseek/AGENTS.md (legacy fallback) + // 4. ~/.codewhale/WHALE.md (deprecated, legacy fallback) + // 5. ~/.agents/WHALE.md (deprecated, vendor-neutral legacy) // 6. ~/.deepseek/WHALE.md (deprecated, legacy) let candidates: &[&[&str]] = &[ GLOBAL_AGENTS_RELATIVE_PATH, - GLOBAL_WHALE_RELATIVE_PATH, GLOBAL_AGENTS_VENDOR_NEUTRAL_PATH, - GLOBAL_WHALE_VENDOR_NEUTRAL_PATH, GLOBAL_AGENTS_LEGACY_PATH, + GLOBAL_WHALE_RELATIVE_PATH, + GLOBAL_WHALE_VENDOR_NEUTRAL_PATH, GLOBAL_WHALE_LEGACY_PATH, ]; @@ -1492,6 +1493,71 @@ mod tests { assert_eq!(ctx.source_path, Some(codewhale_agents)); } + #[test] + fn test_global_agents_wins_over_global_whale_across_paths() { + 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"); + fs::write(codewhale_dir.join("WHALE.md"), "Global WHALE legacy") + .expect("write codewhale whale"); + + let agents_dir = home.path().join(".agents"); + fs::create_dir(&agents_dir).expect("mkdir .agents"); + let global_agents = agents_dir.join("AGENTS.md"); + fs::write(&global_agents, "Global AGENTS canonical").expect("write global 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("Global AGENTS canonical"), + "global AGENTS.md should win:\n{instructions}" + ); + assert!( + !instructions.contains("Global WHALE legacy"), + "global WHALE.md content should be skipped when any global AGENTS.md exists:\n{instructions}" + ); + assert!( + !ctx.warnings + .iter() + .any(|warning| warning.contains("WHALE.md is deprecated")), + "losing WHALE.md should not emit deprecation warning: {:?}", + ctx.warnings + ); + assert_eq!(ctx.source_path, Some(global_agents)); + } + + #[test] + fn test_global_whale_fallback_warns_when_no_global_agents_exists() { + 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 global_whale = codewhale_dir.join("WHALE.md"); + fs::write(&global_whale, "Global WHALE legacy").expect("write codewhale whale"); + + 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("Global WHALE legacy"), + "legacy WHALE.md must still be read when no global AGENTS.md exists:\n{instructions}" + ); + assert!( + ctx.warnings + .iter() + .any(|warning| warning.contains("WHALE.md is deprecated")), + "expected global WHALE.md deprecation warning, got {:?}", + ctx.warnings + ); + assert_eq!(ctx.source_path, Some(global_whale)); + } + #[test] fn test_local_and_global_agents_merge_when_both_exist() { // #1157: when both `~/.deepseek/AGENTS.md` and a project AGENTS.md