From 862a5b3eb396fd8cd4d3f1fc47261415a7ff1927 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Mon, 23 Feb 2026 07:55:37 -0800 Subject: [PATCH] Allow exec resume to parse output-last-message flag after command (#12541) Summary - mark `output-last-message` as a global exec flag so it can follow subcommands like `resume` - add regression tests in both `cli` and `exec` crates verifying the flag order works when invoking `resume` Fixes #12538 --- codex-rs/cli/src/main.rs | 28 ++++++++++++++++++++++++++++ codex-rs/exec/src/cli.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index cdecb18dc..483516fe6 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -1112,6 +1112,34 @@ mod tests { assert_eq!(args.prompt.as_deref(), Some("2+2")); } + #[test] + fn exec_resume_accepts_output_last_message_flag_after_subcommand() { + let cli = MultitoolCli::try_parse_from([ + "codex", + "exec", + "resume", + "session-123", + "-o", + "/tmp/resume-output.md", + "re-review", + ]) + .expect("parse should succeed"); + + let Some(Subcommand::Exec(exec)) = cli.subcommand else { + panic!("expected exec subcommand"); + }; + let Some(codex_exec::Command::Resume(args)) = exec.command else { + panic!("expected exec resume"); + }; + + assert_eq!( + exec.last_message_file, + Some(std::path::PathBuf::from("/tmp/resume-output.md")) + ); + assert_eq!(args.session_id.as_deref(), Some("session-123")); + assert_eq!(args.prompt.as_deref(), Some("re-review")); + } + fn app_server_from_args(args: &[&str]) -> AppServerCommand { let cli = MultitoolCli::try_parse_from(args).expect("parse"); let Subcommand::AppServer(app_server) = cli.subcommand.expect("app-server present") else { diff --git a/codex-rs/exec/src/cli.rs b/codex-rs/exec/src/cli.rs index 05845e00f..c40237160 100644 --- a/codex-rs/exec/src/cli.rs +++ b/codex-rs/exec/src/cli.rs @@ -96,7 +96,12 @@ pub struct Cli { pub json: bool, /// Specifies file where the last message from the agent should be written. - #[arg(long = "output-last-message", short = 'o', value_name = "FILE")] + #[arg( + long = "output-last-message", + short = 'o', + value_name = "FILE", + global = true + )] pub last_message_file: Option, /// Initial instructions for the agent. If not provided as an argument (or @@ -283,4 +288,27 @@ mod tests { }); assert_eq!(effective_prompt.as_deref(), Some(PROMPT)); } + + #[test] + fn resume_accepts_output_last_message_flag_after_subcommand() { + const PROMPT: &str = "echo resume-with-output-file"; + let cli = Cli::parse_from([ + "codex-exec", + "resume", + "session-123", + "-o", + "/tmp/resume-output.md", + PROMPT, + ]); + + assert_eq!( + cli.last_message_file, + Some(PathBuf::from("/tmp/resume-output.md")) + ); + let Some(Command::Resume(args)) = cli.command else { + panic!("expected resume command"); + }; + assert_eq!(args.session_id.as_deref(), Some("session-123")); + assert_eq!(args.prompt.as_deref(), Some(PROMPT)); + } }