-
Notifications
You must be signed in to change notification settings - Fork 6k
feat: load global hints from ~/.agents/AGENTS.md #9736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
79a0725
4f46055
0e2e202
5e77118
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -230,17 +230,30 @@ pub fn load_hint_files( | |
| let mut global_hints_contents = Vec::with_capacity(hints_filenames.len()); | ||
| let mut local_hints_contents = Vec::with_capacity(hints_filenames.len()); | ||
|
|
||
| for hints_filename in hints_filenames { | ||
| let global_hints_path = Paths::in_config_dir(hints_filename); | ||
| let mut global_hints_paths: Vec<PathBuf> = hints_filenames | ||
| .iter() | ||
| .map(|name| Paths::in_config_dir(name)) | ||
| .collect(); | ||
| if hints_filenames | ||
| .iter() | ||
| .any(|name| name == AGENTS_MD_FILENAME) | ||
| { | ||
| global_hints_paths.push(Paths::in_agents_home_dir(AGENTS_MD_FILENAME)); | ||
| } | ||
|
|
||
| for global_hints_path in &global_hints_paths { | ||
| if global_hints_path.is_file() { | ||
| let mut visited = HashSet::new(); | ||
| let hints_dir = global_hints_path.parent().unwrap(); | ||
| let global_ignore_patterns = GitignoreBuilder::new(hints_dir) | ||
| .build() | ||
| .unwrap_or_else(|_| Gitignore::empty()); | ||
| let expanded_content = read_referenced_files( | ||
| &global_hints_path, | ||
| global_hints_path, | ||
| hints_dir, | ||
| &mut visited, | ||
| 0, | ||
| ignore_patterns, | ||
| &global_ignore_patterns, | ||
| ); | ||
| if !expanded_content.is_empty() { | ||
| global_hints_contents.push(expanded_content); | ||
|
|
@@ -314,6 +327,98 @@ mod tests { | |
| assert!(hints.contains("Test hint content")); | ||
| } | ||
|
|
||
| #[test] | ||
| #[serial_test::serial] | ||
| fn test_global_agents_md_in_agents_home() { | ||
| let root = TempDir::new().unwrap(); | ||
| std::env::set_var("GOOSE_PATH_ROOT", root.path()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the unit suite runs with default parallelism, this mutates the process-global Useful? React with 👍 / 👎. |
||
|
|
||
| let agents_home = root.path().join(".agents"); | ||
| fs::create_dir_all(&agents_home).unwrap(); | ||
| fs::write( | ||
| agents_home.join(AGENTS_MD_FILENAME), | ||
| "Global agents home instructions", | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| let project = TempDir::new().unwrap(); | ||
| let gitignore = create_dummy_gitignore(); | ||
| let hints = load_hint_files( | ||
| project.path(), | ||
| &[ | ||
| GOOSE_HINTS_FILENAME.to_string(), | ||
| AGENTS_MD_FILENAME.to_string(), | ||
| ], | ||
| &gitignore, | ||
| ); | ||
|
|
||
| std::env::remove_var("GOOSE_PATH_ROOT"); | ||
|
|
||
| assert!(hints.contains("Global Hints")); | ||
| assert!(hints.contains("Global agents home instructions")); | ||
| } | ||
|
|
||
| #[test] | ||
| #[serial_test::serial] | ||
| fn test_global_agents_md_imports_not_filtered_by_project_gitignore() { | ||
| let root = TempDir::new().unwrap(); | ||
| std::env::set_var("GOOSE_PATH_ROOT", root.path()); | ||
|
|
||
| let agents_home = root.path().join(".agents"); | ||
| fs::create_dir_all(&agents_home).unwrap(); | ||
| fs::write(agents_home.join("policy.md"), "Imported policy content").unwrap(); | ||
| fs::write( | ||
| agents_home.join(AGENTS_MD_FILENAME), | ||
| "Global header\n@policy.md\n", | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| let project = TempDir::new().unwrap(); | ||
| let mut builder = GitignoreBuilder::new(project.path()); | ||
| builder.add_line(None, "*.md").unwrap(); | ||
| let gitignore = builder.build().unwrap(); | ||
|
|
||
| let hints = load_hint_files( | ||
| project.path(), | ||
| &[ | ||
| GOOSE_HINTS_FILENAME.to_string(), | ||
| AGENTS_MD_FILENAME.to_string(), | ||
| ], | ||
| &gitignore, | ||
| ); | ||
|
|
||
| std::env::remove_var("GOOSE_PATH_ROOT"); | ||
|
|
||
| assert!(hints.contains("Imported policy content")); | ||
| } | ||
|
|
||
| #[test] | ||
| #[serial_test::serial] | ||
| fn test_global_agents_md_skipped_when_not_in_context_file_names() { | ||
| let root = TempDir::new().unwrap(); | ||
| std::env::set_var("GOOSE_PATH_ROOT", root.path()); | ||
|
|
||
| let agents_home = root.path().join(".agents"); | ||
| fs::create_dir_all(&agents_home).unwrap(); | ||
| fs::write( | ||
| agents_home.join(AGENTS_MD_FILENAME), | ||
| "Global agents home instructions", | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| let project = TempDir::new().unwrap(); | ||
| let gitignore = create_dummy_gitignore(); | ||
| let hints = load_hint_files( | ||
| project.path(), | ||
| &[GOOSE_HINTS_FILENAME.to_string()], | ||
| &gitignore, | ||
| ); | ||
|
|
||
| std::env::remove_var("GOOSE_PATH_ROOT"); | ||
|
|
||
| assert!(!hints.contains("Global agents home instructions")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_goosehints_when_missing() { | ||
| let dir = TempDir::new().unwrap(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the newly added global path is
~/.agents/AGENTS.md, this call still expands its@...references with theignore_patternsbuilt from the current project.ignore::Gitignorematches patterns such as*.mdagainst absolute paths outside the repo too, so in any project that ignores Markdown files, references like@policy.mdfrom the global AGENTS file are left unexpanded and the global hints silently lose content. Use an empty/global matcher for global hint expansion, or apply the project matcher only to project hint files.Useful? React with 👍 / 👎.