kollabor 0.4.9__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 (128) hide show
  1. core/__init__.py +18 -0
  2. core/application.py +578 -0
  3. core/cli.py +193 -0
  4. core/commands/__init__.py +43 -0
  5. core/commands/executor.py +277 -0
  6. core/commands/menu_renderer.py +319 -0
  7. core/commands/parser.py +186 -0
  8. core/commands/registry.py +331 -0
  9. core/commands/system_commands.py +479 -0
  10. core/config/__init__.py +7 -0
  11. core/config/llm_task_config.py +110 -0
  12. core/config/loader.py +501 -0
  13. core/config/manager.py +112 -0
  14. core/config/plugin_config_manager.py +346 -0
  15. core/config/plugin_schema.py +424 -0
  16. core/config/service.py +399 -0
  17. core/effects/__init__.py +1 -0
  18. core/events/__init__.py +12 -0
  19. core/events/bus.py +129 -0
  20. core/events/executor.py +154 -0
  21. core/events/models.py +258 -0
  22. core/events/processor.py +176 -0
  23. core/events/registry.py +289 -0
  24. core/fullscreen/__init__.py +19 -0
  25. core/fullscreen/command_integration.py +290 -0
  26. core/fullscreen/components/__init__.py +12 -0
  27. core/fullscreen/components/animation.py +258 -0
  28. core/fullscreen/components/drawing.py +160 -0
  29. core/fullscreen/components/matrix_components.py +177 -0
  30. core/fullscreen/manager.py +302 -0
  31. core/fullscreen/plugin.py +204 -0
  32. core/fullscreen/renderer.py +282 -0
  33. core/fullscreen/session.py +324 -0
  34. core/io/__init__.py +52 -0
  35. core/io/buffer_manager.py +362 -0
  36. core/io/config_status_view.py +272 -0
  37. core/io/core_status_views.py +410 -0
  38. core/io/input_errors.py +313 -0
  39. core/io/input_handler.py +2655 -0
  40. core/io/input_mode_manager.py +402 -0
  41. core/io/key_parser.py +344 -0
  42. core/io/layout.py +587 -0
  43. core/io/message_coordinator.py +204 -0
  44. core/io/message_renderer.py +601 -0
  45. core/io/modal_interaction_handler.py +315 -0
  46. core/io/raw_input_processor.py +946 -0
  47. core/io/status_renderer.py +845 -0
  48. core/io/terminal_renderer.py +586 -0
  49. core/io/terminal_state.py +551 -0
  50. core/io/visual_effects.py +734 -0
  51. core/llm/__init__.py +26 -0
  52. core/llm/api_communication_service.py +863 -0
  53. core/llm/conversation_logger.py +473 -0
  54. core/llm/conversation_manager.py +414 -0
  55. core/llm/file_operations_executor.py +1401 -0
  56. core/llm/hook_system.py +402 -0
  57. core/llm/llm_service.py +1629 -0
  58. core/llm/mcp_integration.py +386 -0
  59. core/llm/message_display_service.py +450 -0
  60. core/llm/model_router.py +214 -0
  61. core/llm/plugin_sdk.py +396 -0
  62. core/llm/response_parser.py +848 -0
  63. core/llm/response_processor.py +364 -0
  64. core/llm/tool_executor.py +520 -0
  65. core/logging/__init__.py +19 -0
  66. core/logging/setup.py +208 -0
  67. core/models/__init__.py +5 -0
  68. core/models/base.py +23 -0
  69. core/plugins/__init__.py +13 -0
  70. core/plugins/collector.py +212 -0
  71. core/plugins/discovery.py +386 -0
  72. core/plugins/factory.py +263 -0
  73. core/plugins/registry.py +152 -0
  74. core/storage/__init__.py +5 -0
  75. core/storage/state_manager.py +84 -0
  76. core/ui/__init__.py +6 -0
  77. core/ui/config_merger.py +176 -0
  78. core/ui/config_widgets.py +369 -0
  79. core/ui/live_modal_renderer.py +276 -0
  80. core/ui/modal_actions.py +162 -0
  81. core/ui/modal_overlay_renderer.py +373 -0
  82. core/ui/modal_renderer.py +591 -0
  83. core/ui/modal_state_manager.py +443 -0
  84. core/ui/widget_integration.py +222 -0
  85. core/ui/widgets/__init__.py +27 -0
  86. core/ui/widgets/base_widget.py +136 -0
  87. core/ui/widgets/checkbox.py +85 -0
  88. core/ui/widgets/dropdown.py +140 -0
  89. core/ui/widgets/label.py +78 -0
  90. core/ui/widgets/slider.py +185 -0
  91. core/ui/widgets/text_input.py +224 -0
  92. core/utils/__init__.py +11 -0
  93. core/utils/config_utils.py +656 -0
  94. core/utils/dict_utils.py +212 -0
  95. core/utils/error_utils.py +275 -0
  96. core/utils/key_reader.py +171 -0
  97. core/utils/plugin_utils.py +267 -0
  98. core/utils/prompt_renderer.py +151 -0
  99. kollabor-0.4.9.dist-info/METADATA +298 -0
  100. kollabor-0.4.9.dist-info/RECORD +128 -0
  101. kollabor-0.4.9.dist-info/WHEEL +5 -0
  102. kollabor-0.4.9.dist-info/entry_points.txt +2 -0
  103. kollabor-0.4.9.dist-info/licenses/LICENSE +21 -0
  104. kollabor-0.4.9.dist-info/top_level.txt +4 -0
  105. kollabor_cli_main.py +20 -0
  106. plugins/__init__.py +1 -0
  107. plugins/enhanced_input/__init__.py +18 -0
  108. plugins/enhanced_input/box_renderer.py +103 -0
  109. plugins/enhanced_input/box_styles.py +142 -0
  110. plugins/enhanced_input/color_engine.py +165 -0
  111. plugins/enhanced_input/config.py +150 -0
  112. plugins/enhanced_input/cursor_manager.py +72 -0
  113. plugins/enhanced_input/geometry.py +81 -0
  114. plugins/enhanced_input/state.py +130 -0
  115. plugins/enhanced_input/text_processor.py +115 -0
  116. plugins/enhanced_input_plugin.py +385 -0
  117. plugins/fullscreen/__init__.py +9 -0
  118. plugins/fullscreen/example_plugin.py +327 -0
  119. plugins/fullscreen/matrix_plugin.py +132 -0
  120. plugins/hook_monitoring_plugin.py +1299 -0
  121. plugins/query_enhancer_plugin.py +350 -0
  122. plugins/save_conversation_plugin.py +502 -0
  123. plugins/system_commands_plugin.py +93 -0
  124. plugins/tmux_plugin.py +795 -0
  125. plugins/workflow_enforcement_plugin.py +629 -0
  126. system_prompt/default.md +1286 -0
  127. system_prompt/default_win.md +265 -0
  128. system_prompt/example_with_trender.md +47 -0
