use std::sync::Arc;
use crate::brain::agent::{ApprovalCallback, ProgressCallback, ProgressEvent, ToolApprovalInfo};
pub(crate) fn cli_progress_callback() -> ProgressCallback {
Arc::new(|_session_id, event| match event {
ProgressEvent::ToolStarted { tool_name, .. } => {
println!("🔧 {tool_name}");
}
ProgressEvent::ToolCompleted {
tool_name,
success,
summary,
..
} => {
let icon = if success { "✅" } else { "❌" };
println!("{icon} {tool_name}: {summary}");
}
ProgressEvent::IntermediateText { text, .. } => {
let trimmed = text.trim();
if !trimmed.is_empty() {
println!("{trimmed}");
}
}
_ => {}
})
}
pub(crate) fn approval_from_answer(answer: &str) -> (bool, bool) {
let a = answer.trim().to_lowercase();
let always = matches!(a.as_str(), "a" | "always");
let approved = always || matches!(a.as_str(), "y" | "yes");
(approved, always)
}
pub(crate) fn stdin_approval_callback() -> ApprovalCallback {
Arc::new(|info: ToolApprovalInfo| {
Box::pin(async move {
let prompt = format!(
"\n⚠️ Tool '{}' wants to run (capabilities: {}). Approve? [y/N/a=always] ",
info.tool_name,
if info.capabilities.is_empty() {
"none".to_string()
} else {
info.capabilities.join(", ")
}
);
let answer = tokio::task::spawn_blocking(move || {
use std::io::{self, Write};
print!("{prompt}");
let _ = io::stdout().flush();
let mut line = String::new();
let _ = io::stdin().read_line(&mut line);
line
})
.await
.unwrap_or_default();
Ok(approval_from_answer(&answer))
})
})
}