pomera-ai-commander 0.1.0

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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +680 -0
  3. package/bin/pomera-ai-commander.js +62 -0
  4. package/core/__init__.py +66 -0
  5. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  7. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  8. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  9. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  10. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  11. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  12. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  13. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  14. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  15. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  16. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  17. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  18. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  19. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  20. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  21. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  22. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  23. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  24. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  25. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  26. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  27. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  28. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  29. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  30. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  31. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  32. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  33. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  34. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  35. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  36. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  37. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  38. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  39. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  40. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  41. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  42. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  43. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  44. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  45. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  46. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  47. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  48. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  49. package/core/app_context.py +482 -0
  50. package/core/async_text_processor.py +422 -0
  51. package/core/backup_manager.py +656 -0
  52. package/core/backup_recovery_manager.py +1034 -0
  53. package/core/content_hash_cache.py +509 -0
  54. package/core/context_menu.py +313 -0
  55. package/core/data_validator.py +1067 -0
  56. package/core/database_connection_manager.py +745 -0
  57. package/core/database_curl_settings_manager.py +609 -0
  58. package/core/database_promera_ai_settings_manager.py +447 -0
  59. package/core/database_schema.py +412 -0
  60. package/core/database_schema_manager.py +396 -0
  61. package/core/database_settings_manager.py +1508 -0
  62. package/core/database_settings_manager_interface.py +457 -0
  63. package/core/dialog_manager.py +735 -0
  64. package/core/efficient_line_numbers.py +511 -0
  65. package/core/error_handler.py +747 -0
  66. package/core/error_service.py +431 -0
  67. package/core/event_consolidator.py +512 -0
  68. package/core/mcp/__init__.py +43 -0
  69. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  70. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  71. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  72. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  73. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  74. package/core/mcp/protocol.py +288 -0
  75. package/core/mcp/schema.py +251 -0
  76. package/core/mcp/server_stdio.py +299 -0
  77. package/core/mcp/tool_registry.py +2345 -0
  78. package/core/memory_efficient_text_widget.py +712 -0
  79. package/core/migration_manager.py +915 -0
  80. package/core/migration_test_suite.py +1086 -0
  81. package/core/migration_validator.py +1144 -0
  82. package/core/optimized_find_replace.py +715 -0
  83. package/core/optimized_pattern_engine.py +424 -0
  84. package/core/optimized_search_highlighter.py +553 -0
  85. package/core/performance_monitor.py +675 -0
  86. package/core/persistence_manager.py +713 -0
  87. package/core/progressive_stats_calculator.py +632 -0
  88. package/core/regex_pattern_cache.py +530 -0
  89. package/core/regex_pattern_library.py +351 -0
  90. package/core/search_operation_manager.py +435 -0
  91. package/core/settings_defaults_registry.py +1087 -0
  92. package/core/settings_integrity_validator.py +1112 -0
  93. package/core/settings_serializer.py +558 -0
  94. package/core/settings_validator.py +1824 -0
  95. package/core/smart_stats_calculator.py +710 -0
  96. package/core/statistics_update_manager.py +619 -0
  97. package/core/stats_config_manager.py +858 -0
  98. package/core/streaming_text_handler.py +723 -0
  99. package/core/task_scheduler.py +596 -0
  100. package/core/update_pattern_library.py +169 -0
  101. package/core/visibility_monitor.py +596 -0
  102. package/core/widget_cache.py +498 -0
  103. package/mcp.json +61 -0
  104. package/package.json +57 -0
  105. package/pomera.py +7483 -0
  106. package/pomera_mcp_server.py +144 -0
  107. package/tools/__init__.py +5 -0
  108. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  109. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  110. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  111. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  112. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  113. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  114. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  115. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  116. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  117. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  118. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  119. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  120. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  121. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  122. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  123. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  124. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  125. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  126. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  127. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  128. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  129. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  130. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  131. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  132. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  133. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  134. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  135. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  136. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  137. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  138. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  139. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  140. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  141. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  142. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  143. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  144. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  145. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  146. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  147. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  148. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  149. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  150. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
  151. package/tools/ai_tools.py +2892 -0
  152. package/tools/ascii_art_generator.py +353 -0
  153. package/tools/base64_tools.py +184 -0
  154. package/tools/base_tool.py +511 -0
  155. package/tools/case_tool.py +309 -0
  156. package/tools/column_tools.py +396 -0
  157. package/tools/cron_tool.py +885 -0
  158. package/tools/curl_history.py +601 -0
  159. package/tools/curl_processor.py +1208 -0
  160. package/tools/curl_settings.py +503 -0
  161. package/tools/curl_tool.py +5467 -0
  162. package/tools/diff_viewer.py +1072 -0
  163. package/tools/email_extraction_tool.py +249 -0
  164. package/tools/email_header_analyzer.py +426 -0
  165. package/tools/extraction_tools.py +250 -0
  166. package/tools/find_replace.py +1751 -0
  167. package/tools/folder_file_reporter.py +1463 -0
  168. package/tools/folder_file_reporter_adapter.py +480 -0
  169. package/tools/generator_tools.py +1217 -0
  170. package/tools/hash_generator.py +256 -0
  171. package/tools/html_tool.py +657 -0
  172. package/tools/huggingface_helper.py +449 -0
  173. package/tools/jsonxml_tool.py +730 -0
  174. package/tools/line_tools.py +419 -0
  175. package/tools/list_comparator.py +720 -0
  176. package/tools/markdown_tools.py +562 -0
  177. package/tools/mcp_widget.py +1417 -0
  178. package/tools/notes_widget.py +973 -0
  179. package/tools/number_base_converter.py +373 -0
  180. package/tools/regex_extractor.py +572 -0
  181. package/tools/slug_generator.py +311 -0
  182. package/tools/sorter_tools.py +459 -0
  183. package/tools/string_escape_tool.py +393 -0
  184. package/tools/text_statistics_tool.py +366 -0
  185. package/tools/text_wrapper.py +431 -0
  186. package/tools/timestamp_converter.py +422 -0
  187. package/tools/tool_loader.py +710 -0
  188. package/tools/translator_tools.py +523 -0
  189. package/tools/url_link_extractor.py +262 -0
  190. package/tools/url_parser.py +205 -0
  191. package/tools/whitespace_tools.py +356 -0
  192. package/tools/word_frequency_counter.py +147 -0
