use crate::args::{Cli, Commands, ConfigAction};
use crate::commands;
use crate::commands::interactive::{CliOnboarding, check_config_status};
use crate::console::CliConsole;
use crate::ui;
use sage_core::config::credential::ConfigStatus;
use sage_core::error::{SageError, SageResult};
use std::io::{IsTerminal, Read};
pub async fn route(cli: Cli) -> SageResult<()> {
if let Some(command) = &cli.command {
return match command {
Commands::Config { action } => route_config(action.clone()).await,
Commands::Tools => commands::tools::show_tools().await,
Commands::Doctor { config_file } => commands::diagnostics::doctor(config_file).await,
Commands::Status { config_file } => commands::diagnostics::status(config_file).await,
Commands::Usage {
session_dir,
detailed,
} => commands::diagnostics::usage(session_dir.as_deref(), *detailed).await,
};
}
route_main(cli).await
}
async fn route_main(mut cli: Cli) -> SageResult<()> {
sage_core::ui::init_icons();
let is_tty = std::io::stdin().is_terminal();
if is_tty && !cli.print_mode {
let (config_status, _status_hint) = check_config_status();
if config_status == ConfigStatus::Unconfigured {
let console = CliConsole::new(true);
let mut onboarding = CliOnboarding::new();
match onboarding.run().await {
Ok(true) => {
console.success("Setup complete! Starting sage...");
}
Ok(false) => {
console.warn("Setup incomplete. You can run /login anytime to configure.");
}
Err(e) => {
console.warn(&format!("Setup error: {}. Continuing anyway.", e));
}
}
}
}
if !is_tty && cli.task.is_none() {
let mut input = String::new();
if std::io::stdin().read_to_string(&mut input).is_ok() {
let trimmed = input.trim();
if !trimmed.is_empty() {
cli.task = Some(trimmed.to_string());
}
}
}
let non_interactive = cli.print_mode || !is_tty;
tracing::debug!(
"TTY detection: is_tty={}, non_interactive={}",
is_tty,
non_interactive
);
if non_interactive
&& cli.task.is_none()
&& !cli.continue_session
&& cli.resume_session.is_none()
{
return Err(SageError::invalid_input(
"No task provided. Supply a task argument, pipe input, or use `-c` / `-r` to resume.",
));
}
let should_use_unified = non_interactive
|| cli.task.is_some()
|| cli.continue_session
|| cli.resume_session.is_some();
if should_use_unified {
return commands::unified_execute(commands::UnifiedArgs {
task: cli.task,
config_file: cli.config_file,
working_dir: cli.working_dir,
max_steps: cli.max_steps,
verbose: cli.verbose,
non_interactive,
resume_session_id: cli.resume_session,
continue_recent: cli.continue_session,
stream_json: cli.stream_json,
output_mode: cli.output_mode,
})
.await;
}
ui::run_rnk_app_with_cli(&cli)
.await
.map_err(|e| sage_core::error::SageError::Io {
message: e.to_string(),
path: None,
context: Some("Running rnk App mode".to_string()),
})
}
async fn route_config(action: ConfigAction) -> SageResult<()> {
match action {
ConfigAction::Show { config_file } => commands::config::show(&config_file).await,
ConfigAction::Validate { config_file } => commands::config::validate(&config_file).await,
ConfigAction::Init { config_file, force } => {
commands::config::init(&config_file, force).await
}
}
}