kollabor 0.4.9__py3-none-any.whl → 0.4.15__py3-none-any.whl

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 (192) hide show
  1. agents/__init__.py +2 -0
  2. agents/coder/__init__.py +0 -0
  3. agents/coder/agent.json +4 -0
  4. agents/coder/api-integration.md +2150 -0
  5. agents/coder/cli-pretty.md +765 -0
  6. agents/coder/code-review.md +1092 -0
  7. agents/coder/database-design.md +1525 -0
  8. agents/coder/debugging.md +1102 -0
  9. agents/coder/dependency-management.md +1397 -0
  10. agents/coder/git-workflow.md +1099 -0
  11. agents/coder/refactoring.md +1454 -0
  12. agents/coder/security-hardening.md +1732 -0
  13. agents/coder/system_prompt.md +1448 -0
  14. agents/coder/tdd.md +1367 -0
  15. agents/creative-writer/__init__.py +0 -0
  16. agents/creative-writer/agent.json +4 -0
  17. agents/creative-writer/character-development.md +1852 -0
  18. agents/creative-writer/dialogue-craft.md +1122 -0
  19. agents/creative-writer/plot-structure.md +1073 -0
  20. agents/creative-writer/revision-editing.md +1484 -0
  21. agents/creative-writer/system_prompt.md +690 -0
  22. agents/creative-writer/worldbuilding.md +2049 -0
  23. agents/data-analyst/__init__.py +30 -0
  24. agents/data-analyst/agent.json +4 -0
  25. agents/data-analyst/data-visualization.md +992 -0
  26. agents/data-analyst/exploratory-data-analysis.md +1110 -0
  27. agents/data-analyst/pandas-data-manipulation.md +1081 -0
  28. agents/data-analyst/sql-query-optimization.md +881 -0
  29. agents/data-analyst/statistical-analysis.md +1118 -0
  30. agents/data-analyst/system_prompt.md +928 -0
  31. agents/default/__init__.py +0 -0
  32. agents/default/agent.json +4 -0
  33. agents/default/dead-code.md +794 -0
  34. agents/default/explore-agent-system.md +585 -0
  35. agents/default/system_prompt.md +1448 -0
  36. agents/kollabor/__init__.py +0 -0
  37. agents/kollabor/analyze-plugin-lifecycle.md +175 -0
  38. agents/kollabor/analyze-terminal-rendering.md +388 -0
  39. agents/kollabor/code-review.md +1092 -0
  40. agents/kollabor/debug-mcp-integration.md +521 -0
  41. agents/kollabor/debug-plugin-hooks.md +547 -0
  42. agents/kollabor/debugging.md +1102 -0
  43. agents/kollabor/dependency-management.md +1397 -0
  44. agents/kollabor/git-workflow.md +1099 -0
  45. agents/kollabor/inspect-llm-conversation.md +148 -0
  46. agents/kollabor/monitor-event-bus.md +558 -0
  47. agents/kollabor/profile-performance.md +576 -0
  48. agents/kollabor/refactoring.md +1454 -0
  49. agents/kollabor/system_prompt copy.md +1448 -0
  50. agents/kollabor/system_prompt.md +757 -0
  51. agents/kollabor/trace-command-execution.md +178 -0
  52. agents/kollabor/validate-config.md +879 -0
  53. agents/research/__init__.py +0 -0
  54. agents/research/agent.json +4 -0
  55. agents/research/architecture-mapping.md +1099 -0
  56. agents/research/codebase-analysis.md +1077 -0
  57. agents/research/dependency-audit.md +1027 -0
  58. agents/research/performance-profiling.md +1047 -0
  59. agents/research/security-review.md +1359 -0
  60. agents/research/system_prompt.md +492 -0
  61. agents/technical-writer/__init__.py +0 -0
  62. agents/technical-writer/agent.json +4 -0
  63. agents/technical-writer/api-documentation.md +2328 -0
  64. agents/technical-writer/changelog-management.md +1181 -0
  65. agents/technical-writer/readme-writing.md +1360 -0
  66. agents/technical-writer/style-guide.md +1410 -0
  67. agents/technical-writer/system_prompt.md +653 -0
  68. agents/technical-writer/tutorial-creation.md +1448 -0
  69. core/__init__.py +0 -2
  70. core/application.py +343 -88
  71. core/cli.py +229 -10
  72. core/commands/menu_renderer.py +463 -59
  73. core/commands/registry.py +14 -9
  74. core/commands/system_commands.py +2461 -14
  75. core/config/loader.py +151 -37
  76. core/config/service.py +18 -6
  77. core/events/bus.py +29 -9
  78. core/events/executor.py +205 -75
  79. core/events/models.py +27 -8
  80. core/fullscreen/command_integration.py +20 -24
  81. core/fullscreen/components/__init__.py +10 -1
  82. core/fullscreen/components/matrix_components.py +1 -2
  83. core/fullscreen/components/space_shooter_components.py +654 -0
  84. core/fullscreen/plugin.py +5 -0
  85. core/fullscreen/renderer.py +52 -13
  86. core/fullscreen/session.py +52 -15
  87. core/io/__init__.py +29 -5
  88. core/io/buffer_manager.py +6 -1
  89. core/io/config_status_view.py +7 -29
  90. core/io/core_status_views.py +267 -347
  91. core/io/input/__init__.py +25 -0
  92. core/io/input/command_mode_handler.py +711 -0
  93. core/io/input/display_controller.py +128 -0
  94. core/io/input/hook_registrar.py +286 -0
  95. core/io/input/input_loop_manager.py +421 -0
  96. core/io/input/key_press_handler.py +502 -0
  97. core/io/input/modal_controller.py +1011 -0
  98. core/io/input/paste_processor.py +339 -0
  99. core/io/input/status_modal_renderer.py +184 -0
  100. core/io/input_errors.py +5 -1
  101. core/io/input_handler.py +211 -2452
  102. core/io/key_parser.py +7 -0
  103. core/io/layout.py +15 -3
  104. core/io/message_coordinator.py +111 -2
  105. core/io/message_renderer.py +129 -4
  106. core/io/status_renderer.py +147 -607
  107. core/io/terminal_renderer.py +97 -51
  108. core/io/terminal_state.py +21 -4
  109. core/io/visual_effects.py +816 -165
  110. core/llm/agent_manager.py +1063 -0
  111. core/llm/api_adapters/__init__.py +44 -0
  112. core/llm/api_adapters/anthropic_adapter.py +432 -0
  113. core/llm/api_adapters/base.py +241 -0
  114. core/llm/api_adapters/openai_adapter.py +326 -0
  115. core/llm/api_communication_service.py +167 -113
  116. core/llm/conversation_logger.py +322 -16
  117. core/llm/conversation_manager.py +556 -30
  118. core/llm/file_operations_executor.py +84 -32
  119. core/llm/llm_service.py +934 -103
  120. core/llm/mcp_integration.py +541 -57
  121. core/llm/message_display_service.py +135 -18
  122. core/llm/plugin_sdk.py +1 -2
  123. core/llm/profile_manager.py +1183 -0
  124. core/llm/response_parser.py +274 -56
  125. core/llm/response_processor.py +16 -3
  126. core/llm/tool_executor.py +6 -1
  127. core/logging/__init__.py +2 -0
  128. core/logging/setup.py +34 -6
  129. core/models/resume.py +54 -0
  130. core/plugins/__init__.py +4 -2
  131. core/plugins/base.py +127 -0
  132. core/plugins/collector.py +23 -161
  133. core/plugins/discovery.py +37 -3
  134. core/plugins/factory.py +6 -12
  135. core/plugins/registry.py +5 -17
  136. core/ui/config_widgets.py +128 -28
  137. core/ui/live_modal_renderer.py +2 -1
  138. core/ui/modal_actions.py +5 -0
  139. core/ui/modal_overlay_renderer.py +0 -60
  140. core/ui/modal_renderer.py +268 -7
  141. core/ui/modal_state_manager.py +29 -4
  142. core/ui/widgets/base_widget.py +7 -0
  143. core/updates/__init__.py +10 -0
  144. core/updates/version_check_service.py +348 -0
  145. core/updates/version_comparator.py +103 -0
  146. core/utils/config_utils.py +685 -526
  147. core/utils/plugin_utils.py +1 -1
  148. core/utils/session_naming.py +111 -0
  149. fonts/LICENSE +21 -0
  150. fonts/README.md +46 -0
  151. fonts/SymbolsNerdFont-Regular.ttf +0 -0
  152. fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
  153. fonts/__init__.py +44 -0
  154. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/METADATA +54 -4
  155. kollabor-0.4.15.dist-info/RECORD +228 -0
  156. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/top_level.txt +2 -0
  157. plugins/agent_orchestrator/__init__.py +39 -0
  158. plugins/agent_orchestrator/activity_monitor.py +181 -0
  159. plugins/agent_orchestrator/file_attacher.py +77 -0
  160. plugins/agent_orchestrator/message_injector.py +135 -0
  161. plugins/agent_orchestrator/models.py +48 -0
  162. plugins/agent_orchestrator/orchestrator.py +403 -0
  163. plugins/agent_orchestrator/plugin.py +976 -0
  164. plugins/agent_orchestrator/xml_parser.py +191 -0
  165. plugins/agent_orchestrator_plugin.py +9 -0
  166. plugins/enhanced_input/box_styles.py +1 -0
  167. plugins/enhanced_input/color_engine.py +19 -4
  168. plugins/enhanced_input/config.py +2 -2
  169. plugins/enhanced_input_plugin.py +61 -11
  170. plugins/fullscreen/__init__.py +6 -2
  171. plugins/fullscreen/example_plugin.py +1035 -222
  172. plugins/fullscreen/setup_wizard_plugin.py +592 -0
  173. plugins/fullscreen/space_shooter_plugin.py +131 -0
  174. plugins/hook_monitoring_plugin.py +436 -78
  175. plugins/query_enhancer_plugin.py +66 -30
  176. plugins/resume_conversation_plugin.py +1494 -0
  177. plugins/save_conversation_plugin.py +98 -32
  178. plugins/system_commands_plugin.py +70 -56
  179. plugins/tmux_plugin.py +154 -78
  180. plugins/workflow_enforcement_plugin.py +94 -92
  181. system_prompt/default.md +952 -886
  182. core/io/input_mode_manager.py +0 -402
  183. core/io/modal_interaction_handler.py +0 -315
  184. core/io/raw_input_processor.py +0 -946
  185. core/storage/__init__.py +0 -5
  186. core/storage/state_manager.py +0 -84
  187. core/ui/widget_integration.py +0 -222
  188. core/utils/key_reader.py +0 -171
  189. kollabor-0.4.9.dist-info/RECORD +0 -128
  190. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/WHEEL +0 -0
  191. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/entry_points.txt +0 -0
  192. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/licenses/LICENSE +0 -0
