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,520 @@
1
+ """Tool execution engine for terminal commands, MCP tools, and file operations.
2
+
3
+ Provides unified execution interface for terminal commands, MCP tool calls, and
4
+ file operations with proper error handling, logging, and result processing.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import subprocess
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from .mcp_integration import MCPIntegration
15
+ from .file_operations_executor import FileOperationsExecutor
16
+ from ..events.models import EventType
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ToolExecutionResult:
22
+ """Result of tool execution."""
23
+
24
+ def __init__(self, tool_id: str, tool_type: str, success: bool,
25
+ output: str = "", error: str = "", execution_time: float = 0.0,
26
+ metadata: Dict[str, Any] = None):
27
+ """Initialize tool execution result.
28
+
29
+ Args:
30
+ tool_id: Unique identifier for the tool
31
+ tool_type: Type of tool (terminal, mcp_tool, file_edit, etc.)
32
+ success: Whether execution was successful
33
+ output: Tool output/result
34
+ error: Error message if failed
35
+ execution_time: Execution time in seconds
36
+ metadata: Additional metadata (e.g., diff_info for file edits)
37
+ """
38
+ self.tool_id = tool_id
39
+ self.tool_type = tool_type
40
+ self.success = success
41
+ self.output = output
42
+ self.error = error
43
+ self.execution_time = execution_time
44
+ self.metadata = metadata or {}
45
+ self.timestamp = time.time()
46
+
47
+ def to_dict(self) -> Dict[str, Any]:
48
+ """Convert result to dictionary."""
49
+ return {
50
+ "tool_id": self.tool_id,
51
+ "tool_type": self.tool_type,
52
+ "success": self.success,
53
+ "output": self.output,
54
+ "error": self.error,
55
+ "execution_time": self.execution_time,
56
+ "timestamp": self.timestamp
57
+ }
58
+
59
+ def __str__(self) -> str:
60
+ """String representation of result."""
61
+ status = "SUCCESS" if self.success else "FAILED"
62
+ return f"[{status}] {self.tool_type}:{self.tool_id} ({self.execution_time:.2f}s)"
63
+
64
+
65
+ class ToolExecutor:
66
+ """Execute tools with unified interface for terminal, MCP, and file operations.
67
+
68
+ Handles execution of terminal commands, MCP tool calls, and file operations
69
+ with proper error handling, timeouts, and result logging.
70
+ """
71
+
72
+ def __init__(self, mcp_integration: MCPIntegration, event_bus,
73
+ terminal_timeout: int = 90, mcp_timeout: int = 180, config=None):
74
+ """Initialize tool executor.
75
+
76
+ Args:
77
+ mcp_integration: MCP integration instance
78
+ event_bus: Event bus for hook emissions
79
+ terminal_timeout: Timeout for terminal commands in seconds
80
+ mcp_timeout: Timeout for MCP tool calls in seconds
81
+ config: Configuration manager (optional)
82
+ """
83
+ self.mcp_integration = mcp_integration
84
+ self.event_bus = event_bus
85
+ self.terminal_timeout = terminal_timeout
86
+ self.mcp_timeout = mcp_timeout
87
+
88
+ # File operations executor
89
+ self.file_ops_executor = FileOperationsExecutor(config=config)
90
+
91
+ # Execution statistics
92
+ self.stats = {
93
+ "total_executions": 0,
94
+ "successful_executions": 0,
95
+ "failed_executions": 0,
96
+ "terminal_executions": 0,
97
+ "mcp_executions": 0,
98
+ "file_op_executions": 0,
99
+ "total_execution_time": 0.0
100
+ }
101
+
102
+ logger.info("Tool executor initialized with terminal, MCP, and file operations support")
103
+
104
+ async def execute_tool(self, tool_data: Dict[str, Any]) -> ToolExecutionResult:
105
+ """Execute a single tool (terminal, MCP, or file operation).
106
+
107
+ Args:
108
+ tool_data: Tool information from ResponseParser
109
+
110
+ Returns:
111
+ Tool execution result
112
+ """
113
+ tool_type = tool_data.get("type", "unknown")
114
+ tool_id = tool_data.get("id", "unknown")
115
+
116
+ start_time = time.time()
117
+
118
+ try:
119
+ # Emit pre-execution hook
120
+ await self.event_bus.emit_with_hooks(
121
+ EventType.TOOL_CALL_PRE,
122
+ {"tool_data": tool_data},
123
+ "tool_executor"
124
+ )
125
+
126
+ # Execute based on tool type
127
+ try:
128
+ logger.debug(f"Executing tool {tool_id} of type {tool_type}")
129
+ try:
130
+ if tool_type == "terminal":
131
+ logger.debug(f"About to call _execute_terminal_command for {tool_id}")
132
+ result = await self._execute_terminal_command(tool_data)
133
+ logger.debug(f"_execute_terminal_command completed for {tool_id}")
134
+ elif tool_type == "mcp_tool":
135
+ logger.debug(f"About to call _execute_mcp_tool for {tool_id}")
136
+ result = await self._execute_mcp_tool(tool_data)
137
+ logger.debug(f"_execute_mcp_tool completed for {tool_id}")
138
+ elif tool_type.startswith("file_"):
139
+ # File operation
140
+ logger.debug(f"About to call _execute_file_operation for {tool_id}")
141
+ result = await self._execute_file_operation(tool_data)
142
+ logger.debug(f"_execute_file_operation completed for {tool_id}")
143
+ else:
144
+ result = ToolExecutionResult(
145
+ tool_id=tool_id,
146
+ tool_type=tool_type,
147
+ success=False,
148
+ error=f"Unknown tool type: {tool_type}"
149
+ )
150
+ logger.debug(f"Tool {tool_id} execution result: success={result.success}")
151
+ except Exception as inner_e:
152
+ import traceback
153
+ inner_trace = traceback.format_exc()
154
+ logger.error(f"Inner execution error for {tool_id}: {str(inner_e)}")
155
+ logger.error(f"Inner execution traceback for {tool_id}: {inner_trace}")
156
+ raise # Re-raise for outer handler
157
+ except Exception as e:
158
+ import traceback
159
+ error_details = f"Tool execution exception: {str(e)}\nTraceback: {traceback.format_exc()}"
160
+ logger.error(f"Critical error during tool {tool_id} execution: {error_details}")
161
+ result = ToolExecutionResult(
162
+ tool_id=tool_id,
163
+ tool_type=tool_type,
164
+ success=False,
165
+ error=f"Tool execution error: {str(e)}"
166
+ )
167
+
168
+ # Update execution time
169
+ result.execution_time = time.time() - start_time
170
+
171
+ # Emit post-execution hook
172
+ await self.event_bus.emit_with_hooks(
173
+ EventType.TOOL_CALL_POST,
174
+ {
175
+ "tool_data": tool_data,
176
+ "result": result.to_dict()
177
+ },
178
+ "tool_executor"
179
+ )
180
+
181
+ # Update statistics
182
+ self._update_stats(result)
183
+
184
+ logger.info(f"Tool execution completed: {result}")
185
+ return result
186
+
187
+ except Exception as e:
188
+ execution_time = time.time() - start_time
189
+ error_result = ToolExecutionResult(
190
+ tool_id=tool_id,
191
+ tool_type=tool_type,
192
+ success=False,
193
+ error=f"Execution error: {str(e)}",
194
+ execution_time=execution_time
195
+ )
196
+
197
+ self._update_stats(error_result)
198
+ logger.error(f"Tool execution failed: {e}")
199
+ return error_result
200
+
201
+ async def execute_all_tools(self, tools: List[Dict[str, Any]]) -> List[ToolExecutionResult]:
202
+ """Execute multiple tools in sequence.
203
+
204
+ Args:
205
+ tools: List of tool data from ResponseParser
206
+
207
+ Returns:
208
+ List of execution results in order
209
+ """
210
+ if not tools:
211
+ return []
212
+
213
+ logger.info(f"Executing {len(tools)} tools in sequence")
214
+ results = []
215
+
216
+ for i, tool_data in enumerate(tools):
217
+ logger.debug(f"Executing tool {i+1}/{len(tools)}: {tool_data.get('id', 'unknown')}")
218
+
219
+ result = await self.execute_tool(tool_data)
220
+ results.append(result)
221
+
222
+ # Log intermediate result
223
+ if result.success:
224
+ logger.debug(f"Tool {i+1} succeeded: {len(result.output)} chars output")
225
+ else:
226
+ logger.warning(f"Tool {i+1} failed: {result.error}")
227
+ # Continue executing remaining tools even if one fails
228
+
229
+ logger.info(f"Tool execution batch completed: "
230
+ f"{sum(1 for r in results if r.success)}/{len(results)} successful")
231
+
232
+ return results
233
+
234
+ async def _execute_terminal_command(self, tool_data: Dict[str, Any]) -> ToolExecutionResult:
235
+ """Execute a terminal command.
236
+
237
+ Args:
238
+ tool_data: Terminal tool data with command
239
+
240
+ Returns:
241
+ Execution result
242
+ """
243
+ command = tool_data.get("command", "").strip()
244
+ tool_id = tool_data.get("id", "unknown")
245
+
246
+ if not command:
247
+ return ToolExecutionResult(
248
+ tool_id=tool_id,
249
+ tool_type="terminal",
250
+ success=False,
251
+ error="Empty command"
252
+ )
253
+
254
+ logger.debug(f"Executing terminal command: {command[:100]}...")
255
+
256
+ try:
257
+ # Execute command with timeout
258
+ process = await asyncio.create_subprocess_shell(
259
+ command,
260
+ stdout=asyncio.subprocess.PIPE,
261
+ stderr=asyncio.subprocess.PIPE,
262
+ cwd=Path.cwd()
263
+ )
264
+
265
+ try:
266
+ stdout, stderr = await asyncio.wait_for(
267
+ process.communicate(),
268
+ timeout=self.terminal_timeout
269
+ )
270
+ except asyncio.TimeoutError:
271
+ process.kill()
272
+ await process.wait()
273
+ return ToolExecutionResult(
274
+ tool_id=tool_id,
275
+ tool_type="terminal",
276
+ success=False,
277
+ error=f"Command timed out after {self.terminal_timeout} seconds"
278
+ )
279
+
280
+ # Process results
281
+ stdout_text = stdout.decode('utf-8', errors='replace')
282
+ stderr_text = stderr.decode('utf-8', errors='replace')
283
+
284
+ success = process.returncode == 0
285
+ output = stdout_text if success else stderr_text
286
+ error = "" if success else f"Exit code {process.returncode}: {stderr_text}"
287
+
288
+ return ToolExecutionResult(
289
+ tool_id=tool_id,
290
+ tool_type="terminal",
291
+ success=success,
292
+ output=output,
293
+ error=error
294
+ )
295
+
296
+ except Exception as e:
297
+ import traceback
298
+ error_details = f"Execution exception: {str(e)}\nTraceback: {traceback.format_exc()}"
299
+ logger.error(f"Terminal execution failed for command '{command}': {error_details}")
300
+ return ToolExecutionResult(
301
+ tool_id=tool_id,
302
+ tool_type="terminal",
303
+ success=False,
304
+ error=f"Execution error: {str(e)}"
305
+ )
306
+
307
+ async def _execute_mcp_tool(self, tool_data: Dict[str, Any]) -> ToolExecutionResult:
308
+ """Execute an MCP tool call.
309
+
310
+ Args:
311
+ tool_data: MCP tool data with name and arguments
312
+
313
+ Returns:
314
+ Execution result
315
+ """
316
+ tool_name = tool_data.get("name", "")
317
+ tool_arguments = tool_data.get("arguments", {})
318
+ tool_id = tool_data.get("id", "unknown")
319
+
320
+ if not tool_name:
321
+ return ToolExecutionResult(
322
+ tool_id=tool_id,
323
+ tool_type="mcp_tool",
324
+ success=False,
325
+ error="Missing tool name"
326
+ )
327
+
328
+ logger.debug(f"Executing MCP tool: {tool_name} with args {tool_arguments}")
329
+
330
+ try:
331
+ # Call MCP tool with timeout
332
+ mcp_result = await asyncio.wait_for(
333
+ self.mcp_integration.call_mcp_tool(tool_name, tool_arguments),
334
+ timeout=self.mcp_timeout
335
+ )
336
+
337
+ # Process MCP result
338
+ if "error" in mcp_result:
339
+ return ToolExecutionResult(
340
+ tool_id=tool_id,
341
+ tool_type="mcp_tool",
342
+ success=False,
343
+ error=mcp_result["error"]
344
+ )
345
+ else:
346
+ # Format MCP output for display
347
+ output = self._format_mcp_output(mcp_result)
348
+
349
+ return ToolExecutionResult(
350
+ tool_id=tool_id,
351
+ tool_type="mcp_tool",
352
+ success=True,
353
+ output=output
354
+ )
355
+
356
+ except asyncio.TimeoutError:
357
+ return ToolExecutionResult(
358
+ tool_id=tool_id,
359
+ tool_type="mcp_tool",
360
+ success=False,
361
+ error=f"MCP tool timed out after {self.mcp_timeout} seconds"
362
+ )
363
+ except Exception as e:
364
+ return ToolExecutionResult(
365
+ tool_id=tool_id,
366
+ tool_type="mcp_tool",
367
+ success=False,
368
+ error=f"MCP execution error: {str(e)}"
369
+ )
370
+
371
+ def _format_mcp_output(self, mcp_result: Dict[str, Any]) -> str:
372
+ """Format MCP tool result for display.
373
+
374
+ Args:
375
+ mcp_result: Raw MCP result dictionary
376
+
377
+ Returns:
378
+ Formatted output string
379
+ """
380
+ # Handle different MCP result formats
381
+ if "content" in mcp_result:
382
+ # Standard MCP content format
383
+ content = mcp_result["content"]
384
+ if isinstance(content, list) and content:
385
+ # Multiple content blocks
386
+ parts = []
387
+ for block in content:
388
+ if isinstance(block, dict):
389
+ if block.get("type") == "text":
390
+ parts.append(block.get("text", ""))
391
+ else:
392
+ parts.append(str(block))
393
+ else:
394
+ parts.append(str(block))
395
+ return "\n".join(parts)
396
+ else:
397
+ return str(content)
398
+
399
+ elif "output" in mcp_result:
400
+ # Simple output format
401
+ return str(mcp_result["output"])
402
+
403
+ elif "result" in mcp_result:
404
+ # JSON-RPC result format
405
+ return str(mcp_result["result"])
406
+
407
+ else:
408
+ # Fallback: stringify entire result
409
+ return str(mcp_result)
410
+
411
+ async def _execute_file_operation(self, tool_data: Dict[str, Any]) -> ToolExecutionResult:
412
+ """Execute a file operation.
413
+
414
+ Args:
415
+ tool_data: File operation data from parser
416
+
417
+ Returns:
418
+ Tool execution result
419
+ """
420
+ tool_id = tool_data.get("id", "unknown")
421
+ tool_type = tool_data.get("type", "unknown")
422
+
423
+ logger.debug(f"Executing file operation: {tool_type}")
424
+
425
+ # Run file operation synchronously (file I/O is blocking anyway)
426
+ # Use asyncio.to_thread to avoid blocking the event loop
427
+ try:
428
+ result_dict = await asyncio.to_thread(
429
+ self.file_ops_executor.execute_operation,
430
+ tool_data
431
+ )
432
+
433
+ # Convert to ToolExecutionResult, preserving metadata (e.g., diff_info)
434
+ metadata = {}
435
+ if "diff_info" in result_dict:
436
+ metadata["diff_info"] = result_dict["diff_info"]
437
+
438
+ return ToolExecutionResult(
439
+ tool_id=tool_id,
440
+ tool_type=tool_type,
441
+ success=result_dict.get("success", False),
442
+ output=result_dict.get("output", ""),
443
+ error=result_dict.get("error", ""),
444
+ metadata=metadata
445
+ )
446
+
447
+ except Exception as e:
448
+ import traceback
449
+ error_trace = traceback.format_exc()
450
+ logger.error(f"File operation execution failed: {error_trace}")
451
+ return ToolExecutionResult(
452
+ tool_id=tool_id,
453
+ tool_type=tool_type,
454
+ success=False,
455
+ error=f"File operation error: {str(e)}"
456
+ )
457
+
458
+ def _update_stats(self, result: ToolExecutionResult):
459
+ """Update execution statistics.
460
+
461
+ Args:
462
+ result: Tool execution result
463
+ """
464
+ self.stats["total_executions"] += 1
465
+ self.stats["total_execution_time"] += result.execution_time
466
+
467
+ if result.success:
468
+ self.stats["successful_executions"] += 1
469
+ else:
470
+ self.stats["failed_executions"] += 1
471
+
472
+ if result.tool_type == "terminal":
473
+ self.stats["terminal_executions"] += 1
474
+ elif result.tool_type == "mcp_tool":
475
+ self.stats["mcp_executions"] += 1
476
+ elif result.tool_type.startswith("file_"):
477
+ self.stats["file_op_executions"] += 1
478
+
479
+ def get_execution_stats(self) -> Dict[str, Any]:
480
+ """Get execution statistics.
481
+
482
+ Returns:
483
+ Dictionary of execution statistics
484
+ """
485
+ total = self.stats["total_executions"]
486
+ if total == 0:
487
+ return {**self.stats, "success_rate": 0.0, "average_time": 0.0}
488
+
489
+ return {
490
+ **self.stats,
491
+ "success_rate": self.stats["successful_executions"] / total,
492
+ "average_time": self.stats["total_execution_time"] / total
493
+ }
494
+
495
+ def format_result_for_conversation(self, result: ToolExecutionResult) -> str:
496
+ """Format tool result for conversation history.
497
+
498
+ Args:
499
+ result: Tool execution result
500
+
501
+ Returns:
502
+ Formatted string for conversation logging
503
+ """
504
+ if result.success:
505
+ return f"[{result.tool_type}] {result.output}"
506
+ else:
507
+ return f"[{result.tool_type}] ERROR: {result.error}"
508
+
509
+ def reset_stats(self):
510
+ """Reset execution statistics."""
511
+ self.stats = {
512
+ "total_executions": 0,
513
+ "successful_executions": 0,
514
+ "failed_executions": 0,
515
+ "terminal_executions": 0,
516
+ "mcp_executions": 0,
517
+ "file_op_executions": 0,
518
+ "total_execution_time": 0.0
519
+ }
520
+ logger.info("Tool execution statistics reset")
@@ -0,0 +1,19 @@
1
+ """Logging module for centralized logging configuration."""
2
+
3
+ from .setup import (
4
+ setup_bootstrap_logging,
5
+ setup_from_config,
6
+ get_current_config,
7
+ is_configured,
8
+ CompactFormatter,
9
+ LoggingSetup
10
+ )
11
+
12
+ __all__ = [
13
+ 'setup_bootstrap_logging',
14
+ 'setup_from_config',
15
+ 'get_current_config',
16
+ 'is_configured',
17
+ 'CompactFormatter',
18
+ 'LoggingSetup'
19
+ ]