use anyhow::Result;
pub const SLASH_COMMANDS: &[&str] = &["/clear", "/exit", "/help", "/resume"];
pub fn collect_candidates(line: &str, pos: usize, skill_names: &[String]) -> Vec<String> {
let prefix = &line[..pos];
let Some(slash) = prefix.find('/') else {
return Vec::new();
};
let typed = &prefix[slash..];
let mut candidates: Vec<String> = SLASH_COMMANDS
.iter()
.filter(|cmd| cmd.starts_with(typed))
.map(|cmd| cmd.to_string())
.collect();
let skill_prefix = &typed[1..];
for name in skill_names {
if name.starts_with(skill_prefix) {
candidates.push(format!("/{name}"));
}
}
candidates
}
pub enum SlashResult {
Handled,
NotSlash,
Forward(String),
Exit,
Clear,
Resume,
}
pub async fn handle_slash(line: &str) -> Result<SlashResult> {
if !line.starts_with('/') {
return Ok(SlashResult::NotSlash);
}
let rest = &line[1..];
let (cmd, _arg) = match rest.find(' ') {
Some(pos) => (&rest[..pos], Some(rest[pos + 1..].trim())),
None => (rest, None),
};
match cmd {
"clear" => return Ok(SlashResult::Clear),
"exit" => return Ok(SlashResult::Exit),
"help" => {
println!("Available commands:");
println!(" /clear — start a new conversation");
println!(" /exit — exit the REPL");
println!(" /help — show this help");
println!(" /resume — open session console");
println!(" /<skill> — run a skill");
}
"resume" => return Ok(SlashResult::Resume),
_ => {
return Ok(SlashResult::Forward(line.to_owned()));
}
}
Ok(SlashResult::Handled)
}