core/io/key_parser.py CHANGED
@@ -1,3 +1,10 @@
1
+ """Keyboard input parsing for terminal applications.
2
+
3
+ This module provides comprehensive keyboard input parsing, including
4
+ single character input, control key detection, and escape sequence
5
+ handling for terminal applications.
6
+ """
7
+
1
8
  import logging
2
9
  from dataclasses import dataclass
3
10
  from enum import Enum
core/io/layout.py CHANGED
@@ -1,4 +1,8 @@
1
- """Layout management system for terminal rendering."""
1
+ """Layout management system for terminal rendering.
2
+
3
+ This module provides comprehensive layout management for terminal rendering,
4
+ including area management, adaptive sizing, and thinking animation support.
5
+ """
2
6
 
3
7
  import re
4
8
  from collections import deque
@@ -166,16 +170,24 @@ class ThinkingAnimationManager:
166
170
  Returns:
167
171
  List of formatted display lines.
168
172
  """
173
+ import time
174
+
169
175
  if not self.is_active or not self.messages:
170
176
  return []
171
177
 
172
178
  lines = []
173
179
  spinner = self.get_next_frame()
174
180
 
181
+ # Calculate elapsed time
182
+ elapsed = ""
183
+ if self.start_time:
184
+ duration = time.time() - self.start_time
185
+ elapsed = f" ({duration:.0f}s - esc to cancel)"
186
+
175
187
  for i, msg in enumerate(self.messages):
176
188
  if i == len(self.messages) - 1:
177
- # Main thinking line with spinner
178
- formatted_text = apply_effect_func(f"{spinner} Thinking: {msg}")
189
+ # Main thinking line with spinner and elapsed time
190
+ formatted_text = apply_effect_func(f"{spinner} Thinking{elapsed}: {msg}")
179
191
  lines.append(formatted_text)
180
192
  else:
181
193
  # Secondary thinking line
@@ -3,22 +3,30 @@
3
3
  This coordinator solves the fundamental race condition where multiple
4
4
  message writing systems interfere with each other, causing messages
5
5
  to be overwritten or cleared unexpectedly.
6
+
7
+ IMPORTANT: All terminal state changes (input rendering, clearing, buffer
8
+ transitions) should go through this coordinator to prevent state bugs.
9
+
10
+ This module provides atomic message display coordination and unified
11
+ state management to prevent interference between different message
12
+ writing systems.
6
13
  """
