2025-03-26 17:27:54 -07:00
use anyhow ::Result ;
use clap ::{ Args , Parser , Subcommand } ;
2025-04-09 18:57:24 -07:00
use goose ::config ::{ Config , ExtensionConfig } ;
2025-03-26 17:27:54 -07:00
2025-04-08 14:43:43 -04:00
use crate ::commands ::bench ::agent_generator ;
2025-03-26 17:27:54 -07:00
use crate ::commands ::configure ::handle_configure ;
use crate ::commands ::info ::handle_info ;
use crate ::commands ::mcp ::run_server ;
2025-05-13 09:05:10 +10:00
use crate ::commands ::project ::{ handle_project_default , handle_projects_interactive } ;
2025-04-09 18:57:24 -07:00
use crate ::commands ::recipe ::{ handle_deeplink , handle_validate } ;
2025-05-27 10:36:27 -07:00
// Import the new handlers from commands::schedule
use crate ::commands ::schedule ::{
2025-06-10 08:17:45 -07:00
handle_schedule_add , handle_schedule_cron_help , handle_schedule_list , handle_schedule_remove ,
handle_schedule_run_now , handle_schedule_services_status , handle_schedule_services_stop ,
2025-05-27 10:36:27 -07:00
handle_schedule_sessions ,
} ;
2025-04-22 15:51:33 -07:00
use crate ::commands ::session ::{ handle_session_list , handle_session_remove } ;
2025-03-26 17:27:54 -07:00
use crate ::logging ::setup_logging ;
2025-05-15 10:57:05 +10:00
use crate ::recipes ::recipe ::{ explain_recipe_with_parameters , load_recipe_as_template } ;
2025-03-26 17:27:54 -07:00
use crate ::session ;
2025-06-12 21:32:01 -04:00
use crate ::session ::{ build_session , SessionBuilderConfig , SessionSettings } ;
2025-04-08 14:43:43 -04:00
use goose_bench ::bench_config ::BenchRunConfig ;
use goose_bench ::runners ::bench_runner ::BenchRunner ;
use goose_bench ::runners ::eval_runner ::EvalRunner ;
2025-05-21 15:00:13 -04:00
use goose_bench ::runners ::metric_aggregator ::MetricAggregator ;
2025-04-08 14:43:43 -04:00
use goose_bench ::runners ::model_runner ::ModelRunner ;
2025-03-26 17:27:54 -07:00
use std ::io ::Read ;
use std ::path ::PathBuf ;
#[ derive(Parser) ]
#[ command(author, version, display_name = " " , about, long_about = None) ]
struct Cli {
#[ command(subcommand) ]
command : Option < Command > ,
}
2025-06-04 23:27:21 +02:00
#[ derive(Args, Debug) ]
2025-03-26 17:27:54 -07:00
#[ group(required = false, multiple = false) ]
struct Identifier {
#[ arg(
short,
long,
value_name = " NAME " ,
help = " Name for the chat session (e.g., 'project-x') " ,
long_help = " Specify a name for your chat session. When used with --resume, will resume this specific session if it exists. "
) ]
name : Option < String > ,
#[ arg(
short,
long,
value_name = " PATH " ,
help = " Path for the chat session (e.g., './playground.jsonl') " ,
long_help = " Specify a path for your chat session. When used with --resume, will resume this specific session if it exists. "
) ]
path : Option < PathBuf > ,
}
fn extract_identifier ( identifier : Identifier ) -> session ::Identifier {
if let Some ( name ) = identifier . name {
session ::Identifier ::Name ( name )
} else if let Some ( path ) = identifier . path {
session ::Identifier ::Path ( path )
} else {
unreachable! ( )
}
}
2025-05-02 08:47:45 +10:00
fn parse_key_val ( s : & str ) -> Result < ( String , String ) , String > {
match s . split_once ( '=' ) {
Some ( ( key , value ) ) = > Ok ( ( key . to_string ( ) , value . to_string ( ) ) ) ,
None = > Err ( format! ( " invalid KEY=VALUE: {} " , s ) ) ,
}
}
2025-03-26 17:27:54 -07:00
#[ derive(Subcommand) ]
enum SessionCommand {
#[ command(about = " List all available sessions " ) ]
List {
#[ arg(short, long, help = " List all available sessions " ) ]
verbose : bool ,
#[ arg(
short,
long,
help = " Output format (text, json) " ,
default_value = " text "
) ]
format : String ,
2025-04-23 17:21:27 +02:00
#[ arg(
long = " ascending " ,
help = " Sort by date in ascending order (oldest first) " ,
long_help = " Sort sessions by date in ascending order (oldest first). Default is descending order (newest first). "
) ]
ascending : bool ,
2025-03-26 17:27:54 -07:00
} ,
2025-05-25 19:27:15 +02:00
#[ command(about = " Remove sessions. Runs interactively if no ID or regex is provided. " ) ]
2025-04-22 15:51:33 -07:00
Remove {
2025-05-25 19:27:15 +02:00
#[ arg(short, long, help = " Session ID to be removed (optional) " ) ]
id : Option < String > ,
#[ arg(short, long, help = " Regex for removing matched sessions (optional) " ) ]
regex : Option < String > ,
2025-04-22 15:51:33 -07:00
} ,
2025-06-04 23:27:21 +02:00
#[ command(about = " Export a session to Markdown format " ) ]
Export {
#[ command(flatten) ]
identifier : Option < Identifier > ,
#[ arg(
short,
long,
help = " Output file path (default: stdout) " ,
long_help = " Path to save the exported Markdown. If not provided, output will be sent to stdout "
) ]
output : Option < PathBuf > ,
} ,
2025-03-26 17:27:54 -07:00
}
2025-05-27 10:36:27 -07:00
#[ derive(Subcommand, Debug) ]
enum SchedulerCommand {
#[ command(about = " Add a new scheduled job " ) ]
Add {
#[ arg(long, help = " Unique ID for the job " ) ]
id : String ,
2025-06-10 08:17:45 -07:00
#[ arg(
long,
help = " Cron expression for the schedule " ,
long_help = " Cron expression for when to run the job. Examples: \n '0 * * * *' - Every hour at minute 0 \n '0 */2 * * *' - Every 2 hours \n '@hourly' - Every hour (shorthand) \n '0 9 * * *' - Every day at 9:00 AM \n '0 9 * * 1' - Every Monday at 9:00 AM \n '0 0 1 * *' - First day of every month at midnight "
) ]
2025-05-27 10:36:27 -07:00
cron : String ,
#[ arg(
long,
help = " Recipe source (path to file, or base64 encoded recipe string) "
) ]
recipe_source : String ,
} ,
#[ command(about = " List all scheduled jobs " ) ]
List { } ,
#[ command(about = " Remove a scheduled job by ID " ) ]
Remove {
#[ arg(long, help = " ID of the job to remove " ) ] // Changed from positional to named --id
id : String ,
} ,
/// List sessions created by a specific schedule
#[ command(about = " List sessions created by a specific schedule " ) ]
Sessions {
/// ID of the schedule
#[ arg(long, help = " ID of the schedule " ) ] // Explicitly make it --id
id : String ,
/// Maximum number of sessions to return
#[ arg(long, help = " Maximum number of sessions to return " ) ]
limit : Option < u32 > ,
} ,
/// Run a scheduled job immediately
#[ command(about = " Run a scheduled job immediately " ) ]
RunNow {
/// ID of the schedule to run
#[ arg(long, help = " ID of the schedule to run " ) ] // Explicitly make it --id
id : String ,
} ,
2025-06-10 08:17:45 -07:00
/// Check status of Temporal services (temporal scheduler only)
#[ command(about = " Check status of Temporal services " ) ]
ServicesStatus { } ,
/// Stop Temporal services (temporal scheduler only)
#[ command(about = " Stop Temporal services " ) ]
ServicesStop { } ,
/// Show cron expression examples and help
#[ command(about = " Show cron expression examples and help " ) ]
CronHelp { } ,
2025-05-27 10:36:27 -07:00
}
2025-04-08 14:43:43 -04:00
#[ derive(Subcommand) ]
pub enum BenchCommand {
#[ command(name = " init-config " , about = " Create a new starter-config " ) ]
InitConfig {
#[ arg(short, long, help = " filename with extension for generated config " ) ]
name : String ,
} ,
#[ command(about = " Run all benchmarks from a config " ) ]
Run {
#[ arg(
short,
long,
help = " A config file generated by the config-init command "
) ]
config : PathBuf ,
} ,
#[ command(about = " List all available selectors " ) ]
Selectors {
#[ arg(
short,
long,
help = " A config file generated by the config-init command "
) ]
config : Option < PathBuf > ,
} ,
#[ command(name = " eval-model " , about = " Run an eval of model " ) ]
EvalModel {
#[ arg(short, long, help = " A serialized config file for the model only. " ) ]
config : String ,
} ,
#[ command(name = " exec-eval " , about = " run a single eval " ) ]
ExecEval {
#[ arg(short, long, help = " A serialized config file for the eval only. " ) ]
config : String ,
} ,
2025-05-21 15:00:13 -04:00
#[ command(
name = " generate-leaderboard " ,
about = " Generate a leaderboard CSV from benchmark results "
) ]
GenerateLeaderboard {
#[ arg(
short,
long,
help = " Path to the benchmark directory containing model evaluation results "
) ]
benchmark_dir : PathBuf ,
} ,
2025-04-08 14:43:43 -04:00
}
2025-04-09 18:57:24 -07:00
#[ derive(Subcommand) ]
enum RecipeCommand {
/// Validate a recipe file
2025-05-06 09:54:35 +10:00
#[ command(about = " Validate a recipe " ) ]
2025-04-09 18:57:24 -07:00
Validate {
2025-05-06 09:54:35 +10:00
/// Recipe name to get recipe file to validate
#[ arg(help = " recipe name to get recipe file or full path to the recipe file to validate " ) ]
recipe_name : String ,
2025-04-09 18:57:24 -07:00
} ,
/// Generate a deeplink for a recipe file
2025-05-06 09:54:35 +10:00
#[ command(about = " Generate a deeplink for a recipe " ) ]
2025-04-09 18:57:24 -07:00
Deeplink {
2025-05-06 09:54:35 +10:00
/// Recipe name to get recipe file to generate deeplink
#[ arg(
help = " recipe name to get recipe file or full path to the recipe file to generate deeplink "
) ]
recipe_name : String ,
2025-04-09 18:57:24 -07:00
} ,
}
2025-03-26 17:27:54 -07:00
#[ derive(Subcommand) ]
enum Command {
/// Configure Goose settings
#[ command(about = " Configure Goose settings " ) ]
Configure { } ,
/// Display Goose configuration information
#[ command(about = " Display Goose information " ) ]
Info {
/// Show verbose information including current configuration
#[ arg(short, long, help = " Show verbose information including config.yaml " ) ]
verbose : bool ,
} ,
/// Manage system prompts and behaviors
#[ command(about = " Run one of the mcp servers bundled with goose " ) ]
Mcp { name : String } ,
/// Start or resume interactive chat sessions
#[ command(
about = " Start or resume interactive chat sessions " ,
visible_alias = " s "
) ]
Session {
#[ command(subcommand) ]
command : Option < SessionCommand > ,
/// Identifier for the chat session
#[ command(flatten) ]
identifier : Option < Identifier > ,
/// Resume a previous session
#[ arg(
short,
long,
help = " Resume a previous session (last used or specified by --name) " ,
long_help = " Continue from a previous chat session. If --name or --path is provided, resumes that specific session. Otherwise resumes the last used session. "
) ]
resume : bool ,
2025-05-08 01:42:52 +02:00
/// Show message history when resuming
#[ arg(
long,
help = " Show previous messages when resuming a session " ,
requires = " resume "
) ]
history : bool ,
2025-03-26 17:27:54 -07:00
/// Enable debug output mode
#[ arg(
long,
help = " Enable debug output mode with full content and no truncation " ,
long_help = " When enabled, shows complete tool responses without truncation and full paths. "
) ]
debug : bool ,
2025-05-14 14:46:37 -07:00
/// Maximum number of consecutive identical tool calls allowed
#[ arg(
long = " max-tool-repetitions " ,
value_name = " NUMBER " ,
help = " Maximum number of consecutive identical tool calls allowed " ,
long_help = " Set a limit on how many times the same tool can be called consecutively with identical parameters. Helps prevent infinite loops. "
) ]
max_tool_repetitions : Option < u32 > ,
2025-03-26 17:27:54 -07:00
/// Add stdio extensions with environment variables and commands
#[ arg(
long = " with-extension " ,
value_name = " COMMAND " ,
help = " Add stdio extensions (can be specified multiple times) " ,
long_help = " Add stdio extensions from full commands with environment variables. Can be specified multiple times. Format: 'ENV1=val1 ENV2=val2 command args...' " ,
action = clap::ArgAction::Append
) ]
2025-04-09 18:57:24 -07:00
extensions : Vec < String > ,
2025-03-26 17:27:54 -07:00
2025-04-07 13:42:38 -07:00
/// Add remote extensions with a URL
#[ arg(
long = " with-remote-extension " ,
value_name = " URL " ,
help = " Add remote extensions (can be specified multiple times) " ,
long_help = " Add remote extensions from a URL. Can be specified multiple times. Format: 'url...' " ,
action = clap::ArgAction::Append
) ]
2025-04-09 18:57:24 -07:00
remote_extensions : Vec < String > ,
2025-04-07 13:42:38 -07:00
2025-03-26 17:27:54 -07:00
/// Add builtin extensions by name
#[ arg(
long = " with-builtin " ,
value_name = " NAME " ,
help = " Add builtin extensions by name (e.g., 'developer' or multiple: 'developer,github') " ,
long_help = " Add one or more builtin extensions that are bundled with goose by specifying their names, comma-separated " ,
value_delimiter = ','
) ]
2025-04-09 18:57:24 -07:00
builtins : Vec < String > ,
2025-03-26 17:27:54 -07:00
} ,
2025-05-13 09:05:10 +10:00
/// Open the last project directory
#[ command(about = " Open the last project directory " , visible_alias = " p " ) ]
Project { } ,
/// List recent project directories
#[ command(about = " List recent project directories " , visible_alias = " ps " ) ]
Projects ,
2025-03-26 17:27:54 -07:00
/// Execute commands from an instruction file
#[ command(about = " Execute commands from an instruction file or stdin " ) ]
Run {
/// Path to instruction file containing commands
#[ arg(
short,
long,
value_name = " FILE " ,
help = " Path to instruction file containing commands. Use - for stdin. " ,
2025-04-09 18:57:24 -07:00
conflicts_with = " input_text " ,
conflicts_with = " recipe "
2025-03-26 17:27:54 -07:00
) ]
instructions : Option < String > ,
/// Input text containing commands
#[ arg(
short = 't',
long = " text " ,
value_name = " TEXT " ,
help = " Input text to provide to Goose directly " ,
long_help = " Input text containing commands for Goose. Use this in lieu of the instructions argument. " ,
2025-04-09 18:57:24 -07:00
conflicts_with = " instructions " ,
conflicts_with = " recipe "
2025-03-26 17:27:54 -07:00
) ]
input_text : Option < String > ,
2025-05-06 09:54:35 +10:00
/// Recipe name or full path to the recipe file
2025-04-09 18:57:24 -07:00
#[ arg(
short = None,
long = " recipe " ,
2025-05-06 09:54:35 +10:00
value_name = " RECIPE_NAME or FULL_PATH_TO_RECIPE_FILE " ,
2025-05-29 00:24:30 +10:00
help = " Recipe name to get recipe file or the full path of the recipe file (use --explain to see recipe details) " ,
long_help = " Recipe name to get recipe file or the full path of the recipe file that defines a custom agent configuration. Use --explain to see the recipe's title, description, and parameters. " ,
2025-04-09 18:57:24 -07:00
conflicts_with = " instructions " ,
conflicts_with = " input_text "
) ]
recipe : Option < String > ,
2025-05-02 08:47:45 +10:00
#[ arg(
long,
value_name = " KEY=VALUE " ,
help = " Dynamic parameters (e.g., --params username=alice --params channel_name=goose-channel) " ,
long_help = " Key-value parameters to pass to the recipe file. Can be specified multiple times. " ,
action = clap::ArgAction::Append,
value_parser = parse_key_val,
) ]
params : Vec < ( String , String ) > ,
2025-03-26 17:27:54 -07:00
/// Continue in interactive mode after processing input
#[ arg(
short = 's',
long = " interactive " ,
help = " Continue in interactive mode after processing initial input "
) ]
interactive : bool ,
2025-05-13 09:48:34 -07:00
/// Run without storing a session file
#[ arg(
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. " ,
2025-05-14 18:01:02 +10:00
conflicts_with_all = [ " resume " , " name " , " path " ]
2025-05-13 09:48:34 -07:00
) ]
no_session : bool ,
2025-05-15 10:57:05 +10:00
/// Show the recipe title, description, and parameters
#[ arg(
long = " explain " ,
help = " Show the recipe title, description, and parameters "
) ]
explain : bool ,
2025-05-14 14:46:37 -07:00
/// Maximum number of consecutive identical tool calls allowed
#[ arg(
long = " max-tool-repetitions " ,
value_name = " NUMBER " ,
help = " Maximum number of consecutive identical tool calls allowed " ,
long_help = " Set a limit on how many times the same tool can be called consecutively with identical parameters. Helps prevent infinite loops. "
) ]
max_tool_repetitions : Option < u32 > ,
2025-03-26 17:27:54 -07:00
/// Identifier for this run session
#[ command(flatten) ]
identifier : Option < Identifier > ,
/// Resume a previous run
#[ arg(
short,
long,
action = clap::ArgAction::SetTrue,
help = " Resume from a previous run " ,
long_help = " Continue from a previous run, maintaining the execution state and context. "
) ]
resume : bool ,
/// Enable debug output mode
#[ arg(
long,
help = " Enable debug output mode with full content and no truncation " ,
long_help = " When enabled, shows complete tool responses without truncation and full paths. "
) ]
debug : bool ,
/// Add stdio extensions with environment variables and commands
#[ arg(
long = " with-extension " ,
value_name = " COMMAND " ,
help = " Add stdio extensions (can be specified multiple times) " ,
long_help = " Add stdio extensions from full commands with environment variables. Can be specified multiple times. Format: 'ENV1=val1 ENV2=val2 command args...' " ,
action = clap::ArgAction::Append
) ]
2025-04-09 18:57:24 -07:00
extensions : Vec < String > ,
2025-03-26 17:27:54 -07:00
2025-04-07 13:42:38 -07:00
/// Add remote extensions
#[ arg(
long = " with-remote-extension " ,
value_name = " URL " ,
help = " Add remote extensions (can be specified multiple times) " ,
long_help = " Add remote extensions. Can be specified multiple times. Format: 'url...' " ,
action = clap::ArgAction::Append
) ]
2025-04-09 18:57:24 -07:00
remote_extensions : Vec < String > ,
2025-04-07 13:42:38 -07:00
2025-03-26 17:27:54 -07:00
/// Add builtin extensions by name
#[ arg(
long = " with-builtin " ,
value_name = " NAME " ,
help = " Add builtin extensions by name (e.g., 'developer' or multiple: 'developer,github') " ,
long_help = " Add one or more builtin extensions that are bundled with goose by specifying their names, comma-separated " ,
value_delimiter = ','
) ]
2025-04-09 18:57:24 -07:00
builtins : Vec < String > ,
} ,
/// Recipe utilities for validation and deeplinking
#[ command(about = " Recipe utilities for validation and deeplinking " ) ]
Recipe {
#[ command(subcommand) ]
command : RecipeCommand ,
2025-03-26 17:27:54 -07:00
} ,
2025-05-27 10:36:27 -07:00
/// Manage scheduled jobs
#[ command(about = " Manage scheduled jobs " , visible_alias = " sched " ) ]
Schedule {
#[ command(subcommand) ]
command : SchedulerCommand ,
} ,
2025-03-26 17:27:54 -07:00
/// Update the Goose CLI version
#[ command(about = " Update the goose CLI version " ) ]
Update {
/// Update to canary version
#[ arg(
short,
long,
help = " Update to canary version " ,
long_help = " Update to the latest canary version of the goose CLI, otherwise updates to the latest stable version. "
) ]
canary : bool ,
/// Enforce to re-configure Goose during update
#[ arg(short, long, help = " Enforce to re-configure goose during update " ) ]
reconfigure : bool ,
} ,
Bench {
2025-04-08 14:43:43 -04:00
#[ command(subcommand) ]
cmd : BenchCommand ,
2025-03-26 17:27:54 -07:00
} ,
2025-06-05 13:32:57 +10:00
/// Start a web server with a chat interface
#[ command(about = " Start a web server with a chat interface " , hide = true) ]
Web {
/// Port to run the web server on
#[ arg(
short,
long,
default_value = " 3000 " ,
help = " Port to run the web server on "
) ]
port : u16 ,
/// Host to bind the web server to
#[ arg(
long,
default_value = " 127.0.0.1 " ,
help = " Host to bind the web server to "
) ]
host : String ,
/// Open browser automatically
#[ arg(long, help = " Open browser automatically when server starts " ) ]
open : bool ,
} ,
2025-03-26 17:27:54 -07:00
}
#[ derive(clap::ValueEnum, Clone, Debug) ]
enum CliProviderVariant {
OpenAi ,
Databricks ,
Ollama ,
}
2025-06-12 21:32:01 -04:00
#[ derive(Debug) ]
2025-04-09 18:57:24 -07:00
struct InputConfig {
contents : Option < String > ,
extensions_override : Option < Vec < ExtensionConfig > > ,
additional_system_prompt : Option < String > ,
}
2025-03-26 17:27:54 -07:00
pub async fn cli ( ) -> Result < ( ) > {
let cli = Cli ::parse ( ) ;
2025-05-13 09:05:10 +10:00
// Track the current directory in projects.json
if let Err ( e ) = crate ::project_tracker ::update_project_tracker ( None , None ) {
eprintln! ( " Warning: Failed to update project tracker: {} " , e ) ;
}
2025-03-26 17:27:54 -07:00
match cli . command {
Some ( Command ::Configure { } ) = > {
let _ = handle_configure ( ) . await ;
return Ok ( ( ) ) ;
}
Some ( Command ::Info { verbose } ) = > {
handle_info ( verbose ) ? ;
return Ok ( ( ) ) ;
}
Some ( Command ::Mcp { name } ) = > {
let _ = run_server ( & name ) . await ;
}
Some ( Command ::Session {
command ,
identifier ,
resume ,
2025-05-08 01:42:52 +02:00
history ,
2025-03-26 17:27:54 -07:00
debug ,
2025-05-14 14:46:37 -07:00
max_tool_repetitions ,
2025-04-09 18:57:24 -07:00
extensions ,
remote_extensions ,
builtins ,
2025-03-26 17:27:54 -07:00
} ) = > {
2025-04-08 14:43:43 -04:00
return match command {
2025-04-23 17:21:27 +02:00
Some ( SessionCommand ::List {
verbose ,
format ,
ascending ,
} ) = > {
handle_session_list ( verbose , format , ascending ) ? ;
2025-04-08 14:43:43 -04:00
Ok ( ( ) )
2025-03-26 17:27:54 -07:00
}
2025-04-22 15:51:33 -07:00
Some ( SessionCommand ::Remove { id , regex } ) = > {
handle_session_remove ( id , regex ) ? ;
return Ok ( ( ) ) ;
}
2025-06-04 23:27:21 +02:00
Some ( SessionCommand ::Export { identifier , output } ) = > {
let session_identifier = if let Some ( id ) = identifier {
extract_identifier ( id )
} else {
// If no identifier is provided, prompt for interactive selection
match crate ::commands ::session ::prompt_interactive_session_selection ( ) {
Ok ( id ) = > id ,
Err ( e ) = > {
eprintln! ( " Error: {} " , e ) ;
return Ok ( ( ) ) ;
}
}
} ;
crate ::commands ::session ::handle_session_export ( session_identifier , output ) ? ;
Ok ( ( ) )
}
2025-03-26 17:27:54 -07:00
None = > {
// Run session command by default
2025-05-02 08:47:45 +10:00
let mut session : crate ::Session = build_session ( SessionBuilderConfig {
2025-04-09 18:57:24 -07:00
identifier : identifier . map ( extract_identifier ) ,
2025-03-26 17:27:54 -07:00
resume ,
2025-05-13 09:48:34 -07:00
no_session : false ,
2025-04-09 18:57:24 -07:00
extensions ,
remote_extensions ,
builtins ,
extensions_override : None ,
additional_system_prompt : None ,
2025-06-12 21:32:01 -04:00
settings : None ,
2025-03-26 17:27:54 -07:00
debug ,
2025-05-14 14:46:37 -07:00
max_tool_repetitions ,
2025-06-12 14:46:56 +10:00
interactive : true , // Session command is always interactive
2025-04-09 18:57:24 -07:00
} )
2025-03-26 17:27:54 -07:00
. await ;
setup_logging (
session . session_file ( ) . file_stem ( ) . and_then ( | s | s . to_str ( ) ) ,
None ,
) ? ;
2025-05-08 01:42:52 +02:00
// Render previous messages if resuming a session and history flag is set
if resume & & history {
session . render_message_history ( ) ;
}
2025-03-26 17:27:54 -07:00
let _ = session . interactive ( None ) . await ;
2025-04-08 14:43:43 -04:00
Ok ( ( ) )
2025-03-26 17:27:54 -07:00
}
2025-04-08 14:43:43 -04:00
} ;
2025-03-26 17:27:54 -07:00
}
2025-05-13 09:05:10 +10:00
Some ( Command ::Project { } ) = > {
// Default behavior: offer to resume the last project
handle_project_default ( ) ? ;
return Ok ( ( ) ) ;
}
Some ( Command ::Projects ) = > {
// Interactive project selection
handle_projects_interactive ( ) ? ;
return Ok ( ( ) ) ;
}
2025-03-26 17:27:54 -07:00
Some ( Command ::Run {
instructions ,
input_text ,
2025-04-09 18:57:24 -07:00
recipe ,
2025-03-26 17:27:54 -07:00
interactive ,
identifier ,
resume ,
2025-05-13 09:48:34 -07:00
no_session ,
2025-03-26 17:27:54 -07:00
debug ,
2025-05-14 14:46:37 -07:00
max_tool_repetitions ,
2025-04-09 18:57:24 -07:00
extensions ,
remote_extensions ,
builtins ,
2025-05-02 08:47:45 +10:00
params ,
2025-05-15 10:57:05 +10:00
explain ,
2025-03-26 17:27:54 -07:00
} ) = > {
2025-06-12 21:32:01 -04:00
let ( input_config , session_settings ) = match ( instructions , input_text , recipe , explain )
{
2025-05-15 10:57:05 +10:00
( Some ( file ) , _ , _ , _ ) if file = = " - " = > {
2025-04-09 18:57:24 -07:00
let mut input = String ::new ( ) ;
2025-03-26 17:27:54 -07:00
std ::io ::stdin ( )
2025-04-09 18:57:24 -07:00
. read_to_string ( & mut input )
2025-03-26 17:27:54 -07:00
. expect ( " Failed to read from stdin " ) ;
2025-04-09 18:57:24 -07:00
2025-06-12 21:32:01 -04:00
(
InputConfig {
contents : Some ( input ) ,
extensions_override : None ,
additional_system_prompt : None ,
} ,
None ,
)
2025-03-26 17:27:54 -07:00
}
2025-05-15 10:57:05 +10:00
( Some ( file ) , _ , _ , _ ) = > {
2025-04-09 18:57:24 -07:00
let contents = std ::fs ::read_to_string ( & file ) . unwrap_or_else ( | err | {
eprintln! (
" Instruction file not found — did you mean to use goose run --text? \n {} " ,
err
) ;
std ::process ::exit ( 1 ) ;
} ) ;
2025-06-12 21:32:01 -04:00
(
InputConfig {
contents : Some ( contents ) ,
extensions_override : None ,
additional_system_prompt : None ,
} ,
None ,
)
}
( _ , Some ( text ) , _ , _ ) = > (
2025-04-09 18:57:24 -07:00
InputConfig {
2025-06-12 21:32:01 -04:00
contents : Some ( text ) ,
2025-04-09 18:57:24 -07:00
extensions_override : None ,
additional_system_prompt : None ,
2025-06-12 21:32:01 -04:00
} ,
None ,
) ,
2025-05-15 10:57:05 +10:00
( _ , _ , Some ( recipe_name ) , explain ) = > {
if explain {
explain_recipe_with_parameters ( & recipe_name , params ) ? ;
return Ok ( ( ) ) ;
}
2025-05-06 09:54:35 +10:00
let recipe =
2025-05-15 10:57:05 +10:00
load_recipe_as_template ( & recipe_name , params ) . unwrap_or_else ( | err | {
2025-05-06 09:54:35 +10:00
eprintln! ( " {} : {} " , console ::style ( " Error " ) . red ( ) . bold ( ) , err ) ;
std ::process ::exit ( 1 ) ;
} ) ;
2025-06-12 21:32:01 -04:00
(
InputConfig {
contents : recipe . prompt ,
extensions_override : recipe . extensions ,
additional_system_prompt : recipe . instructions ,
} ,
recipe . settings . map ( | s | SessionSettings {
goose_provider : s . goose_provider ,
goose_model : s . goose_model ,
temperature : s . temperature ,
} ) ,
)
2025-04-09 18:57:24 -07:00
}
2025-05-15 10:57:05 +10:00
( None , None , None , _ ) = > {
2025-04-11 11:34:46 -07:00
eprintln! ( " Error: Must provide either --instructions (-i), --text (-t), or --recipe. Use -i - for stdin. " ) ;
2025-03-26 17:27:54 -07:00
std ::process ::exit ( 1 ) ;
}
} ;
2025-04-09 18:57:24 -07:00
let mut session = build_session ( SessionBuilderConfig {
identifier : identifier . map ( extract_identifier ) ,
2025-03-26 17:27:54 -07:00
resume ,
2025-05-13 09:48:34 -07:00
no_session ,
2025-04-09 18:57:24 -07:00
extensions ,
remote_extensions ,
builtins ,
extensions_override : input_config . extensions_override ,
additional_system_prompt : input_config . additional_system_prompt ,
2025-06-12 21:32:01 -04:00
settings : session_settings ,
2025-03-26 17:27:54 -07:00
debug ,
2025-05-14 14:46:37 -07:00
max_tool_repetitions ,
2025-06-12 14:46:56 +10:00
interactive , // Use the interactive flag from the Run command
2025-04-09 18:57:24 -07:00
} )
2025-03-26 17:27:54 -07:00
. await ;
setup_logging (
session . session_file ( ) . file_stem ( ) . and_then ( | s | s . to_str ( ) ) ,
None ,
) ? ;
if interactive {
2025-04-11 11:34:33 -07:00
let _ = session . interactive ( input_config . contents ) . await ;
2025-04-09 18:57:24 -07:00
} else if let Some ( contents ) = input_config . contents {
2025-04-11 11:34:33 -07:00
let _ = session . headless ( contents ) . await ;
2025-04-09 18:57:24 -07:00
} else {
eprintln! ( " Error: no text provided for prompt in headless mode " ) ;
std ::process ::exit ( 1 ) ;
2025-03-26 17:27:54 -07:00
}
return Ok ( ( ) ) ;
}
2025-05-27 10:36:27 -07:00
Some ( Command ::Schedule { command } ) = > {
match command {
SchedulerCommand ::Add {
id ,
cron ,
recipe_source ,
} = > {
handle_schedule_add ( id , cron , recipe_source ) . await ? ;
}
SchedulerCommand ::List { } = > {
handle_schedule_list ( ) . await ? ;
}
SchedulerCommand ::Remove { id } = > {
handle_schedule_remove ( id ) . await ? ;
}
SchedulerCommand ::Sessions { id , limit } = > {
// New arm
handle_schedule_sessions ( id , limit ) . await ? ;
}
SchedulerCommand ::RunNow { id } = > {
// New arm
handle_schedule_run_now ( id ) . await ? ;
}
2025-06-10 08:17:45 -07:00
SchedulerCommand ::ServicesStatus { } = > {
handle_schedule_services_status ( ) . await ? ;
}
SchedulerCommand ::ServicesStop { } = > {
handle_schedule_services_stop ( ) . await ? ;
}
SchedulerCommand ::CronHelp { } = > {
handle_schedule_cron_help ( ) . await ? ;
}
2025-05-27 10:36:27 -07:00
}
return Ok ( ( ) ) ;
}
2025-03-26 17:27:54 -07:00
Some ( Command ::Update {
canary ,
reconfigure ,
} ) = > {
crate ::commands ::update ::update ( canary , reconfigure ) ? ;
return Ok ( ( ) ) ;
}
2025-04-08 14:43:43 -04:00
Some ( Command ::Bench { cmd } ) = > {
match cmd {
BenchCommand ::Selectors { config } = > BenchRunner ::list_selectors ( config ) ? ,
2025-05-01 11:00:54 -05:00
BenchCommand ::InitConfig { name } = > {
let mut config = BenchRunConfig ::default ( ) ;
let cwd =
std ::env ::current_dir ( ) . expect ( " Failed to get current working directory " ) ;
config . output_dir = Some ( cwd ) ;
config . save ( name ) ;
}
2025-04-08 14:43:43 -04:00
BenchCommand ::Run { config } = > BenchRunner ::new ( config ) ? . run ( ) ? ,
BenchCommand ::EvalModel { config } = > ModelRunner ::from ( config ) ? . run ( ) ? ,
BenchCommand ::ExecEval { config } = > {
EvalRunner ::from ( config ) ? . run ( agent_generator ) . await ?
2025-03-26 17:27:54 -07:00
}
2025-05-21 15:00:13 -04:00
BenchCommand ::GenerateLeaderboard { benchmark_dir } = > {
MetricAggregator ::generate_csv_from_benchmark_dir ( & benchmark_dir ) ?
}
2025-03-26 17:27:54 -07:00
}
return Ok ( ( ) ) ;
}
2025-04-09 18:57:24 -07:00
Some ( Command ::Recipe { command } ) = > {
match command {
2025-05-06 09:54:35 +10:00
RecipeCommand ::Validate { recipe_name } = > {
handle_validate ( & recipe_name ) ? ;
2025-04-09 18:57:24 -07:00
}
2025-05-06 09:54:35 +10:00
RecipeCommand ::Deeplink { recipe_name } = > {
handle_deeplink ( & recipe_name ) ? ;
2025-04-09 18:57:24 -07:00
}
}
return Ok ( ( ) ) ;
}
2025-06-05 13:32:57 +10:00
Some ( Command ::Web { port , host , open } ) = > {
crate ::commands ::web ::handle_web ( port , host , open ) . await ? ;
return Ok ( ( ) ) ;
}
2025-03-26 17:27:54 -07:00
None = > {
2025-04-08 14:43:43 -04:00
return if ! Config ::global ( ) . exists ( ) {
2025-03-26 17:27:54 -07:00
let _ = handle_configure ( ) . await ;
2025-04-08 14:43:43 -04:00
Ok ( ( ) )
2025-03-26 17:27:54 -07:00
} else {
// Run session command by default
2025-05-13 09:48:34 -07:00
let mut session = build_session ( SessionBuilderConfig {
identifier : None ,
resume : false ,
no_session : false ,
extensions : Vec ::new ( ) ,
remote_extensions : Vec ::new ( ) ,
builtins : Vec ::new ( ) ,
extensions_override : None ,
additional_system_prompt : None ,
2025-06-12 21:32:01 -04:00
settings : None ::< SessionSettings > ,
2025-05-13 09:48:34 -07:00
debug : false ,
2025-05-14 14:46:37 -07:00
max_tool_repetitions : None ,
2025-06-12 14:46:56 +10:00
interactive : true , // Default case is always interactive
2025-05-13 09:48:34 -07:00
} )
. await ;
2025-03-26 17:27:54 -07:00
setup_logging (
session . session_file ( ) . file_stem ( ) . and_then ( | s | s . to_str ( ) ) ,
None ,
) ? ;
2025-04-11 14:16:38 -05:00
if let Err ( e ) = session . interactive ( None ) . await {
eprintln! ( " Session ended with error: {} " , e ) ;
}
2025-04-08 14:43:43 -04:00
Ok ( ( ) )
} ;
2025-03-26 17:27:54 -07:00
}
}
Ok ( ( ) )
}