fix(cli): preserve split prompt words from Windows shims (#1160)

This commit is contained in:
Hunter Bown
2026-05-08 02:39:09 -05:00
committed by GitHub
parent f91970f092
commit 218d797b0b
2 changed files with 90 additions and 17 deletions
+43 -10
View File
@@ -92,15 +92,14 @@ struct Cli {
no_mouse_capture: bool,
#[arg(long = "skip-onboarding")]
skip_onboarding: bool,
#[arg(
short = 'p',
long = "prompt",
value_name = "PROMPT",
conflicts_with = "prompt"
)]
#[arg(short = 'p', long = "prompt", value_name = "PROMPT")]
prompt_flag: Option<String>,
#[arg(value_name = "PROMPT")]
prompt: Option<String>,
#[arg(
value_name = "PROMPT",
trailing_var_arg = true,
allow_hyphen_values = true
)]
prompt: Vec<String>,
#[command(subcommand)]
command: Option<Commands>,
}
@@ -513,7 +512,17 @@ fn run() -> Result<()> {
None => {
let resolved_runtime = resolve_runtime_for_dispatch(&mut store, &runtime_overrides);
let mut forwarded = Vec::new();
if let Some(prompt) = cli.prompt_flag.clone().or_else(|| cli.prompt.clone()) {
let prompt = cli.prompt_flag.iter().chain(cli.prompt.iter()).fold(
String::new(),
|mut acc, part| {
if !acc.is_empty() {
acc.push(' ');
}
acc.push_str(part);
acc
},
);
if !prompt.is_empty() {
forwarded.push("--prompt".to_string());
forwarded.push(prompt);
}
@@ -2533,7 +2542,31 @@ mod tests {
let cli = parse_ok(&["deepseek", "-p", "Reply with exactly OK."]);
assert_eq!(cli.prompt_flag.as_deref(), Some("Reply with exactly OK."));
assert_eq!(cli.prompt, None);
assert!(cli.prompt.is_empty());
}
#[test]
fn parses_split_top_level_prompt_words_for_windows_cmd_shims() {
let cli = parse_ok(&["deepseek", "hello", "world"]);
assert_eq!(cli.prompt, vec!["hello", "world"]);
assert!(cli.command.is_none());
}
#[test]
fn prompt_flag_keeps_split_tail_words_for_windows_cmd_shims() {
let cli = parse_ok(&["deepseek", "-p", "hello", "world"]);
assert_eq!(cli.prompt_flag.as_deref(), Some("hello"));
assert_eq!(cli.prompt, vec!["world"]);
}
#[test]
fn known_subcommands_still_parse_before_prompt_tail() {
let cli = parse_ok(&["deepseek", "doctor"]);
assert!(cli.prompt.is_empty());
assert!(matches!(cli.command, Some(Commands::Doctor(_))));
}
#[test]
+47 -7
View File
@@ -110,8 +110,8 @@ struct Cli {
feature_toggles: FeatureToggles,
/// Send a one-shot prompt (non-interactive)
#[arg(short, long)]
prompt: Option<String>,
#[arg(short, long, value_name = "PROMPT", num_args = 1..)]
prompt: Vec<String>,
/// YOLO mode: enable agent tools + shell execution
#[arg(long)]
@@ -265,7 +265,13 @@ enum Commands {
#[derive(Args, Debug, Clone)]
struct ExecArgs {
/// Prompt to send to the model
prompt: String,
#[arg(
value_name = "PROMPT",
required = true,
trailing_var_arg = true,
allow_hyphen_values = true
)]
prompt: Vec<String>,
/// Override model for this run
#[arg(long)]
model: Option<String>,
@@ -277,6 +283,10 @@ struct ExecArgs {
json: bool,
}
fn join_prompt_parts(parts: &[String]) -> String {
parts.join(" ")
}
#[derive(Args, Debug, Clone, Default)]
struct SetupArgs {
/// Initialize MCP configuration at the configured path
@@ -654,6 +664,7 @@ async fn main() -> Result<()> {
.model
.or_else(|| config.default_text_model.clone())
.unwrap_or_else(|| config.default_model());
let prompt = join_prompt_parts(&args.prompt);
if args.auto || cli.yolo {
let workspace = cli.workspace.clone().unwrap_or_else(|| {
std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
@@ -666,7 +677,7 @@ async fn main() -> Result<()> {
run_exec_agent(
&config,
&model,
&args.prompt,
&prompt,
workspace,
max_subagents,
true,
@@ -675,9 +686,9 @@ async fn main() -> Result<()> {
)
.await
} else if args.json {
run_one_shot_json(&config, &model, &args.prompt).await
run_one_shot_json(&config, &model, &prompt).await
} else {
run_one_shot(&config, &model, &args.prompt).await
run_one_shot(&config, &model, &prompt).await
}
}
Commands::Review(args) => {
@@ -765,7 +776,8 @@ async fn main() -> Result<()> {
// One-shot prompt mode
let config = load_config_from_cli(&cli)?;
if let Some(prompt) = cli.prompt {
if !cli.prompt.is_empty() {
let prompt = join_prompt_parts(&cli.prompt);
let model = config.default_model();
return run_one_shot(&config, &model, &prompt).await;
}
@@ -4567,6 +4579,34 @@ mod terminal_mode_tests {
Cli::try_parse_from(args).expect("CLI args should parse")
}
#[test]
fn prompt_flag_accepts_split_prompt_words_for_windows_cmd_shims() {
let cli = parse_cli(&["deepseek", "-p", "hello", "world"]);
assert_eq!(cli.prompt, vec!["hello", "world"]);
}
#[test]
fn exec_accepts_split_prompt_words_for_windows_cmd_shims() {
let cli = parse_cli(&["deepseek", "exec", "hello", "world"]);
let Some(Commands::Exec(args)) = cli.command else {
panic!("expected exec command");
};
assert_eq!(args.prompt, vec!["hello", "world"]);
}
#[test]
fn exec_keeps_flags_before_split_prompt_words() {
let cli = parse_cli(&["deepseek", "exec", "--json", "hello", "world"]);
let Some(Commands::Exec(args)) = cli.command else {
panic!("expected exec command");
};
assert!(args.json);
assert_eq!(args.prompt, vec!["hello", "world"]);
}
#[test]
fn alternate_screen_defaults_on_in_auto_mode() {
let cli = parse_cli(&["deepseek"]);