7
14
 
8
15
  import logging
9
- from typing import List, Tuple, Dict, Any
16
+ from typing import List, Tuple, Dict, Any, Optional
10
17
 
11
18
  logger = logging.getLogger(__name__)
12
19
 
13
20
 
14
21
  class MessageDisplayCoordinator:
15
- """Coordinates message display to prevent interference between systems.
22
+ """Coordinates message display AND render state to prevent interference.
16
23
 
17
24
  Key Features:
18
25
  - Atomic message sequences (all messages display together)
19
26
  - Unified state management (prevents clearing conflicts)
20
27
  - Proper ordering (system messages before responses)
21
28
  - Protection from interference (no race conditions)
29
+ - Buffer transition management (modal open/close state preservation)
22
30
  """
23
31
 
24
32
  def __init__(self, terminal_renderer):
@@ -31,8 +39,26 @@ class MessageDisplayCoordinator:
31
39
  self.message_queue: List[Tuple[str, str, Dict[str, Any]]] = []
32
40
  self.is_displaying = False
33
41
 
42
+ # Saved state for buffer transitions (modal, fullscreen, etc.)
43
+ self._saved_main_buffer_state: Optional[Dict[str, Any]] = None
44
+ self._in_alternate_buffer = False
45
+
34
46
  logger.debug("MessageDisplayCoordinator initialized")
35
47
 
48
+ def _capture_render_state(self) -> Dict[str, Any]:
49
+ """Capture current render state for later restoration.
50
+
51
+ Returns:
52
+ Dictionary containing render state snapshot.
53
+ """
54
+ return {
55
+ "writing_messages": self.terminal_renderer.writing_messages,
56
+ "input_line_written": self.terminal_renderer.input_line_written,
57
+ "last_line_count": self.terminal_renderer.last_line_count,
58
+ "conversation_active": self.terminal_renderer.conversation_active,
59
+ "thinking_active": self.terminal_renderer.thinking_active,
60
+ }
61
+
36
62
  def queue_message(self, message_type: str, content: str, **kwargs) -> None:
37
63
  """Queue a message for coordinated display.
