diff --git a/crates/jp_cli/src/cmd/query.rs b/crates/jp_cli/src/cmd/query.rs index 3c2f8acf..5d06fda2 100644 --- a/crates/jp_cli/src/cmd/query.rs +++ b/crates/jp_cli/src/cmd/query.rs @@ -434,15 +434,13 @@ impl Query { chat_request.schema = schema.as_object().cloned(); } - // If the query was composed in an editor, the user has lost sight - // of what they wrote by the time the editor closes. Echo it back - // through the same role-aware rendering machinery used by replay - // and live streaming — a labeled user header followed by the - // request body — so the boundary between user input and the - // forthcoming assistant response is visually clear. Render this - // before any post-edit work (MCP init, attachments, tools) so that - // failures in those stages don't swallow the user's message. - if query_from_editor { + // Echo the request back through the same role-aware rendering + // machinery used by replay and live streaming — a labeled user header + // followed by the request body — so the boundary between user input + // and the forthcoming assistant response is visually clear. Render + // this before any post-edit work (MCP init, attachments, tools) so + // that failures in those stages don't swallow the user's message. + if self.should_echo_request(query_from_editor) { let mut echo = TurnView::new( ctx.printer.clone(), cfg.style.clone(), @@ -846,6 +844,17 @@ impl Query { .await } + /// Whether the chat request should be echoed to the terminal before the + /// turn starts. + /// + /// Echo when the user can't already see what's being sent: a query composed + /// in an editor (the editor took over the screen) or a `--replay` (the + /// message is pulled from history, not typed on the command line). + /// A plain inline query needs no echo — the user just typed it. + fn should_echo_request(&self, query_from_editor: bool) -> bool { + query_from_editor || self.replay + } + /// Returns `true` if editing is explicitly disabled. /// /// This signals that even if no query is provided, no editor should be diff --git a/crates/jp_cli/src/cmd/query_tests.rs b/crates/jp_cli/src/cmd/query_tests.rs index 1cba44d1..43f2d0ce 100644 --- a/crates/jp_cli/src/cmd/query_tests.rs +++ b/crates/jp_cli/src/cmd/query_tests.rs @@ -929,6 +929,23 @@ fn no_title_does_not_persist_into_partial_config() { assert_eq!(with_flag.conversation.title.generate.auto, None); } +#[test] +fn echo_request_when_from_editor_or_replay() { + // Editor-composed query: the editor took over the screen, so echo. + assert!(Query::default().should_echo_request(true)); + + // Plain inline query, no editor: the user already sees their input. + assert!(!Query::default().should_echo_request(false)); + + // Replay without an editor: the message comes from history and isn't + // otherwise visible on the terminal, so it must be echoed. + let replay = Query { + replay: true, + ..Default::default() + }; + assert!(replay.should_echo_request(false)); +} + #[test] fn blockquote_prefixes_each_line() { assert_eq!(blockquote("hello"), "> hello");