diff --git a/crates/goose/src/agents/platform_extensions/summon.rs b/crates/goose/src/agents/platform_extensions/summon.rs index 5125da7530f1..8ce4c2bca180 100644 --- a/crates/goose/src/agents/platform_extensions/summon.rs +++ b/crates/goose/src/agents/platform_extensions/summon.rs @@ -142,6 +142,7 @@ fn parse_agent_content(content: &str, path: &Path) -> Option { fn scan_recipes_from_dir( dir: &Path, kind: SourceType, + suppress_config_warnings: bool, sources: &mut Vec, seen: &mut std::collections::HashSet, ) { @@ -187,6 +188,13 @@ fn scan_recipes_from_dir( }); } Err(e) => { + // The working directory commonly contains project config like package.json + // and tsconfig.json, which parse as valid JSON but lack Recipe fields. In that + // case treat them as "not a recipe" rather than warning. Dedicated recipe + // directories still warn so a real recipe with a typo is not silently dropped. + if suppress_config_warnings && e.to_string().contains("missing field") { + continue; + } warn!("Failed to parse recipe {}: {}", path.display(), e); } } @@ -239,7 +247,6 @@ pub fn discover_filesystem_sources(working_dir: &Path) -> Vec { let config = Paths::config_dir(); let local_recipe_dirs: Vec = vec![ - working_dir.to_path_buf(), working_dir.join(".goose/recipes"), working_dir.join(".agents/recipes"), ]; @@ -278,8 +285,16 @@ pub fn discover_filesystem_sources(working_dir: &Path) -> Vec { .flatten() .collect(); + scan_recipes_from_dir( + working_dir, + SourceType::Recipe, + true, + &mut sources, + &mut seen, + ); + for dir in local_recipe_dirs { - scan_recipes_from_dir(&dir, SourceType::Recipe, &mut sources, &mut seen); + scan_recipes_from_dir(&dir, SourceType::Recipe, false, &mut sources, &mut seen); } for dir in local_agent_dirs { @@ -287,7 +302,7 @@ pub fn discover_filesystem_sources(working_dir: &Path) -> Vec { } for dir in global_recipe_dirs { - scan_recipes_from_dir(&dir, SourceType::Recipe, &mut sources, &mut seen); + scan_recipes_from_dir(&dir, SourceType::Recipe, false, &mut sources, &mut seen); } for dir in global_agent_dirs { @@ -2072,6 +2087,40 @@ You review code."#; assert_eq!(sources[0].name, "reviewer"); } + #[test] + fn test_recipe_scan_skips_non_recipe_project_config_files() { + let temp_dir = TempDir::new().unwrap(); + fs::write( + temp_dir.path().join("package.json"), + r#"{"scripts":{"test":"cargo test"}}"#, + ) + .unwrap(); + fs::write( + temp_dir.path().join("tsconfig.json"), + r#"{"compilerOptions":{"strict":true}}"#, + ) + .unwrap(); + fs::write( + temp_dir.path().join("valid.yaml"), + "title: Valid\ndescription: Real recipe\ninstructions: Run valid steps", + ) + .unwrap(); + + let mut sources = Vec::new(); + let mut seen = HashSet::new(); + scan_recipes_from_dir( + temp_dir.path(), + SourceType::Recipe, + true, + &mut sources, + &mut seen, + ); + + assert_eq!(sources.len(), 1); + assert_eq!(sources[0].name, "valid"); + assert_eq!(sources[0].description, "Real recipe"); + } + #[tokio::test] async fn test_discover_recipes_and_agents() { let temp_dir = TempDir::new().unwrap();