38
64
 
@@ -92,6 +118,11 @@ class MessageDisplayCoordinator:
92
118
  self.terminal_renderer.writing_messages = False
93
119
  self.message_queue.clear()
94
120
  self.is_displaying = False
121
+ # Reset render state for clean input box rendering
122
+ # This prevents duplicate input boxes when render loop resumes
123
+ self.terminal_renderer.input_line_written = False
124
+ self.terminal_renderer.last_line_count = 0
125
+ self.terminal_renderer.invalidate_render_cache()
95
126
  logger.debug("Completed atomic message display")
96
127
 
97
128
  def display_message_sequence(
@@ -169,6 +200,16 @@ class MessageDisplayCoordinator:
169
200
  format_style=MessageFormat.HIGHLIGHTED, # Uses red color from _format_highlighted
170
201
  **kwargs,
171
202
  )
203
+ elif message_type == "info":
204
+ # For info messages, use MessageType.SYSTEM with plain format
205
+ from .message_renderer import MessageType, MessageFormat
206
+
207
+ self.terminal_renderer.message_renderer.conversation_renderer.write_message(
208
+ content,
209
+ message_type=MessageType.SYSTEM,
210
+ format_style=MessageFormat.PLAIN,
211
+ **kwargs,
212
+ )
172
213
  else:
173
214
  logger.warning(f"Unknown message type: {message_type}")
174
215
  # Fallback to regular message
@@ -202,3 +243,71 @@ class MessageDisplayCoordinator:
202
243
  "is_displaying": self.is_displaying,
203
244
  "queued_types": [msg[0] for msg in self.message_queue],
204
245
  }
