Refactor command builders (#601)
* refactor command builders * remove log * codex * cursor model * consistent cmd overrides * shared types * default for CmdOverrides
This commit is contained in:
committed by
GitHub
parent
a8515d788e
commit
0bf8138742
@@ -42,7 +42,7 @@
|
||||
"CODEX": {
|
||||
"DEFAULT": {
|
||||
"CODEX": {
|
||||
"dangerously_bypass_approvals_and_sandbox": true
|
||||
"sandbox": "danger-full-access"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS, Default)]
|
||||
pub struct CmdOverrides {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub base_command_override: Option<String>,
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Amp {
|
||||
let mut builder = CommandBuilder::new("npx -y @sourcegraph/amp@latest")
|
||||
.params(["--execute", "--stream-json"]);
|
||||
if self.dangerously_allow_all.unwrap_or(false) {
|
||||
builder = builder.params(["--dangerously-allow-all"]);
|
||||
builder = builder.extend_params(["--dangerously-allow-all"]);
|
||||
}
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
|
||||
@@ -32,23 +32,6 @@ fn base_command(claude_code_router: bool) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_command_builder(
|
||||
claude_code_router: bool,
|
||||
plan: bool,
|
||||
dangerously_skip_permissions: bool,
|
||||
) -> CommandBuilder {
|
||||
let mut params: Vec<&'static str> = vec!["-p"];
|
||||
if plan {
|
||||
params.push("--permission-mode=plan");
|
||||
}
|
||||
if dangerously_skip_permissions {
|
||||
params.push("--dangerously-skip-permissions");
|
||||
}
|
||||
params.extend_from_slice(&["--verbose", "--output-format=stream-json"]);
|
||||
|
||||
CommandBuilder::new(base_command(claude_code_router)).params(params)
|
||||
}
|
||||
|
||||
/// An executor that uses Claude CLI to process tasks
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS)]
|
||||
pub struct ClaudeCode {
|
||||
@@ -73,14 +56,19 @@ impl ClaudeCode {
|
||||
);
|
||||
}
|
||||
|
||||
apply_overrides(
|
||||
build_command_builder(
|
||||
self.claude_code_router.unwrap_or(false),
|
||||
self.plan.unwrap_or(false),
|
||||
self.dangerously_skip_permissions.unwrap_or(false),
|
||||
),
|
||||
&self.cmd,
|
||||
)
|
||||
let mut builder =
|
||||
CommandBuilder::new(base_command(self.claude_code_router.unwrap_or(false)))
|
||||
.params(["-p"]);
|
||||
|
||||
if self.plan.unwrap_or(false) {
|
||||
builder = builder.extend_params(["--permission-mode=plan"]);
|
||||
}
|
||||
if self.dangerously_skip_permissions.unwrap_or(false) {
|
||||
builder = builder.extend_params(["--dangerously-skip-permissions"]);
|
||||
}
|
||||
builder = builder.extend_params(["--verbose", "--output-format=stream-json"]);
|
||||
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use command_group::{AsyncCommandGroup, AsyncGroupChild};
|
||||
use futures::StreamExt;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::AsRefStr;
|
||||
use tokio::{io::AsyncWriteExt, process::Command};
|
||||
use ts_rs::TS;
|
||||
use utils::{
|
||||
@@ -15,7 +16,7 @@ use utils::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::CommandBuilder,
|
||||
command::{CmdOverrides, CommandBuilder, apply_overrides},
|
||||
executors::{ExecutorError, StandardCodingAgentExecutor},
|
||||
logs::{
|
||||
ActionType, FileChange, NormalizedEntry, NormalizedEntryType,
|
||||
@@ -23,6 +24,16 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// Sandbox policy modes for Codex
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS, AsRefStr)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum SandboxMode {
|
||||
ReadOnly,
|
||||
WorkspaceWrite,
|
||||
DangerFullAccess,
|
||||
}
|
||||
|
||||
/// Handles session management for Codex executor
|
||||
pub struct SessionHandler;
|
||||
|
||||
@@ -110,20 +121,33 @@ pub struct Codex {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub append_prompt: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub dangerously_bypass_approvals_and_sandbox: Option<bool>,
|
||||
pub sandbox: Option<SandboxMode>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub oss: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub cmd: CmdOverrides,
|
||||
}
|
||||
|
||||
impl Codex {
|
||||
fn build_command_builder(&self) -> CommandBuilder {
|
||||
let mut builder = CommandBuilder::new("npx -y @openai/codex exec")
|
||||
.params(["--json", "--skip-git-repo-check"]);
|
||||
if self
|
||||
.dangerously_bypass_approvals_and_sandbox
|
||||
.unwrap_or(false)
|
||||
{
|
||||
builder = builder.params(["--dangerously-bypass-approvals-and-sandbox"]);
|
||||
|
||||
if let Some(sandbox) = &self.sandbox {
|
||||
builder = builder.extend_params(["--sandbox", sandbox.as_ref()]);
|
||||
}
|
||||
builder
|
||||
|
||||
if self.oss.unwrap_or(false) {
|
||||
builder = builder.extend_params(["--oss"]);
|
||||
}
|
||||
|
||||
if let Some(model) = &self.model {
|
||||
builder = builder.extend_params(["--model", model]);
|
||||
}
|
||||
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use utils::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::CommandBuilder,
|
||||
command::{CmdOverrides, CommandBuilder, apply_overrides},
|
||||
executors::{ExecutorError, StandardCodingAgentExecutor},
|
||||
logs::{
|
||||
ActionType, FileChange, NormalizedEntry, NormalizedEntryType, TodoItem,
|
||||
@@ -34,16 +34,26 @@ pub struct Cursor {
|
||||
pub append_prompt: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub force: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub cmd: CmdOverrides,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
fn build_command_builder(&self) -> CommandBuilder {
|
||||
let mut builder =
|
||||
CommandBuilder::new("cursor-agent").params(["-p", "--output-format=stream-json"]);
|
||||
|
||||
if self.force.unwrap_or(false) {
|
||||
builder = builder.params(["--force"]);
|
||||
builder = builder.extend_params(["--force"]);
|
||||
}
|
||||
builder
|
||||
|
||||
if let Some(model) = &self.model {
|
||||
builder = builder.extend_params(["--model", model]);
|
||||
}
|
||||
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1060,6 +1070,8 @@ mod tests {
|
||||
// No command field needed anymore
|
||||
append_prompt: None,
|
||||
force: None,
|
||||
model: None,
|
||||
cmd: Default::default(),
|
||||
};
|
||||
let msg_store = Arc::new(MsgStore::new());
|
||||
let current_dir = std::path::PathBuf::from("/tmp/test-worktree");
|
||||
|
||||
@@ -35,17 +35,14 @@ impl GeminiModel {
|
||||
"npx -y @google/gemini-cli@latest"
|
||||
}
|
||||
|
||||
fn build_command_builder(&self, yolo: bool) -> CommandBuilder {
|
||||
let mut params: Vec<&'static str> = vec![];
|
||||
if yolo {
|
||||
params.push("--yolo");
|
||||
}
|
||||
fn build_command_builder(&self) -> CommandBuilder {
|
||||
let mut builder = CommandBuilder::new(self.base_command());
|
||||
|
||||
if let GeminiModel::Flash = self {
|
||||
params.extend_from_slice(&["--model", "gemini-2.5-flash"]);
|
||||
builder = builder.extend_params(["--model", "gemini-2.5-flash"]);
|
||||
}
|
||||
|
||||
CommandBuilder::new(self.base_command()).params(params)
|
||||
builder
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +60,13 @@ pub struct Gemini {
|
||||
|
||||
impl Gemini {
|
||||
fn build_command_builder(&self) -> CommandBuilder {
|
||||
apply_overrides(
|
||||
self.model.build_command_builder(self.yolo.unwrap_or(false)),
|
||||
&self.cmd,
|
||||
)
|
||||
let mut builder = self.model.build_command_builder();
|
||||
|
||||
if self.yolo.unwrap_or(false) {
|
||||
builder = builder.extend_params(["--yolo"]);
|
||||
}
|
||||
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use utils::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::CommandBuilder,
|
||||
command::{CmdOverrides, CommandBuilder, apply_overrides},
|
||||
executors::{ExecutorError, StandardCodingAgentExecutor},
|
||||
logs::{
|
||||
ActionType, FileChange, NormalizedEntry, NormalizedEntryType, TodoItem,
|
||||
@@ -29,11 +29,28 @@ use crate::{
|
||||
pub struct Opencode {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub append_prompt: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub agent: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub cmd: CmdOverrides,
|
||||
}
|
||||
|
||||
impl Opencode {
|
||||
fn build_command_builder(&self) -> CommandBuilder {
|
||||
CommandBuilder::new("npx -y opencode-ai@latest run").params(["--print-logs"])
|
||||
let mut builder =
|
||||
CommandBuilder::new("npx -y opencode-ai@latest run").params(["--print-logs"]);
|
||||
|
||||
if let Some(model) = &self.model {
|
||||
builder = builder.extend_params(["--model", model]);
|
||||
}
|
||||
|
||||
if let Some(agent) = &self.agent {
|
||||
builder = builder.extend_params(["--agent", agent]);
|
||||
}
|
||||
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use ts_rs::TS;
|
||||
use utils::{msg_store::MsgStore, shell::get_shell_command};
|
||||
|
||||
use crate::{
|
||||
command::CommandBuilder,
|
||||
command::{CmdOverrides, CommandBuilder, apply_overrides},
|
||||
executors::{ExecutorError, StandardCodingAgentExecutor, gemini::Gemini},
|
||||
logs::{stderr_processor::normalize_stderr_logs, utils::EntryIndexProvider},
|
||||
};
|
||||
@@ -20,15 +20,19 @@ pub struct QwenCode {
|
||||
pub append_prompt: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub yolo: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub cmd: CmdOverrides,
|
||||
}
|
||||
|
||||
impl QwenCode {
|
||||
fn build_command_builder(&self) -> CommandBuilder {
|
||||
let mut builder = CommandBuilder::new("npx -y @qwen-code/qwen-code@latest");
|
||||
|
||||
if self.yolo.unwrap_or(false) {
|
||||
builder = builder.params(["--yolo"]);
|
||||
builder = builder.extend_params(["--yolo"]);
|
||||
}
|
||||
builder
|
||||
|
||||
apply_overrides(builder, &self.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ fn generate_types_content() -> String {
|
||||
utils::diff::FileDiffDetails::decl(),
|
||||
services::services::github_service::RepositoryInfo::decl(),
|
||||
executors::command::CommandBuilder::decl(),
|
||||
// New executor profile types
|
||||
executors::profile::ExecutorProfileId::decl(),
|
||||
executors::profile::ExecutorProfile::decl(),
|
||||
executors::profile::VariantAgentConfig::decl(),
|
||||
@@ -69,6 +68,7 @@ fn generate_types_content() -> String {
|
||||
executors::executors::gemini::GeminiModel::decl(),
|
||||
executors::executors::amp::Amp::decl(),
|
||||
executors::executors::codex::Codex::decl(),
|
||||
executors::executors::codex::SandboxMode::decl(),
|
||||
executors::executors::cursor::Cursor::decl(),
|
||||
executors::executors::opencode::Opencode::decl(),
|
||||
executors::executors::qwen::QwenCode::decl(),
|
||||
|
||||
@@ -136,13 +136,15 @@ export type GeminiModel = "default" | "flash";
|
||||
|
||||
export type Amp = { append_prompt?: string | null, dangerously_allow_all?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
|
||||
|
||||
export type Codex = { append_prompt?: string | null, dangerously_bypass_approvals_and_sandbox?: boolean | null, };
|
||||
export type Codex = { append_prompt?: string | null, sandbox?: SandboxMode | null, oss?: boolean | null, model?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
|
||||
|
||||
export type Cursor = { append_prompt?: string | null, force?: boolean | null, };
|
||||
export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
||||
|
||||
export type Opencode = { append_prompt?: string | null, };
|
||||
export type Cursor = { append_prompt?: string | null, force?: boolean | null, model?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
|
||||
|
||||
export type QwenCode = { append_prompt?: string | null, yolo?: boolean | null, };
|
||||
export type Opencode = { append_prompt?: string | null, model?: string | null, agent?: string | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
|
||||
|
||||
export type QwenCode = { append_prompt?: string | null, yolo?: boolean | null, base_command_override?: string | null, additional_params?: Array<string> | null, };
|
||||
|
||||
export type CodingAgentInitialRequest = { prompt: string,
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user