use std::path::Path;
use std::process::Child;
use anyhow::Result;
use super::interpretation::Interpretation;
use super::settings::Terminal;
use super::{Choice, Harness, ModelAndEffort, Settings, agy, claude, codex, grok, muse, opencode};
pub trait Adapter: Sync {
fn name(&self) -> &'static str;
fn product_name(&self) -> &'static str;
fn project_skills(&self) -> &'static str;
fn skill_loading(&self) -> SkillLoading;
fn stop_signals(&self) -> &'static [libc::c_int];
fn settings<'a>(&self, settings: &'a Settings) -> &'a ModelAndEffort;
fn settings_mut<'a>(&self, settings: &'a mut Settings) -> &'a mut ModelAndEffort;
fn session(&self, choice: &Choice, resume: Option<&str>, prompt: &str) -> Invocation;
fn check(&self, choice: &mut Choice) -> Result<()>;
fn ask_settings(
&self,
outside: &mut dyn Terminal,
current: &ModelAndEffort,
) -> Result<Option<ModelAndEffort>>;
fn interpretation(&self, worktree: &Path, prompt: &str) -> Interpretation;
fn stop(&self, child: &mut Child) {
crate::process::stop(child, self.stop_signals());
}
fn environment(&self) -> &'static [(&'static str, &'static str)] {
&[]
}
fn link_instruction_fallback(&self, _worktree: &Path) -> Result<()> {
Ok(())
}
}
pub struct Invocation {
pub args: Vec<String>,
pub stdin: Option<String>,
}
pub enum SkillLoading {
Slash,
Dollar,
Tool,
}
impl SkillLoading {
pub fn prompt(&self, prompt: &str) -> String {
let Some(rest) = prompt.strip_prefix("/thirdshift-") else {
return prompt.to_string();
};
match self {
Self::Slash => prompt.to_string(),
Self::Dollar => format!("$thirdshift-{rest}"),
Self::Tool => {
let end = rest.find(char::is_whitespace).unwrap_or(rest.len());
let (skill, remaining) = rest.split_at(end);
format!("Load thirdshift-{skill} with your skill tool.{remaining}")
}
}
}
}
pub const REGISTERED: [(Harness, &dyn Adapter); 6] = [
claude::REGISTRATION,
codex::REGISTRATION,
agy::REGISTRATION,
grok::REGISTRATION,
muse::REGISTRATION,
opencode::REGISTRATION,
];