246
+
247
+ # === Buffer Transition Management ===
248
+ # These methods handle state preservation during modal/fullscreen transitions
249
+
250
+ def enter_alternate_buffer(self) -> None:
251
+ """Mark entering alternate buffer and pause render loop.
252
+
253
+ Call this BEFORE opening a modal or entering fullscreen mode.
254
+ Captures current render state for potential restoration.
255
+ """
256
+ if self._in_alternate_buffer:
257
+ logger.warning("Already in alternate buffer")
258
+ return
259
+
260
+ # Capture state BEFORE modifying anything
261
+ self._saved_main_buffer_state = self._capture_render_state()
262
+ logger.debug(f"Captured render state: {self._saved_main_buffer_state}")
263
+
264
+ self._in_alternate_buffer = True
265
+ # Prevent render loop interference during modal
266
+ self.terminal_renderer.writing_messages = True
267
+ logger.debug("Entered alternate buffer mode")
268
+
269
+ def exit_alternate_buffer(self, restore_state: bool = False) -> None:
270
+ """Exit alternate buffer mode and reset render state.
271
+
272
+ Call this AFTER closing a modal or exiting fullscreen mode.
273
+
274
+ Args:
275
+ restore_state: If True, restore captured state. If False (default),
276
+ reset to clean state for fresh input rendering.
277
+ """
278
+ if not self._in_alternate_buffer:
279
+ logger.warning("Not in alternate buffer")
280
+ return
281
+
282
+ self._in_alternate_buffer = False
283
+
284
+ if restore_state and self._saved_main_buffer_state:
285
+ # Restore previously captured state
286
+ self.terminal_renderer.writing_messages = self._saved_main_buffer_state[
287
+ "writing_messages"
288
+ ]
289
+ self.terminal_renderer.input_line_written = self._saved_main_buffer_state[
290
+ "input_line_written"
291
+ ]
292
+ self.terminal_renderer.last_line_count = self._saved_main_buffer_state[
293
+ "last_line_count"
294
+ ]
295
+ logger.debug(f"Restored render state: {self._saved_main_buffer_state}")
296
+ else:
297
+ # Reset to clean state (default - prevents duplicate input boxes)
298
+ self.terminal_renderer.writing_messages = False
299
+ self.terminal_renderer.input_line_written = False
300
+ self.terminal_renderer.last_line_count = 0
301
+ logger.debug("Reset to clean render state")
302
+
303
+ # Always invalidate cache after buffer transition
304
+ self.terminal_renderer.invalidate_render_cache()
305
+ self._saved_main_buffer_state = None
306
+
307
+ def get_saved_state(self) -> Optional[Dict[str, Any]]:
308
+ """Get the saved render state (for debugging).
309
+
310
+ Returns:
311
+ Saved state dict if in alternate buffer, None otherwise.
312
+ """
313
+ return self._saved_main_buffer_state
@@ -1,6 +1,12 @@
1
- """Message rendering system for conversation display."""
1
+ """Message rendering system for conversation display.
2
+
3
+ This module provides comprehensive message rendering for conversation display,
4
+ including message formatting, conversation buffer management, and streaming
5
+ response support.
6
+ """
2
7
 
3
8
  import logging
9
+ import re
4
10
  from collections import deque
5
11
  from dataclasses import dataclass
6
12
  from enum import Enum
@@ -10,6 +16,102 @@ from typing import List, Optional, Dict, Any, Callable
10
16
  logger = logging.getLogger(__name__)
11
17
 
12
18
 
