Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/cortex-cli/src/agent_cmd/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ pub struct CreateArgs {
#[arg(short, long, value_name = "DESCRIPTION")]
pub generate: Option<String>,

/// Model to use for AI generation (default: gpt-4o).
#[arg(long, default_value = "gpt-4o")]
pub model: String,
/// Model to use for AI generation or as an agent override.
/// Defaults to gpt-4o when generating with --generate.
#[arg(long)]
pub model: Option<String>,
}

/// Arguments for edit command.
Expand Down
49 changes: 31 additions & 18 deletions src/cortex-cli/src/agent_cmd/handlers/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ pub async fn run_create(args: CreateArgs) -> Result<()> {
}
}

fn model_override_from_input(model_input: &str) -> Option<String> {
let model_input = model_input.trim();
if model_input.is_empty() {
return None;
}

// Issue #2328: Validate model name if provided
match validate_model_name(model_input) {
Ok(valid_model) => Some(valid_model),
Err(e) => {
eprintln!("Warning: {}", e);
eprintln!(
"Using model name as-is. The agent may fail to run if the model doesn't exist."
);
Some(model_input.to_string())
}
}
}

// Only show banner in interactive mode
if !args.non_interactive {
println!("🤖 Cortex Agent Creator");
Expand Down Expand Up @@ -220,30 +239,24 @@ pub async fn run_create(args: CreateArgs) -> Result<()> {

// Get optional settings
let (temperature, model, color) = if args.non_interactive {
(None, None, None)
(
None,
model_override_from_input(args.model.as_deref().unwrap_or("")),
None,
)
} else {
println!("\nOptional Settings (press Enter to skip):");

let temp_input = prompt_input(&stdin, &mut stdout, " Temperature (0.0-2.0)", Some(""))?;
let temperature = temp_input.parse::<f32>().ok();

let model_input = prompt_input(&stdin, &mut stdout, " Model override", Some(""))?;
// Issue #2328: Validate model name if provided
let model = if model_input.is_empty() {
None
} else {
// Validate the model name to prevent typos from being accepted
match validate_model_name(&model_input) {
Ok(valid_model) => Some(valid_model),
Err(e) => {
eprintln!("Warning: {}", e);
eprintln!(
"Using model name as-is. The agent may fail to run if the model doesn't exist."
);
Some(model_input)
}
}
};
let model_input = prompt_input(
&stdin,
&mut stdout,
" Model override",
Some(args.model.as_deref().unwrap_or("")),
)?;
let model = model_override_from_input(&model_input);

let color = prompt_input(
&stdin,
Expand Down
2 changes: 1 addition & 1 deletion src/cortex-cli/src/agent_cmd/handlers/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub async fn run_generate(args: CreateArgs) -> Result<()> {
};

// Validate model argument
let model_arg = args.model.trim();
let model_arg = args.model.as_deref().unwrap_or("gpt-4o").trim();
if model_arg.is_empty() {
bail!("Error: Model name cannot be empty");
}
Expand Down
68 changes: 68 additions & 0 deletions src/cortex-cli/tests/agent_create_model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::fs;
use std::process::Command;

use tempfile::tempdir;

fn combined_output(output: &std::process::Output) -> String {
format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)
}

#[test]
fn agent_create_non_interactive_writes_requested_model() {
let home = tempdir().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_Cortex"))
.args([
"agent",
"create",
"--name",
"demo-agent",
"--non-interactive",
"--model",
"claude-sonnet-4-20250514",
])
.env("HOME", home.path())
.env_remove("CORTEX_HOME")
.output()
.unwrap();

assert!(
output.status.success(),
"agent create failed:\n{}",
combined_output(&output)
);

let agent_path = home.path().join(".cortex/agents/demo-agent.md");
let content = fs::read_to_string(&agent_path).unwrap();
assert!(content.contains("model: claude-sonnet-4-20250514"));
}

#[test]
fn agent_create_non_interactive_without_model_omits_model_override() {
let home = tempdir().unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_Cortex"))
.args([
"agent",
"create",
"--name",
"default-agent",
"--non-interactive",
])
.env("HOME", home.path())
.env_remove("CORTEX_HOME")
.output()
.unwrap();

assert!(
output.status.success(),
"agent create failed:\n{}",
combined_output(&output)
);

let agent_path = home.path().join(".cortex/agents/default-agent.md");
let content = fs::read_to_string(&agent_path).unwrap();
assert!(!content.contains("model:"));
}