code-puppy 0.0.157__tar.gz → 0.0.159__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. {code_puppy-0.0.157 → code_puppy-0.0.159}/PKG-INFO +1 -1
  2. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/command_handler.py +7 -8
  3. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/config.py +15 -8
  4. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/queue_console.py +3 -2
  5. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/model_factory.py +29 -0
  6. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/models.json +0 -6
  7. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/chat_view.py +38 -21
  8. {code_puppy-0.0.157 → code_puppy-0.0.159}/pyproject.toml +1 -1
  9. {code_puppy-0.0.157 → code_puppy-0.0.159}/.gitignore +0 -0
  10. {code_puppy-0.0.157 → code_puppy-0.0.159}/LICENSE +0 -0
  11. {code_puppy-0.0.157 → code_puppy-0.0.159}/README.md +0 -0
  12. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/__init__.py +0 -0
  13. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/__main__.py +0 -0
  14. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agent.py +0 -0
  15. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/__init__.py +0 -0
  16. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/agent_code_puppy.py +0 -0
  17. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/agent_creator_agent.py +0 -0
  18. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/agent_manager.py +0 -0
  19. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/agent_orchestrator.json +0 -0
  20. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/base_agent.py +0 -0
  21. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/json_agent.py +0 -0
  22. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/agents/runtime_manager.py +0 -0
  23. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/callbacks.py +0 -0
  24. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/__init__.py +0 -0
  25. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/file_path_completion.py +0 -0
  26. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/load_context_completion.py +0 -0
  27. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/__init__.py +0 -0
  28. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/add_command.py +0 -0
  29. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/base.py +0 -0
  30. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/handler.py +0 -0
  31. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/help_command.py +0 -0
  32. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/install_command.py +0 -0
  33. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/list_command.py +0 -0
  34. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/logs_command.py +0 -0
  35. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/remove_command.py +0 -0
  36. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/restart_command.py +0 -0
  37. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/search_command.py +0 -0
  38. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/start_all_command.py +0 -0
  39. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/start_command.py +0 -0
  40. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/status_command.py +0 -0
  41. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
  42. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/stop_command.py +0 -0
  43. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/test_command.py +0 -0
  44. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/utils.py +0 -0
  45. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
  46. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/meta_command_handler.py +0 -0
  47. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/model_picker_completion.py +0 -0
  48. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/motd.py +0 -0
  49. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
  50. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/command_line/utils.py +0 -0
  51. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/http_utils.py +0 -0
  52. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/main.py +0 -0
  53. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/__init__.py +0 -0
  54. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/async_lifecycle.py +0 -0
  55. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/blocking_startup.py +0 -0
  56. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/captured_stdio_server.py +0 -0
  57. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/circuit_breaker.py +0 -0
  58. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/config_wizard.py +0 -0
  59. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/dashboard.py +0 -0
  60. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/error_isolation.py +0 -0
  61. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/examples/retry_example.py +0 -0
  62. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/health_monitor.py +0 -0
  63. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/managed_server.py +0 -0
  64. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/manager.py +0 -0
  65. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/registry.py +0 -0
  66. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/retry_manager.py +0 -0
  67. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/server_registry_catalog.py +0 -0
  68. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/status_tracker.py +0 -0
  69. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/mcp/system_tools.py +0 -0
  70. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/message_history_processor.py +0 -0
  71. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/__init__.py +0 -0
  72. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/message_queue.py +0 -0
  73. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/renderers.py +0 -0
  74. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/spinner/__init__.py +0 -0
  75. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/spinner/console_spinner.py +0 -0
  76. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/spinner/spinner_base.py +0 -0
  77. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/messaging/spinner/textual_spinner.py +0 -0
  78. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/plugins/__init__.py +0 -0
  79. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/reopenable_async_client.py +0 -0
  80. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/round_robin_model.py +0 -0
  81. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/state_management.py +0 -0
  82. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/status_display.py +0 -0
  83. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/summarization_agent.py +0 -0
  84. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/__init__.py +0 -0
  85. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/agent_tools.py +0 -0
  86. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/command_runner.py +0 -0
  87. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/common.py +0 -0
  88. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/file_modifications.py +0 -0
  89. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/file_operations.py +0 -0
  90. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tools/tools_content.py +0 -0
  91. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/__init__.py +0 -0
  92. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/app.py +0 -0
  93. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/__init__.py +0 -0
  94. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/command_history_modal.py +0 -0
  95. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/copy_button.py +0 -0
  96. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/custom_widgets.py +0 -0
  97. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/human_input_modal.py +0 -0
  98. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/input_area.py +0 -0
  99. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/sidebar.py +0 -0
  100. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/components/status_bar.py +0 -0
  101. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/messages.py +0 -0
  102. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/models/__init__.py +0 -0
  103. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/models/chat_message.py +0 -0
  104. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/models/command_history.py +0 -0
  105. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/models/enums.py +0 -0
  106. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/screens/__init__.py +0 -0
  107. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/screens/help.py +0 -0
  108. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/screens/mcp_install_wizard.py +0 -0
  109. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/screens/settings.py +0 -0
  110. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/tui/screens/tools.py +0 -0
  111. {code_puppy-0.0.157 → code_puppy-0.0.159}/code_puppy/version_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.157
