use crate::config::CodemodeMode;
use super::{App, CommandInvocation, Entry, InteractiveRuntime};
const CODEMODE_USAGE: &str = "usage: /codemode [status|on|only]";
pub(super) trait CodemodeRuntime {
fn codemode_mode(&self) -> CodemodeMode;
fn set_codemode_mode(&mut self, mode: CodemodeMode) -> anyhow::Result<Option<String>>;
fn tool_specs(&self) -> Vec<rho_sdk::model::ToolSpec>;
}
impl CodemodeRuntime for InteractiveRuntime {
fn codemode_mode(&self) -> CodemodeMode {
InteractiveRuntime::codemode_mode(self)
}
fn set_codemode_mode(&mut self, mode: CodemodeMode) -> anyhow::Result<Option<String>> {
InteractiveRuntime::set_codemode_mode(self, mode)
}
fn tool_specs(&self) -> Vec<rho_sdk::model::ToolSpec> {
InteractiveRuntime::tool_specs(self)
}
}
impl App {
pub(super) fn execute_codemode_command(
&mut self,
invocation: CommandInvocation,
agent: &mut impl CodemodeRuntime,
) -> anyhow::Result<()> {
let current = agent.codemode_mode();
let args = invocation.args.trim();
if args.is_empty() || args.eq_ignore_ascii_case("status") {
self.report_codemode(current);
return Ok(());
}
let Some(requested) = CodemodeMode::parse(&invocation.args) else {
self.insert_entry(&Entry::Error(CODEMODE_USAGE.into()));
self.set_status("invalid codemode mode");
return Ok(());
};
let notice = match agent.set_codemode_mode(requested) {
Ok(notice) => notice,
Err(error) => {
self.insert_entry(&Entry::Error(format!(
"could not apply codemode to this session: {error}"
)));
self.set_status("codemode change failed");
return Ok(());
}
};
let Some(display) = notice else {
self.report_codemode(current);
return Ok(());
};
self.insert_entry(&Entry::Notice(display));
self.mirror_codemode_diagnostics(agent);
if let Err(error) = self
.info
.services
.config_repository
.update(|config| config.codemode.mode = requested)
{
let detail = match agent.set_codemode_mode(current) {
Ok(reverse) => {
if let Some(reverse) = reverse {
self.insert_entry(&Entry::Notice(reverse));
}
self.mirror_codemode_diagnostics(agent);
format!("could not save codemode setting: {error}")
}
Err(compensation_error) => format!(
"could not save codemode setting: {error}; could not restore codemode mode: {compensation_error}"
),
};
self.insert_entry(&Entry::Error(detail));
self.set_status("config save failed");
return Ok(());
}
self.report_codemode(requested);
Ok(())
}
fn mirror_codemode_diagnostics(&self, agent: &impl CodemodeRuntime) {
let diagnostics = &self.info.services.diagnostics;
diagnostics.update_tools(&agent.tool_specs());
diagnostics.update_codemode_mode(agent.codemode_mode());
}
fn report_codemode(&mut self, mode: CodemodeMode) {
let status = match mode {
CodemodeMode::On => "codemode on: direct tools and codemode are both available",
CodemodeMode::Only => "codemode only: direct tools are reached through codemode",
};
self.set_status(status);
}
}
#[cfg(test)]
#[path = "codemode_command_tests.rs"]
mod tests;