use std::path::{Path, PathBuf};
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
use crate::types::config::{Config, ConsensusRule};
use crate::TetradResult;
pub fn run_interactive_config(config_path: &Path) -> TetradResult<()> {
let theme = ColorfulTheme::default();
println!("\n🔧 Configuração Interativa do Tetrad\n");
let mut config = if config_path.exists() {
Config::load(config_path)?
} else {
println!("Criando nova configuração...\n");
Config::default_config()
};
loop {
let options = vec![
"Configurações Gerais",
"Executores (Codex, Gemini, Qwen)",
"Consenso",
"ReasoningBank",
"Cache",
"Salvar e Sair",
"Sair sem Salvar",
];
let selection = Select::with_theme(&theme)
.with_prompt("O que deseja configurar?")
.items(&options)
.default(0)
.interact()?;
match selection {
0 => configure_general(&theme, &mut config)?,
1 => configure_executors(&theme, &mut config)?,
2 => configure_consensus(&theme, &mut config)?,
3 => configure_reasoning(&theme, &mut config)?,
4 => configure_cache(&theme, &mut config)?,
5 => {
config.save(config_path)?;
println!("\n✓ Configuração salva em: {}\n", config_path.display());
break;
}
6 => {
if Confirm::with_theme(&theme)
.with_prompt("Deseja realmente sair sem salvar?")
.default(false)
.interact()?
{
println!("\nSaindo sem salvar.\n");
break;
}
}
_ => {}
}
}
Ok(())
}
fn configure_general(theme: &ColorfulTheme, config: &mut Config) -> TetradResult<()> {
println!("\n📋 Configurações Gerais\n");
let log_levels = vec!["error", "warn", "info", "debug", "trace"];
let current_idx = log_levels
.iter()
.position(|&l| l == config.general.log_level)
.unwrap_or(2);
let log_level_idx = Select::with_theme(theme)
.with_prompt("Nível de log")
.items(&log_levels)
.default(current_idx)
.interact()?;
config.general.log_level = log_levels[log_level_idx].to_string();
let log_formats = vec!["text", "json"];
let current_format_idx = log_formats
.iter()
.position(|&f| f == config.general.log_format)
.unwrap_or(0);
let log_format_idx = Select::with_theme(theme)
.with_prompt("Formato de log")
.items(&log_formats)
.default(current_format_idx)
.interact()?;
config.general.log_format = log_formats[log_format_idx].to_string();
let timeout: u64 = Input::with_theme(theme)
.with_prompt("Timeout geral (segundos)")
.default(config.general.timeout_secs)
.interact_text()?;
config.general.timeout_secs = timeout;
println!("\n✓ Configurações gerais atualizadas.\n");
Ok(())
}
fn configure_executors(theme: &ColorfulTheme, config: &mut Config) -> TetradResult<()> {
println!("\n🤖 Configuração dos Executores\n");
let executors = vec!["Codex", "Gemini", "Qwen", "Voltar"];
loop {
let selection = Select::with_theme(theme)
.with_prompt("Qual executor configurar?")
.items(&executors)
.default(0)
.interact()?;
match selection {
0 => configure_single_executor(theme, "Codex", &mut config.executors.codex)?,
1 => configure_single_executor(theme, "Gemini", &mut config.executors.gemini)?,
2 => configure_single_executor(theme, "Qwen", &mut config.executors.qwen)?,
3 => break,
_ => {}
}
}
Ok(())
}
fn configure_single_executor(
theme: &ColorfulTheme,
name: &str,
executor: &mut crate::types::config::ExecutorConfig,
) -> TetradResult<()> {
println!("\n⚙️ Configurando {}\n", name);
executor.enabled = Confirm::with_theme(theme)
.with_prompt(format!("{} habilitado?", name))
.default(executor.enabled)
.interact()?;
if !executor.enabled {
println!("{} desabilitado.\n", name);
return Ok(());
}
let command: String = Input::with_theme(theme)
.with_prompt("Comando")
.default(executor.command.clone())
.interact_text()?;
executor.command = command;
let args_str: String = Input::with_theme(theme)
.with_prompt("Argumentos (separados por espaço)")
.default(executor.args.join(" "))
.interact_text()?;
executor.args = args_str.split_whitespace().map(String::from).collect();
let timeout: u64 = Input::with_theme(theme)
.with_prompt("Timeout (segundos)")
.default(executor.timeout_secs)
.interact_text()?;
executor.timeout_secs = timeout;
let weight: u8 = Input::with_theme(theme)
.with_prompt("Peso no consenso (1-10)")
.default(executor.weight)
.interact_text()?;
executor.weight = weight.clamp(1, 10);
println!("\n✓ {} configurado.\n", name);
Ok(())
}
fn configure_consensus(theme: &ColorfulTheme, config: &mut Config) -> TetradResult<()> {
println!("\n🤝 Configuração de Consenso\n");
let rules = vec![
"Golden (unanimidade)",
"Strong (3/3 ou 2/3 com alta confiança)",
"Weak (maioria simples)",
];
let current_idx = match config.consensus.default_rule {
ConsensusRule::Golden => 0,
ConsensusRule::Strong => 1,
ConsensusRule::Weak => 2,
};
let rule_idx = Select::with_theme(theme)
.with_prompt("Regra de consenso padrão")
.items(&rules)
.default(current_idx)
.interact()?;
config.consensus.default_rule = match rule_idx {
0 => ConsensusRule::Golden,
1 => ConsensusRule::Strong,
_ => ConsensusRule::Weak,
};
let min_score: u8 = Input::with_theme(theme)
.with_prompt("Score mínimo para aprovação (0-100)")
.default(config.consensus.min_score)
.interact_text()?;
config.consensus.min_score = min_score.min(100);
let max_loops: u8 = Input::with_theme(theme)
.with_prompt("Número máximo de loops de refinamento")
.default(config.consensus.max_loops)
.interact_text()?;
config.consensus.max_loops = max_loops;
println!("\n✓ Consenso configurado.\n");
Ok(())
}
fn configure_reasoning(theme: &ColorfulTheme, config: &mut Config) -> TetradResult<()> {
println!("\n🧠 Configuração do ReasoningBank\n");
config.reasoning.enabled = Confirm::with_theme(theme)
.with_prompt("ReasoningBank habilitado?")
.default(config.reasoning.enabled)
.interact()?;
if !config.reasoning.enabled {
println!("ReasoningBank desabilitado.\n");
return Ok(());
}
let db_path: String = Input::with_theme(theme)
.with_prompt("Caminho do banco de dados")
.default(config.reasoning.db_path.display().to_string())
.interact_text()?;
config.reasoning.db_path = PathBuf::from(db_path);
let max_patterns: usize = Input::with_theme(theme)
.with_prompt("Máximo de patterns por consulta")
.default(config.reasoning.max_patterns_per_query)
.interact_text()?;
config.reasoning.max_patterns_per_query = max_patterns;
let consolidation_interval: usize = Input::with_theme(theme)
.with_prompt("Intervalo de consolidação (avaliações)")
.default(config.reasoning.consolidation_interval)
.interact_text()?;
config.reasoning.consolidation_interval = consolidation_interval;
println!("\n✓ ReasoningBank configurado.\n");
Ok(())
}
fn configure_cache(theme: &ColorfulTheme, config: &mut Config) -> TetradResult<()> {
println!("\n💾 Configuração do Cache\n");
config.cache.enabled = Confirm::with_theme(theme)
.with_prompt("Cache habilitado?")
.default(config.cache.enabled)
.interact()?;
if !config.cache.enabled {
println!("Cache desabilitado.\n");
return Ok(());
}
let capacity: usize = Input::with_theme(theme)
.with_prompt("Capacidade máxima (número de entradas)")
.default(config.cache.capacity)
.interact_text()?;
config.cache.capacity = capacity;
let ttl: u64 = Input::with_theme(theme)
.with_prompt("Tempo de vida (segundos)")
.default(config.cache.ttl_secs)
.interact_text()?;
config.cache.ttl_secs = ttl;
println!("\n✓ Cache configurado.\n");
Ok(())
}
pub fn show_config_summary(config: &Config) {
println!("\n📊 Resumo da Configuração\n");
println!("┌─────────────────────────────────────────┐");
println!("│ Geral │");
println!("├─────────────────────────────────────────┤");
println!("│ Log level: {:<28} │", config.general.log_level);
println!("│ Timeout: {:<29}s │", config.general.timeout_secs);
println!("├─────────────────────────────────────────┤");
println!("│ Executores │");
println!("├─────────────────────────────────────────┤");
println!(
"│ Codex: {} ({:<26}) │",
if config.executors.codex.enabled {
"✓"
} else {
"✗"
},
config.executors.codex.command
);
println!(
"│ Gemini: {} ({:<26}) │",
if config.executors.gemini.enabled {
"✓"
} else {
"✗"
},
config.executors.gemini.command
);
println!(
"│ Qwen: {} ({:<26}) │",
if config.executors.qwen.enabled {
"✓"
} else {
"✗"
},
config.executors.qwen.command
);
println!("├─────────────────────────────────────────┤");
println!("│ Consenso │");
println!("├─────────────────────────────────────────┤");
println!(
"│ Regra: {:<32} │",
format!("{:?}", config.consensus.default_rule)
);
println!("│ Score mínimo: {:<25} │", config.consensus.min_score);
println!("│ Max loops: {:<28} │", config.consensus.max_loops);
println!("├─────────────────────────────────────────┤");
println!("│ ReasoningBank │");
println!("├─────────────────────────────────────────┤");
println!(
"│ Habilitado: {:<27} │",
if config.reasoning.enabled {
"Sim"
} else {
"Não"
}
);
if config.reasoning.enabled {
println!(
"│ Consolidação: a cada {:<17} │",
format!("{} avaliações", config.reasoning.consolidation_interval)
);
}
println!("├─────────────────────────────────────────┤");
println!("│ Cache │");
println!("├─────────────────────────────────────────┤");
println!(
"│ Habilitado: {:<27} │",
if config.cache.enabled { "Sim" } else { "Não" }
);
if config.cache.enabled {
println!("│ Capacidade: {:<27} │", config.cache.capacity);
println!("│ TTL: {:<33}s │", config.cache.ttl_secs);
}
println!("└─────────────────────────────────────────┘");
println!();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_show_config_summary() {
let config = Config::default_config();
show_config_summary(&config);
}
}