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,302 @@
1
+ """Full-screen manager for plugin coordination and modal integration."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from typing import Dict, List, Optional, Any
6
+
7
+ from ..events.models import EventType
8
+ from .plugin import FullScreenPlugin, PluginMetadata
9
+ from .session import FullScreenSession
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class FullScreenManager:
15
+ """Manages full-screen plugins and their integration with the modal system.
16
+
17
+ This class coordinates between plugins, the event bus, and the modal system
18
+ to provide seamless full-screen experiences that properly pause the main
19
+ application interface.
20
+ """
21
+
22
+ def __init__(self, event_bus, terminal_renderer):
23
+ """Initialize the full-screen manager.
24
+
25
+ Args:
26
+ event_bus: Event bus for modal system integration.
27
+ terminal_renderer: Main terminal renderer (for compatibility).
28
+ """
29
+ self.event_bus = event_bus
30
+ self.terminal_renderer = terminal_renderer
31
+
32
+ # Plugin registry
33
+ self.plugins: Dict[str, FullScreenPlugin] = {}
34
+ self.plugin_aliases: Dict[str, str] = {}
35
+
36
+ # Session management
37
+ self.current_session: Optional[FullScreenSession] = None
38
+ self.session_history: List[Dict[str, Any]] = []
39
+
40
+ logger.info("FullScreenManager initialized")
41
+
42
+ def register_plugin(self, plugin: FullScreenPlugin) -> bool:
43
+ """Register a full-screen plugin.
44
+
45
+ Args:
46
+ plugin: The plugin to register.
47
+
48
+ Returns:
49
+ True if registration was successful, False otherwise.
50
+ """
51
+ try:
52
+ if plugin.name in self.plugins:
53
+ logger.warning(f"Plugin {plugin.name} already registered, overriding")
54
+
55
+ # Register main name
56
+ self.plugins[plugin.name] = plugin
57
+
58
+ # Register aliases
59
+ for alias in plugin.metadata.aliases:
60
+ if alias in self.plugin_aliases:
61
+ logger.warning(f"Alias {alias} already registered, overriding")
62
+ self.plugin_aliases[alias] = plugin.name
63
+
64
+ logger.info(f"Registered plugin: {plugin.name} with {len(plugin.metadata.aliases)} aliases")
65
+ return True
66
+
67
+ except Exception as e:
68
+ logger.error(f"Failed to register plugin {plugin.name}: {e}")
69
+ return False
70
+
71
+ def unregister_plugin(self, name: str) -> bool:
72
+ """Unregister a plugin.
73
+
74
+ Args:
75
+ name: Name of the plugin to unregister.
76
+
77
+ Returns:
78
+ True if unregistration was successful, False otherwise.
79
+ """
80
+ try:
81
+ if name not in self.plugins:
82
+ logger.warning(f"Plugin {name} not found for unregistration")
83
+ return False
84
+
85
+ plugin = self.plugins[name]
86
+
87
+ # Remove aliases
88
+ for alias in plugin.metadata.aliases:
89
+ if alias in self.plugin_aliases:
90
+ del self.plugin_aliases[alias]
91
+
92
+ # Remove main registration
93
+ del self.plugins[name]
94
+
95
+ logger.info(f"Unregistered plugin: {name}")
96
+ return True
97
+
98
+ except Exception as e:
99
+ logger.error(f"Failed to unregister plugin {name}: {e}")
100
+ return False
101
+
102
+ def get_plugin(self, name: str) -> Optional[FullScreenPlugin]:
103
+ """Get a plugin by name or alias.
104
+
105
+ Args:
106
+ name: Plugin name or alias.
107
+
108
+ Returns:
109
+ Plugin instance if found, None otherwise.
110
+ """
111
+ # Check direct name first
112
+ if name in self.plugins:
113
+ return self.plugins[name]
114
+
115
+ # Check aliases
116
+ if name in self.plugin_aliases:
117
+ actual_name = self.plugin_aliases[name]
118
+ return self.plugins.get(actual_name)
119
+
120
+ return None
121
+
122
+ def list_plugins(self) -> List[PluginMetadata]:
123
+ """List all registered plugins.
124
+
125
+ Returns:
126
+ List of plugin metadata.
127
+ """
128
+ return [plugin.metadata for plugin in self.plugins.values()]
129
+
130
+ async def launch_plugin(self, name: str, **kwargs) -> bool:
131
+ """Launch a full-screen plugin with modal integration.
132
+
133
+ Args:
134
+ name: Plugin name or alias to launch.
135
+ **kwargs: Additional arguments for plugin configuration.
136
+
137
+ Returns:
138
+ True if plugin launched successfully, False otherwise.
139
+ """
140
+ try:
141
+ # Check if session is already active
142
+ if self.current_session and self.current_session.running:
143
+ logger.warning("Cannot launch plugin - session already active")
144
+ return False
145
+
146
+ # Get plugin
147
+ plugin = self.get_plugin(name)
148
+ if not plugin:
149
+ logger.error(f"Plugin not found: {name}")
150
+ return False
151
+
152
+ logger.info(f"Launching full-screen plugin: {plugin.name}")
153
+
154
+ # Enter modal mode using the proven modal system
155
+ await self._enter_modal_mode(plugin)
156
+
157
+ try:
158
+ # Create and run session
159
+ logger.info(f"Creating session for plugin: {plugin.name}")
160
+ self.current_session = FullScreenSession(plugin, self.event_bus, **kwargs)
161
+ logger.info(f"Running session for plugin: {plugin.name}")
162
+ success = await self.current_session.run()
163
+ logger.info(f"Session completed with success: {success}")
164
+
165
+ # Record session in history
166
+ self._record_session(plugin, success)
167
+
168
+ return success
169
+
170
+ finally:
171
+ # Always exit modal mode
172
+ await self._exit_modal_mode(plugin)
173
+ self.current_session = None
174
+
175
+ except Exception as e:
176
+ logger.error(f"Failed to launch plugin {name}: {e}")
177
+ return False
178
+
179
+ async def stop_current_session(self) -> bool:
180
+ """Stop the currently running session.
181
+
182
+ Returns:
183
+ True if session was stopped, False if no session active.
184
+ """
185
+ if not self.current_session:
186
+ return False
187
+
188
+ try:
189
+ self.current_session.stop()
190
+ return True
191
+ except Exception as e:
192
+ logger.error(f"Error stopping session: {e}")
193
+ return False
194
+
195
+ def is_session_active(self) -> bool:
196
+ """Check if a full-screen session is currently active.
197
+
198
+ Returns:
199
+ True if session is active, False otherwise.
200
+ """
201
+ return (self.current_session is not None and
202
+ self.current_session.running)
203
+
204
+ def get_current_plugin(self) -> Optional[FullScreenPlugin]:
205
+ """Get the currently running plugin.
206
+
207
+ Returns:
208
+ Current plugin if session is active, None otherwise.
209
+ """
210
+ if self.current_session:
211
+ return self.current_session.plugin
212
+ return None
213
+
214
+ async def _enter_modal_mode(self, plugin: FullScreenPlugin):
215
+ """Enter modal mode for full-screen plugin.
216
+
217
+ Uses the same modal system as /config command for consistent behavior.
218
+
219
+ Args:
220
+ plugin: The plugin being launched.
221
+ """
222
+ try:
223
+ # Emit MODAL_TRIGGER event (same as Matrix effect)
224
+ await self.event_bus.emit_with_hooks(
225
+ EventType.MODAL_TRIGGER,
226
+ {
227
+ "trigger_source": "fullscreen_plugin",
228
+ "plugin_name": plugin.name,
229
+ "mode": "fullscreen",
230
+ "fullscreen_plugin": True
231
+ },
232
+ "fullscreen_manager"
233
+ )
234
+ logger.info(f"🎯 Entered modal mode for plugin: {plugin.name}")
235
+
236
+ except Exception as e:
237
+ logger.error(f"Failed to enter modal mode for {plugin.name}: {e}")
238
+ raise
239
+
240
+ async def _exit_modal_mode(self, plugin: FullScreenPlugin):
241
+ """Exit modal mode after plugin finishes.
242
+
243
+ Args:
244
+ plugin: The plugin that finished.
245
+ """
246
+ try:
247
+ # Emit MODAL_HIDE event
248
+ await self.event_bus.emit_with_hooks(
249
+ EventType.MODAL_HIDE,
250
+ {
251
+ "source": "fullscreen_plugin",
252
+ "plugin_name": plugin.name,
253
+ "completed": True
254
+ },
255
+ "fullscreen_manager"
256
+ )
257
+ logger.info(f"🔄 Exited modal mode for plugin: {plugin.name}")
258
+
259
+ except Exception as e:
260
+ logger.error(f"Failed to exit modal mode for {plugin.name}: {e}")
261
+
262
+ def _record_session(self, plugin: FullScreenPlugin, success: bool):
263
+ """Record session in history.
264
+
265
+ Args:
266
+ plugin: The plugin that ran.
267
+ success: Whether the session completed successfully.
268
+ """
269
+ session_record = {
270
+ "plugin_name": plugin.name,
271
+ "success": success,
272
+ "stats": self.current_session.get_stats() if self.current_session else None,
273
+ "timestamp": asyncio.get_event_loop().time()
274
+ }
275
+ self.session_history.append(session_record)
276
+
277
+ # Keep only last 100 sessions
278
+ if len(self.session_history) > 100:
279
+ self.session_history = self.session_history[-100:]
280
+
281
+ def get_session_history(self) -> List[Dict[str, Any]]:
282
+ """Get session execution history.
283
+
284
+ Returns:
285
+ List of session records.
286
+ """
287
+ return self.session_history.copy()
288
+
289
+ def get_manager_stats(self) -> Dict[str, Any]:
290
+ """Get manager statistics.
291
+
292
+ Returns:
293
+ Dictionary with manager statistics.
294
+ """
295
+ return {
296
+ "plugins_registered": len(self.plugins),
297
+ "aliases_registered": len(self.plugin_aliases),
298
+ "session_active": self.is_session_active(),
299
+ "current_plugin": self.get_current_plugin().name if self.get_current_plugin() else None,
300
+ "total_sessions": len(self.session_history),
301
+ "successful_sessions": sum(1 for s in self.session_history if s["success"])
302
+ }
@@ -0,0 +1,204 @@
1
+ """Base class for full-screen plugins."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from abc import ABC, abstractmethod
6
+ from typing import Dict, Any, Optional
7
+ from dataclasses import dataclass, field
8
+
9
+ from ..io.key_parser import KeyPress
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ @dataclass
15
+ class PluginMetadata:
16
+ """Metadata for a full-screen plugin."""
17
+ name: str
18
+ description: str = ""
19
+ version: str = "1.0.0"
20
+ author: str = ""
21
+ category: str = "general"
22
+ icon: str = ""
23
+ aliases: list = field(default_factory=list)
24
+ settings: Dict[str, Any] = field(default_factory=dict)
25
+
26
+
27
+ class FullScreenPlugin(ABC):
28
+ """Base class for all full-screen plugins.
29
+
30
+ This class provides the standard interface that all full-screen plugins
31
+ must implement. It handles the lifecycle, rendering, and input management
32
+ for plugins that take complete terminal control.
33
+ """
34
+
35
+ def __init__(self, metadata: PluginMetadata):
36
+ """Initialize the full-screen plugin.
37
+
38
+ Args:
39
+ metadata: Plugin metadata including name, description, etc.
40
+ """
41
+ self.metadata = metadata
42
+ self.renderer: Optional['FullScreenRenderer'] = None
43
+ self.running = False
44
+ self.initialized = False
45
+
46
+ # Plugin state
47
+ self.start_time = 0.0
48
+ self.frame_count = 0
49
+ self.last_frame_time = 0.0
50
+
51
+ logger.info(f"Initialized full-screen plugin: {metadata.name}")
52
+
53
+ @property
54
+ def name(self) -> str:
55
+ """Get the plugin name."""
56
+ return self.metadata.name
57
+
58
+ @property
59
+ def description(self) -> str:
60
+ """Get the plugin description."""
61
+ return self.metadata.description
62
+
63
+ async def initialize(self, renderer: 'FullScreenRenderer') -> bool:
64
+ """Initialize the plugin with a renderer.
65
+
66
+ This method is called once when the plugin is loaded. Override
67
+ this method to set up any resources your plugin needs.
68
+
69
+ Args:
70
+ renderer: The full-screen renderer instance.
71
+
72
+ Returns:
73
+ True if initialization was successful, False otherwise.
74
+ """
75
+ try:
76
+ self.renderer = renderer
77
+ self.initialized = True
78
+ logger.info(f"Plugin {self.name} initialized successfully")
79
+ return True
80
+ except Exception as e:
81
+ logger.error(f"Failed to initialize plugin {self.name}: {e}")
82
+ return False
83
+
84
+ @abstractmethod
85
+ async def render_frame(self, delta_time: float) -> bool:
86
+ """Render a single frame.
87
+
88
+ This method is called every frame while the plugin is active.
89
+ Override this method to implement your plugin's visual output.
90
+
91
+ Args:
92
+ delta_time: Time elapsed since last frame in seconds.
93
+
94
+ Returns:
95
+ True to continue running, False to exit the plugin.
96
+ """
97
+ pass
98
+
99
+ @abstractmethod
100
+ async def handle_input(self, key_press: KeyPress) -> bool:
101
+ """Handle user input.
102
+
103
+ This method is called whenever the user presses a key while
104
+ your plugin is active. Override this method to handle input.
105
+
106
+ Args:
107
+ key_press: The key that was pressed.
108
+
109
+ Returns:
110
+ True to exit the plugin, False to continue running.
111
+ """
112
+ pass
113
+
114
+ async def on_start(self):
115
+ """Called when the plugin starts running.
116
+
117
+ Override this method to perform any setup that should happen
118
+ when the plugin begins execution (after initialization).
119
+ """
120
+ self.running = True
121
+ self.start_time = asyncio.get_event_loop().time()
122
+ self.frame_count = 0
123
+ logger.info(f"Plugin {self.name} started")
124
+
125
+ async def on_stop(self):
126
+ """Called when the plugin stops running.
127
+
128
+ Override this method to perform any cleanup that should happen
129
+ when the plugin finishes execution.
130
+ """
131
+ self.running = False
132
+ logger.info(f"Plugin {self.name} stopped")
133
+
134
+ async def cleanup(self):
135
+ """Clean up plugin resources.
136
+
137
+ This method is called when the plugin is being unloaded.
138
+ Override this method to clean up any resources your plugin allocated.
139
+ """
140
+ self.initialized = False
141
+ self.renderer = None
142
+ logger.info(f"Plugin {self.name} cleaned up")
143
+
144
+ def get_runtime_stats(self) -> Dict[str, Any]:
145
+ """Get runtime statistics for the plugin.
146
+
147
+ Returns:
148
+ Dictionary with runtime statistics.
149
+ """
150
+ current_time = asyncio.get_event_loop().time()
151
+ runtime = current_time - self.start_time if self.running else 0
152
+
153
+ return {
154
+ "name": self.name,
155
+ "running": self.running,
156
+ "initialized": self.initialized,
157
+ "runtime_seconds": runtime,
158
+ "frame_count": self.frame_count,
159
+ "fps": self.frame_count / runtime if runtime > 0 else 0
160
+ }
161
+
162
+ def update_frame_stats(self):
163
+ """Update frame statistics. Called by the framework."""
164
+ self.frame_count += 1
165
+ self.last_frame_time = asyncio.get_event_loop().time()
166
+
167
+
168
+ class ExamplePlugin(FullScreenPlugin):
169
+ """Example plugin implementation for reference."""
170
+
171
+ def __init__(self):
172
+ """Initialize the example plugin."""
173
+ metadata = PluginMetadata(
174
+ name="example",
175
+ description="Example full-screen plugin for demonstration",
176
+ author="Framework",
177
+ category="demo"
178
+ )
179
+ super().__init__(metadata)
180
+
181
+ async def render_frame(self, delta_time: float) -> bool:
182
+ """Render example content."""
183
+ if not self.renderer:
184
+ return False
185
+
186
+ # Clear screen and show example content
187
+ self.renderer.clear_screen()
188
+
189
+ # Center message
190
+ width, height = self.renderer.get_terminal_size()
191
+ message = f"Example Plugin - Frame {self.frame_count}"
192
+ x = (width - len(message)) // 2
193
+ y = height // 2
194
+
195
+ self.renderer.write_at(x, y, message)
196
+ self.renderer.write_at(x - 5, y + 2, "Press 'q' or ESC to exit")
197
+
198
+ self.update_frame_stats()
199
+ return True
200
+
201
+ async def handle_input(self, key_press: KeyPress) -> bool:
202
+ """Handle input for example plugin."""
203
+ # Exit on 'q' or ESC
204
+ return key_press.char in ['q', '\x1b'] or key_press.name == "Escape"