From 1a66da4eb3919dafa5200e271b7d9f20322130c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E4=BA=91=E9=BE=99?= <76432572+nankingjing@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:45:50 +0800 Subject: [PATCH 1/3] fix(cli): stop reprinting streamed assistant text after 'Done' --- rust/crates/rusty-claude-cli/src/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 665ce632cf..9fa6f89f50 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -7763,15 +7763,20 @@ impl LiveCli { match result { Ok(summary) => { self.replace_runtime(runtime)?; + // The assistant response was already streamed to the terminal live + // during the turn (emit_output = true), so reprinting the full text + // here would duplicate the entire message. When text was streamed, + // emit a newline first so `spinner.finish`'s line-clear lands on a + // fresh line instead of erasing the last streamed line. + let final_text = final_assistant_text(&summary); + if !final_text.is_empty() { + println!(); + } spinner.finish( "✨ Done", TerminalRenderer::new().color_theme(), &mut stdout, )?; - let final_text = final_assistant_text(&summary); - if !final_text.is_empty() { - println!("{final_text}"); - } println!(); if let Some(event) = summary.auto_compaction { println!( From 971d350e536ecb0965d5a94f71b540a4ad29754e Mon Sep 17 00:00:00 2001 From: nankingjing <1079826437@qq.com> Date: Tue, 14 Jul 2026 01:18:47 +0000 Subject: [PATCH 2/3] fix(cli): preserve retried streamed response tail --- rust/crates/rusty-claude-cli/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 9fa6f89f50..d8f851b19d 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -7908,6 +7908,9 @@ impl LiveCli { match new_runtime.run_turn(input, Some(&mut rp)) { Ok(summary) => { self.replace_runtime(new_runtime)?; + if !final_assistant_text(&summary).is_empty() { + println!(); + } spinner.finish( if round == 0 { "✨ Done (after auto-compact)" From 811403286b7ee4db17e55fbb47e25d0bf716351e Mon Sep 17 00:00:00 2001 From: gaebal-gajae Date: Sun, 11 Oct 2026 20:35:09 +0000 Subject: [PATCH 3/3] test(cli): assert streamed response is emitted once --- .../rusty-claude-cli/tests/compact_output.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rust/crates/rusty-claude-cli/tests/compact_output.rs b/rust/crates/rusty-claude-cli/tests/compact_output.rs index 6f3cb53810..948a44c833 100644 --- a/rust/crates/rusty-claude-cli/tests/compact_output.rs +++ b/rust/crates/rusty-claude-cli/tests/compact_output.rs @@ -130,7 +130,7 @@ fn compact_flag_streaming_text_only_emits_final_message_text() { } #[test] -fn text_prompt_mode_prints_final_assistant_text_after_spinner() { +fn text_prompt_mode_streams_assistant_text_once_before_spinner_completion() { // given a workspace pointed at the mock Anthropic service running the // streaming_text scenario which only emits a single assistant text block let runtime = tokio::runtime::Runtime::new().expect("tokio runtime should build"); @@ -162,7 +162,7 @@ fn text_prompt_mode_prints_final_assistant_text_after_spinner() { ], ); - // then stdout should contain the final assistant text, not just spinner output + // then the streamed answer should appear once before the spinner completion assert!( output.status.success(), "text prompt run should succeed\nstdout:\n{}\n\nstderr:\n{}", @@ -171,19 +171,19 @@ fn text_prompt_mode_prints_final_assistant_text_after_spinner() { ); let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8"); let plain_stdout = strip_ansi_codes(&stdout); - assert!( - plain_stdout.contains("Mock streaming says hello from the parity harness."), - "text prompt stdout should include the assistant text ({stdout:?})" + let assistant_text = "Mock streaming says hello from the parity harness."; + assert_eq!( + plain_stdout.matches(assistant_text).count(), + 1, + "text prompt stdout should contain the streamed assistant text exactly once ({stdout:?})" ); assert!( plain_stdout.contains("✔ ✨ Done"), "text prompt stdout should still include spinner completion ({stdout:?})" ); assert!( - plain_stdout - .lines() - .any(|line| line == "Mock streaming says hello from the parity harness."), - "text prompt stdout should print the assistant text as its own line ({stdout:?})" + plain_stdout.find(assistant_text) < plain_stdout.find("✔ ✨ Done"), + "text prompt stdout should stream the assistant text before spinner completion ({stdout:?})" ); fs::remove_dir_all(&workspace).expect("workspace cleanup should succeed");