3
+ Version: 0.0.159
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -1,9 +1,6 @@
1
1
  import os
2
2
 
3
- from code_puppy.command_line.model_picker_completion import (
4
- load_model_names,
5
- update_model_in_input,
6
- )
3
+ from code_puppy.command_line.model_picker_completion import update_model_in_input
7
4
  from code_puppy.command_line.motd import print_motd
8
5
  from code_puppy.command_line.utils import make_directory_table
9
6
  from code_puppy.config import get_config_keys
@@ -60,7 +57,7 @@ def get_commands_help():
60
57
  )
61
58
  help_lines.append(
62
59
  Text("/compact", style="cyan")
63
- + Text(" Summarize and compact current chat history")
60
+ + Text(" Summarize and compact current chat history (uses compaction_strategy config)")
64
61
  )
65
62
  help_lines.append(
66
63
  Text("/dump_context", style="cyan")
@@ -137,18 +134,18 @@ def handle_command(command: str):
137
134
 
138
135
  before_tokens = sum(estimate_tokens_for_message(m) for m in history)
139
136
  compaction_strategy = get_compaction_strategy()
137
+ protected_tokens = get_protected_token_count()
140
138
  emit_info(
141
139
  f"🤔 Compacting {len(history)} messages using {compaction_strategy} strategy... (~{before_tokens} tokens)"
142
140
  )
143
141
 
144
142
  if compaction_strategy == "truncation":
145
- protected_tokens = get_protected_token_count()
146
143
  compacted = truncation(history, protected_tokens)
147
144
  summarized_messages = [] # No summarization in truncation mode
148
145
  else:
149
146
  # Default to summarization
150
147
  compacted, summarized_messages = summarize_messages(
151
- history, with_protection=False
148
+ history, with_protection=True
152
149
  )
153
150
 
154
151
  if not compacted:
@@ -372,6 +369,9 @@ def handle_command(command: str):
372
369
  # Convert /model to /m for internal processing
373
370
  model_command = command.replace("/model", "/m", 1)
374
371
 
372
+ # If no model matched, show available models
373
+ from code_puppy.command_line.model_picker_completion import load_model_names
374
+
375
375
  new_input = update_model_in_input(model_command)
376
376
  if new_input is not None:
377
377
  from code_puppy.agents.runtime_manager import get_runtime_agent_manager
@@ -383,7 +383,6 @@ def handle_command(command: str):
383
383
  manager.reload_agent()
384
384
  emit_success(f"Active model set and loaded: {model}")
385
385
  return True
386
- # If no model matched, show available models
387
386
  model_names = load_model_names()
388
387
  emit_warning("Usage: /model <model-name> or /m <model-name>")
389
388
  emit_warning(f"Available models: {', '.join(model_names)}")
@@ -110,6 +110,8 @@ def get_config_keys():
110
110
  "yolo_mode",
111
111
  "model",
112
112
  "compaction_strategy",
113
+ "protected_token_count",
114
+ "compaction_threshold",
113
115
  "message_limit",
114
116
  "allow_recursion",
115
117
  ]
