From 25af0df2c192ea719c35612c0f5839bfcfe889d7 Mon Sep 17 00:00:00 2001 From: rajamohan1950 Date: Wed, 8 Jul 2026 10:39:53 +0530 Subject: [PATCH] Fix Rich markup corrupting/crashing streamed answers and echoed input Streamed AI answers were emitted via console.print(chunk) with Rich markup enabled, so any bracketed text in the model output (e.g. List[str], decorators, list literals) was interpreted as style tags. This silently dropped content (List[str] rendered as "List ") and could raise MarkupError on tag-like sequences such as [/close], aborting the response mid-stream. The same class of bug affected the echoed user question (default, non-streaming path) and the debug keywords line, where untrusted text was embedded inside markup f-strings. - Print streamed chunks with markup=False, highlight=False - Escape the question and keywords with rich.markup.escape --- src/code_qna/cli.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/code_qna/cli.py b/src/code_qna/cli.py index eb8130b..ec4b898 100644 --- a/src/code_qna/cli.py +++ b/src/code_qna/cli.py @@ -9,6 +9,7 @@ from rich.panel import Panel from rich.table import Table from rich.tree import Tree +from rich.markup import escape import time from .context import ContextExtractor @@ -133,7 +134,7 @@ def main(question, path, model, api_key, interactive, stream, max_context, debug def process_question(question, context_extractor, gemini, stream=False, debug=False, chat_session=None, show_progress=True): """Process a single question.""" - console.print(f"\n[bold]Question:[/bold] {question}") + console.print(f"\n[bold]Question:[/bold] {escape(question)}") # Extract context if not show_progress: @@ -160,7 +161,7 @@ def process_question(question, context_extractor, gemini, stream=False, debug=Fa if not show_progress: # Only show basic timing if detailed progress was disabled console.print(f"\n[dim]Extraction time: {extract_time:.2f}s[/dim]") - console.print(f"[dim]Keywords: {', '.join(context['keywords'])}[/dim]") + console.print(f"[dim]Keywords: {escape(', '.join(context['keywords']))}[/dim]") console.print(f"[dim]Files found: {context['context_files']['total_files']}[/dim]") console.print(f"[dim]Context size: {context['context_files']['total_size']:,} chars[/dim]") @@ -201,7 +202,7 @@ def process_question(question, context_extractor, gemini, stream=False, debug=Fa generator = gemini.generate_answer_stream(question, context) for chunk in generator: - console.print(chunk, end="") + console.print(chunk, end="", markup=False, highlight=False) response_parts.append(chunk) console.print() # New line after streaming