From cc51ec0941dac4de84ce6fda7c6be2baab15de5e Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Wed, 18 Feb 2026 17:01:47 +1100 Subject: [PATCH] fix: subrecipe relative path with summon (#7295) --- .../goose-server/src/routes/recipe_utils.rs | 32 ++++++++++++++++--- crates/goose/src/recipe/build_recipe/mod.rs | 2 +- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/goose-server/src/routes/recipe_utils.rs b/crates/goose-server/src/routes/recipe_utils.rs index ba068b7322..800283ddb4 100644 --- a/crates/goose-server/src/routes/recipe_utils.rs +++ b/crates/goose-server/src/routes/recipe_utils.rs @@ -10,7 +10,9 @@ use crate::state::AppState; use anyhow::Result; use axum::http::StatusCode; use goose::agents::Agent; -use goose::recipe::build_recipe::{build_recipe_from_template, RecipeError}; +use goose::recipe::build_recipe::{ + build_recipe_from_template, resolve_sub_recipe_path, RecipeError, +}; use goose::recipe::local_recipes::{get_recipe_library_dir, list_local_recipes}; use goose::recipe::validate_recipe::validate_recipe_template_from_content; use goose::recipe::Recipe; @@ -44,13 +46,23 @@ pub fn short_id_from_path(path: &str) -> String { pub fn get_all_recipes_manifests() -> Result> { let recipes_with_path = list_local_recipes()?; let mut recipe_manifests_with_path = Vec::new(); - for (file_path, recipe) in recipes_with_path { + for (file_path, mut recipe) in recipes_with_path { let Ok(last_modified) = fs::metadata(file_path.clone()) .map(|m| chrono::DateTime::::from(m.modified().unwrap()).to_rfc3339()) else { continue; }; + if let Some(recipe_dir) = file_path.parent() { + if let Some(ref mut sub_recipes) = recipe.sub_recipes { + for sr in sub_recipes.iter_mut() { + if let Ok(resolved) = resolve_sub_recipe_path(&sr.path, recipe_dir) { + sr.path = resolved; + } + } + } + } + let manifest_with_path = RecipeManifest { id: short_id_from_path(file_path.to_string_lossy().as_ref()), recipe, @@ -126,10 +138,22 @@ pub async fn get_recipe_file_path_by_id( pub async fn load_recipe_by_id(state: &AppState, id: &str) -> Result { let path = get_recipe_file_path_by_id(state, id).await?; - Recipe::from_file_path(&path).map_err(|err| ErrorResponse { + let mut recipe = Recipe::from_file_path(&path).map_err(|err| ErrorResponse { message: format!("Failed to load recipe: {}", err), status: StatusCode::INTERNAL_SERVER_ERROR, - }) + })?; + + if let Some(recipe_dir) = path.parent() { + if let Some(ref mut sub_recipes) = recipe.sub_recipes { + for sr in sub_recipes.iter_mut() { + if let Ok(resolved) = resolve_sub_recipe_path(&sr.path, recipe_dir) { + sr.path = resolved; + } + } + } + } + + Ok(recipe) } pub async fn build_recipe_with_parameter_values( diff --git a/crates/goose/src/recipe/build_recipe/mod.rs b/crates/goose/src/recipe/build_recipe/mod.rs index 7c7e91518b..ddd7be58de 100644 --- a/crates/goose/src/recipe/build_recipe/mod.rs +++ b/crates/goose/src/recipe/build_recipe/mod.rs @@ -153,7 +153,7 @@ where Ok((param_map, missing_params)) } -fn resolve_sub_recipe_path( +pub fn resolve_sub_recipe_path( sub_recipe_path: &str, parent_recipe_dir: &Path, ) -> Result {