Clearer usage errors for sigit run prompts
A second positional prompt now says "unexpected extra argument" instead of "unknown argument", a bare `sigit run` says "missing prompt", and a mistyped flag like --quite is rejected instead of becoming the prompt.
Seto Elkahfi committed
Sep 25, 2026 at 18:16 UTC
26f8c3c629238f60264aa8f634e942880c865988
1 file changed
+40
-2
src/headless.rs
+40
-2
@@ -94,7 +94,7 @@ pub fn parse_args(args: &[String]) -> Result<Option<HeadlessConfig>, String> {
94
.next()
95
.ok_or_else(|| format!("{arg} requires a prompt argument"))?;
96
if prompt.is_some() {
97
- return Err(format!("{arg} was given more than once"));
97
+ return Err("the prompt was given more than once".to_string());
98
}
99
prompt = Some(value.clone());
100
}
@@ -143,14 +143,28 @@ pub fn parse_args(args: &[String]) -> Result<Option<HeadlessConfig>, String> {
143
.ok_or_else(|| "--deny-tool requires a tool name".to_string())?;
144
deny_tools.push(value.clone());
145
}
146
+ // A mistyped flag must not become the prompt. A prompt that
147
+ // really starts with '-' can still go through -p.
148
+ other if other.starts_with('-') => return Err(format!("unknown argument: {other}")),
149
other if run_command && prompt.is_none() => prompt = Some(other.to_string()),
150
+ other if run_command => {
151
+ return Err(format!(
152
+ "unexpected extra argument: {other} (quote the prompt as one argument)"
153
+ ));
154
+ }
155
other => return Err(format!("unknown argument: {other}")),
156
}
157
}
158
159
// Reaching this without a prompt means `run` had no positional prompt, or
160
// a legacy prompt flag was consumed as another flag's value.
153
- let prompt = prompt.ok_or_else(|| "missing -p/--prompt".to_string())?;
161
+ let prompt = prompt.ok_or_else(|| {
162
+ if run_command {
163
+ "missing prompt".to_string()
164
+ } else {
165
+ "missing -p/--prompt".to_string()
166
+ }
167
+ })?;
168
if prompt.trim().is_empty() {
169
return Err("the prompt must not be empty".to_string());
170
}
@@ -682,6 +696,30 @@ mod tests {
696
assert!(parse_args(&args(&["-p", "a", "--prompt", "b"])).is_err());
697
}
698
699
+ #[test]
700
+ fn run_rejects_a_second_prompt_from_either_source() {
701
+ let error = parse_args(&args(&["run", "a", "b"])).unwrap_err();
702
+ assert!(error.contains("unexpected extra argument: b"), "{error}");
703
+ let error = parse_args(&args(&["run", "a", "-p", "b"])).unwrap_err();
704
+ assert!(error.contains("more than once"), "{error}");
705
+ let error = parse_args(&args(&["run", "-p", "a", "b"])).unwrap_err();
706
+ assert!(error.contains("unexpected extra argument: b"), "{error}");
707
+ }
708
+
709
+ #[test]
710
+ fn run_does_not_take_a_mistyped_flag_as_the_prompt() {
711
+ let error = parse_args(&args(&["run", "--quite", "fix it"])).unwrap_err();
712
+ assert!(error.contains("unknown argument: --quite"), "{error}");
713
+ }
714
+
715
+ #[test]
716
+ fn bare_run_reports_a_missing_prompt() {
717
+ assert_eq!(
718
+ parse_args(&args(&["run"])),
719
+ Err("missing prompt".to_string())
720
+ );
721
+ }
722
+
723
#[test]
724
fn empty_prompt_is_a_usage_error() {
725
assert!(parse_args(&args(&["-p", " "])).is_err());