//! Aphoria CLI - A code-level truth linter powered by Episteme //! //! CLI binaries use println! for user-facing output (not tracing) #![allow(clippy::print_stdout, clippy::print_stderr)] use std::process::ExitCode; use clap::Parser; use tracing_subscriber::EnvFilter; use aphoria::AphoriaConfig; mod cli; mod handlers; use cli::Cli; #[tokio::main] async fn main() -> ExitCode { let cli = Cli::parse(); // Initialize tracing only if verbose or RUST_LOG is set // Default: silent (clean CLI output) if cli.verbose || std::env::var("RUST_LOG").is_ok() { let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("aphoria=info")); tracing_subscriber::fmt().with_env_filter(filter).init(); } // Load configuration let config = match load_config(cli.config.as_deref()) { Ok(cfg) => cfg, Err(e) => { eprintln!("Error loading configuration: {e}"); return ExitCode::from(3); } }; handlers::handle_command(cli.command, &config).await } /// Load configuration from file or use defaults. fn load_config(path: Option<&std::path::Path>) -> Result { if let Some(p) = path { AphoriaConfig::from_file(p) } else { // Try default locations let default_paths = ["aphoria.toml", ".aphoria/config.toml"]; for default in default_paths { let p = std::path::Path::new(default); if p.exists() { return AphoriaConfig::from_file(p); } } // No config file found, use defaults Ok(AphoriaConfig::default()) } }