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
@@ -0,0 +1,502 @@
1
+ """Key press handler component for Kollabor CLI.
2
+
3
+ Responsible for processing keyboard input and dispatching to appropriate handlers.
4
+ Handles character processing, key press dispatch, Enter/Escape keys, and hook integration.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import time
10
+ from typing import Dict, Any, Optional, Callable, Awaitable
11
+
12
+ from ...events import EventType
13
+ from ...events.models import CommandMode
14
+ from ..key_parser import KeyParser, KeyPress, KeyType as KeyTypeEnum
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class KeyPressHandler:
20
+ """Handles key press processing and dispatch.
21
+
22
+ This component manages:
23
+ - Character processing with paste detection hooks
24
+ - Key press event emission and plugin integration
25
+ - Key dispatch to specific handlers (Enter, Escape, arrows, etc.)
26
+ - Command mode detection and routing
27
+ - Status view navigation (Alt+Left/Right arrows)
28
+
29
+ Attributes:
30
+ buffer_manager: Buffer manager for text manipulation.
31
+ key_parser: Parser for converting raw input to KeyPress objects.
32
+ event_bus: Event bus for emitting key press events.
33
+ error_handler: Error handler for key processing errors.
34
+ display_controller: Controller for display updates.
35
+ renderer: Terminal renderer for display clearing.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ buffer_manager: Any,
41
+ key_parser: KeyParser,
42
+ event_bus: Any,
43
+ error_handler: Any,
44
+ display_controller: Any,
45
+ paste_processor: Any,
46
+ renderer: Any,
47
+ command_mode_handler: Optional[Any] = None,
48
+ ) -> None:
49
+ """Initialize the key press handler.
50
+
51
+ Args:
52
+ buffer_manager: Buffer manager instance.
53
+ key_parser: Key parser instance.
54
+ event_bus: Event bus for emitting events.
55
+ error_handler: Error handler for key processing errors.
56
+ display_controller: Display controller for UI updates.
57
+ paste_processor: Paste processor for paste detection.
58
+ renderer: Terminal renderer for display clearing.
59
+ command_mode_handler: Optional handler for command mode operations.
60
+ """
61
+ self.buffer_manager = buffer_manager
62
+ self.key_parser = key_parser
63
+ self.event_bus = event_bus
64
+ self.error_handler = error_handler
65
+ self.display_controller = display_controller
66
+ self.paste_processor = paste_processor
67
+ self.renderer = renderer
68
+ self.command_mode_handler = command_mode_handler
69
+
70
+ # Callbacks for methods we don't own (set by parent)
71
+ self._enter_command_mode_callback: Optional[Callable[[], Awaitable[None]]] = None
72
+ self._handle_command_mode_keypress_callback: Optional[
73
+ Callable[[KeyPress], Awaitable[bool]]
74
+ ] = None
75
+ self._handle_status_view_previous_callback: Optional[
76
+ Callable[[], Awaitable[None]]
77
+ ] = None
78
+ self._handle_status_view_next_callback: Optional[
79
+ Callable[[], Awaitable[None]]
80
+ ] = None
81
+ self._expand_paste_placeholders_callback: Optional[
82
+ Callable[[str], str]
83
+ ] = None
84
+
85
+ # State tracking
86
+ self._command_mode = CommandMode.NORMAL
87
+
88
+ logger.debug("KeyPressHandler initialized")
89
+
90
+ def set_callbacks(
91
+ self,
92
+ enter_command_mode: Optional[Callable[[], Awaitable[None]]] = None,
93
+ handle_command_mode_keypress: Optional[
94
+ Callable[[KeyPress], Awaitable[bool]]
95
+ ] = None,
96
+ handle_status_view_previous: Optional[Callable[[], Awaitable[None]]] = None,
97
+ handle_status_view_next: Optional[Callable[[], Awaitable[None]]] = None,
98
+ expand_paste_placeholders: Optional[Callable[[str], str]] = None,
99
+ ) -> None:
100
+ """Set callbacks for methods owned by parent InputHandler.
101
+
102
+ Args:
103
+ enter_command_mode: Callback to enter command mode.
104
+ handle_command_mode_keypress: Callback to handle command mode keys.
105
+ handle_status_view_previous: Callback to switch to previous status view.
106
+ handle_status_view_next: Callback to switch to next status view.
107
+ expand_paste_placeholders: Callback to expand paste placeholders.
108
+ """
109
+ self._enter_command_mode_callback = enter_command_mode
110
+ self._handle_command_mode_keypress_callback = handle_command_mode_keypress
111
+ self._handle_status_view_previous_callback = handle_status_view_previous
112
+ self._handle_status_view_next_callback = handle_status_view_next
113
+ self._expand_paste_placeholders_callback = expand_paste_placeholders
114
+
115
+ @property
116
+ def command_mode(self) -> CommandMode:
117
+ """Get current command mode.
118
+
119
+ Delegates to command_mode_handler if available for consistent state.
120
+ """
121
+ if self.command_mode_handler:
122
+ return self.command_mode_handler.command_mode
123
+ return self._command_mode
124
+
125
+ @command_mode.setter
126
+ def command_mode(self, value: CommandMode) -> None:
127
+ """Set current command mode."""
128
+ if self.command_mode_handler:
129
+ self.command_mode_handler.command_mode = value
130
+ self._command_mode = value
131
+
132
+ async def process_character(self, char: str) -> None:
133
+ """Process a single character input.
134
+
135
+ Args:
136
+ char: Character received from terminal.
137
+ """
138
+ try:
139
+ current_time = time.time()
140
+
141
+ # Check for slash command initiation
142
+ # (before parsing for immediate response)
143
+ if (
144
+ char == "/"
145
+ and self.buffer_manager.is_empty
146
+ and self.command_mode == CommandMode.NORMAL
147
+ ):
148
+ if self._enter_command_mode_callback:
149
+ await self._enter_command_mode_callback()
150
+ else:
151
+ logger.warning(
152
+ "Slash command detected but no enter_command_mode callback set"
153
+ )
154
+ return
155
+
156
+ # SECONDARY PASTE DETECTION:
157
+ # Character-by-character timing (DISABLED)
158
+ # This is a fallback system - primary chunk detection
159
+ # above handles most cases
160
+ if self.paste_processor.paste_detection_enabled:
161
+ # Currently False - secondary system disabled
162
+ paste_handled = await self.paste_processor.simple_paste_detection(
163
+ char, current_time
164
+ )
165
+ if paste_handled:
166
+ # Character consumed by paste detection,
167
+ # skip normal processing
168
+ return
169
+
170
+ # Parse character into structured key press
171
+ # (this handles escape sequences)
172
+ key_press = self.key_parser.parse_char(char)
173
+ if not key_press:
174
+ # For command modes, add timeout-based
175
+ # standalone escape detection
176
+ if self.command_mode in (CommandMode.MODAL, CommandMode.STATUS_MODAL, CommandMode.LIVE_MODAL, CommandMode.MENU_POPUP):
177
+ # Schedule delayed check for standalone escape
178
+ # (100ms delay)
179
+ async def delayed_escape_check():
180
+ await asyncio.sleep(0.1)
181
+ standalone_escape = (
182
+ self.key_parser.check_for_standalone_escape()
183
+ )
184
+ if standalone_escape:
185
+ if self._handle_command_mode_keypress_callback:
186
+ await self._handle_command_mode_keypress_callback(
187
+ standalone_escape
188
+ )
189
+
190
+ asyncio.create_task(delayed_escape_check())
191
+ # Incomplete escape sequence - wait for more characters
192
+ return
193
+
194
+ # Check for slash command mode handling AFTER parsing
195
+ # (so arrow keys work)
196
+ if self.command_mode != CommandMode.NORMAL:
197
+ logger.info(
198
+ f"Processing key '{key_press.name}' "
199
+ f"in command mode: {self.command_mode}"
200
+ )
201
+ if self._handle_command_mode_keypress_callback:
202
+ handled = await self._handle_command_mode_keypress_callback(
203
+ key_press
204
+ )
205
+ if handled:
206
+ return
207
+
208
+ # Emit key press event for plugins
209
+ key_result = await self.event_bus.emit_with_hooks(
210
+ EventType.KEY_PRESS,
211
+ {
212
+ "key": key_press.name,
213
+ "char_code": key_press.code,
214
+ "key_type": key_press.type.value,
215
+ "modifiers": key_press.modifiers,
216
+ },
217
+ "input",
218
+ )
219
+
220
+ # Check if any plugin handled this key
221
+ prevent_default = self._check_prevent_default(key_result)
222
+
223
+ # Process key if not prevented by plugins
224
+ if not prevent_default:
225
+ await self._handle_key_press(key_press)
226
+
227
+ # Update renderer
228
+ await self.display_controller.update_display()
229
+
230
+ except Exception as e:
231
+ from ..input_errors import ErrorType, ErrorSeverity
232
+
233
+ await self.error_handler.handle_error(
234
+ ErrorType.PARSING_ERROR,
235
+ f"Error processing character: {e}",
236
+ ErrorSeverity.MEDIUM,
237
+ {"char": repr(char), "buffer_manager": self.buffer_manager},
238
+ )
239
+
240
+ def _check_prevent_default(self, key_result: Dict[str, Any]) -> bool:
241
+ """Check if plugins want to prevent default key handling.
242
+
243
+ Args:
244
+ key_result: Result from key press event.
245
+
246
+ Returns:
247
+ True if default handling should be prevented.
248
+ """
249
+ if "main" in key_result:
250
+ for hook_result in key_result["main"].values():
251
+ if isinstance(hook_result, dict) and hook_result.get(
252
+ "prevent_default"
253
+ ):
254
+ return True
255
+ return False
256
+
257
+ async def _handle_key_press(self, key_press: KeyPress) -> None:
258
+ """Handle a parsed key press.
259
+
260
+ Args:
261
+ key_press: Parsed key press event.
262
+ """
263
+ # Process key press
264
+ try:
265
+ # Log all key presses for debugging
266
+ logger.info(
267
+ f"Key press: name='{key_press.name}', "
268
+ f"char='{key_press.char}', code={key_press.code}, "
269
+ f"type={key_press.type}, "
270
+ f"modifiers={getattr(key_press, 'modifiers', None)}"
271
+ )
272
+
273
+ # CRITICAL FIX: Modal input isolation
274
+ # capture ALL input when in modal mode
275
+ if self.command_mode == CommandMode.MODAL:
276
+ logger.info(
277
+ f"Modal mode active - routing ALL input "
278
+ f"to modal handler: {key_press.name}"
279
+ )
280
+ if self._handle_command_mode_keypress_callback:
281
+ await self._handle_command_mode_keypress_callback(key_press)
282
+ return
283
+
284
+ # Handle control keys
285
+ if self.key_parser.is_control_key(key_press, "Ctrl+C"):
286
+ logger.info("Ctrl+C received")
287
+ raise KeyboardInterrupt
288
+
289
+ elif self.key_parser.is_control_key(key_press, "Enter"):
290
+ await self._handle_enter()
291
+
292
+ elif self.key_parser.is_control_key(key_press, "Backspace"):
293
+ self.buffer_manager.delete_char()
294
+
295
+ elif key_press.name == "Escape":
296
+ await self._handle_escape()
297
+
298
+ elif key_press.name == "Delete":
299
+ self.buffer_manager.delete_forward()
300
+
301
+ # Handle arrow keys for cursor movement and history
302
+ elif key_press.name == "ArrowLeft":
303
+ moved = self.buffer_manager.move_cursor("left")
304
+ if moved:
305
+ logger.debug(
306
+ f"Arrow Left: cursor moved to position {self.buffer_manager.cursor_position}"
307
+ )
308
+ await self.display_controller.update_display(force_render=True)
309
+
310
+ elif key_press.name == "ArrowRight":
311
+ moved = self.buffer_manager.move_cursor("right")
312
+ if moved:
313
+ logger.debug(
314
+ f"Arrow Right: cursor moved to position {self.buffer_manager.cursor_position}"
315
+ )
316
+ await self.display_controller.update_display(force_render=True)
317
+
318
+ elif key_press.name == "ArrowUp":
319
+ self.buffer_manager.navigate_history("up")
320
+ await self.display_controller.update_display(force_render=True)
321
+
322
+ elif key_press.name == "ArrowDown":
323
+ self.buffer_manager.navigate_history("down")
324
+ await self.display_controller.update_display(force_render=True)
325
+
326
+ # Handle Home/End keys
327
+ elif key_press.name == "Home":
328
+ self.buffer_manager.move_to_start()
329
+ await self.display_controller.update_display(force_render=True)
330
+
331
+ elif key_press.name == "End":
332
+ self.buffer_manager.move_to_end()
333
+ await self.display_controller.update_display(force_render=True)
334
+
335
+ # Handle Option/Alt+Arrow keys for status view navigation
336
+ # Support both Alt+Arrow and Alt+b/f (macOS terminals often send the latter)
337
+ elif key_press.name in ("Alt+ArrowLeft", "Alt+b"):
338
+ logger.info(
339
+ f"{key_press.name} detected - switching to previous status view"
340
+ )
341
+ if self._handle_status_view_previous_callback:
342
+ await self._handle_status_view_previous_callback()
343
+
344
+ elif key_press.name in ("Alt+ArrowRight", "Alt+f"):
345
+ logger.info(
346
+ f"{key_press.name} detected - switching to next status view"
347
+ )
348
+ if self._handle_status_view_next_callback:
349
+ await self._handle_status_view_next_callback()
350
+
351
+ # Handle Cmd key combinations (mapped to Ctrl sequences on macOS)
352
+ elif self.key_parser.is_control_key(key_press, "Ctrl+A"):
353
+ logger.info("Ctrl+A (Cmd+Left) - moving cursor to start")
354
+ self.buffer_manager.move_to_start()
355
+ await self.display_controller.update_display(force_render=True)
356
+
357
+ elif self.key_parser.is_control_key(key_press, "Ctrl+E"):
358
+ logger.info("Ctrl+E (Cmd+Right) - moving cursor to end")
359
+ self.buffer_manager.move_to_end()
360
+ await self.display_controller.update_display(force_render=True)
361
+
362
+ elif self.key_parser.is_control_key(key_press, "Ctrl+U"):
363
+ logger.info("Ctrl+U (Cmd+Backspace) - clearing line")
364
+ self.buffer_manager.clear()
365
+ await self.display_controller.update_display(force_render=True)
366
+
367
+ # Handle printable characters
368
+ elif self.key_parser.is_printable_char(key_press):
369
+ # Normal character processing
370
+ success = self.buffer_manager.insert_char(key_press.char)
371
+ if not success:
372
+ from ..input_errors import ErrorType, ErrorSeverity
373
+
374
+ await self.error_handler.handle_error(
375
+ ErrorType.BUFFER_ERROR,
376
+ "Failed to insert character - buffer limit reached",
377
+ ErrorSeverity.LOW,
378
+ {
379
+ "char": key_press.char,
380
+ "buffer_manager": self.buffer_manager,
381
+ },
382
+ )
383
+
384
+ # Handle other special keys (F1-F12, etc.)
385
+ elif key_press.type == KeyTypeEnum.EXTENDED:
386
+ logger.debug(f"Extended key pressed: {key_press.name}")
387
+ # Could emit special events for function keys, etc.
388
+
389
+ except Exception as e:
390
+ from ..input_errors import ErrorType, ErrorSeverity
391
+
392
+ await self.error_handler.handle_error(
393
+ ErrorType.EVENT_ERROR,
394
+ f"Error handling key press: {e}",
395
+ ErrorSeverity.MEDIUM,
396
+ {
397
+ "key_press": key_press,
398
+ "buffer_manager": self.buffer_manager,
399
+ },
400
+ )
401
+
402
+ async def _handle_enter(self) -> None:
403
+ """Handle Enter key press with enhanced validation."""
404
+ try:
405
+ if self.buffer_manager.is_empty:
406
+ return
407
+
408
+ # Validate input before processing
409
+ validation_errors = self.buffer_manager.validate_content()
410
+ if validation_errors:
411
+ for error in validation_errors:
412
+ logger.warning(f"Input validation warning: {error}")
413
+
414
+ # Get message and clear buffer
415
+ message = self.buffer_manager.get_content_and_clear()
416
+
417
+ # Check if this is a slash command - handle immediately without paste expansion
418
+ if message.strip().startswith("/"):
419
+ logger.info(
420
+ f"Detected slash command, bypassing paste expansion: '{message}'"
421
+ )
422
+ expanded_message = message
423
+ else:
424
+ # GENIUS PASTE BUCKET: Immediate expansion - no waiting needed!
425
+ logger.debug(f"GENIUS SUBMIT: Original message: '{message}'")
426
+ logger.debug(
427
+ f"GENIUS SUBMIT: Paste bucket contains: {list(self.paste_processor.paste_bucket.keys())}"
428
+ )
429
+
430
+ if self._expand_paste_placeholders_callback:
431
+ expanded_message = self._expand_paste_placeholders_callback(
432
+ message
433
+ )
434
+ else:
435
+ # Fallback to direct expansion if callback not set
436
+ expanded_message = self.paste_processor.expand_paste_placeholders(
437
+ message
438
+ )
439
+
440
+ logger.debug(
441
+ f"GENIUS SUBMIT: Final expanded: '{expanded_message[:100]}...' ({len(expanded_message)} chars)"
442
+ )
443
+
444
+ # Add to history (with expanded content)
445
+ self.buffer_manager.add_to_history(expanded_message)
446
+
447
+ # CRITICAL: Clear the input display before emitting event
448
+ # This matches the original InputHandler._handle_enter behavior
449
+ self.renderer.input_buffer = ""
450
+ self.renderer.clear_active_area()
451
+
452
+ # Emit user input event (with expanded content!)
453
+ await self.event_bus.emit_with_hooks(
454
+ EventType.USER_INPUT,
455
+ {
456
+ "message": expanded_message,
457
+ "validation_errors": validation_errors,
458
+ },
459
+ "user",
460
+ )
461
+
462
+ logger.debug(
463
+ f"Processed user input: {message[:100]}..."
464
+ if len(message) > 100
465
+ else f"Processed user input: {message}"
466
+ )
467
+
468
+ except Exception as e:
469
+ from ..input_errors import ErrorType, ErrorSeverity
470
+
471
+ await self.error_handler.handle_error(
472
+ ErrorType.EVENT_ERROR,
473
+ f"Error handling Enter key: {e}",
474
+ ErrorSeverity.HIGH,
475
+ {"buffer_manager": self.buffer_manager},
476
+ )
477
+
478
+ async def _handle_escape(self) -> None:
479
+ """Handle Escape key press for request cancellation."""
480
+ try:
481
+ logger.info("_handle_escape called - emitting CANCEL_REQUEST event")
482
+
483
+ # Emit cancellation event
484
+ result = await self.event_bus.emit_with_hooks(
485
+ EventType.CANCEL_REQUEST,
486
+ {"reason": "user_escape", "source": "input_handler"},
487
+ "input",
488
+ )
489
+
490
+ logger.info(
491
+ f"ESC key pressed - cancellation request sent, result: {result}"
492
+ )
493
+
494
+ except Exception as e:
495
+ from ..input_errors import ErrorType, ErrorSeverity
496
+
497
+ await self.error_handler.handle_error(
498
+ ErrorType.EVENT_ERROR,
499
+ f"Error handling Escape key: {e}",
500
+ ErrorSeverity.MEDIUM,
501
+ {"buffer_manager": self.buffer_manager},
502
+ )