@@ -0,0 +1,224 @@
1
+ """Text input widget for modal UI components."""
2
+
3
+ from typing import List
4
+ from .base_widget import BaseWidget
5
+ from ...io.key_parser import KeyPress
6
+ from ...io.visual_effects import ColorPalette
7
+
8
+
9
+ class TextInputWidget(BaseWidget):
10
+ """Interactive text input widget with cursor ▌.
11
+
12
+ Displays as [text▌] with blinking cursor and accepts character input.
13
+ Uses ColorPalette.BRIGHT for focus highlighting.
14
+ """
15
+
16
+ def __init__(self, config: dict, config_path: str, config_service=None):
17
+ """Initialize text input widget.
18
+
19
+ Args:
20
+ config: Widget configuration with optional placeholder.
21
+ config_path: Dot-notation path to config value.
22
+ config_service: ConfigService instance for reading/writing config values.
23
+ """
24
+ super().__init__(config, config_path, config_service)
25
+ self.cursor_position = 0
26
+ self._show_cursor = True
27
+
28
+ def render(self) -> List[str]:
29
+ """Render text input with cursor indicator.
30
+
31
+ Returns:
32
+ List containing text input display line.
33
+ """
34
+ # Get current text value
35
+ current_value = self.get_pending_value()
36
+ text = str(current_value) if current_value is not None else ""
37
+ label = self.get_label()
38
+ placeholder = self.config.get("placeholder", "")
39
+
40
+ # Display text or placeholder
41
+ if text:
42
+ display_text = text
43
+ elif placeholder and not self.focused:
44
+ display_text = placeholder
45
+ else:
46
+ display_text = ""
47
+
48
+ # Add cursor when focused
49
+ if self.focused and self._show_cursor:
50
+ # Insert cursor at current position
51
+ cursor_pos = min(self.cursor_position, len(display_text))
52
+ display_with_cursor = (
53
+ display_text[:cursor_pos] + "▌" + display_text[cursor_pos:]
54
+ )
55
+ else:
56
+ display_with_cursor = display_text
57
+
58
+ # Apply focus highlighting
59
+ if self.focused:
60
+ return [f"{ColorPalette.BRIGHT_WHITE} {label}: [{display_with_cursor}]{ColorPalette.RESET}"]
61
+ else:
62
+ if placeholder and not text:
63
+ # Show placeholder in dim text
64
+ return [f" {label}: [{ColorPalette.DIM}{placeholder}{ColorPalette.RESET}]"]
65
+ else:
66
+ return [f" {label}: [{display_with_cursor}]"]
67
+
68
+ def handle_input(self, key_press: KeyPress) -> bool:
69
+ """Handle text input - character insertion and navigation.
70
+
71
+ Args:
72
+ key_press: Key press event to handle.
73
+
74
+ Returns:
75
+ True if key was handled.
76
+ """
77
+ current_text = str(self.get_pending_value() or "")
78
+
79
+ # Handle special keys
80
+ if key_press.name == "Backspace":
81
+ if self.cursor_position > 0:
82
+ # Remove character before cursor
83
+ new_text = (
84
+ current_text[:self.cursor_position - 1] +
85
+ current_text[self.cursor_position:]
86
+ )
87
+ self.set_value(new_text)
88
+ self.cursor_position = max(0, self.cursor_position - 1)
89
+ return True
90
+
91
+ elif key_press.name == "Delete":
92
+ if self.cursor_position < len(current_text):
93
+ # Remove character at cursor
94
+ new_text = (
95
+ current_text[:self.cursor_position] +
96
+ current_text[self.cursor_position + 1:]
97
+ )
98
+ self.set_value(new_text)
99
+ return True
100
+
101
+ elif key_press.name == "ArrowLeft":
102
+ # Move cursor left
103
+ self.cursor_position = max(0, self.cursor_position - 1)
104
+ return True
105
+
106
+ elif key_press.name == "ArrowRight":
107
+ # Move cursor right
108
+ self.cursor_position = min(len(current_text), self.cursor_position + 1)
109
+ return True
110
+
111
+ elif key_press.name == "Home" or key_press.name == "Ctrl+A":
112
+ # Move cursor to beginning
113
+ self.cursor_position = 0
114
+ return True
115
+
116
+ elif key_press.name == "End" or key_press.name == "Ctrl+E":
117
+ # Move cursor to end
118
+ self.cursor_position = len(current_text)
119
+ return True
120
+
121
+ elif key_press.name == "Ctrl+U":
122
+ # Clear entire line
123
+ self.set_value("")
124
+ self.cursor_position = 0
125
+ return True
126
+
127
+ elif key_press.name == "Ctrl+K":
128
+ # Clear from cursor to end
129
+ new_text = current_text[:self.cursor_position]
130
+ self.set_value(new_text)
131
+ return True
132
+
133
+ # Handle printable characters
134
+ elif key_press.char and key_press.char.isprintable():
135
+ # CRITICAL FIX: Add input validation for specific field types
136
+ validation_type = self.config.get("validation", "text")
137
+
138
+ # For numeric fields, only allow digits and decimal points
139
+ if validation_type == "integer" and not key_press.char.isdigit():
140
+ return True # Reject non-numeric input
141
+ elif validation_type == "number" and not (key_press.char.isdigit() or key_press.char == "."):
142
+ return True # Reject non-numeric input
143
+
144
+ # Insert character at cursor position
145
+ new_text = (
146
+ current_text[:self.cursor_position] +
147
+ key_press.char +
148
+ current_text[self.cursor_position:]
149
+ )
150
+
151
+ # Validate the complete value
152
+ if self._validate_complete_value(new_text, validation_type):
153
+ self.set_value(new_text)
154
+ self.cursor_position += 1
155
+ return True
156
+
157
+ return False
158
+
159
+ def set_focus(self, focused: bool):
160
+ """Set focus and reset cursor position.
161
+
162
+ Args:
163
+ focused: Whether widget should be focused.
164
+ """
165
+ super().set_focus(focused)
166
+ if focused:
167
+ # Move cursor to end when gaining focus
168
+ current_text = str(self.get_pending_value() or "")
169
+ self.cursor_position = len(current_text)
170
+
171
+ def set_value(self, value):
172
+ """Set text value and adjust cursor position.
173
+
174
+ Args:
175
+ value: New text value.
176
+ """
177
+ super().set_value(str(value) if value is not None else "")
178
+ # Ensure cursor position is valid
179
+ new_text = str(value) if value is not None else ""
180
+ self.cursor_position = min(self.cursor_position, len(new_text))
181
+
182
+ def _validate_complete_value(self, value: str, validation_type: str) -> bool:
183
+ """Validate complete input value based on validation type.
184
+
185
+ Args:
186
+ value: String value to validate.
187
+ validation_type: Type of validation (text, integer, number).
188
+
189
+ Returns:
190
+ True if value is valid for the field type.
191
+ """
192
+ if validation_type == "integer":
193
+ if not value: # Empty is valid (will use default)
194
+ return True
195
+ try:
196
+ int(value)
197
+ return True
198
+ except ValueError:
199
+ return False
200
+ elif validation_type == "number":
201
+ if not value: # Empty is valid (will use default)
202
+ return True
203
+ try:
204
+ float(value)
205
+ return True
206
+ except ValueError:
207
+ return False
208
+ return True # Text validation always passes
209
+
210
+ def is_valid_value(self, value) -> bool:
211
+ """Validate text input value - must be string-convertible.
212
+
213
+ Args:
214
+ value: Value to validate.
215
+
216
+ Returns:
217
+ True if value can be converted to string.
218
+ """
219
+ try:
220
+ str(value)
221
+ validation_type = self.config.get("validation", "text")
222
+ return self._validate_complete_value(str(value), validation_type)
223
+ except (TypeError, ValueError):
224
+ return False
core/utils/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """Shared utility modules for Kollabor CLI."""
2
+
3
+ from .dict_utils import deep_merge, safe_get, safe_set
4
+ from .plugin_utils import has_method, safe_call_method, get_plugin_metadata
5
+ from .error_utils import log_and_continue, safe_execute
6
+
7
+ __all__ = [
8
+ 'deep_merge', 'safe_get', 'safe_set',
9
+ 'has_method', 'safe_call_method', 'get_plugin_metadata',
10
+ 'log_and_continue', 'safe_execute'
11
+ ]