@@ -162,7 +164,7 @@ _default_model_cache = None
162
164
  def _default_model_from_models_json():
163
165
  """Attempt to load the first model name from models.json.
164
166
 
165
- Falls back to the hard-coded default (``claude-4-0-sonnet``) if the file
167
+ Falls back to the hard-coded default (``gpt-5``) if the file
166
168
  cannot be read for any reason or is empty.
167
169
  """
168
170
  global _default_model_cache
@@ -176,11 +178,17 @@ def _default_model_from_models_json():
176
178
  from code_puppy.model_factory import ModelFactory
177
179
 
178
180
  models_config = ModelFactory.load_config()
179
- first_key = next(iter(models_config)) # Raises StopIteration if empty
180
- _default_model_cache = first_key
181
- return first_key
181
+ if models_config:
182
+ # Get the first key from the models config
183
+ first_key = next(iter(models_config))
184
+ _default_model_cache = first_key
185
+ return first_key
186
+ else:
187
+ # If models_config is empty, fall back to gpt-5
188
+ _default_model_cache = "gpt-5"
189
+ return "gpt-5"
182
190
  except Exception:
183
- # Any problem (network, file missing, empty dict, etc.) => fall back
191
+ # Any problem (network, file missing, empty dict, etc.) => fall back to gpt-5
184
192
  _default_model_cache = "gpt-5"
185
193
  return "gpt-5"
186
194
 
@@ -196,8 +204,7 @@ def _validate_model_exists(model_name: str) -> bool:
196
204
  try:
197
205
  from code_puppy.model_factory import ModelFactory
198
206
 
199
- models_config_path = os.path.join(CONFIG_DIR, "models.json")
200
- models_config = ModelFactory.load_config(models_config_path)
207
+ models_config = ModelFactory.load_config()
201
208
  exists = model_name in models_config
202
209
 
203
210
  # Cache the result
@@ -485,4 +492,4 @@ def save_command_to_history(command: str):
485
492
  error_msg = (
486
493
  f"❌ An unexpected error occurred while saving command history: {str(e)}"
487
494
  )
488
- direct_console.print(f"[bold red]{error_msg}[/bold red]")
495
+ direct_console.print(f"[bold red]{error_msg}[/bold red]")
@@ -58,9 +58,10 @@ class QueueConsole:
58
58
  from rich.console import Console
59
59
 
60
60
  string_io = StringIO()
61
- # Use markup=False to prevent interpretation of square brackets as markup
61
+ # Use markup=True to properly process rich styling
62
+ # Use a reasonable width to prevent wrapping issues
62
63
  temp_console = Console(
63
- file=string_io, width=80, legacy_windows=False, markup=False
64
+ file=string_io, width=80, legacy_windows=False, markup=True
64
65
  )
65
66
  temp_console.print(v)
66
67
  processed_values.append(string_io.getvalue().rstrip("\n"))
@@ -14,6 +14,7 @@ from pydantic_ai.providers.anthropic import AnthropicProvider
14
14
  from pydantic_ai.providers.google_gla import GoogleGLAProvider
15
15
  from pydantic_ai.providers.openai import OpenAIProvider
16
16
  from pydantic_ai.providers.cerebras import CerebrasProvider
17
+ from pydantic_ai.providers.openrouter import OpenRouterProvider
17
18
 
18
19
  from . import callbacks
19
20
  from .config import EXTRA_MODELS_FILE
@@ -248,6 +249,34 @@ class ModelFactory:
248
249
  setattr(model, "provider", provider)
249
250
  return model
250
251
 
252
+ elif model_type == "openrouter":
253
+ # Get API key from config, which can be an environment variable reference or raw value
254
+ api_key_config = model_config.get("api_key")
255
+ api_key = None
256
+
257
+ if api_key_config:
258
+ if api_key_config.startswith("$"):
259
+ # It's an environment variable reference
260
+ env_var_name = api_key_config[1:] # Remove the $ prefix
261
+ api_key = os.environ.get(env_var_name)
262
+ if api_key is None:
263
+ raise ValueError(
264
+ f"OpenRouter API key environment variable '{env_var_name}' not found or is empty. "
265
+ f"Please set the environment variable: export {env_var_name}=your_value"
266
+ )
267
+ else:
268
+ # It's a raw API key value
269
+ api_key = api_key_config
270
+ else:
271
+ # No API key in config, try to get it from the default environment variable
272
+ api_key = os.environ.get("OPENROUTER_API_KEY")
273
+
274
+ provider = OpenRouterProvider(api_key=api_key)
275
+
276
+ model = OpenAIChatModel(model_name=model_config["name"], provider=provider)
277
+ setattr(model, "provider", provider)
278
+ return model
279
+
251
280
  elif model_type == "round_robin":
