diff --git a/crates/goose-cli/src/commands/goose_swarm/evaluate.yaml b/crates/goose-cli/src/commands/goose_swarm/evaluate.yaml index f190631d64..38a82c8d92 100644 --- a/crates/goose-cli/src/commands/goose_swarm/evaluate.yaml +++ b/crates/goose-cli/src/commands/goose_swarm/evaluate.yaml @@ -1,4 +1,5 @@ -name: evaluate +version: 1.0.0 +title: Evaluate description: Evaluate completed PRs and determine next steps instructions: | You are a Goose Swarm evaluation agent for GitHub repository {{ repo }}. diff --git a/crates/goose-cli/src/commands/goose_swarm/mod.rs b/crates/goose-cli/src/commands/goose_swarm/mod.rs index c426b23eb1..7836ea49e4 100644 --- a/crates/goose-cli/src/commands/goose_swarm/mod.rs +++ b/crates/goose-cli/src/commands/goose_swarm/mod.rs @@ -1 +1,3 @@ pub mod repo; +#[cfg(test)] +mod tests; diff --git a/crates/goose-cli/src/commands/goose_swarm/plan_work.yaml b/crates/goose-cli/src/commands/goose_swarm/plan_work.yaml index a9a1e3fe5a..d6cf4cb3f5 100644 --- a/crates/goose-cli/src/commands/goose_swarm/plan_work.yaml +++ b/crates/goose-cli/src/commands/goose_swarm/plan_work.yaml @@ -1,4 +1,5 @@ -name: plan_work +version: 1.0.0 +title: Plan Work description: Break down GitHub issues into parallel executable tasks instructions: | You are a Goose Swarm planner agent working on GitHub repository {{ repo }}. diff --git a/crates/goose-cli/src/commands/goose_swarm/swarm_drone.yaml b/crates/goose-cli/src/commands/goose_swarm/swarm_drone.yaml index c4dc6fe36c..e5696d80f2 100644 --- a/crates/goose-cli/src/commands/goose_swarm/swarm_drone.yaml +++ b/crates/goose-cli/src/commands/goose_swarm/swarm_drone.yaml @@ -1,4 +1,5 @@ -name: swarm_drone +version: 1.0.0 +title: Swarm Drone description: Execute a GitHub task issue and create a PR instructions: | You are a Goose Swarm drone worker for GitHub repository {{ repo }}. diff --git a/crates/goose-cli/src/commands/goose_swarm/tests.rs b/crates/goose-cli/src/commands/goose_swarm/tests.rs new file mode 100644 index 0000000000..5f1c5fbcce --- /dev/null +++ b/crates/goose-cli/src/commands/goose_swarm/tests.rs @@ -0,0 +1,111 @@ +#[cfg(test)] +mod tests { + use goose::recipe::Recipe; + + // Define test recipe constants directly in the test module + const TEST_PLAN_WORK_RECIPE: &str = include_str!("../goose_swarm/plan_work.yaml"); + const TEST_SWARM_DRONE_RECIPE: &str = include_str!("../goose_swarm/swarm_drone.yaml"); + const TEST_EVALUATE_RECIPE: &str = include_str!("../goose_swarm/evaluate.yaml"); + + #[test] + fn test_plan_work_recipe_parses() { + let result = serde_yaml::from_str::(TEST_PLAN_WORK_RECIPE); + assert!( + result.is_ok(), + "Failed to parse plan_work.yaml: {:?}", + result.err() + ); + + let recipe = result.unwrap(); + assert_eq!(recipe.title, "Plan Work"); + assert_eq!( + recipe.description, + "Break down GitHub issues into parallel executable tasks" + ); + assert!(recipe.instructions.is_some()); + assert!(recipe.prompt.is_some()); + } + + #[test] + fn test_swarm_drone_recipe_parses() { + let result = serde_yaml::from_str::(TEST_SWARM_DRONE_RECIPE); + assert!( + result.is_ok(), + "Failed to parse swarm_drone.yaml: {:?}", + result.err() + ); + + let recipe = result.unwrap(); + assert_eq!(recipe.title, "Swarm Drone"); + assert_eq!( + recipe.description, + "Execute a GitHub task issue and create a PR" + ); + assert!(recipe.instructions.is_some()); + assert!(recipe.prompt.is_some()); + } + + #[test] + fn test_evaluate_recipe_parses() { + let result = serde_yaml::from_str::(TEST_EVALUATE_RECIPE); + assert!( + result.is_ok(), + "Failed to parse evaluate.yaml: {:?}", + result.err() + ); + + let recipe = result.unwrap(); + assert_eq!(recipe.title, "Evaluate"); + assert_eq!( + recipe.description, + "Evaluate completed PRs and determine next steps" + ); + assert!(recipe.instructions.is_some()); + assert!(recipe.prompt.is_some()); + } + + #[test] + fn test_recipe_template_substitution() { + // Test that our recipe prompt templates work with substitution + let recipe: Recipe = serde_yaml::from_str(TEST_PLAN_WORK_RECIPE).unwrap(); + let prompt_template = recipe.prompt.unwrap(); + + // Verify template has placeholders (using single braces as per the YAML) + assert!( + prompt_template.contains("{{ context }}"), + "Should contain context placeholder" + ); + assert!( + prompt_template.contains("{{ repo_dir }}"), + "Should contain repo_dir placeholder" + ); + assert!( + prompt_template.contains("{{ work_dir }}"), + "Should contain work_dir placeholder" + ); + assert!( + prompt_template.contains("{{ available_nodes }}"), + "Should contain available_nodes placeholder" + ); + + // Test substitution + let mut prompt = prompt_template.clone(); + prompt = prompt.replace("{{ context }}", "test context"); + prompt = prompt.replace("{{ repo_dir }}", "/test/repo"); + + assert!(prompt.contains("test context")); + assert!(prompt.contains("/test/repo")); + assert!(!prompt.contains("{{ context }}")); + assert!(!prompt.contains("{{ repo_dir }}")); + } + + #[test] + fn test_worker_id_format() { + // Test that generated worker IDs follow expected format + // This is a minimal test since we can't access the private functions from swarm.rs + // But we can test that our test recipe YAML files are valid + assert!(TEST_PLAN_WORK_RECIPE.contains("Worker ID: {{ worker_id }}")); + assert!(TEST_SWARM_DRONE_RECIPE.contains("Worker ID: {{ worker_id }}")); + assert!(TEST_EVALUATE_RECIPE.contains("Worker ID: {{ worker_id }}")); + } +}