fix(cli): stop reprinting streamed assistant text after 'Done' in non-compact text mode#3273
fix(cli): stop reprinting streamed assistant text after 'Done' in non-compact text mode#3273nankingjing wants to merge 2 commits into
Conversation
|
Pushed one follow-up on top of #3273: extended the same newline-before-spinner.finish guard to the auto-compaction retry success path. That branch also runs with New head: |
|
Nice fix! The root cause is clear: |
94e0859 to
971d350
Compare
|
Thanks for the thorough review and for catching the retry-path exposure. That follow-up was important since the auto-compaction retry has the same emit_output=true path and would have had the same issue. Glad the approach landed cleanly. |
|
The approach of guarding just the tail line before spinner.finish is clean and minimal. Glad the retry path was also covered — that would have been an easy edge case to miss. This should noticeably improve the non-compact CLI experience. |
|
Confirmed the double-printing issue in non-compact mode — this was a real UX annoyance. The fix is clean, and covering the retry path as well was the right call. Thanks @nankingjing! |
|
Nice fix — the duplicate output in non-compact mode was confusing. The guard on !state.compact_output looks clean. Thanks @nankingjing! |
|
Thanks for the follow-up @nankingjing — extending the same newline-before-spinner.finish guard to the auto-compaction retry path makes sense. The fix looks clean with good scope discipline. 👍 |
|
Thanks for the review feedback @1716775457damn! All 6 PRs are green on CI. If you have a moment, could you submit a formal PR review approval (Review changes → Approve) on each? That would let them merge cleanly. Much appreciated! |
|
Approved. The root cause is well-documented: spinner.finish clears the last streamed line, and reprinting the full text was a blunt workaround. The newline-before-finish guard is the minimal correct fix, and covering the auto-compaction retry path was a good catch — same emit_output=true path, same symptom. |
Summary
Fixes #3258.
In non-compact text mode (and the interactive REPL),
LiveCli::run_turnprints the entire assistant response twice:
emit_output = true, soAnthropicRuntimeClient::consume_streamalready streams and renders every text delta to the terminal live
(
main.rs,ContentBlockDelta::TextDelta/MessageStop).spinner.finish("✨ Done")it then callsprintln!("{final_text}"), reprinting the whole message that was just streamed.The reprint was originally added because
Spinner::finishrunsMoveToColumn(0)+Clear(ClearType::CurrentLine)and would otherwise erasethe last streamed line (the streamed tail has no trailing newline). Reprinting
the full text "fixed" the erased line but duplicated everything above it.
Fix
Protect only the last streamed line instead of reprinting the whole message:
println!()beforespinner.finishso the line-clear lands on a fresh empty line rather than thelast content line.
println!("{final_text}")reprint.final_textis still computed and used to decide whether a newline is needed,so the empty-response path (tool-only turns) is unchanged. Compact/JSON paths
use
emit_output = falseand print the message exactly once — they areuntouched.
Scope
rust/crates/rusty-claude-cli/src/main.rs(LiveCli::run_turn), +9 -4.Verification
Verified by reading and tracing the streaming path (
consume_streamwrites eachtext delta live when
emit_outputis true;prepare_turn_runtime(true)is usedby
run_turn, while the compact/JSON paths useprepare_turn_runtime(false))and by inspecting
Spinner::finish's line-clear behavior. Not compiled orexecuted in this environment (full Rust workspace build not available here).
The change is a local reordering plus removal of one
println!;final_textremains used, so no unused-variable warning is introduced.