252
281
  # Get the list of model names to use in the round-robin
253
282
  model_names = model_config.get("models")
@@ -87,12 +87,6 @@
87
87
  },
88
88
  "context_length": 64000
89
89
  },
90
- "openrouter": {
91
- "type": "openrouter",
92
- "name": "meta-llama/llama-4-maverick:free",
93
- "api_key": "$OPENROUTER_API_KEY",
94
- "context_length": 131072
95
- },
96
90
  "azure-gpt-4.1": {
97
91
  "type": "azure_openai",
98
92
  "name": "gpt-4.1",
@@ -261,8 +261,35 @@ class ChatView(VerticalScroll):
261
261
  else:
262
262
  separator = "\n"
263
263
 
264
- # Update the message content
265
- last_message.content += separator + message.content
264
+ # Handle content concatenation carefully to preserve Rich objects
265
+ if hasattr(last_message.content, "__rich_console__") or hasattr(message.content, "__rich_console__"):
266
+ # If either content is a Rich object, convert both to text and concatenate
267
+ from io import StringIO
268
+ from rich.console import Console
269
+
270
+ # Convert existing content to string
271
+ if hasattr(last_message.content, "__rich_console__"):
272
+ string_io = StringIO()
273
+ temp_console = Console(file=string_io, width=80, legacy_windows=False, markup=False)
274
+ temp_console.print(last_message.content)
275
+ existing_content = string_io.getvalue().rstrip("\n")
276
+ else:
277
+ existing_content = str(last_message.content)
278
+
279
+ # Convert new content to string
280
+ if hasattr(message.content, "__rich_console__"):
281
+ string_io = StringIO()
282
+ temp_console = Console(file=string_io, width=80, legacy_windows=False, markup=False)
283
+ temp_console.print(message.content)
284
+ new_content = string_io.getvalue().rstrip("\n")
285
+ else:
286
+ new_content = str(message.content)
287
+
288
+ # Combine as plain text
289
+ last_message.content = existing_content + separator + new_content
290
+ else:
291
+ # Both are strings, safe to concatenate
292
+ last_message.content += separator + message.content
266
293
 
267
294
  # Update the widget based on message type
268
295
  if last_message.type == MessageType.AGENT_RESPONSE:
@@ -282,25 +309,15 @@ class ChatView(VerticalScroll):
282
309
  copy_button.update_text_to_copy(last_message.content)
283
310
  else:
284
311
  # Handle other message types
285
- content = last_message.content
286
-
287
- # Apply the same rendering logic as in add_message
288
- if (
289
- "[" in content
290
- and "]" in content
291
- and (
292
- content.strip().startswith("$ ")
293
- or content.strip().startswith("git ")
294
- )
295
- ):
296
- # Treat as literal text
297
- last_widget.update(Text(content))
298
- else:
299
- # Try to render markup
300
- try:
301
- last_widget.update(Text.from_markup(content))
302
- except Exception:
303
- last_widget.update(Text(content))
312
+ # After the content concatenation above, content is always a string
313
+ # Try to parse markup when safe to do so
314
+ try:
315
+ # Try to parse as markup first - this handles rich styling correctly
316
+ last_widget.update(Text.from_markup(last_message.content))
317
+ except Exception:
318
+ # If markup parsing fails, fall back to plain text
319
+ # This handles cases where content contains literal square brackets
320
+ last_widget.update(Text(last_message.content))
304
321
 
305
322
  # Add the new message to our tracking lists
306
323
  self.messages.append(message)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "code-puppy"
7
- version = "0.0.157"
7
+ version = "0.0.159"
8
8
  description = "Code generation agent"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes
File without changes