19
+ class DisplayFilterRegistry:
20
+ """Registry for message display filters.
21
+
22
+ Plugins can register filter functions that transform message content
23
+ before display. This allows plugins to strip/transform their own
24
+ XML commands without hardcoding patterns in core.
25
+
26
+ Example:
27
+ # In plugin initialize():
28
+ DisplayFilterRegistry.register(
29
+ "my_plugin",
30
+ self._strip_my_xml,
31
+ message_types=[MessageType.ASSISTANT]
32
+ )
33
+
34
+ # Filter function signature:
35
+ def _strip_my_xml(content: str, message_type: 'MessageType') -> str:
36
+ return re.sub(r"<my-tag>.*?</my-tag>", "", content)
37
+ """
38
+
39
+ _filters: Dict[str, Dict[str, Any]] = {}
40
+
41
+ @classmethod
42
+ def register(
43
+ cls,
44
+ name: str,
45
+ filter_fn: Callable[[str, "MessageType"], str],
46
+ message_types: Optional[List["MessageType"]] = None,
47
+ priority: int = 100,
48
+ ) -> None:
49
+ """Register a display filter.
50
+
51
+ Args:
52
+ name: Unique name for this filter (usually plugin name).
53
+ filter_fn: Function that takes (content, message_type) and returns transformed content.
54
+ message_types: List of message types to apply filter to (None = all types).
55
+ priority: Execution priority (higher = runs first).
56
+ """
57
+ cls._filters[name] = {
58
+ "fn": filter_fn,
59
+ "message_types": message_types,
60
+ "priority": priority,
61
+ }
62
+ logger.debug(f"Registered display filter: {name}")
63
+
64
+ @classmethod
65
+ def unregister(cls, name: str) -> None:
66
+ """Unregister a display filter.
67
+
68
+ Args:
69
+ name: Name of the filter to remove.
70
+ """
71
+ if name in cls._filters:
72
+ del cls._filters[name]
73
+ logger.debug(f"Unregistered display filter: {name}")
74
+
75
+ @classmethod
76
+ def apply_filters(cls, content: str, message_type: "MessageType") -> str:
77
+ """Apply all registered filters to content.
78
+
79
+ Args:
80
+ content: Original message content.
81
+ message_type: Type of message being displayed.
82
+
83
+ Returns:
84
+ Transformed content after all applicable filters.
85
+ """
86
+ if not cls._filters:
87
+ return content
88
+
89
+ # Sort by priority (higher first)
90
+ sorted_filters = sorted(
91
+ cls._filters.items(),
92
+ key=lambda x: x[1]["priority"],
93
+ reverse=True,
94
+ )
95
+
96
+ for name, filter_info in sorted_filters:
97
+ # Check if filter applies to this message type
98
+ allowed_types = filter_info["message_types"]
99
+ if allowed_types is not None and message_type not in allowed_types:
100
+ continue
101
+
102
+ try:
103
+ content = filter_info["fn"](content, message_type)
104
+ except Exception as e:
105
+ logger.error(f"Display filter '{name}' failed: {e}")
106
+
107
+ return content
108
+
109
+ @classmethod
110
+ def clear(cls) -> None:
111
+ """Clear all registered filters (useful for testing)."""
112
+ cls._filters.clear()
113
+
114
+
13
115
  class MessageType(Enum):
14
116
  """Types of messages in conversation."""
15
117
 
@@ -281,12 +383,19 @@ class ConversationRenderer:
281
383
  immediate_display: Whether to display immediately.
282
384
  **metadata: Additional message metadata.
283
385
  """
386
+ # Apply registered display filters (plugins can strip their XML commands)
387
+ display_content = DisplayFilterRegistry.apply_filters(content, message_type)
388
+ display_content = display_content.strip()
389
+
390
+ if not display_content:
391
+ return # Nothing to display after filtering
392
+
284
393
  # Add to buffer
285
- self.buffer.add_message(content, message_type, format_style, **metadata)
394
+ self.buffer.add_message(display_content, message_type, format_style, **metadata)
286
395
 
287
396
  # Display immediately if requested
288
397
  if immediate_display:
289
- self._display_message_immediately(content, message_type, format_style)
398
+ self._display_message_immediately(display_content, message_type, format_style)
290
399
 
291
400
  def start_streaming_response(self) -> None:
292
401
  """Start a streaming response by setting up the display area."""
@@ -308,8 +417,19 @@ class ConversationRenderer:
308
417
  content: User message content.
309
418
  **metadata: Additional metadata.
310
419
  """
420
+ # Strip hidden system message blocks (used for plugin instruction injection)
421
+ display_content = re.sub(
422
+ r"<sys_msg>.*?</sys_msg>\s*",
423
+ "",
424
+ content,
425
+ flags=re.DOTALL,
426
+ ).strip()
427
+
428
+ if not display_content:
429
+ return # Nothing to display after stripping
430
+
311
431
  self.write_message(
312
- content, MessageType.USER, MessageFormat.GRADIENT, **metadata
432
+ display_content, MessageType.USER, MessageFormat.GRADIENT, **metadata
313
433
  )
314
434
 
315
435
  def write_system_message(self, content: str, **metadata) -> None:
@@ -391,6 +511,11 @@ class ConversationRenderer:
391
511
 
392
512
  try:
393
513
  # Write to terminal
514
+ line_count = formatted_content.count('\n') + 1
515
+ logger.info(
516
+ f"DISPLAY: Printing {message_type.value} message: "
517
+ f"{len(content)} chars, {line_count} lines"
518
+ )
394
519
  print(formatted_content, flush=self.flush_immediately)
395
520
  # Add blank line for visual separation between messages
396
521
  print("", flush=self.flush_immediately)