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
core/events/models.py ADDED
@@ -0,0 +1,258 @@
1
+ """Event system data models and enums for Kollabor CLI."""
2
+
3
+ from collections import defaultdict, deque
4
+ from dataclasses import dataclass, field
5
+ from datetime import datetime
6
+ from enum import Enum
7
+ from typing import Any, Callable, Dict, List, Optional
8
+
9
+
10
+ class HookPriority(Enum):
11
+ """Priority levels for hook execution."""
12
+ SYSTEM = 1000
13
+ SECURITY = 900
14
+ PREPROCESSING = 500
15
+ LLM = 100
16
+ POSTPROCESSING = 50
17
+ DISPLAY = 10
18
+
19
+
20
+ class HookStatus(Enum):
21
+ """Status states for hook execution."""
22
+ PENDING = "pending"
23
+ STARTING = "starting"
24
+ WORKING = "working"
25
+ COMPLETED = "completed"
26
+ FAILED = "failed"
27
+ TIMEOUT = "timeout"
28
+
29
+
30
+ class EventType(Enum):
31
+ """Event types for the hook system."""
32
+ # User input events
33
+ USER_INPUT_PRE = "user_input_pre"
34
+ USER_INPUT = "user_input"
35
+ USER_INPUT_POST = "user_input_post"
36
+
37
+ # Key press events
38
+ KEY_PRESS_PRE = "key_press_pre"
39
+ KEY_PRESS = "key_press"
40
+ KEY_PRESS_POST = "key_press_post"
41
+
42
+ # Paste events
43
+ PASTE_DETECTED = "paste_detected"
44
+
45
+ # LLM events
46
+ LLM_REQUEST_PRE = "llm_request_pre"
47
+ LLM_REQUEST = "llm_request"
48
+ LLM_REQUEST_POST = "llm_request_post"
49
+ LLM_RESPONSE_PRE = "llm_response_pre"
50
+ LLM_RESPONSE = "llm_response"
51
+ LLM_RESPONSE_POST = "llm_response_post"
52
+ LLM_THINKING = "llm_thinking"
53
+ CANCEL_REQUEST = "cancel_request"
54
+
55
+ # Tool events
56
+ TOOL_CALL_PRE = "tool_call_pre"
57
+ TOOL_CALL = "tool_call"
58
+ TOOL_CALL_POST = "tool_call_post"
59
+
60
+ # System events
61
+ SYSTEM_STARTUP = "system_startup"
62
+ SYSTEM_SHUTDOWN = "system_shutdown"
63
+ RENDER_FRAME = "render_frame"
64
+
65
+ # Input rendering events
66
+ INPUT_RENDER_PRE = "input_render_pre"
67
+ INPUT_RENDER = "input_render"
68
+ INPUT_RENDER_POST = "input_render_post"
69
+
70
+ # Command menu events
71
+ COMMAND_MENU_SHOW = "command_menu_show"
72
+ COMMAND_MENU_NAVIGATE = "command_menu_navigate"
73
+ COMMAND_MENU_SELECT = "command_menu_select"
74
+ COMMAND_MENU_HIDE = "command_menu_hide"
75
+ COMMAND_MENU_RENDER = "command_menu_render"
76
+
77
+ # Status display events
78
+ STATUS_VIEW_CHANGED = "status_view_changed"
79
+ STATUS_CONTENT_UPDATE = "status_content_update"
80
+ STATUS_BLOCK_RESIZE = "status_block_resize"
81
+
82
+ # Slash command events
83
+ SLASH_COMMAND_DETECTED = "slash_command_detected"
84
+ SLASH_COMMAND_EXECUTE = "slash_command_execute"
85
+ SLASH_COMMAND_COMPLETE = "slash_command_complete"
86
+ SLASH_COMMAND_ERROR = "slash_command_error"
87
+
88
+ # Command output display events
89
+ COMMAND_OUTPUT_DISPLAY = "command_output_display"
90
+
91
+ # Command menu events (enhanced)
92
+ COMMAND_MENU_FILTER = "command_menu_filter"
93
+
94
+ # Modal events
95
+ MODAL_TRIGGER = "modal_trigger"
96
+ STATUS_MODAL_TRIGGER = "status_modal_trigger"
97
+ STATUS_MODAL_RENDER = "status_modal_render"
98
+ LIVE_MODAL_TRIGGER = "live_modal_trigger"
99
+
100
+ # Rendering control events
101
+ PAUSE_RENDERING = "pause_rendering"
102
+ RESUME_RENDERING = "resume_rendering"
103
+ MODAL_SHOW = "modal_show"
104
+ MODAL_HIDE = "modal_hide"
105
+ MODAL_SAVE = "modal_save"
106
+ FULLSCREEN_INPUT = "fullscreen_input"
107
+ COMMAND_MENU_ACTIVATE = "command_menu_activate"
108
+
109
+ # Status area takeover events
110
+ STATUS_TAKEOVER_START = "status_takeover_start"
111
+ STATUS_TAKEOVER_NAVIGATE = "status_takeover_navigate"
112
+ STATUS_TAKEOVER_ACTION = "status_takeover_action"
113
+ STATUS_TAKEOVER_END = "status_takeover_end"
114
+
115
+
116
+ @dataclass
117
+ class Hook:
118
+ """Hook definition for the event system.
119
+
120
+ Attributes:
121
+ name: Unique name for the hook.
122
+ plugin_name: Name of the plugin that owns this hook.
123
+ event_type: Type of event this hook responds to.
124
+ priority: Execution priority (higher numbers execute first).
125
+ callback: Async function to call when event occurs.
126
+ enabled: Whether the hook is currently enabled.
127
+ timeout: Maximum execution time in seconds.
128
+ retry_attempts: Number of retry attempts on failure.
129
+ error_action: Action to take on error ("continue" or "stop").
130
+ status: Current execution status.
131
+ status_area: Status area identifier.
132
+ icon_set: Icons for different states.
133
+ """
134
+ name: str
135
+ plugin_name: str
136
+ event_type: EventType
137
+ priority: int
138
+ callback: Callable
139
+ enabled: bool = True
140
+ timeout: int = 30
141
+ retry_attempts: int = 3
142
+ error_action: str = "continue"
143
+ status: HookStatus = HookStatus.PENDING
144
+ status_area: str = "A"
145
+ icon_set: Dict[str, str] = field(default_factory=lambda: {
146
+ "thinking": "[THINK]", "processing": "[PROC]", "complete": "[OK]", "error": "[ERR]"
147
+ })
148
+
149
+
150
+ @dataclass
151
+ class Event:
152
+ """Event data structure for the hook system.
153
+
154
+ Attributes:
155
+ type: Type of event.
156
+ data: Event-specific data.
157
+ source: Source that generated the event.
158
+ timestamp: When the event was created.
159
+ processed: Whether the event has been processed.
160
+ cancelled: Whether the event was cancelled during processing.
161
+ result: Results from hook processing.
162
+ """
163
+ type: EventType
164
+ data: Dict[str, Any]
165
+ source: str
166
+ timestamp: datetime = field(default_factory=datetime.now)
167
+ processed: bool = False
168
+ cancelled: bool = False
169
+ result: Dict[str, Any] = field(default_factory=dict)
170
+
171
+
172
+ # Slash Command System Models
173
+
174
+ class CommandMode(Enum):
175
+ """Different interaction modes for slash commands."""
176
+ NORMAL = "normal" # Regular input mode
177
+ INSTANT = "instant" # /clear - executes immediately
178
+ MENU_POPUP = "menu_popup" # Shows command menu overlay
179
+ STATUS_TAKEOVER = "status_takeover" # /agents - takes over status area
180
+ STATUS_MODAL = "status_modal" # Modal within status area only
181
+ INLINE_INPUT = "inline_input" # /save [filename] - inline parameters
182
+ MODAL = "modal" # Modal overlay mode
183
+ LIVE_MODAL = "live_modal" # Live-updating modal (e.g., tmux view)
184
+
185
+
186
+ class CommandCategory(Enum):
187
+ """Categories for organizing commands."""
188
+ SYSTEM = "system"
189
+ CONVERSATION = "conversation"
190
+ AGENT = "agent"
191
+ DEVELOPMENT = "development"
192
+ FILE = "file"
193
+ TASK = "task"
194
+ CUSTOM = "custom"
195
+
196
+
197
+ @dataclass
198
+ class UIConfig:
199
+ """Configuration for command UI interfaces."""
200
+ type: str # "list", "tree", "form", "table", "menu", "modal"
201
+ navigation: List[str] = field(default_factory=lambda: ["↑↓", "Enter", "Esc"])
202
+ height: int = 10
203
+ width: Optional[int] = None
204
+ scrollable: bool = True
205
+ title: str = ""
206
+ footer: str = ""
207
+ modal_config: Optional[Dict[str, Any]] = None # Modal-specific configuration
208
+
209
+
210
+ @dataclass
211
+ class ParameterDefinition:
212
+ """Definition for command parameters."""
213
+ name: str
214
+ type: str # "string", "int", "bool", "file", "choice"
215
+ description: str
216
+ required: bool = False
217
+ default: Any = None
218
+ choices: Optional[List[str]] = None
219
+ validation: Optional[str] = None
220
+
221
+
222
+ @dataclass
223
+ class CommandDefinition:
224
+ """Complete definition of a slash command."""
225
+ name: str
226
+ description: str
227
+ handler: Callable
228
+ plugin_name: str
229
+ aliases: List[str] = field(default_factory=list)
230
+ mode: CommandMode = CommandMode.INSTANT
231
+ category: CommandCategory = CommandCategory.CUSTOM
232
+ parameters: List[ParameterDefinition] = field(default_factory=list)
233
+ ui_config: Optional[UIConfig] = None
234
+ icon: str = ""
235
+ hidden: bool = False
236
+ enabled: bool = True
237
+
238
+
239
+ @dataclass
240
+ class SlashCommand:
241
+ """Parsed slash command from user input."""
242
+ name: str
243
+ args: List[str] = field(default_factory=list)
244
+ raw_input: str = ""
245
+ parameters: Dict[str, Any] = field(default_factory=dict)
246
+ timestamp: datetime = field(default_factory=datetime.now)
247
+
248
+
249
+ @dataclass
250
+ class CommandResult:
251
+ """Result from command execution."""
252
+ success: bool
253
+ message: str = ""
254
+ data: Dict[str, Any] = field(default_factory=dict)
255
+ status_ui: Optional[Any] = None # UI component for status takeover
256
+ ui_config: Optional['UIConfig'] = None # UI configuration for modal/status display
257
+ display_type: str = "info" # "info", "success", "warning", "error"
258
+ should_clear_input: bool = True
@@ -0,0 +1,176 @@
1
+ """Event processor for handling pre/post event processing logic."""
2
+
3
+ import logging
4
+ from typing import Any, Dict, Optional, Tuple
5
+
6
+ from .models import Event, EventType
7
+ from .executor import HookExecutor
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from .registry import HookRegistry
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class EventProcessor:
17
+ """Handles pre/post event processing with clean separation of concerns.
18
+
19
+ This class manages the complex logic of processing events through
20
+ their pre, main, and post phases while maintaining data flow integrity.
21
+ """
22
+
23
+ def __init__(self, hook_registry: 'HookRegistry', hook_executor: HookExecutor):
24
+ """Initialize the event processor.
25
+
26
+ Args:
27
+ hook_registry: Registry for managing hooks.
28
+ hook_executor: Executor for individual hooks.
29
+ """
30
+ self.hook_registry = hook_registry
31
+ self.hook_executor = hook_executor
32
+
33
+ # Pre/post event type mappings
34
+ self.pre_post_map = {
35
+ EventType.USER_INPUT: (EventType.USER_INPUT_PRE, EventType.USER_INPUT_POST),
36
+ EventType.KEY_PRESS: (EventType.KEY_PRESS_PRE, EventType.KEY_PRESS_POST),
37
+ EventType.LLM_REQUEST: (EventType.LLM_REQUEST_PRE, EventType.LLM_REQUEST_POST),
38
+ EventType.LLM_RESPONSE: (EventType.LLM_RESPONSE_PRE, EventType.LLM_RESPONSE_POST),
39
+ EventType.TOOL_CALL: (EventType.TOOL_CALL_PRE, EventType.TOOL_CALL_POST),
40
+ }
41
+
42
+ logger.debug("EventProcessor initialized")
43
+
44
+ async def process_event_with_phases(self, event_type: EventType, data: Dict[str, Any], source: str) -> Dict[str, Any]:
45
+ """Process an event through pre, main, and post phases.
46
+
47
+ Args:
48
+ event_type: Type of event to process.
49
+ data: Event data.
50
+ source: Source of the event.
51
+
52
+ Returns:
53
+ Results from all phases of processing.
54
+ """
55
+ results = {
56
+ "pre": {},
57
+ "main": {},
58
+ "post": {},
59
+ "cancelled": False,
60
+ "source": source,
61
+ "event_type": event_type.value
62
+ }
63
+
64
+ event_data = data.copy()
65
+ pre_event_type, post_event_type = self.pre_post_map.get(event_type, (None, None))
66
+
67
+ # Phase 1: PRE hooks
68
+ if pre_event_type:
69
+ pre_results = await self._process_phase(pre_event_type, event_data, source, "PRE")
70
+ results["pre"] = pre_results
71
+
72
+ if pre_results.get("cancelled", False):
73
+ results["cancelled"] = True
74
+ logger.info(f"Event {event_type.value} cancelled during PRE phase")
75
+ return results
76
+
77
+ # Update data from PRE phase
78
+ event_data = pre_results.get("final_data", event_data)
79
+
80
+ # Phase 2: MAIN event
81
+ main_results = await self._process_phase(event_type, event_data, source, "MAIN")
82
+ results["main"] = main_results
83
+
84
+ if main_results.get("cancelled", False):
85
+ results["cancelled"] = True
86
+ logger.info(f"Event {event_type.value} cancelled during MAIN phase")
87
+ return results
88
+
89
+ # Update data from MAIN phase
90
+ event_data = main_results.get("final_data", event_data)
91
+
92
+ # Phase 3: POST hooks (only if main wasn't cancelled)
93
+ if post_event_type and not main_results.get("cancelled", False):
94
+ post_results = await self._process_phase(post_event_type, event_data, source, "POST")
95
+ results["post"] = post_results
96
+
97
+ if post_results.get("cancelled", False):
98
+ results["cancelled"] = True
99
+ logger.info(f"Event {event_type.value} cancelled during POST phase")
100
+
101
+ return results
102
+
103
+ async def _process_phase(self, event_type: EventType, data: Dict[str, Any], source: str, phase_name: str) -> Dict[str, Any]:
104
+ """Process a single phase (PRE, MAIN, or POST) of an event.
105
+
106
+ Args:
107
+ event_type: Type of event for this phase.
108
+ data: Event data.
109
+ source: Source of the event.
110
+ phase_name: Name of the phase for logging.
111
+
112
+ Returns:
113
+ Results from processing this phase.
114
+ """
115
+ phase_results = {
116
+ "phase": phase_name,
117
+ "event_type": event_type.value,
118
+ "hook_results": [],
119
+ "cancelled": False,
120
+ "final_data": data.copy(),
121
+ "stats": {}
122
+ }
123
+
124
+ # Get hooks for this event type
125
+ hooks = self.hook_registry.get_hooks_for_event(event_type)
126
+
127
+ if not hooks:
128
+ logger.debug(f"No hooks registered for {event_type.value} ({phase_name} phase)")
129
+ phase_results["stats"] = {"total_hooks": 0}
130
+ return phase_results
131
+
132
+ # Create event object
133
+ event = Event(type=event_type, data=data.copy(), source=source)
134
+ # Execute hooks in priority order
135
+ for hook in hooks:
136
+ if event.cancelled:
137
+ logger.debug(f"Skipping remaining hooks due to event cancellation")
138
+ break
139
+
140
+ hook_result = await self.hook_executor.execute_hook(hook, event)
141
+ phase_results["hook_results"].append(hook_result)
142
+
143
+ # Update hook status in registry
144
+ hook_key = hook_result["hook_key"]
145
+ if hook_result["success"]:
146
+ self.hook_registry.update_hook_status(hook_key, "completed")
147
+ elif hook_result.get("error") == "timeout":
148
+ self.hook_registry.update_hook_status(hook_key, "timeout")
149
+ elif hook_result.get("error"):
150
+ self.hook_registry.update_hook_status(hook_key, "failed")
151
+
152
+ # Update final results
153
+ phase_results["cancelled"] = event.cancelled
154
+ phase_results["final_data"] = event.data
155
+ phase_results["stats"] = self.hook_executor.get_execution_stats(phase_results["hook_results"])
156
+
157
+ return phase_results
158
+
159
+ def add_event_type_mapping(self, main_event: EventType, pre_event: EventType, post_event: EventType) -> None:
160
+ """Add a new event type mapping for pre/post processing.
161
+
162
+ Args:
163
+ main_event: The main event type.
164
+ pre_event: The pre-processing event type.
165
+ post_event: The post-processing event type.
166
+ """
167
+ self.pre_post_map[main_event] = (pre_event, post_event)
168
+ logger.info(f"Added event type mapping: {main_event.value} -> {pre_event.value}/{post_event.value}")
169
+
170
+ def get_supported_event_types(self) -> Dict[EventType, Tuple[Optional[EventType], Optional[EventType]]]:
171
+ """Get all supported event types and their pre/post mappings.
172
+
173
+ Returns:
174
+ Dictionary mapping main events to their pre/post event types.
175
+ """
176
+ return self.pre_post_map.copy()