@@ -0,0 +1,710 @@
1
+ """
2
+ Tool Loader - Centralized tool registration and lazy loading.
3
+
4
+ This module replaces the 38+ try/except import blocks in pomera.py with a cleaner
5
+ registry-based approach that supports lazy loading.
6
+
7
+ Author: Pomera AI Commander Team
8
+ """
9
+
10
+ import importlib
11
+ import logging
12
+ from typing import Dict, Any, Optional, Callable, Type, List, Tuple
13
+ from dataclasses import dataclass, field
14
+ from enum import Enum
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ToolCategory(Enum):
21
+ """Tool categories for organization."""
22
+ CORE = "Core Tools"
23
+ AI = "AI Tools"
24
+ EXTRACTION = "Extraction Tools"
25
+ CONVERSION = "Conversion Tools"
26
+ TEXT_MANIPULATION = "Text Manipulation"
27
+ GENERATORS = "Generators"
28
+ ANALYSIS = "Analysis Tools"
29
+ UTILITY = "Utility Tools"
30
+ MCP = "MCP Tools"
31
+
32
+
33
+ @dataclass
34
+ class ToolSpec:
35
+ """
36
+ Specification for a loadable tool.
37
+
38
+ Attributes:
39
+ name: Display name of the tool (used in UI)
40
+ module_path: Python module path (e.g., "tools.case_tool")
41
+ class_name: Name of the class to import (e.g., "CaseTool")
42
+ category: Tool category for organization
43
+ widget_class: Optional separate widget class name
44
+ dependencies: Optional list of required pip packages
45
+ description: Tool description for UI/help
46
+ is_widget: True if the class is a full widget (not just a tool class)
47
+ available_flag: Legacy flag name for backwards compatibility
48
+ """
49
+ name: str
50
+ module_path: str
51
+ class_name: str
52
+ category: ToolCategory = ToolCategory.UTILITY
53
+ widget_class: Optional[str] = None
54
+ dependencies: List[str] = field(default_factory=list)
55
+ description: str = ""
56
+ is_widget: bool = False
57
+ available_flag: str = "" # e.g., "CASE_TOOL_MODULE_AVAILABLE"
58
+
59
+
60
+ # Complete tool specifications registry
61
+ TOOL_SPECS: Dict[str, ToolSpec] = {
62
+ # Core Tools
63
+ "Case Tool": ToolSpec(
64
+ name="Case Tool",
65
+ module_path="tools.case_tool",
66
+ class_name="CaseTool",
67
+ category=ToolCategory.CORE,
68
+ description="Transform text case (uppercase, lowercase, title case, etc.)",
69
+ available_flag="CASE_TOOL_MODULE_AVAILABLE"
70
+ ),
71
+ "Find & Replace": ToolSpec(
72
+ name="Find & Replace",
73
+ module_path="tools.find_replace",
74
+ class_name="FindReplaceWidget",
75
+ category=ToolCategory.CORE,
76
+ is_widget=True,
77
+ description="Find and replace text with regex support",
78
+ available_flag="FIND_REPLACE_MODULE_AVAILABLE"
79
+ ),
80
+ "Diff Viewer": ToolSpec(
81
+ name="Diff Viewer",
82
+ module_path="tools.diff_viewer",
83
+ class_name="DiffViewerWidget",
84
+ widget_class="DiffViewerSettingsWidget",
85
+ category=ToolCategory.CORE,
86
+ is_widget=True,
87
+ description="Compare and view differences between texts",
88
+ available_flag="DIFF_VIEWER_MODULE_AVAILABLE"
89
+ ),
90
+
91
+ # AI Tools
92
+ "AI Tools": ToolSpec(
93
+ name="AI Tools",
94
+ module_path="tools.ai_tools",
95
+ class_name="AIToolsWidget",
96
+ category=ToolCategory.AI,
97
+ is_widget=True,
98
+ description="AI-powered text processing with multiple providers",
99
+ available_flag="AI_TOOLS_AVAILABLE"
100
+ ),
101
+
102
+ # Extraction Tools
103
+ "Email Extraction": ToolSpec(
104
+ name="Email Extraction",
105
+ module_path="tools.email_extraction_tool",
106
+ class_name="EmailExtractionTool",
107
+ category=ToolCategory.EXTRACTION,
108
+ description="Extract email addresses from text",
109
+ available_flag="EMAIL_EXTRACTION_MODULE_AVAILABLE"
110
+ ),
111
+ "Email Header Analyzer": ToolSpec(
112
+ name="Email Header Analyzer",
113
+ module_path="tools.email_header_analyzer",
114
+ class_name="EmailHeaderAnalyzer",
115
+ category=ToolCategory.EXTRACTION,
116
+ description="Analyze email headers for routing and authentication info",
117
+ available_flag="EMAIL_HEADER_ANALYZER_MODULE_AVAILABLE"
118
+ ),
119
+ "URL Link Extractor": ToolSpec(
120
+ name="URL Link Extractor",
121
+ module_path="tools.url_link_extractor",
122
+ class_name="URLLinkExtractor",
123
+ category=ToolCategory.EXTRACTION,
124
+ description="Extract URLs and links from text",
125
+ available_flag="URL_LINK_EXTRACTOR_MODULE_AVAILABLE"
126
+ ),
127
+ "Regex Extractor": ToolSpec(
128
+ name="Regex Extractor",
129
+ module_path="tools.regex_extractor",
130
+ class_name="RegexExtractor",
131
+ category=ToolCategory.EXTRACTION,
132
+ description="Extract text patterns using regular expressions",
133
+ available_flag="REGEX_EXTRACTOR_MODULE_AVAILABLE"
134
+ ),
135
+ "URL Parser": ToolSpec(
136
+ name="URL Parser",
137
+ module_path="tools.url_parser",
138
+ class_name="URLParser",
139
+ category=ToolCategory.EXTRACTION,
140
+ description="Parse and analyze URL components",
141
+ available_flag="URL_PARSER_MODULE_AVAILABLE"
142
+ ),
143
+ "HTML Tool": ToolSpec(
144
+ name="HTML Tool",
145
+ module_path="tools.html_tool",
146
+ class_name="HTMLExtractionTool",
147
+ category=ToolCategory.EXTRACTION,
148
+ description="Extract content from HTML",
149
+ available_flag="HTML_EXTRACTION_TOOL_MODULE_AVAILABLE"
150
+ ),
151
+ "Extraction Tools": ToolSpec(
152
+ name="Extraction Tools",
153
+ module_path="tools.extraction_tools",
154
+ class_name="ExtractionTools",
155
+ category=ToolCategory.EXTRACTION,
156
+ description="General purpose extraction utilities",
157
+ available_flag="EXTRACTION_TOOLS_MODULE_AVAILABLE"
158
+ ),
159
+
160
+ # Conversion Tools
161
+ "Base64 Tools": ToolSpec(
162
+ name="Base64 Tools",
163
+ module_path="tools.base64_tools",
164
+ class_name="Base64Tools",
165
+ widget_class="Base64ToolsWidget",
166
+ category=ToolCategory.CONVERSION,
167
+ description="Encode and decode Base64",
168
+ available_flag="BASE64_TOOLS_MODULE_AVAILABLE"
169
+ ),
170
+ "JSON/XML Tool": ToolSpec(
171
+ name="JSON/XML Tool",
172
+ module_path="tools.jsonxml_tool",
173
+ class_name="JSONXMLTool",
174
+ category=ToolCategory.CONVERSION,
175
+ description="Convert between JSON and XML formats",
176
+ available_flag="JSONXML_TOOL_MODULE_AVAILABLE"
177
+ ),
178
+ "Hash Generator": ToolSpec(
179
+ name="Hash Generator",
180
+ module_path="tools.hash_generator",
181
+ class_name="HashGenerator",
182
+ category=ToolCategory.CONVERSION,
183
+ description="Generate MD5, SHA1, SHA256 and other hashes",
184
+ available_flag="HASH_GENERATOR_MODULE_AVAILABLE"
185
+ ),
186
+ "Number Base Converter": ToolSpec(
187
+ name="Number Base Converter",
188
+ module_path="tools.number_base_converter",
189
+ class_name="NumberBaseConverter",
190
+ category=ToolCategory.CONVERSION,
191
+ description="Convert numbers between binary, octal, decimal, hex",
192
+ available_flag="NUMBER_BASE_CONVERTER_MODULE_AVAILABLE"
193
+ ),
194
+ "Timestamp Converter": ToolSpec(
195
+ name="Timestamp Converter",
196
+ module_path="tools.timestamp_converter",
197
+ class_name="TimestampConverter",
198
+ category=ToolCategory.CONVERSION,
199
+ description="Convert between timestamp formats",
200
+ available_flag="TIMESTAMP_CONVERTER_MODULE_AVAILABLE"
201
+ ),
202
+ "String Escape Tool": ToolSpec(
203
+ name="String Escape Tool",
204
+ module_path="tools.string_escape_tool",
205
+ class_name="StringEscapeTool",
206
+ category=ToolCategory.CONVERSION,
207
+ description="Escape/unescape strings for various formats",
208
+ available_flag="STRING_ESCAPE_TOOL_MODULE_AVAILABLE"
209
+ ),
210
+
211
+ # Text Manipulation Tools
212
+ "Sorter Tools": ToolSpec(
213
+ name="Sorter Tools",
214
+ module_path="tools.sorter_tools",
215
+ class_name="SorterTools",
216
+ category=ToolCategory.TEXT_MANIPULATION,
217
+ description="Sort lines alphabetically or numerically",
218
+ available_flag="SORTER_TOOLS_MODULE_AVAILABLE"
219
+ ),
220
+ "Line Tools": ToolSpec(
221
+ name="Line Tools",
222
+ module_path="tools.line_tools",
223
+ class_name="LineTools",
224
+ category=ToolCategory.TEXT_MANIPULATION,
225
+ description="Line manipulation (remove duplicates, number lines, etc.)",
226
+ available_flag="LINE_TOOLS_MODULE_AVAILABLE"
227
+ ),
228
+ "Whitespace Tools": ToolSpec(
229
+ name="Whitespace Tools",
230
+ module_path="tools.whitespace_tools",
231
+ class_name="WhitespaceTools",
232
+ category=ToolCategory.TEXT_MANIPULATION,
233
+ description="Trim, normalize whitespace and line endings",
234
+ available_flag="WHITESPACE_TOOLS_MODULE_AVAILABLE"
235
+ ),
236
+ "Column Tools": ToolSpec(
237
+ name="Column Tools",
238
+ module_path="tools.column_tools",
239
+ class_name="ColumnTools",
240
+ category=ToolCategory.TEXT_MANIPULATION,
241
+ description="CSV and column manipulation",
242
+ available_flag="COLUMN_TOOLS_MODULE_AVAILABLE"
243
+ ),
244
+ "Text Wrapper": ToolSpec(
245
+ name="Text Wrapper",
246
+ module_path="tools.text_wrapper",
247
+ class_name="TextWrapper",
248
+ category=ToolCategory.TEXT_MANIPULATION,
249
+ description="Wrap text to specified width",
250
+ available_flag="TEXT_WRAPPER_MODULE_AVAILABLE"
251
+ ),
252
+ "Markdown Tools": ToolSpec(
253
+ name="Markdown Tools",
254
+ module_path="tools.markdown_tools",
255
+ class_name="MarkdownTools",
256
+ category=ToolCategory.TEXT_MANIPULATION,
257
+ description="Process and extract from Markdown",
258
+ available_flag="MARKDOWN_TOOLS_MODULE_AVAILABLE"
259
+ ),
260
+ "Slug Generator": ToolSpec(
261
+ name="Slug Generator",
262
+ module_path="tools.slug_generator",
263
+ class_name="SlugGenerator",
264
+ category=ToolCategory.TEXT_MANIPULATION,
265
+ description="Generate URL-friendly slugs",
266
+ available_flag="SLUG_GENERATOR_MODULE_AVAILABLE"
267
+ ),
268
+ "Translator Tools": ToolSpec(
269
+ name="Translator Tools",
270
+ module_path="tools.translator_tools",
271
+ class_name="TranslatorTools",
272
+ category=ToolCategory.TEXT_MANIPULATION,
273
+ description="Translate to/from Morse code and binary",
274
+ available_flag="TRANSLATOR_TOOLS_MODULE_AVAILABLE"
275
+ ),
276
+
277
+ # Generator Tools
278
+ "Generator Tools": ToolSpec(
279
+ name="Generator Tools",
280
+ module_path="tools.generator_tools",
281
+ class_name="GeneratorTools",
282
+ widget_class="GeneratorToolsWidget",
283
+ category=ToolCategory.GENERATORS,
284
+ description="Generate passwords, UUIDs, Lorem Ipsum",
285
+ available_flag="GENERATOR_TOOLS_MODULE_AVAILABLE"
286
+ ),
287
+ "ASCII Art Generator": ToolSpec(
288
+ name="ASCII Art Generator",
289
+ module_path="tools.ascii_art_generator",
290
+ class_name="ASCIIArtGenerator",
291
+ category=ToolCategory.GENERATORS,
292
+ description="Generate ASCII art from text",
293
+ available_flag="ASCII_ART_GENERATOR_MODULE_AVAILABLE"
294
+ ),
295
+
296
+ # Analysis Tools
297
+ "Word Frequency Counter": ToolSpec(
298
+ name="Word Frequency Counter",
299
+ module_path="tools.word_frequency_counter",
300
+ class_name="WordFrequencyCounter",
301
+ category=ToolCategory.ANALYSIS,
302
+ description="Count word frequencies in text",
303
+ available_flag="WORD_FREQUENCY_COUNTER_MODULE_AVAILABLE"
304
+ ),
305
+ "Text Statistics": ToolSpec(
306
+ name="Text Statistics",
307
+ module_path="tools.text_statistics_tool",
308
+ class_name="TextStatistics",
309
+ category=ToolCategory.ANALYSIS,
310
+ description="Calculate text statistics (chars, words, lines)",
311
+ available_flag="TEXT_STATISTICS_MODULE_AVAILABLE"
312
+ ),
313
+ "Cron Tool": ToolSpec(
314
+ name="Cron Tool",
315
+ module_path="tools.cron_tool",
316
+ class_name="CronTool",
317
+ category=ToolCategory.ANALYSIS,
318
+ description="Parse and explain cron expressions",
319
+ available_flag="CRON_TOOL_MODULE_AVAILABLE"
320
+ ),
321
+
322
+ # Utility Tools
323
+ "cURL Tool": ToolSpec(
324
+ name="cURL Tool",
325
+ module_path="tools.curl_tool",
326
+ class_name="CurlToolWidget",
327
+ category=ToolCategory.UTILITY,
328
+ is_widget=True,
329
+ description="Make HTTP requests",
330
+ available_flag="CURL_TOOL_MODULE_AVAILABLE"
331
+ ),
332
+ "List Comparator": ToolSpec(
333
+ name="List Comparator",
334
+ module_path="tools.list_comparator",
335
+ class_name="DiffApp",
336
+ category=ToolCategory.UTILITY,
337
+ description="Compare two lists and find differences",
338
+ available_flag="LIST_COMPARATOR_MODULE_AVAILABLE"
339
+ ),
340
+ "Notes Widget": ToolSpec(
341
+ name="Notes Widget",
342
+ module_path="tools.notes_widget",
343
+ class_name="NotesWidget",
344
+ category=ToolCategory.UTILITY,
345
+ is_widget=True,
346
+ description="Save and manage notes",
347
+ available_flag="NOTES_WIDGET_MODULE_AVAILABLE"
348
+ ),
349
+ "Folder File Reporter": ToolSpec(
350
+ name="Folder File Reporter",
351
+ module_path="tools.folder_file_reporter_adapter",
352
+ class_name="FolderFileReporterAdapter",
353
+ category=ToolCategory.UTILITY,
354
+ description="Generate reports of folder contents",
355
+ available_flag="FOLDER_FILE_REPORTER_MODULE_AVAILABLE"
356
+ ),
357
+
358
+ # MCP Tools
359
+ "MCP Manager": ToolSpec(
360
+ name="MCP Manager",
361
+ module_path="tools.mcp_widget",
362
+ class_name="MCPManager",
363
+ category=ToolCategory.MCP,
364
+ is_widget=True,
365
+ description="Model Context Protocol server management",
366
+ available_flag="MCP_WIDGET_MODULE_AVAILABLE"
367
+ ),
368
+ }
369
+
370
+
371
+ class ToolLoader:
372
+ """
373
+ Centralized tool loading with lazy initialization.
374
+
375
+ Benefits:
376
+ - Single place to manage all tool imports
377
+ - Lazy loading - tools only loaded when first accessed
378
+ - Clean availability checking
379
+ - Reduces startup time
380
+ - Caches loaded modules and classes
381
+
382
+ Usage:
383
+ loader = get_tool_loader()
384
+
385
+ # Check if tool is available
386
+ if loader.is_available("Case Tool"):
387
+ # Get the tool class
388
+ CaseTool = loader.get_tool_class("Case Tool")
389
+ tool = CaseTool()
390
+
391
+ # Or create instance directly
392
+ tool = loader.create_instance("Case Tool")
393
+
394
+ # Get all available tools
395
+ available = loader.get_available_tools()
396
+ """
397
+
398
+ def __init__(self, tool_specs: Optional[Dict[str, ToolSpec]] = None):
399
+ """
400
+ Initialize the tool loader.
401
+
402
+ Args:
403
+ tool_specs: Optional custom tool specifications (uses TOOL_SPECS if None)
404
+ """
405
+ self._specs = tool_specs or TOOL_SPECS.copy()
406
+ self._loaded_modules: Dict[str, Any] = {}
407
+ self._loaded_classes: Dict[str, Type] = {}
408
+ self._availability_cache: Dict[str, bool] = {}
409
+ self._load_errors: Dict[str, str] = {}
410
+ self._widget_classes: Dict[str, Type] = {}
411
+
412
+ def register_tool(self, spec: ToolSpec) -> None:
413
+ """
414
+ Register a new tool specification.
415
+
416
+ Args:
417
+ spec: Tool specification to register
418
+ """
419
+ self._specs[spec.name] = spec
420
+ # Clear caches for this tool
421
+ self._availability_cache.pop(spec.name, None)
422
+ self._loaded_classes.pop(spec.name, None)
423
+ self._load_errors.pop(spec.name, None)
424
+ logger.debug(f"Registered tool: {spec.name}")
425
+
426
+ def unregister_tool(self, name: str) -> bool:
427
+ """
428
+ Unregister a tool.
429
+
430
+ Args:
431
+ name: Tool name to unregister
432
+
433
+ Returns:
434
+ True if tool was found and removed
435
+ """
436
+ if name in self._specs:
437
+ del self._specs[name]
438
+ self._availability_cache.pop(name, None)
439
+ self._loaded_classes.pop(name, None)
440
+ self._load_errors.pop(name, None)
441
+ return True
442
+ return False
443
+
444
+ def is_available(self, tool_name: str) -> bool:
445
+ """
446
+ Check if a tool is available (can be imported).
447
+
448
+ Args:
449
+ tool_name: Name of the tool
450
+
451
+ Returns:
452
+ True if tool module can be imported
453
+ """
454
+ if tool_name in self._availability_cache:
455
+ return self._availability_cache[tool_name]
456
+
457
+ if tool_name not in self._specs:
458
+ self._availability_cache[tool_name] = False
459
+ return False
460
+
461
+ spec = self._specs[tool_name]
462
+ try:
463
+ importlib.import_module(spec.module_path)
464
+ self._availability_cache[tool_name] = True
465
+ return True
466
+ except ImportError as e:
467
+ self._availability_cache[tool_name] = False
468
+ self._load_errors[tool_name] = str(e)
469
+ logger.debug(f"Tool '{tool_name}' not available: {e}")
470
+ return False
471
+
472
+ def get_tool_class(self, tool_name: str) -> Optional[Type]:
473
+ """
474
+ Get the tool class (lazy loaded).
475
+
476
+ Args:
477
+ tool_name: Name of the tool
478
+
479
+ Returns:
480
+ The tool class, or None if not available
481
+ """
482
+ if tool_name in self._loaded_classes:
483
+ return self._loaded_classes[tool_name]
484
+
485
+ if not self.is_available(tool_name):
486
+ return None
487
+
488
+ spec = self._specs[tool_name]
489
+ try:
490
+ module = self._get_module(spec.module_path)
491
+ tool_class = getattr(module, spec.class_name)
492
+ self._loaded_classes[tool_name] = tool_class
493
+ logger.info(f"Loaded tool class: {tool_name}")
494
+ return tool_class
495
+ except (ImportError, AttributeError) as e:
496
+ self._load_errors[tool_name] = str(e)
497
+ logger.error(f"Failed to load tool class '{tool_name}': {e}")
498
+ return None
499
+
500
+ def get_widget_class(self, tool_name: str) -> Optional[Type]:
501
+ """
502
+ Get the widget class for a tool (if it has a separate one).
503
+
504
+ Args:
505
+ tool_name: Name of the tool
506
+
507
+ Returns:
508
+ The widget class, or None if not available
509
+ """
510
+ if tool_name in self._widget_classes:
511
+ return self._widget_classes[tool_name]
512
+
513
+ if tool_name not in self._specs:
514
+ return None
515
+
516
+ spec = self._specs[tool_name]
517
+
518
+ # If no separate widget class, return the main class
519
+ if not spec.widget_class:
520
+ return self.get_tool_class(tool_name)
521
+
522
+ try:
523
+ module = self._get_module(spec.module_path)
524
+ widget_class = getattr(module, spec.widget_class)
525
+ self._widget_classes[tool_name] = widget_class
526
+ return widget_class
527
+ except (ImportError, AttributeError) as e:
528
+ logger.error(f"Failed to load widget class for '{tool_name}': {e}")
529
+ return None
530
+
531
+ def create_instance(self, tool_name: str, *args, **kwargs) -> Optional[Any]:
532
+ """
533
+ Create an instance of a tool.
534
+
535
+ Args:
536
+ tool_name: Name of the tool
537
+ *args, **kwargs: Arguments to pass to the constructor
538
+
539
+ Returns:
540
+ Tool instance, or None if not available
541
+ """
542
+ tool_class = self.get_tool_class(tool_name)
543
+ if tool_class is None:
544
+ return None
545
+
546
+ try:
547
+ return tool_class(*args, **kwargs)
548
+ except Exception as e:
549
+ logger.error(f"Failed to instantiate tool '{tool_name}': {e}")
550
+ return None
551
+
552
+ def _get_module(self, module_path: str) -> Any:
553
+ """Get a module (cached)."""
554
+ if module_path not in self._loaded_modules:
555
+ self._loaded_modules[module_path] = importlib.import_module(module_path)
556
+ return self._loaded_modules[module_path]
557
+
558
+ def get_available_tools(self) -> List[str]:
559
+ """
560
+ Get list of all available tool names.
561
+
562
+ Returns:
563
+ List of tool names that can be loaded
564
+ """
565
+ return [name for name in self._specs.keys() if self.is_available(name)]
566
+
567
+ def get_all_tool_names(self) -> List[str]:
568
+ """
569
+ Get list of all registered tool names.
570
+
571
+ Returns:
572
+ List of all tool names (whether available or not)
573
+ """
574
+ return list(self._specs.keys())
575
+
576
+ def get_tools_by_category(self, category: ToolCategory) -> List[str]:
577
+ """
578
+ Get tools in a specific category.
579
+
580
+ Args:
581
+ category: Tool category
582
+
583
+ Returns:
584
+ List of tool names in that category
585
+ """
586
+ return [
587
+ name for name, spec in self._specs.items()
588
+ if spec.category == category and self.is_available(name)
589
+ ]
590
+
591
+ def get_tool_spec(self, tool_name: str) -> Optional[ToolSpec]:
592
+ """
593
+ Get the specification for a tool.
594
+
595
+ Args:
596
+ tool_name: Name of the tool
597
+
598
+ Returns:
599
+ ToolSpec or None if not found
600
+ """
601
+ return self._specs.get(tool_name)
602
+
603
+ def get_load_error(self, tool_name: str) -> Optional[str]:
604
+ """
605
+ Get the error message if a tool failed to load.
606
+
607
+ Args:
608
+ tool_name: Name of the tool
609
+
610
+ Returns:
611
+ Error message, or None if no error
612
+ """
613
+ return self._load_errors.get(tool_name)
614
+
615
+ def get_availability_report(self) -> Dict[str, Dict[str, Any]]:
616
+ """
617
+ Get a report of all tools and their availability.
618
+
619
+ Returns:
620
+ Dictionary with tool availability information
621
+ """
622
+ report = {}
623
+ for name, spec in self._specs.items():
624
+ available = self.is_available(name)
625
+ report[name] = {
626
+ 'available': available,
627
+ 'category': spec.category.value,
628
+ 'module': spec.module_path,
629
+ 'class': spec.class_name,
630
+ 'is_widget': spec.is_widget,
631
+ 'error': self._load_errors.get(name) if not available else None
632
+ }
633
+ return report
634
+
635
+ def preload_tools(self, tool_names: Optional[List[str]] = None) -> Dict[str, bool]:
636
+ """
637
+ Preload specified tools (or all if none specified).
638
+
639
+ Args:
640
+ tool_names: List of tool names to preload, or None for all
641
+
642
+ Returns:
643
+ Dictionary of tool names to load success status
644
+ """
645
+ names = tool_names or list(self._specs.keys())
646
+ results = {}
647
+ for name in names:
648
+ results[name] = self.get_tool_class(name) is not None
649
+ return results
650
+
651
+ def clear_cache(self) -> None:
652
+ """Clear all cached modules and classes."""
653
+ self._loaded_modules.clear()
654
+ self._loaded_classes.clear()
655
+ self._availability_cache.clear()
656
+ self._load_errors.clear()
657
+ self._widget_classes.clear()
658
+ logger.debug("Tool loader cache cleared")
659
+
660
+ def get_legacy_flags(self) -> Dict[str, bool]:
661
+ """
662
+ Get legacy availability flags for backwards compatibility.
663
+
664
+ Returns:
665
+ Dictionary of flag names to boolean values
666
+ """
667
+ flags = {}
668
+ for name, spec in self._specs.items():
669
+ if spec.available_flag:
670
+ flags[spec.available_flag] = self.is_available(name)
671
+ return flags
672
+
673
+
674
+ # Global instance
675
+ _tool_loader: Optional[ToolLoader] = None
676
+
677
+
678
+ def get_tool_loader() -> ToolLoader:
679
+ """
680
+ Get the global tool loader instance.
681
+
682
+ Returns:
683
+ Global ToolLoader instance
684
+ """
685
+ global _tool_loader
686
+ if _tool_loader is None:
687
+ _tool_loader = ToolLoader()
688
+ return _tool_loader
689
+
690
+
691
+ def init_tool_loader(tool_specs: Optional[Dict[str, ToolSpec]] = None) -> ToolLoader:
692
+ """
693
+ Initialize the global tool loader.
694
+
695
+ Args:
696
+ tool_specs: Optional custom tool specifications
697
+
698
+ Returns:
699
+ Initialized ToolLoader
700
+ """
701
+ global _tool_loader
702
+ _tool_loader = ToolLoader(tool_specs)
703
+ return _tool_loader
704
+
705
+
706
+ def reset_tool_loader() -> None:
707
+ """Reset the global tool loader."""
708
+ global _tool_loader
709
+ _tool_loader = None
710
+