From 3b8b681e2f36a438e85376abfa3a9fcec1a5996c Mon Sep 17 00:00:00 2001 From: toyamagu2021 Date: Sun, 8 Jun 2025 03:00:25 +0900 Subject: [PATCH 1/6] feat: recipe-list Signed-off-by: toyamagu2021 --- crates/goose-cli/src/cli.rs | 24 ++- crates/goose-cli/src/commands/recipe.rs | 64 ++++++++ crates/goose-cli/src/recipes/github_recipe.rs | 142 +++++++++++++++++- crates/goose-cli/src/recipes/recipe.rs | 2 +- crates/goose-cli/src/recipes/search_recipe.rs | 90 ++++++++++- 5 files changed, 316 insertions(+), 6 deletions(-) diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 36066c24d414..0994a5b95a49 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -8,7 +8,7 @@ use crate::commands::configure::handle_configure; use crate::commands::info::handle_info; use crate::commands::mcp::run_server; use crate::commands::project::{handle_project_default, handle_projects_interactive}; -use crate::commands::recipe::{handle_deeplink, handle_validate}; +use crate::commands::recipe::{handle_deeplink, handle_list, handle_validate}; // Import the new handlers from commands::schedule use crate::commands::schedule::{ handle_schedule_add, handle_schedule_cron_help, handle_schedule_list, handle_schedule_remove, @@ -244,6 +244,23 @@ enum RecipeCommand { )] recipe_name: String, }, + + /// List available recipes + #[command(about = "List available recipes")] + List { + /// Output format (text, json) + #[arg( + long = "format", + value_name = "FORMAT", + help = "Output format (text, json)", + default_value = "text" + )] + format: String, + + /// Show verbose information including recipe descriptions + #[arg(short, long, help = "Show verbose information including recipe descriptions")] + verbose: bool, + }, } #[derive(Subcommand)] @@ -410,7 +427,7 @@ enum Command { long = "no-session", help = "Run without storing a session file", long_help = "Execute commands without creating or using a session file. Useful for automated runs.", - conflicts_with_all = ["resume", "name", "path"] + conflicts_with_all = ["resume", "name", "path"] )] no_session: bool, @@ -852,6 +869,9 @@ pub async fn cli() -> Result<()> { RecipeCommand::Deeplink { recipe_name } => { handle_deeplink(&recipe_name)?; } + RecipeCommand::List { format, verbose } => { + handle_list(&format, verbose)?; + } } return Ok(()); } diff --git a/crates/goose-cli/src/commands/recipe.rs b/crates/goose-cli/src/commands/recipe.rs index d4c700c27bb6..9a4c194647a2 100644 --- a/crates/goose-cli/src/commands/recipe.rs +++ b/crates/goose-cli/src/commands/recipe.rs @@ -1,8 +1,11 @@ use anyhow::Result; use base64::Engine; use console::style; +use serde_json; use crate::recipes::recipe::load_recipe; +use crate::recipes::search_recipe::list_available_recipes; +use crate::recipes::github_recipe::RecipeSource; /// Validates a recipe file /// @@ -58,3 +61,64 @@ pub fn handle_deeplink(recipe_name: &str) -> Result<()> { } } } + +/// Lists all available recipes from local paths and GitHub repositories +/// +/// # Arguments +/// +/// * `format` - Output format ("text" or "json") +/// * `verbose` - Whether to show detailed information +/// +/// # Returns +/// +/// Result indicating success or failure +pub fn handle_list(format: &str, verbose: bool) -> Result<()> { + let recipes = match list_available_recipes() { + Ok(recipes) => recipes, + Err(e) => { + return Err(anyhow::anyhow!("Failed to list recipes: {}", e)); + } + }; + + match format { + "json" => { + println!("{}", serde_json::to_string(&recipes)?); + } + _ => { + if recipes.is_empty() { + println!("No recipes found"); + return Ok(()); + } else { + println!("Available recipes:"); + for recipe in recipes { + let source_info = match recipe.source { + RecipeSource::Local => format!("local: {}", recipe.path), + RecipeSource::GitHub => format!("github: {}", recipe.path), + }; + + let description = if let Some(desc) = &recipe.description { + if desc.is_empty() { + "(none)" + } else { + desc + } + } else { + "(none)" + }; + + let output = format!("{} - {} - {}", recipe.name, description, source_info); + if verbose { + println!(" {}", output); + if let Some(title) = &recipe.title { + println!(" Title: {}", title); + } + println!(" Path: {}", recipe.path); + } else { + println!("{}", output); + } + } + } + } + } + Ok(()) +} diff --git a/crates/goose-cli/src/recipes/github_recipe.rs b/crates/goose-cli/src/recipes/github_recipe.rs index 962da236bb36..88e371154daf 100644 --- a/crates/goose-cli/src/recipes/github_recipe.rs +++ b/crates/goose-cli/src/recipes/github_recipe.rs @@ -1,5 +1,6 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use console::style; +use serde::{Serialize, Deserialize}; use std::env; use std::fs; use std::path::Path; @@ -10,6 +11,21 @@ use tar::Archive; use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RecipeInfo { + pub name: String, + pub source: RecipeSource, + pub path: String, + pub title: Option, + pub description: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum RecipeSource { + Local, + GitHub, +} + pub const GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY: &str = "GOOSE_RECIPE_GITHUB_REPO"; pub fn retrieve_recipe_from_github( recipe_name: &str, @@ -76,7 +92,7 @@ fn clone_and_download_recipe(recipe_name: &str, recipe_repo_full_name: &str) -> get_folder_from_github(&local_repo_path, recipe_name) } -fn ensure_gh_authenticated() -> Result<()> { +pub fn ensure_gh_authenticated() -> Result<()> { // Check authentication status let status = Command::new("gh") .args(["auth", "status"]) @@ -190,3 +206,125 @@ fn list_files(dir: &Path) -> Result<()> { } Ok(()) } + +/// Lists all available recipes from a GitHub repository +pub fn list_github_recipes(repo: &str) -> Result> { + discover_github_recipes(repo) +} + +fn discover_github_recipes(repo: &str) -> Result> { + use std::process::Command; + use serde_json::Value; + + // Ensure GitHub CLI is authenticated + ensure_gh_authenticated()?; + + // Get repository contents using GitHub CLI + let output = Command::new("gh") + .args(["api", &format!("repos/{}/contents", repo)]) + .output() + .map_err(|e| anyhow!("Failed to execute gh api command: {}", e))?; + + if !output.status.success() { + let error_msg = String::from_utf8_lossy(&output.stderr); + return Err(anyhow!("GitHub API request failed: {}", error_msg)); + } + + let contents: Value = serde_json::from_slice(&output.stdout) + .map_err(|e| anyhow!("Failed to parse GitHub API response: {}", e))?; + + let mut recipes = Vec::new(); + + if let Some(items) = contents.as_array() { + for item in items { + if let (Some(name), Some(item_type)) = ( + item.get("name").and_then(|n| n.as_str()), + item.get("type").and_then(|t| t.as_str()), + ) { + if item_type == "dir" { + // Check if this directory contains a recipe file + if let Ok(recipe_info) = check_github_directory_for_recipe(repo, name) { + recipes.push(recipe_info); + } + } + } + } + } + + Ok(recipes) +} + +fn check_github_directory_for_recipe(repo: &str, dir_name: &str) -> Result { + use std::process::Command; + use serde_json::Value; + + // Check directory contents for recipe files + let output = Command::new("gh") + .args(["api", &format!("repos/{}/contents/{}", repo, dir_name)]) + .output() + .map_err(|e| anyhow!("Failed to check directory contents: {}", e))?; + + if !output.status.success() { + return Err(anyhow!("Failed to access directory: {}", dir_name)); + } + + let contents: Value = serde_json::from_slice(&output.stdout) + .map_err(|e| anyhow!("Failed to parse directory contents: {}", e))?; + + if let Some(items) = contents.as_array() { + for item in items { + if let Some(name) = item.get("name").and_then(|n| n.as_str()) { + if name == "recipe.yaml" || name == "recipe.json" { + // Found a recipe file, get its content + return get_github_recipe_info(repo, dir_name, name); + } + } + } + } + + Err(anyhow!("No recipe file found in directory: {}", dir_name)) +} + +fn get_github_recipe_info(repo: &str, dir_name: &str, recipe_filename: &str) -> Result { + use std::process::Command; + use serde_json::Value; + + // Get the recipe file content + let output = Command::new("gh") + .args([ + "api", + &format!("repos/{}/contents/{}/{}", repo, dir_name, recipe_filename), + ]) + .output() + .map_err(|e| anyhow!("Failed to get recipe file content: {}", e))?; + + if !output.status.success() { + return Err(anyhow!("Failed to access recipe file: {}/{}", dir_name, recipe_filename)); + } + + let file_info: Value = serde_json::from_slice(&output.stdout) + .map_err(|e| anyhow!("Failed to parse file info: {}", e))?; + + if let Some(content_b64) = file_info.get("content").and_then(|c| c.as_str()) { + // Decode base64 content + use base64::{Engine as _, engine::general_purpose}; + let content_bytes = general_purpose::STANDARD.decode(content_b64.replace('\n', "")) + .map_err(|e| anyhow!("Failed to decode base64 content: {}", e))?; + + let content = String::from_utf8(content_bytes) + .map_err(|e| anyhow!("Failed to convert content to string: {}", e))?; + + // Parse the recipe content + let recipe = crate::recipes::recipe::parse_recipe_content(&content)?; + + return Ok(RecipeInfo { + name: dir_name.to_string(), + source: RecipeSource::GitHub, + path: format!("{}/{}", repo, dir_name), + title: Some(recipe.title), + description: Some(recipe.description), + }); + } + + Err(anyhow!("Failed to get recipe content from GitHub")) +} diff --git a/crates/goose-cli/src/recipes/recipe.rs b/crates/goose-cli/src/recipes/recipe.rs index b90de236a06b..8f93575718f6 100644 --- a/crates/goose-cli/src/recipes/recipe.rs +++ b/crates/goose-cli/src/recipes/recipe.rs @@ -183,7 +183,7 @@ fn validate_optional_parameters(recipe: &Recipe) -> Result<()> { } } -fn parse_recipe_content(content: &str) -> Result { +pub fn parse_recipe_content(content: &str) -> Result { if serde_json::from_str::(content).is_ok() { Ok(serde_json::from_str(content)?) } else if serde_yaml::from_str::(content).is_ok() { diff --git a/crates/goose-cli/src/recipes/search_recipe.rs b/crates/goose-cli/src/recipes/search_recipe.rs index 398e5a016715..c80fd56cc09c 100644 --- a/crates/goose-cli/src/recipes/search_recipe.rs +++ b/crates/goose-cli/src/recipes/search_recipe.rs @@ -5,7 +5,7 @@ use std::{env, fs}; use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS; -use super::github_recipe::{retrieve_recipe_from_github, GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY}; +use super::github_recipe::{retrieve_recipe_from_github, list_github_recipes, GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY, RecipeInfo, RecipeSource}; const GOOSE_RECIPE_PATH_ENV_VAR: &str = "GOOSE_RECIPE_PATH"; @@ -99,3 +99,91 @@ fn read_recipe_file>(recipe_path: P) -> Result<(String, PathBuf)> Ok((content, parent_dir)) } + +/// Lists all available recipes from local paths and GitHub repositories +pub fn list_available_recipes() -> Result> { + let mut recipes = Vec::new(); + + // Search local recipes + if let Ok(local_recipes) = discover_local_recipes() { + recipes.extend(local_recipes); + } + + // Search GitHub recipes if configured + if let Some(repo) = configured_github_recipe_repo() { + if let Ok(github_recipes) = list_github_recipes(&repo) { + recipes.extend(github_recipes); + } + } + + Ok(recipes) +} + +fn discover_local_recipes() -> Result> { + let mut recipes = Vec::new(); + let mut search_dirs = vec![PathBuf::from(".")]; + + // Add GOOSE_RECIPE_PATH directories + if let Ok(recipe_path_env) = env::var(GOOSE_RECIPE_PATH_ENV_VAR) { + let path_separator = if cfg!(windows) { ';' } else { ':' }; + let recipe_path_env_dirs: Vec = recipe_path_env + .split(path_separator) + .map(PathBuf::from) + .collect(); + search_dirs.extend(recipe_path_env_dirs); + } + + for dir in search_dirs { + if let Ok(dir_recipes) = scan_directory_for_recipes(&dir) { + recipes.extend(dir_recipes); + } + } + + Ok(recipes) +} + +fn scan_directory_for_recipes(dir: &Path) -> Result> { + let mut recipes = Vec::new(); + + if !dir.exists() || !dir.is_dir() { + return Ok(recipes); + } + + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + if let Some(extension) = path.extension() { + if RECIPE_FILE_EXTENSIONS.contains(&extension.to_string_lossy().as_ref()) { + if let Ok(recipe_info) = create_local_recipe_info(&path) { + recipes.push(recipe_info); + } + } + } + } + } + + Ok(recipes) +} + +fn create_local_recipe_info(path: &Path) -> Result { + let content = fs::read_to_string(path)?; + let recipe = crate::recipes::recipe::parse_recipe_content(&content)?; + + let name = path + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("unknown") + .to_string(); + + let path_str = path.to_string_lossy().to_string(); + + Ok(RecipeInfo { + name, + source: RecipeSource::Local, + path: path_str, + title: Some(recipe.title), + description: Some(recipe.description), + }) +} From 31b31f4c6386ff602485c4e816b8d66fe1dccf10 Mon Sep 17 00:00:00 2001 From: toyamagu-2021 Date: Sun, 29 Jun 2025 00:08:49 +0900 Subject: [PATCH 2/6] fix: improve error msg Signed-off-by: toyamagu-2021 --- crates/goose-cli/src/cli.rs | 6 +++++- crates/goose-cli/src/commands/recipe.rs | 2 +- crates/goose-cli/src/recipes/github_recipe.rs | 21 ++++++++++++------- crates/goose-cli/src/recipes/search_recipe.rs | 5 ++++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 0994a5b95a49..1ba137ce2734 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -258,7 +258,11 @@ enum RecipeCommand { format: String, /// Show verbose information including recipe descriptions - #[arg(short, long, help = "Show verbose information including recipe descriptions")] + #[arg( + short, + long, + help = "Show verbose information including recipe descriptions" + )] verbose: bool, }, } diff --git a/crates/goose-cli/src/commands/recipe.rs b/crates/goose-cli/src/commands/recipe.rs index 9a4c194647a2..bd967034f455 100644 --- a/crates/goose-cli/src/commands/recipe.rs +++ b/crates/goose-cli/src/commands/recipe.rs @@ -3,9 +3,9 @@ use base64::Engine; use console::style; use serde_json; +use crate::recipes::github_recipe::RecipeSource; use crate::recipes::recipe::load_recipe; use crate::recipes::search_recipe::list_available_recipes; -use crate::recipes::github_recipe::RecipeSource; /// Validates a recipe file /// diff --git a/crates/goose-cli/src/recipes/github_recipe.rs b/crates/goose-cli/src/recipes/github_recipe.rs index 88e371154daf..45d0a0e0bc73 100644 --- a/crates/goose-cli/src/recipes/github_recipe.rs +++ b/crates/goose-cli/src/recipes/github_recipe.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Result}; use console::style; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use std::env; use std::fs; use std::path::Path; @@ -213,8 +213,8 @@ pub fn list_github_recipes(repo: &str) -> Result> { } fn discover_github_recipes(repo: &str) -> Result> { - use std::process::Command; use serde_json::Value; + use std::process::Command; // Ensure GitHub CLI is authenticated ensure_gh_authenticated()?; @@ -223,7 +223,7 @@ fn discover_github_recipes(repo: &str) -> Result> { let output = Command::new("gh") .args(["api", &format!("repos/{}/contents", repo)]) .output() - .map_err(|e| anyhow!("Failed to execute gh api command: {}", e))?; + .map_err(|e| anyhow!("Failed to fetch repository contents using 'gh api' command (executed when GOOSE_RECIPE_GITHUB_REPO is configured). This requires GitHub CLI (gh) to be installed and authenticated. Error: {}", e))?; if !output.status.success() { let error_msg = String::from_utf8_lossy(&output.stderr); @@ -255,8 +255,8 @@ fn discover_github_recipes(repo: &str) -> Result> { } fn check_github_directory_for_recipe(repo: &str, dir_name: &str) -> Result { - use std::process::Command; use serde_json::Value; + use std::process::Command; // Check directory contents for recipe files let output = Command::new("gh") @@ -286,8 +286,8 @@ fn check_github_directory_for_recipe(repo: &str, dir_name: &str) -> Result Result { - use std::process::Command; use serde_json::Value; + use std::process::Command; // Get the recipe file content let output = Command::new("gh") @@ -299,7 +299,11 @@ fn get_github_recipe_info(repo: &str, dir_name: &str, recipe_filename: &str) -> .map_err(|e| anyhow!("Failed to get recipe file content: {}", e))?; if !output.status.success() { - return Err(anyhow!("Failed to access recipe file: {}/{}", dir_name, recipe_filename)); + return Err(anyhow!( + "Failed to access recipe file: {}/{}", + dir_name, + recipe_filename + )); } let file_info: Value = serde_json::from_slice(&output.stdout) @@ -307,8 +311,9 @@ fn get_github_recipe_info(repo: &str, dir_name: &str, recipe_filename: &str) -> if let Some(content_b64) = file_info.get("content").and_then(|c| c.as_str()) { // Decode base64 content - use base64::{Engine as _, engine::general_purpose}; - let content_bytes = general_purpose::STANDARD.decode(content_b64.replace('\n', "")) + use base64::{engine::general_purpose, Engine as _}; + let content_bytes = general_purpose::STANDARD + .decode(content_b64.replace('\n', "")) .map_err(|e| anyhow!("Failed to decode base64 content: {}", e))?; let content = String::from_utf8(content_bytes) diff --git a/crates/goose-cli/src/recipes/search_recipe.rs b/crates/goose-cli/src/recipes/search_recipe.rs index c80fd56cc09c..bca1d56314ad 100644 --- a/crates/goose-cli/src/recipes/search_recipe.rs +++ b/crates/goose-cli/src/recipes/search_recipe.rs @@ -5,7 +5,10 @@ use std::{env, fs}; use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS; -use super::github_recipe::{retrieve_recipe_from_github, list_github_recipes, GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY, RecipeInfo, RecipeSource}; +use super::github_recipe::{ + list_github_recipes, retrieve_recipe_from_github, RecipeInfo, RecipeSource, + GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY, +}; const GOOSE_RECIPE_PATH_ENV_VAR: &str = "GOOSE_RECIPE_PATH"; From 45ef7a0dafc7f63dd1dd18313dc66279dc88e3ac Mon Sep 17 00:00:00 2001 From: toyamagu-2021 Date: Sun, 29 Jun 2025 00:33:04 +0900 Subject: [PATCH 3/6] fix: remove hardcorded exts Signed-off-by: toyamagu-2021 --- crates/goose-cli/src/recipes/github_recipe.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/goose-cli/src/recipes/github_recipe.rs b/crates/goose-cli/src/recipes/github_recipe.rs index 45d0a0e0bc73..2f639ac1b016 100644 --- a/crates/goose-cli/src/recipes/github_recipe.rs +++ b/crates/goose-cli/src/recipes/github_recipe.rs @@ -1,6 +1,8 @@ use anyhow::{anyhow, Result}; use console::style; use serde::{Deserialize, Serialize}; + +use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS; use std::env; use std::fs; use std::path::Path; @@ -9,8 +11,6 @@ use std::process::Command; use std::process::Stdio; use tar::Archive; -use crate::recipes::recipe::RECIPE_FILE_EXTENSIONS; - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RecipeInfo { pub name: String, @@ -274,7 +274,10 @@ fn check_github_directory_for_recipe(repo: &str, dir_name: &str) -> Result Date: Sun, 29 Jun 2025 00:57:34 +0900 Subject: [PATCH 4/6] fix: conflict Signed-off-by: toyamagu-2021 --- crates/goose-cli/src/recipes/github_recipe.rs | 5 ++++- crates/goose-cli/src/recipes/search_recipe.rs | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/goose-cli/src/recipes/github_recipe.rs b/crates/goose-cli/src/recipes/github_recipe.rs index 2f639ac1b016..73e568601e34 100644 --- a/crates/goose-cli/src/recipes/github_recipe.rs +++ b/crates/goose-cli/src/recipes/github_recipe.rs @@ -323,7 +323,10 @@ fn get_github_recipe_info(repo: &str, dir_name: &str, recipe_filename: &str) -> .map_err(|e| anyhow!("Failed to convert content to string: {}", e))?; // Parse the recipe content - let recipe = crate::recipes::recipe::parse_recipe_content(&content)?; + let (recipe, _) = crate::recipes::template_recipe::parse_recipe_content( + &content, + format!("{}/{}", repo, dir_name), + )?; return Ok(RecipeInfo { name: dir_name.to_string(), diff --git a/crates/goose-cli/src/recipes/search_recipe.rs b/crates/goose-cli/src/recipes/search_recipe.rs index bca1d56314ad..7b3f8f73878e 100644 --- a/crates/goose-cli/src/recipes/search_recipe.rs +++ b/crates/goose-cli/src/recipes/search_recipe.rs @@ -172,7 +172,12 @@ fn scan_directory_for_recipes(dir: &Path) -> Result> { fn create_local_recipe_info(path: &Path) -> Result { let content = fs::read_to_string(path)?; - let recipe = crate::recipes::recipe::parse_recipe_content(&content)?; + let recipe_dir = path + .parent() + .unwrap_or_else(|| Path::new(".")) + .to_string_lossy() + .to_string(); + let (recipe, _) = crate::recipes::template_recipe::parse_recipe_content(&content, recipe_dir)?; let name = path .file_stem() From bd5fb84fe7e6cb1f6f92072827870a32d10c625a Mon Sep 17 00:00:00 2001 From: toyamagu-2021 Date: Tue, 1 Jul 2025 00:01:27 +0900 Subject: [PATCH 5/6] fix: cargo clippy Signed-off-by: toyamagu-2021 --- crates/goose-cli/src/recipes/github_recipe.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/goose-cli/src/recipes/github_recipe.rs b/crates/goose-cli/src/recipes/github_recipe.rs index 73e568601e34..5a58e13bd1e8 100644 --- a/crates/goose-cli/src/recipes/github_recipe.rs +++ b/crates/goose-cli/src/recipes/github_recipe.rs @@ -276,7 +276,7 @@ fn check_github_directory_for_recipe(repo: &str, dir_name: &str) -> Result Date: Sat, 5 Jul 2025 02:17:21 +0900 Subject: [PATCH 6/6] fix: lint Signed-off-by: toyamagu2021@gmail.com --- crates/goose-cli/src/recipes/github_recipe.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/goose-cli/src/recipes/github_recipe.rs b/crates/goose-cli/src/recipes/github_recipe.rs index 31cd6261af8b..0fcbbe9e6c95 100644 --- a/crates/goose-cli/src/recipes/github_recipe.rs +++ b/crates/goose-cli/src/recipes/github_recipe.rs @@ -27,7 +27,6 @@ pub enum RecipeSource { GitHub, } - pub const GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY: &str = "GOOSE_RECIPE_GITHUB_REPO"; pub fn retrieve_recipe_from_github( recipe_name: &str,