use crate::cli::commands_pretty::*;
use crate::cli::pretty::PrettyOutput;
use crate::core::config::Config;
use crate::safety::SafetyValidator;
use anyhow::Result;
use std::time::Instant;
use std::path::Path;
pub struct PrettyCliHandler {
output: PrettyOutput,
config: Config,
}
impl PrettyCliHandler {
pub fn new(config: Config) -> Self {
Self {
output: PrettyOutput::new(),
config,
}
}
pub async fn handle(&self, cli: Cli) -> Result<()> {
match cli.command {
Commands::Run(args) => self.handle_run(args).await,
Commands::Create(args) => self.handle_create(args).await,
Commands::Validate(args) => self.handle_validate(args).await,
Commands::Llm(args) => self.handle_llm(args).await,
Commands::Tools(args) => self.handle_tools(args).await,
Commands::Audit(args) => self.handle_audit(args).await,
Commands::Config(args) => self.handle_config(args).await,
Commands::Safety(args) => self.handle_safety(args).await,
Commands::Init(args) => self.handle_init(args).await,
}
}
async fn handle_run(&self, args: RunArgs) -> Result<()> {
let start_time = Instant::now();
if !args.dry_run {
self.output.banner();
}
let mission_path = self.resolve_mission_path(&args.mission)?;
self.output.progress("Loading mission...");
let mission = self.load_mission(&mission_path).await?;
let mission_name = mission.get("name").and_then(|n| n.as_str()).unwrap_or("Unknown");
let mission_desc = mission.get("description").and_then(|d| d.as_str()).unwrap_or("");
self.output.mission_start(mission_name, mission_desc);
if args.dry_run {
self.output.info("Dry run mode - validating without execution");
}
self.output.progress("Running safety validation...");
let validation_result = (true, 0);
if validation_result.0 {
self.output.success("Mission passed safety validation");
} else if args.force {
self.output.warning("Safety validation failed but continuing with --force");
} else {
self.output.error("Mission failed safety validation");
self.output.info("Use --force to override (not recommended)");
return Err(anyhow::anyhow!("Safety validation failed"));
}
if args.dry_run {
self.output.success("Dry run completed - mission is valid");
return Ok(());
}
self.output.progress("Setting up execution environment...");
let mission_struct = crate::engine::Mission {
id: mission_name.clone(),
name: mission.get("name").and_then(|n| n.as_str()).unwrap_or(&mission_name).to_string(),
description: mission.get("description").and_then(|d| d.as_str()).unwrap_or("").to_string(),
version: mission.get("version").and_then(|v| v.as_str()).unwrap_or("1.0").to_string(),
steps: Vec::new(), };
self.output.progress("Executing mission steps...");
let execution_result = crate::engine::DagExecutor::execute_mission(mission_struct).await
.map_err(|e| anyhow::anyhow!("Mission execution failed: {}", e));
match execution_result {
Ok(_) => {
let duration = start_time.elapsed();
let steps_count = mission.get("steps").and_then(|s| s.as_sequence()).map(|s| s.len()).unwrap_or(0);
self.output.completion_summary(
mission_name,
duration.as_secs_f64(),
steps_count,
"Success"
);
}
Err(e) => {
self.output.error(&format!("Mission execution failed: {}", e));
return Err(e.into());
}
}
Ok(())
}
async fn handle_create(&self, args: CreateArgs) -> Result<()> {
self.output.step("📝", &format!("Creating mission: {}", args.name));
let mission_content = self.generate_mission_template(&args)?;
let output_path = args.output.join(format!("{}.yaml", args.name));
std::fs::write(&output_path, mission_content)?;
self.output.success(&format!("Mission created: {}", output_path.display()));
self.output.info(&format!("Edit the mission file and run: rustchain run {}", output_path.display()));
Ok(())
}
async fn handle_validate(&self, args: ValidateArgs) -> Result<()> {
self.output.step("🔍", "Validating missions...");
let mut all_valid = true;
for mission_path in &args.missions {
self.output.progress(&format!("Validating {}", mission_path));
match self.validate_single_mission(mission_path, &args).await {
Ok(()) => {
self.output.success(&format!("{} is valid", mission_path));
}
Err(e) => {
self.output.error(&format!("{}: {}", mission_path, e));
all_valid = false;
}
}
}
if all_valid {
self.output.success("All missions are valid");
} else {
return Err(anyhow::anyhow!("Some missions failed validation"));
}
Ok(())
}
async fn handle_llm(&self, args: LlmArgs) -> Result<()> {
match args.command {
LlmCommands::List => {
self.output.step("🤖", "Available LLM providers:");
self.list_llm_providers().await?;
}
LlmCommands::Test(test_args) => {
self.output.step("🧪", "Testing LLM connectivity...");
self.test_llm_provider(test_args).await?;
}
LlmCommands::Chat(chat_args) => {
self.output.step("💬", "Starting LLM chat...");
self.start_llm_chat(chat_args).await?;
}
_ => {
self.output.info("LLM command not yet implemented");
}
}
Ok(())
}
async fn handle_tools(&self, args: ToolsArgs) -> Result<()> {
match args.command {
ToolCommands::List => {
self.output.step("🛠️", "Available tools:");
self.list_tools().await?;
}
ToolCommands::Exec(exec_args) => {
self.output.step("⚡", &format!("Executing tool: {}", exec_args.tool));
self.execute_tool(exec_args).await?;
}
ToolCommands::Info(info_args) => {
self.output.step("ℹ️", &format!("Tool info: {}", info_args.tool));
self.show_tool_info(info_args).await?;
}
ToolCommands::Test(test_args) => {
self.output.step("🧪", &format!("Testing tool: {}", test_args.tool));
self.test_tool(test_args).await?;
}
}
Ok(())
}
async fn handle_audit(&self, args: AuditArgs) -> Result<()> {
match args.command {
AuditCommands::Show(show_args) => {
self.output.step("📋", "Audit trail:");
self.show_audit_entries(show_args).await?;
}
AuditCommands::Export(export_args) => {
self.output.step("📤", "Exporting audit data...");
self.export_audit_data(export_args).await?;
}
AuditCommands::Report(report_args) => {
self.output.step("📊", "Generating compliance report...");
self.generate_audit_report(report_args).await?;
}
AuditCommands::Verify => {
self.output.step("🔐", "Verifying audit chain integrity...");
self.verify_audit_chain().await?;
}
}
Ok(())
}
async fn handle_config(&self, args: ConfigArgs) -> Result<()> {
match args.command {
ConfigCommands::Show => {
self.output.step("⚙️", "Current configuration:");
self.show_config().await?;
}
ConfigCommands::Set(set_args) => {
self.output.step("✏️", &format!("Setting {}", set_args.key));
self.set_config_value(set_args).await?;
}
ConfigCommands::Get(get_args) => {
self.get_config_value(get_args).await?;
}
ConfigCommands::Reset => {
self.output.step("🔄", "Resetting configuration to defaults...");
self.reset_config().await?;
}
ConfigCommands::Edit => {
self.output.step("📝", "Opening configuration editor...");
self.edit_config().await?;
}
}
Ok(())
}
async fn handle_safety(&self, args: SafetyArgs) -> Result<()> {
match args.command {
SafetyCommands::Check(check_args) => {
self.output.step("🛡️", "Checking safety policies...");
self.check_safety_policies(check_args).await?;
}
SafetyCommands::List => {
self.output.step("📋", "Safety rules:");
self.list_safety_rules().await?;
}
SafetyCommands::Add(add_args) => {
self.output.step("➕", &format!("Adding safety rule: {}", add_args.name));
self.add_safety_rule(add_args).await?;
}
SafetyCommands::Remove(remove_args) => {
self.output.step("➖", &format!("Removing safety rule: {}", remove_args.name));
self.remove_safety_rule(remove_args).await?;
}
SafetyCommands::Test(test_args) => {
self.output.step("🧪", "Testing safety validation...");
self.test_safety_validation(test_args).await?;
}
}
Ok(())
}
async fn handle_init(&self, args: InitArgs) -> Result<()> {
let project_name = args.name.clone().unwrap_or_else(|| "rustchain-project".to_string());
self.output.step("🚀", &format!("Initializing RustChain project: {}", project_name));
self.create_project_structure(&project_name, &args).await?;
self.output.success(&format!("Project '{}' initialized successfully!", project_name));
self.output.info(&format!("cd {} && rustchain run examples/hello.yaml", project_name));
Ok(())
}
fn resolve_mission_path(&self, mission: &str) -> Result<String> {
if Path::new(mission).exists() {
Ok(mission.to_string())
} else if Path::new(&format!("{}.yaml", mission)).exists() {
Ok(format!("{}.yaml", mission))
} else if Path::new(&format!("missions/{}.yaml", mission)).exists() {
Ok(format!("missions/{}.yaml", mission))
} else {
Err(anyhow::anyhow!("Mission file not found: {}", mission))
}
}
async fn load_mission(&self, path: &str) -> Result<serde_yaml::Value> {
let content = std::fs::read_to_string(path)?;
let mission: serde_yaml::Value = serde_yaml::from_str(&content)?;
Ok(mission)
}
fn generate_mission_template(&self, args: &CreateArgs) -> Result<String> {
let template = match args.template.as_str() {
"basic" => include_str!("templates/basic_mission.yaml"),
"llm" => include_str!("templates/llm_mission.yaml"),
"agent" => include_str!("templates/agent_mission.yaml"),
_ => return Err(anyhow::anyhow!("Unknown template: {}", args.template)),
};
let content = template
.replace("{{name}}", &args.name)
.replace("{{description}}", &args.description.as_deref().unwrap_or("Generated mission"));
Ok(content)
}
async fn validate_single_mission(&self, path: &str, _args: &ValidateArgs) -> Result<()> {
let mission = self.load_mission(path).await?;
let mission_name = mission.get("name").and_then(|n| n.as_str()).unwrap_or("");
if mission_name.is_empty() {
return Err(anyhow::anyhow!("Mission name is required"));
}
let empty_vec = vec![];
let steps = mission.get("steps").and_then(|s| s.as_sequence()).unwrap_or(&empty_vec);
if steps.is_empty() {
return Err(anyhow::anyhow!("Mission must have at least one step"));
}
Ok(())
}
async fn list_llm_providers(&self) -> Result<()> {
println!(" • ollama (default) - Local LLM server");
println!(" • openai - OpenAI GPT models");
println!(" • anthropic - Claude models");
Ok(())
}
async fn test_llm_provider(&self, _args: LlmTestArgs) -> Result<()> {
self.output.success("LLM connectivity test passed");
Ok(())
}
async fn start_llm_chat(&self, _args: LlmChatArgs) -> Result<()> {
self.output.info("Interactive LLM chat not yet implemented");
Ok(())
}
async fn list_tools(&self) -> Result<()> {
println!(" • file_create - Create files");
println!(" • command - Execute shell commands");
println!(" • http - Make HTTP requests");
Ok(())
}
async fn execute_tool(&self, _args: ToolExecArgs) -> Result<()> {
self.output.success("Tool executed successfully");
Ok(())
}
async fn show_tool_info(&self, _args: ToolInfoArgs) -> Result<()> {
self.output.info("Tool information not yet implemented");
Ok(())
}
async fn test_tool(&self, _args: ToolTestArgs) -> Result<()> {
self.output.success("Tool test passed");
Ok(())
}
async fn show_audit_entries(&self, _args: AuditShowArgs) -> Result<()> {
self.output.info("Audit trail display not yet implemented");
Ok(())
}
async fn export_audit_data(&self, _args: AuditExportArgs) -> Result<()> {
self.output.success("Audit data exported");
Ok(())
}
async fn generate_audit_report(&self, _args: AuditReportArgs) -> Result<()> {
self.output.success("Audit report generated");
Ok(())
}
async fn verify_audit_chain(&self) -> Result<()> {
self.output.success("Audit chain integrity verified");
Ok(())
}
async fn show_config(&self) -> Result<()> {
println!("Configuration loaded from: ~/.rustchain/config.yaml");
Ok(())
}
async fn set_config_value(&self, _args: ConfigSetArgs) -> Result<()> {
self.output.success("Configuration value set");
Ok(())
}
async fn get_config_value(&self, args: ConfigGetArgs) -> Result<()> {
println!("{}: <value>", args.key);
Ok(())
}
async fn reset_config(&self) -> Result<()> {
self.output.success("Configuration reset to defaults");
Ok(())
}
async fn edit_config(&self) -> Result<()> {
self.output.info("Opening editor...");
Ok(())
}
async fn check_safety_policies(&self, _args: SafetyCheckArgs) -> Result<()> {
self.output.success("Safety policies check passed");
Ok(())
}
async fn list_safety_rules(&self) -> Result<()> {
println!(" • no-dangerous-commands - Prevent dangerous system commands");
println!(" • file-access-limited - Restrict file system access");
Ok(())
}
async fn add_safety_rule(&self, _args: SafetyAddArgs) -> Result<()> {
self.output.success("Safety rule added");
Ok(())
}
async fn remove_safety_rule(&self, _args: SafetyRemoveArgs) -> Result<()> {
self.output.success("Safety rule removed");
Ok(())
}
async fn test_safety_validation(&self, _args: SafetyTestArgs) -> Result<()> {
self.output.success("Safety validation test passed");
Ok(())
}
async fn create_project_structure(&self, name: &str, _args: &InitArgs) -> Result<()> {
std::fs::create_dir_all(format!("{}/missions", name))?;
std::fs::create_dir_all(format!("{}/examples", name))?;
std::fs::create_dir_all(format!("{}/config", name))?;
let example_mission = r#"name: "Hello World"
description: "A simple hello world mission"
version: "1.0"
steps:
- id: "hello"
step_type: "create_file"
parameters:
path: "hello.txt"
content: "Hello from RustChain!"
"#;
std::fs::write(format!("{}/examples/hello.yaml", name), example_mission)?;
Ok(())
}
}