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/llm/plugin_sdk.py ADDED
@@ -0,0 +1,396 @@
1
+ """Plugin SDK for creating custom Kollabor plugins.
2
+
3
+ Provides a framework for creating custom plugins that integrate with
4
+ the LLM core service, separate from MCP tools.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ from pathlib import Path
10
+ from typing import Any, Callable, Dict, List, Optional
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class KollaborPluginSDK:
16
+ """SDK for creating and managing custom Kollabor plugins.
17
+
18
+ Provides tools for plugin creation, validation, and registration,
19
+ enabling developers to extend LLM functionality with custom tools.
20
+ """
21
+
22
+ # Plugin template
23
+ PLUGIN_TEMPLATE = '''"""Custom Kollabor Plugin: {name}
24
+
25
+ {description}
26
+ """
27
+
28
+ from typing import Any, Dict, List
29
+ from core.events import Event, EventType, Hook, HookPriority
30
+
31
+
32
+ class {class_name}:
33
+ """Custom plugin for {name}."""
34
+
35
+ @staticmethod
36
+ def get_default_config() -> Dict[str, Any]:
37
+ """Get default configuration for this plugin."""
38
+ return {{
39
+ "plugins": {{
40
+ "{plugin_id}": {{
41
+ "enabled": True,
42
+ "show_status": True,
43
+ # Add your configuration options here
44
+ }}
45
+ }}
46
+ }}
47
+
48
+ def __init__(self, name: str, state_manager, event_bus, renderer, config):
49
+ """Initialize the plugin."""
50
+ self.name = name
51
+ self.state_manager = state_manager
52
+ self.event_bus = event_bus
53
+ self.renderer = renderer
54
+ self.config = config
55
+
56
+ # Initialize your plugin state here
57
+
58
+ async def initialize(self):
59
+ """Initialize plugin resources."""
60
+ # Setup any async resources here
61
+ pass
62
+
63
+ async def register_hooks(self):
64
+ """Register plugin hooks with event bus."""
65
+ # Register your hooks here
66
+ # Example:
67
+ # hook = Hook(
68
+ # name="my_hook",
69
+ # plugin_name=self.name,
70
+ # event_type=EventType.USER_INPUT,
71
+ # priority=HookPriority.PREPROCESSING.value,
72
+ # callback=self._handle_input
73
+ # )
74
+ # await self.event_bus.register_hook(hook)
75
+ pass
76
+
77
+ def get_status_line(self) -> Dict[str, List[str]]:
78
+ """Get status line for display."""
79
+ return {{
80
+ "A": [], # Area A status items
81
+ "B": [], # Area B status items
82
+ "C": [] # Area C status items
83
+ }}
84
+
85
+ async def shutdown(self):
86
+ """Cleanup plugin resources."""
87
+ # Cleanup any resources here
88
+ pass
89
+
90
+ # Add your plugin methods here
91
+ async def process_command(self, command: str, params: Dict[str, Any]) -> Dict[str, Any]:
92
+ """Process a custom command.
93
+
94
+ Args:
95
+ command: Command to execute
96
+ params: Command parameters
97
+
98
+ Returns:
99
+ Command result
100
+ """
101
+ # Implement your command processing
102
+ return {{"status": "success", "message": "Command processed"}}
103
+ '''
104
+
105
+ def __init__(self):
106
+ """Initialize the plugin SDK."""
107
+ self.registered_plugins = {}
108
+ self.custom_tools = {}
109
+ self.plugin_validators = []
110
+
111
+ # Plugin directory
112
+ self.plugins_dir = Path.cwd() / "plugins"
113
+ self.plugins_dir.mkdir(exist_ok=True)
114
+
115
+ logger.info("Kollabor Plugin SDK initialized")
116
+
117
+ def create_plugin_template(self, plugin_name: str,
118
+ description: str = "A custom Kollabor plugin",
119
+ output_dir: Optional[Path] = None) -> Path:
120
+ """Generate a plugin template file.
121
+
122
+ Args:
123
+ plugin_name: Name of the plugin
124
+ description: Plugin description
125
+ output_dir: Directory to save the plugin (defaults to plugins/)
126
+
127
+ Returns:
128
+ Path to the created plugin file
129
+ """
130
+ # Sanitize plugin name
131
+ plugin_id = plugin_name.lower().replace(" ", "_").replace("-", "_")
132
+ class_name = "".join(word.capitalize() for word in plugin_id.split("_")) + "Plugin"
133
+
134
+ # Generate plugin content
135
+ plugin_content = self.PLUGIN_TEMPLATE.format(
136
+ name=plugin_name,
137
+ description=description,
138
+ class_name=class_name,
139
+ plugin_id=plugin_id
140
+ )
141
+
142
+ # Determine output path
143
+ if output_dir is None:
144
+ output_dir = self.plugins_dir
145
+ output_path = output_dir / f"{plugin_id}_plugin.py"
146
+
147
+ # Write plugin file
148
+ with open(output_path, 'w') as f:
149
+ f.write(plugin_content)
150
+
151
+ logger.info(f"Created plugin template: {output_path}")
152
+ return output_path
153
+
154
+ def register_custom_tool(self, tool_definition: Dict[str, Any]) -> bool:
155
+ """Register a custom tool for LLM use.
156
+
157
+ Args:
158
+ tool_definition: Tool definition including name, description, parameters
159
+
160
+ Returns:
161
+ True if registration successful
162
+ """
163
+ required_fields = ["name", "description", "handler"]
164
+ if not all(field in tool_definition for field in required_fields):
165
+ logger.error(f"Tool definition missing required fields: {required_fields}")
166
+ return False
167
+
168
+ tool_name = tool_definition["name"]
169
+
170
+ # Validate handler is callable
171
+ if not callable(tool_definition["handler"]):
172
+ logger.error(f"Tool handler for '{tool_name}' is not callable")
173
+ return False
174
+
175
+ # Register the tool
176
+ self.custom_tools[tool_name] = {
177
+ "definition": tool_definition,
178
+ "enabled": tool_definition.get("enabled", True),
179
+ "plugin": tool_definition.get("plugin", "unknown")
180
+ }
181
+
182
+ logger.info(f"Registered custom tool: {tool_name}")
183
+ return True
184
+
185
+ async def execute_custom_tool(self, tool_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
186
+ """Execute a registered custom tool.
187
+
188
+ Args:
189
+ tool_name: Name of the tool to execute
190
+ params: Tool parameters
191
+
192
+ Returns:
193
+ Tool execution result
194
+ """
195
+ if tool_name not in self.custom_tools:
196
+ return {
197
+ "error": f"Tool '{tool_name}' not found",
198
+ "available_tools": list(self.custom_tools.keys())
199
+ }
200
+
201
+ tool_info = self.custom_tools[tool_name]
202
+ if not tool_info["enabled"]:
203
+ return {"error": f"Tool '{tool_name}' is disabled"}
204
+
205
+ try:
206
+ handler = tool_info["definition"]["handler"]
207
+
208
+ # Execute handler (async or sync)
209
+ if asyncio.iscoroutinefunction(handler):
210
+ result = await handler(params)
211
+ else:
212
+ result = handler(params)
213
+
214
+ logger.info(f"Executed custom tool: {tool_name}")
215
+ return {"status": "success", "result": result}
216
+
217
+ except Exception as e:
218
+ logger.error(f"Failed to execute custom tool {tool_name}: {e}")
219
+ return {"error": str(e)}
220
+
221
+ def validate_plugin(self, plugin_path: str) -> Dict[str, Any]:
222
+ """Validate a plugin structure and security.
223
+
224
+ Args:
225
+ plugin_path: Path to the plugin file
226
+
227
+ Returns:
228
+ Validation result with any issues found
229
+ """
230
+ validation_result = {
231
+ "valid": True,
232
+ "errors": [],
233
+ "warnings": [],
234
+ "info": []
235
+ }
236
+
237
+ plugin_file = Path(plugin_path)
238
+
239
+ # Check file exists
240
+ if not plugin_file.exists():
241
+ validation_result["valid"] = False
242
+ validation_result["errors"].append(f"Plugin file not found: {plugin_path}")
243
+ return validation_result
244
+
245
+ # Check file extension
246
+ if not plugin_file.suffix == ".py":
247
+ validation_result["valid"] = False
248
+ validation_result["errors"].append("Plugin must be a Python file (.py)")
249
+ return validation_result
250
+
251
+ try:
252
+ # Read plugin content
253
+ content = plugin_file.read_text()
254
+
255
+ # Check for required methods
256
+ required_methods = ["__init__", "get_default_config"]
257
+ for method in required_methods:
258
+ if f"def {method}" not in content:
259
+ validation_result["warnings"].append(f"Missing recommended method: {method}")
260
+
261
+ # Check for security issues
262
+ dangerous_imports = ["os.system", "subprocess.call", "eval", "exec", "__import__"]
263
+ for dangerous in dangerous_imports:
264
+ if dangerous in content:
265
+ validation_result["warnings"].append(f"Potentially dangerous code: {dangerous}")
266
+
267
+ # Check for proper class structure
268
+ if "class " not in content:
269
+ validation_result["errors"].append("No class definition found")
270
+ validation_result["valid"] = False
271
+
272
+ # Try to import and validate structure
273
+ try:
274
+ import importlib.util
275
+ spec = importlib.util.spec_from_file_location("temp_plugin", plugin_file)
276
+ if spec and spec.loader:
277
+ module = importlib.util.module_from_spec(spec)
278
+ spec.loader.exec_module(module)
279
+
280
+ # Find plugin class
281
+ plugin_class = None
282
+ for name in dir(module):
283
+ obj = getattr(module, name)
284
+ if isinstance(obj, type) and name.endswith("Plugin"):
285
+ plugin_class = obj
286
+ break
287
+
288
+ if plugin_class:
289
+ validation_result["info"].append(f"Found plugin class: {plugin_class.__name__}")
290
+
291
+ # Check for required methods
292
+ if hasattr(plugin_class, 'get_default_config'):
293
+ validation_result["info"].append("Has get_default_config method")
294
+ else:
295
+ validation_result["warnings"].append("No plugin class found (should end with 'Plugin')")
296
+
297
+ except Exception as e:
298
+ validation_result["errors"].append(f"Failed to import plugin: {e}")
299
+ validation_result["valid"] = False
300
+
301
+ except Exception as e:
302
+ validation_result["errors"].append(f"Failed to read plugin file: {e}")
303
+ validation_result["valid"] = False
304
+
305
+ # Run custom validators
306
+ for validator in self.plugin_validators:
307
+ try:
308
+ validator_result = validator(plugin_path)
309
+ if validator_result:
310
+ validation_result["warnings"].extend(validator_result.get("warnings", []))
311
+ validation_result["errors"].extend(validator_result.get("errors", []))
312
+ except Exception as e:
313
+ logger.warning(f"Custom validator failed: {e}")
314
+
315
+ return validation_result
316
+
317
+ def add_validator(self, validator: Callable) -> None:
318
+ """Add a custom plugin validator.
319
+
320
+ Args:
321
+ validator: Validation function that takes plugin path and returns issues
322
+ """
323
+ self.plugin_validators.append(validator)
324
+ logger.debug(f"Added custom plugin validator")
325
+
326
+ def list_custom_tools(self) -> List[Dict[str, Any]]:
327
+ """List all registered custom tools.
328
+
329
+ Returns:
330
+ List of custom tools with their information
331
+ """
332
+ tools = []
333
+ for tool_name, tool_info in self.custom_tools.items():
334
+ tools.append({
335
+ "name": tool_name,
336
+ "description": tool_info["definition"].get("description", ""),
337
+ "enabled": tool_info["enabled"],
338
+ "plugin": tool_info["plugin"],
339
+ "parameters": tool_info["definition"].get("parameters", {})
340
+ })
341
+ return tools
342
+
343
+ def enable_tool(self, tool_name: str) -> bool:
344
+ """Enable a custom tool.
345
+
346
+ Args:
347
+ tool_name: Name of the tool to enable
348
+
349
+ Returns:
350
+ True if tool was enabled
351
+ """
352
+ if tool_name in self.custom_tools:
353
+ self.custom_tools[tool_name]["enabled"] = True
354
+ logger.info(f"Enabled custom tool: {tool_name}")
355
+ return True
356
+ return False
357
+
358
+ def disable_tool(self, tool_name: str) -> bool:
359
+ """Disable a custom tool.
360
+
361
+ Args:
362
+ tool_name: Name of the tool to disable
363
+
364
+ Returns:
365
+ True if tool was disabled
366
+ """
367
+ if tool_name in self.custom_tools:
368
+ self.custom_tools[tool_name]["enabled"] = False
369
+ logger.info(f"Disabled custom tool: {tool_name}")
370
+ return True
371
+ return False
372
+
373
+ def get_plugin_info(self, plugin_name: str) -> Optional[Dict[str, Any]]:
374
+ """Get information about a registered plugin.
375
+
376
+ Args:
377
+ plugin_name: Name of the plugin
378
+
379
+ Returns:
380
+ Plugin information or None if not found
381
+ """
382
+ return self.registered_plugins.get(plugin_name)
383
+
384
+ def register_plugin(self, plugin_name: str, plugin_info: Dict[str, Any]) -> bool:
385
+ """Register a plugin with the SDK.
386
+
387
+ Args:
388
+ plugin_name: Name of the plugin
389
+ plugin_info: Plugin information
390
+
391
+ Returns:
392
+ True if registration successful
393
+ """
394
+ self.registered_plugins[plugin_name] = plugin_info
395
+ logger.info(f"Registered plugin with SDK: {plugin_name}")
396
+ return True