use std::sync::Arc;
use crate::agent::Agent;
use crate::claude::explain::Pool;
use crate::codex::explain::{self as codex, Slot};
use crate::context::ScreenSel;
use crate::copilot::explain as copilot;
#[derive(Debug)]
pub enum Progress {
Started {
model: String,
source: &'static str,
},
Delta(String),
Done,
Failed(String),
TooBig {
tokens: usize,
ratio: usize,
},
}
pub struct Request {
pub screen: ScreenSel,
pub cwd: String,
pub max_lines: usize,
pub deep: bool,
pub force: bool,
pub agent_pid: Option<u32>,
}
pub const DEEP_ASK_ABOVE_TOKENS: usize = 40_000;
#[derive(Clone)]
pub enum Explainer {
Codex(Arc<Slot>),
Claude(Arc<Pool>),
Copilot(Arc<copilot::Slot>),
}
impl Explainer {
pub fn new(agent: Agent) -> Self {
match agent {
Agent::Codex => Explainer::Codex(Arc::default()),
Agent::Claude => Explainer::Claude(Arc::default()),
Agent::Copilot => Explainer::Copilot(Arc::default()),
}
}
pub fn for_other_program() -> Self {
let installed = Agent::ALL
.into_iter()
.find(|a| crate::launch::find_real(a.command()).is_some());
Self::new(installed.unwrap_or(Agent::Codex))
}
pub fn explain(&self, req: Request, tx: impl Fn(Progress)) {
match self {
Explainer::Codex(slot) => match slot.get() {
Ok(server) => codex::explain(&server, req, tx),
Err(e) => tx(Progress::Failed(format!("explainer unavailable: {e:#}"))),
},
Explainer::Claude(pool) => crate::claude::explain::explain(pool, req, tx),
Explainer::Copilot(slot) => copilot::explain(slot, req, tx),
}
}
pub fn shutdown(&self) {
match self {
Explainer::Codex(slot) => slot.shutdown(),
Explainer::Claude(pool) => pool.shutdown(),
Explainer::Copilot(slot) => slot.shutdown(),
}
}
}