From 59773cb432affbe8150c957f5da3199fdbaa103e Mon Sep 17 00:00:00 2001 From: Thump604 Date: Mon, 15 Jun 2026 17:13:54 -0500 Subject: [PATCH 1/2] fix(summon): skip non-recipe project config files --- .../src/agents/platform_extensions/summon.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/goose/src/agents/platform_extensions/summon.rs b/crates/goose/src/agents/platform_extensions/summon.rs index d6e5cc579765..b1bc4041b286 100644 --- a/crates/goose/src/agents/platform_extensions/summon.rs +++ b/crates/goose/src/agents/platform_extensions/summon.rs @@ -177,6 +177,12 @@ fn scan_recipes_from_dir( }); } Err(e) => { + // Files like package.json and tsconfig.json are common in project roots. + // Treat valid YAML/JSON that is missing Recipe fields as "not a recipe" + // rather than warning on every normal project config file. + if e.to_string().contains("missing field") { + continue; + } warn!("Failed to parse recipe {}: {}", path.display(), e); } } @@ -1998,6 +2004,34 @@ 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, &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(); From 18b79737555f0937c9915ed9f226cdaa3f1434c8 Mon Sep 17 00:00:00 2001 From: Douwe M Osinga Date: Wed, 17 Jun 2026 09:41:53 -0400 Subject: [PATCH 2/2] fix(summon): scope config-file warning suppression to working dir Only suppress 'missing field' parse warnings for the raw working directory, where project config like package.json and tsconfig.json live. Dedicated recipe directories (.goose/recipes, .agents/recipes, configured paths) still warn so a real recipe with a typo or missing required field is not silently dropped. Addresses codex P2 review feedback. Signed-off-by: Douwe M Osinga --- .../src/agents/platform_extensions/summon.rs | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/goose/src/agents/platform_extensions/summon.rs b/crates/goose/src/agents/platform_extensions/summon.rs index 8c0e5f291ee8..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,10 +188,11 @@ fn scan_recipes_from_dir( }); } Err(e) => { - // Files like package.json and tsconfig.json are common in project roots. - // Treat valid YAML/JSON that is missing Recipe fields as "not a recipe" - // rather than warning on every normal project config file. - if e.to_string().contains("missing field") { + // 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); @@ -245,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"), ]; @@ -284,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 { @@ -293,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 { @@ -2099,7 +2108,13 @@ You review code."#; let mut sources = Vec::new(); let mut seen = HashSet::new(); - scan_recipes_from_dir(temp_dir.path(), SourceType::Recipe, &mut sources, &mut seen); + 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");