use crate::brain::mission_control::{
activity_service, analytics_service, inbox_service, schedule_service,
};
use crate::brain::tools::dynamic::DynamicToolLoader;
use crate::brain::tools::rsi_proposals::RsiProposalsTool;
use crate::tui::app::App;
use crate::tui::app::mission_control::McPanel;
use crate::tui::events::AppMode;
const ACTIVITY_LIMIT: usize = 100;
pub async fn open(app: &mut App) {
if app.mode == AppMode::MissionControl {
return;
}
app.mode = AppMode::MissionControl;
refresh(app).await;
}
pub async fn refresh(app: &mut App) {
app.mc.activity = activity_service::recent(ACTIVITY_LIMIT);
let pool = app.agent_service.context().pool();
app.mc.schedule = schedule_service::list(pool.clone()).await;
app.mc.analytics = analytics_service::summary(pool).await;
}
pub async fn apply_selected(app: &mut App) {
if app.mc.focused_panel != McPanel::Inbox {
return;
}
let items = inbox_service::list();
let Some(item) = items.get(app.mc.selected_index).cloned() else {
return;
};
let tool = match build_proposals_tool(app) {
Some(t) => t,
None => {
notify(app, "Apply failed: tools.toml path unavailable");
return;
}
};
let result = match item.kind {
crate::brain::mission_control::McInboxKind::ProposedTool => tool.apply_tool(&item.id),
crate::brain::mission_control::McInboxKind::ProposedCommand => tool.apply_command(&item.id),
crate::brain::mission_control::McInboxKind::ProposedSkill => tool.apply_skill(&item.id),
crate::brain::mission_control::McInboxKind::ProposedBrainDedup => {
tool.apply_brain_dedup(&item.id)
}
};
let msg = match result {
Ok(s) => s,
Err(e) => format!("Apply failed: {e}"),
};
notify(app, &msg);
finalize_selection_after_action(app);
refresh(app).await;
}
pub async fn reject_selected(app: &mut App) {
if app.mc.focused_panel != McPanel::Inbox {
return;
}
let items = inbox_service::list();
let Some(item) = items.get(app.mc.selected_index).cloned() else {
return;
};
let tool = match build_proposals_tool(app) {
Some(t) => t,
None => {
notify(app, "Reject failed: tools.toml path unavailable");
return;
}
};
let result = tool.reject(&item.id, None);
let msg = match result {
Ok(s) => s,
Err(e) => format!("Reject failed: {e}"),
};
notify(app, &msg);
finalize_selection_after_action(app);
refresh(app).await;
}
fn finalize_selection_after_action(app: &mut App) {
let count = inbox_service::list().len();
if count == 0 {
app.mc.selected_index = 0;
} else {
app.mc.selected_index = app.mc.selected_index.min(count - 1);
}
}
fn build_proposals_tool(app: &App) -> Option<RsiProposalsTool> {
let registry = app.agent_service.tool_registry().clone();
let tools_path = DynamicToolLoader::default_path()?;
let brain_path = app.brain_path.clone();
Some(RsiProposalsTool::new(registry, tools_path, brain_path))
}
fn notify(app: &mut App, message: &str) {
app.notification = Some(message.to_string());
app.notification_shown_at = Some(std::time::Instant::now());
}