diff --git a/backend/src/bin/generate_types.rs b/backend/src/bin/generate_types.rs index 0a5f2ddd..e980ff84 100644 --- a/backend/src/bin/generate_types.rs +++ b/backend/src/bin/generate_types.rs @@ -9,7 +9,7 @@ export const EXECUTOR_TYPES: string[] = [ "claude", "amp", "gemini", - "opencode" + "charmopencode" ]; export const EDITOR_TYPES: EditorType[] = [ @@ -26,7 +26,7 @@ export const EXECUTOR_LABELS: Record = { "claude": "Claude", "amp": "Amp", "gemini": "Gemini", - "opencode": "OpenCode" + "charmopencode": "Charm Opencode" }; export const EDITOR_LABELS: Record = { diff --git a/backend/src/executor.rs b/backend/src/executor.rs index 39c052ea..87ad3373 100644 --- a/backend/src/executor.rs +++ b/backend/src/executor.rs @@ -7,7 +7,7 @@ use ts_rs::TS; use uuid::Uuid; use crate::executors::{ - AmpExecutor, ClaudeExecutor, EchoExecutor, GeminiExecutor, OpencodeExecutor, + AmpExecutor, CharmOpencodeExecutor, ClaudeExecutor, EchoExecutor, GeminiExecutor, SetupScriptExecutor, }; @@ -345,8 +345,8 @@ pub enum ExecutorConfig { Claude, Amp, Gemini, - Opencode, SetupScript { script: String }, + CharmOpencode, // Future executors can be added here // Shell { command: String }, // Docker { image: String, command: String }, @@ -369,7 +369,7 @@ impl FromStr for ExecutorConfig { "claude" => Ok(ExecutorConfig::Claude), "amp" => Ok(ExecutorConfig::Amp), "gemini" => Ok(ExecutorConfig::Gemini), - "opencode" => Ok(ExecutorConfig::Opencode), + "charmopencode" => Ok(ExecutorConfig::CharmOpencode), "setup_script" => Ok(ExecutorConfig::SetupScript { script: "setup script".to_string(), }), @@ -385,7 +385,7 @@ impl ExecutorConfig { ExecutorConfig::Claude => Box::new(ClaudeExecutor), ExecutorConfig::Amp => Box::new(AmpExecutor), ExecutorConfig::Gemini => Box::new(GeminiExecutor), - ExecutorConfig::Opencode => Box::new(OpencodeExecutor), + ExecutorConfig::CharmOpencode => Box::new(CharmOpencodeExecutor), ExecutorConfig::SetupScript { script } => { Box::new(SetupScriptExecutor::new(script.clone())) } @@ -395,7 +395,9 @@ impl ExecutorConfig { pub fn config_path(&self) -> Option { match self { ExecutorConfig::Echo => None, - ExecutorConfig::Opencode => dirs::home_dir().map(|home| home.join(".opencode.json")), + ExecutorConfig::CharmOpencode => { + dirs::home_dir().map(|home| home.join(".opencode.json")) + } ExecutorConfig::Claude => dirs::home_dir().map(|home| home.join(".claude.json")), ExecutorConfig::Amp => { dirs::config_dir().map(|config| config.join("amp").join("settings.json")) @@ -411,7 +413,7 @@ impl ExecutorConfig { pub fn mcp_attribute_path(&self) -> Option> { match self { ExecutorConfig::Echo => None, // Echo doesn't support MCP - ExecutorConfig::Opencode => Some(vec!["mcpServers"]), + ExecutorConfig::CharmOpencode => Some(vec!["mcpServers"]), ExecutorConfig::Claude => Some(vec!["mcpServers"]), ExecutorConfig::Amp => Some(vec!["amp", "mcpServers"]), // Nested path for Amp ExecutorConfig::Gemini => Some(vec!["mcpServers"]), @@ -431,7 +433,7 @@ impl ExecutorConfig { pub fn display_name(&self) -> &'static str { match self { ExecutorConfig::Echo => "Echo (Test Mode)", - ExecutorConfig::Opencode => "Opencode", + ExecutorConfig::CharmOpencode => "Charm Opencode", ExecutorConfig::Claude => "Claude", ExecutorConfig::Amp => "Amp", ExecutorConfig::Gemini => "Gemini", @@ -447,7 +449,7 @@ impl std::fmt::Display for ExecutorConfig { ExecutorConfig::Claude => "claude", ExecutorConfig::Amp => "amp", ExecutorConfig::Gemini => "gemini", - ExecutorConfig::Opencode => "opencode", + ExecutorConfig::CharmOpencode => "charmopencode", ExecutorConfig::SetupScript { .. } => "setup_script", }; write!(f, "{}", s) diff --git a/backend/src/executors/opencode.rs b/backend/src/executors/charm_opencode.rs similarity index 89% rename from backend/src/executors/opencode.rs rename to backend/src/executors/charm_opencode.rs index 4e4c31b9..21a22d3f 100644 --- a/backend/src/executors/opencode.rs +++ b/backend/src/executors/charm_opencode.rs @@ -9,16 +9,16 @@ use crate::{ }; /// An executor that uses OpenCode to process tasks -pub struct OpencodeExecutor; +pub struct CharmOpencodeExecutor; /// An executor that continues an OpenCode thread -pub struct OpencodeFollowupExecutor { +pub struct CharmOpencodeFollowupExecutor { pub session_id: String, pub prompt: String, } #[async_trait] -impl Executor for OpencodeExecutor { +impl Executor for CharmOpencodeExecutor { async fn spawn( &self, pool: &sqlx::SqlitePool, @@ -70,9 +70,9 @@ Task title: {}"#, let child = command .group_spawn() // Create new process group so we can kill entire tree .map_err(|e| { - crate::executor::SpawnContext::from_command(&command, "OpenCode") + crate::executor::SpawnContext::from_command(&command, "CharmOpenCode") .with_task(task_id, Some(task.title.clone())) - .with_context("OpenCode CLI execution for new task") + .with_context("CharmOpenCode CLI execution for new task") .spawn_error(e) })?; @@ -81,7 +81,7 @@ Task title: {}"#, } #[async_trait] -impl Executor for OpencodeFollowupExecutor { +impl Executor for CharmOpencodeFollowupExecutor { async fn spawn( &self, _pool: &sqlx::SqlitePool, @@ -111,9 +111,9 @@ impl Executor for OpencodeFollowupExecutor { let child = command .group_spawn() // Create new process group so we can kill entire tree .map_err(|e| { - crate::executor::SpawnContext::from_command(&command, "OpenCode") + crate::executor::SpawnContext::from_command(&command, "CharmOpenCode") .with_context(format!( - "OpenCode CLI followup execution for session {}", + "CharmOpenCode CLI followup execution for session {}", self.session_id )) .spawn_error(e) diff --git a/backend/src/executors/mod.rs b/backend/src/executors/mod.rs index 602a9c96..8c57e2b3 100644 --- a/backend/src/executors/mod.rs +++ b/backend/src/executors/mod.rs @@ -1,15 +1,15 @@ pub mod amp; +pub mod charm_opencode; pub mod claude; pub mod dev_server; pub mod echo; pub mod gemini; -pub mod opencode; pub mod setup_script; pub use amp::{AmpExecutor, AmpFollowupExecutor}; +pub use charm_opencode::{CharmOpencodeExecutor, CharmOpencodeFollowupExecutor}; pub use claude::{ClaudeExecutor, ClaudeFollowupExecutor}; pub use dev_server::DevServerExecutor; pub use echo::EchoExecutor; pub use gemini::{GeminiExecutor, GeminiFollowupExecutor}; -pub use opencode::{OpencodeExecutor, OpencodeFollowupExecutor}; pub use setup_script::SetupScriptExecutor; diff --git a/backend/src/routes/task_attempts.rs b/backend/src/routes/task_attempts.rs index 6227556a..c5aae7b0 100644 --- a/backend/src/routes/task_attempts.rs +++ b/backend/src/routes/task_attempts.rs @@ -1217,7 +1217,6 @@ pub async fn get_execution_process_normalized_logs( if !stdout.trim().is_empty() { // Determine executor type and create appropriate executor for normalization let executor_type = process.executor_type.as_deref().unwrap_or("unknown"); - let executor_config = if process.process_type == ExecutionProcessType::SetupScript { // For setup scripts, use the setup script executor ExecutorConfig::SetupScript { @@ -1227,13 +1226,9 @@ pub async fn get_execution_process_normalized_logs( .unwrap_or_else(|| "setup script".to_string()), } } else { - match executor_type { - "amp" => ExecutorConfig::Amp, - "claude" => ExecutorConfig::Claude, - "echo" => ExecutorConfig::Echo, - "gemini" => ExecutorConfig::Gemini, - "opencode" => ExecutorConfig::Opencode, - _ => { + match executor_type.to_string().parse() { + Ok(config) => config, + Err(_) => { tracing::warn!( "Unsupported executor type: {}, cannot normalize logs properly", executor_type diff --git a/backend/src/services/process_service.rs b/backend/src/services/process_service.rs index 39387c04..568e398b 100644 --- a/backend/src/services/process_service.rs +++ b/backend/src/services/process_service.rs @@ -415,20 +415,18 @@ impl ProcessService { ) })?; - // Determine the executor config from the stored executor_type - let executor_config = match most_recent_coding_agent.executor_type.as_deref() { - Some("claude") => crate::executor::ExecutorConfig::Claude, - Some("amp") => crate::executor::ExecutorConfig::Amp, - Some("gemini") => crate::executor::ExecutorConfig::Gemini, - Some("echo") => crate::executor::ExecutorConfig::Echo, - Some("opencode") => crate::executor::ExecutorConfig::Opencode, + let executor_config: crate::executor::ExecutorConfig = match most_recent_coding_agent + .executor_type + .as_deref() + { + Some(executor_str) => executor_str.parse().unwrap(), _ => { tracing::error!( - "Invalid or missing executor type '{}' for execution process {} (task attempt {})", - most_recent_coding_agent.executor_type.as_deref().unwrap_or("None"), - most_recent_coding_agent.id, - attempt_id - ); + "Invalid or missing executor type '{}' for execution process {} (task attempt {})", + most_recent_coding_agent.executor_type.as_deref().unwrap_or("None"), + most_recent_coding_agent.id, + attempt_id + ); return Err(TaskAttemptError::ValidationError(format!( "Invalid executor type for follow-up: {}", most_recent_coding_agent @@ -640,7 +638,7 @@ impl ProcessService { Some("claude") => crate::executor::ExecutorConfig::Claude, Some("amp") => crate::executor::ExecutorConfig::Amp, Some("gemini") => crate::executor::ExecutorConfig::Gemini, - Some("opencode") => crate::executor::ExecutorConfig::Opencode, + Some("charmopencode") => crate::executor::ExecutorConfig::CharmOpencode, _ => crate::executor::ExecutorConfig::Echo, // Default for "echo" or None } } @@ -667,35 +665,13 @@ impl ProcessService { None, // Dev servers don't have an executor type ), crate::executor::ExecutorType::CodingAgent(config) => { - let executor_type_str = match config { - crate::executor::ExecutorConfig::Echo => "echo", - crate::executor::ExecutorConfig::Claude => "claude", - crate::executor::ExecutorConfig::Amp => "amp", - crate::executor::ExecutorConfig::Gemini => "gemini", - crate::executor::ExecutorConfig::Opencode => "opencode", - crate::executor::ExecutorConfig::SetupScript { .. } => "setup_script", - }; - ( - "executor".to_string(), - None, - Some(executor_type_str.to_string()), - ) - } - crate::executor::ExecutorType::FollowUpCodingAgent { config, .. } => { - let executor_type_str = match config { - crate::executor::ExecutorConfig::Echo => "echo", - crate::executor::ExecutorConfig::Claude => "claude", - crate::executor::ExecutorConfig::Amp => "amp", - crate::executor::ExecutorConfig::Gemini => "gemini", - crate::executor::ExecutorConfig::Opencode => "opencode", - crate::executor::ExecutorConfig::SetupScript { .. } => "setup_script", - }; - ( - "followup_executor".to_string(), - None, - Some(executor_type_str.to_string()), - ) + ("executor".to_string(), None, Some(format!("{}", config))) } + crate::executor::ExecutorType::FollowUpCodingAgent { config, .. } => ( + "followup_executor".to_string(), + None, + Some(format!("{}", config)), + ), }; let create_process = CreateExecutionProcess { @@ -803,8 +779,8 @@ impl ProcessService { prompt, } => { use crate::executors::{ - AmpFollowupExecutor, ClaudeFollowupExecutor, GeminiFollowupExecutor, - OpencodeFollowupExecutor, + AmpFollowupExecutor, CharmOpencodeFollowupExecutor, ClaudeFollowupExecutor, + GeminiFollowupExecutor, }; let executor: Box = match config { @@ -839,9 +815,9 @@ impl ProcessService { // Echo doesn't support followup, use regular echo config.create_executor() } - crate::executor::ExecutorConfig::Opencode => { + crate::executor::ExecutorConfig::CharmOpencode => { if let Some(sid) = session_id { - Box::new(OpencodeFollowupExecutor { + Box::new(CharmOpencodeFollowupExecutor { session_id: sid.clone(), prompt: prompt.clone(), }) diff --git a/frontend/src/components/OnboardingDialog.tsx b/frontend/src/components/OnboardingDialog.tsx index 3151366c..52ee06ad 100644 --- a/frontend/src/components/OnboardingDialog.tsx +++ b/frontend/src/components/OnboardingDialog.tsx @@ -81,9 +81,7 @@ export function OnboardingDialog({ open, onComplete }: OnboardingDialogProps) {