janito 3.1.0__py3-none-any.whl → 3.3.0__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.
- janito/cli/chat_mode/bindings.py +0 -26
- janito/cli/chat_mode/session.py +1 -7
- janito/cli/cli_commands/list_plugins.py +8 -13
- janito/cli/prompt_core.py +9 -19
- janito/llm/agent.py +17 -30
- janito/llm/driver.py +0 -7
- janito/plugins/__init__.py +21 -29
- janito/plugins/__main__.py +85 -0
- janito/plugins/base.py +57 -0
- janito/plugins/builtin.py +1 -1
- janito/plugins/core/filemanager/tools/copy_file.py +25 -1
- janito/plugins/core/filemanager/tools/create_directory.py +28 -1
- janito/plugins/core/filemanager/tools/create_file.py +25 -1
- janito/plugins/core/filemanager/tools/delete_text_in_file.py +1 -0
- janito/plugins/core/imagedisplay/plugin.py +1 -1
- janito/plugins/core_loader.py +144 -0
- janito/plugins/discovery.py +3 -3
- janito/plugins/example_plugin.py +1 -1
- janito/plugins/manager.py +1 -1
- janito/plugins_backup_20250825_070018/__init__.py +36 -0
- janito/plugins_backup_20250825_070018/builtin.py +102 -0
- janito/plugins_backup_20250825_070018/config.py +84 -0
- janito/plugins_backup_20250825_070018/core/__init__.py +7 -0
- janito/plugins_backup_20250825_070018/core/codeanalyzer/__init__.py +43 -0
- janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/__init__.py +1 -0
- janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/core.py +122 -0
- janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/__init__.py +1 -0
- janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/core.py +205 -0
- janito/plugins_backup_20250825_070018/core/filemanager/__init__.py +124 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/copy_file.py +87 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/create_directory.py +70 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/create_file.py +87 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/delete_text_in_file.py +135 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/find_files.py +143 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/move_file.py +131 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/read_files.py +58 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/remove_directory.py +55 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/remove_file.py +58 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/replace_text_in_file.py +270 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/__init__.py +1 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/core.py +114 -0
- janito/plugins_backup_20250825_070018/core/filemanager/tools/view_file.py +172 -0
- janito/plugins_backup_20250825_070018/core/imagedisplay/__init__.py +14 -0
- janito/plugins_backup_20250825_070018/core/imagedisplay/plugin.py +51 -0
- janito/plugins_backup_20250825_070018/core/imagedisplay/tools/__init__.py +1 -0
- janito/plugins_backup_20250825_070018/core/imagedisplay/tools/show_image.py +83 -0
- janito/plugins_backup_20250825_070018/core/imagedisplay/tools/show_image_grid.py +84 -0
- janito/plugins_backup_20250825_070018/core/system/__init__.py +23 -0
- janito/plugins_backup_20250825_070018/core/system/tools/run_bash_command.py +183 -0
- janito/plugins_backup_20250825_070018/core/system/tools/run_powershell_command.py +218 -0
- janito/plugins_backup_20250825_070018/dev/__init__.py +7 -0
- janito/plugins_backup_20250825_070018/dev/pythondev/__init__.py +37 -0
- janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_code_run.py +172 -0
- janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_command_run.py +171 -0
- janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_file_run.py +172 -0
- janito/plugins_backup_20250825_070018/dev/visualization/__init__.py +23 -0
- janito/plugins_backup_20250825_070018/dev/visualization/tools/read_chart.py +259 -0
- janito/plugins_backup_20250825_070018/discovery.py +289 -0
- janito/plugins_backup_20250825_070018/example_plugin.py +108 -0
- janito/plugins_backup_20250825_070018/manager.py +243 -0
- janito/{plugins → plugins_backup_20250825_070018}/tools/delete_text_in_file.py +1 -0
- janito/plugins_backup_20250825_070018/tools/get_file_outline/java_outline.py +47 -0
- janito/plugins_backup_20250825_070018/tools/get_file_outline/markdown_outline.py +14 -0
- janito/plugins_backup_20250825_070018/tools/get_file_outline/python_outline.py +303 -0
- janito/plugins_backup_20250825_070018/tools/get_file_outline/search_outline.py +36 -0
- janito/plugins_backup_20250825_070018/tools/search_text/match_lines.py +67 -0
- janito/plugins_backup_20250825_070018/tools/search_text/pattern_utils.py +73 -0
- janito/plugins_backup_20250825_070018/tools/search_text/traverse_directory.py +145 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/css_validator.py +35 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/html_validator.py +100 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/jinja2_validator.py +50 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/js_validator.py +27 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/json_validator.py +6 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/markdown_validator.py +109 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/ps1_validator.py +32 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/python_validator.py +5 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/xml_validator.py +11 -0
- janito/plugins_backup_20250825_070018/tools/validate_file_syntax/yaml_validator.py +6 -0
- janito/plugins_backup_20250825_070018/ui/__init__.py +7 -0
- janito/plugins_backup_20250825_070018/ui/userinterface/__init__.py +16 -0
- janito/plugins_backup_20250825_070018/ui/userinterface/tools/ask_user.py +110 -0
- janito/plugins_backup_20250825_070018/web/__init__.py +7 -0
- janito/plugins_backup_20250825_070018/web/webtools/__init__.py +33 -0
- janito/plugins_backup_20250825_070018/web/webtools/tools/fetch_url.py +458 -0
- janito/plugins_backup_20250825_070018/web/webtools/tools/open_html_in_browser.py +51 -0
- janito/plugins_backup_20250825_070018/web/webtools/tools/open_url.py +37 -0
- janito/tools/base.py +31 -1
- janito/tools/cli_initializer.py +1 -1
- janito/tools/function_adapter.py +35 -1
- janito/tools/initialize.py +1 -1
- janito/tools/tool_base.py +142 -114
- janito/tools/tools_schema.py +12 -6
- {janito-3.1.0.dist-info → janito-3.3.0.dist-info}/METADATA +1 -1
- {janito-3.1.0.dist-info → janito-3.3.0.dist-info}/RECORD +154 -87
- janito/llm/cancellation_manager.py +0 -62
- janito/llm/enter_cancellation.py +0 -93
- /janito/{plugin_system → plugin_system_backup_20250825_070018}/__init__.py +0 -0
- /janito/{plugin_system → plugin_system_backup_20250825_070018}/base.py +0 -0
- /janito/{plugin_system → plugin_system_backup_20250825_070018}/core_loader.py +0 -0
- /janito/{plugin_system → plugin_system_backup_20250825_070018}/core_loader_fixed.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/auto_loader.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/auto_loader_fixed.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/java_outline.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/markdown_outline.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/python_outline.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/search_outline.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/search_text/match_lines.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/search_text/pattern_utils.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/search_text/traverse_directory.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/css_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/html_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/jinja2_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/js_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/json_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/markdown_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/ps1_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/python_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/xml_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/yaml_validator.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/core_adapter.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/discovery_core.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/__init__.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/ask_user.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/copy_file.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/core_tools_plugin.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/create_directory.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/create_file.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/decorators.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/fetch_url.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/find_files.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/get_file_outline/__init__.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/get_file_outline/core.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/move_file.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/open_html_in_browser.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/open_url.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/python_code_run.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/python_command_run.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/python_file_run.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/read_chart.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/read_files.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/remove_directory.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/remove_file.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/replace_text_in_file.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/run_bash_command.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/run_powershell_command.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/search_text/__init__.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/search_text/core.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/show_image.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/show_image_grid.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/validate_file_syntax/__init__.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/validate_file_syntax/core.py +0 -0
- /janito/{plugins → plugins_backup_20250825_070018}/tools/view_file.py +0 -0
- {janito-3.1.0.dist-info → janito-3.3.0.dist-info}/WHEEL +0 -0
- {janito-3.1.0.dist-info → janito-3.3.0.dist-info}/entry_points.txt +0 -0
- {janito-3.1.0.dist-info → janito-3.3.0.dist-info}/licenses/LICENSE +0 -0
- {janito-3.1.0.dist-info → janito-3.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,243 @@
|
|
1
|
+
"""
|
2
|
+
Plugin manager for loading and managing plugins.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import sys
|
7
|
+
import importlib
|
8
|
+
import importlib.util
|
9
|
+
from pathlib import Path
|
10
|
+
from typing import Dict, List, Optional, Any
|
11
|
+
import logging
|
12
|
+
|
13
|
+
from janito.plugin_system.base import Plugin, PluginMetadata
|
14
|
+
from .discovery import discover_plugins
|
15
|
+
from .config import load_plugins_config, get_user_plugins_dir
|
16
|
+
from .builtin import BuiltinPluginRegistry, load_builtin_plugin
|
17
|
+
from janito.tools.adapters.local import LocalToolsAdapter
|
18
|
+
|
19
|
+
logger = logging.getLogger(__name__)
|
20
|
+
|
21
|
+
|
22
|
+
class PluginManager:
|
23
|
+
"""
|
24
|
+
Manages plugin loading, registration, and lifecycle.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(self, tools_adapter: Optional[LocalToolsAdapter] = None):
|
28
|
+
self.tools_adapter = tools_adapter or LocalToolsAdapter()
|
29
|
+
self.plugins: Dict[str, Plugin] = {}
|
30
|
+
self.plugin_configs: Dict[str, Dict[str, Any]] = {}
|
31
|
+
self.plugin_paths: List[Path] = []
|
32
|
+
|
33
|
+
def add_plugin_path(self, path: str) -> None:
|
34
|
+
"""Add a directory to search for plugins."""
|
35
|
+
plugin_path = Path(path)
|
36
|
+
if plugin_path.exists() and plugin_path.is_dir():
|
37
|
+
self.plugin_paths.append(plugin_path)
|
38
|
+
if str(plugin_path) not in sys.path:
|
39
|
+
sys.path.insert(0, str(plugin_path))
|
40
|
+
|
41
|
+
def load_plugin(
|
42
|
+
self, plugin_name: str, config: Optional[Dict[str, Any]] = None
|
43
|
+
) -> bool:
|
44
|
+
"""
|
45
|
+
Load a plugin by name.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
plugin_name: Name of the plugin to load
|
49
|
+
config: Optional configuration for the plugin
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
True if plugin loaded successfully
|
53
|
+
"""
|
54
|
+
try:
|
55
|
+
if plugin_name in self.plugins:
|
56
|
+
logger.warning(f"Plugin {plugin_name} already loaded")
|
57
|
+
return True
|
58
|
+
|
59
|
+
plugin = discover_plugins(plugin_name, self.plugin_paths)
|
60
|
+
if not plugin:
|
61
|
+
logger.error(f"Plugin {plugin_name} not found")
|
62
|
+
return False
|
63
|
+
|
64
|
+
# Store config
|
65
|
+
if config:
|
66
|
+
self.plugin_configs[plugin_name] = config
|
67
|
+
|
68
|
+
# Validate config if provided
|
69
|
+
if config and hasattr(plugin, "validate_config"):
|
70
|
+
if not plugin.validate_config(config):
|
71
|
+
logger.error(f"Invalid configuration for plugin {plugin_name}")
|
72
|
+
return False
|
73
|
+
|
74
|
+
# Initialize plugin
|
75
|
+
plugin.initialize()
|
76
|
+
|
77
|
+
# Register tools
|
78
|
+
tools = plugin.get_tools()
|
79
|
+
for tool_class in tools:
|
80
|
+
self.tools_adapter.register_tool(tool_class)
|
81
|
+
|
82
|
+
# Store plugin
|
83
|
+
self.plugins[plugin_name] = plugin
|
84
|
+
|
85
|
+
logger.info(f"Successfully loaded plugin: {plugin_name}")
|
86
|
+
return True
|
87
|
+
|
88
|
+
except Exception as e:
|
89
|
+
logger.error(f"Failed to load plugin {plugin_name}: {e}")
|
90
|
+
return False
|
91
|
+
|
92
|
+
def unload_plugin(self, plugin_name: str) -> bool:
|
93
|
+
"""
|
94
|
+
Unload a plugin.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
plugin_name: Name of the plugin to unload
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
True if plugin unloaded successfully
|
101
|
+
"""
|
102
|
+
try:
|
103
|
+
if plugin_name not in self.plugins:
|
104
|
+
logger.warning(f"Plugin {plugin_name} not loaded")
|
105
|
+
return False
|
106
|
+
|
107
|
+
plugin = self.plugins[plugin_name]
|
108
|
+
|
109
|
+
# Unregister tools
|
110
|
+
tools = plugin.get_tools()
|
111
|
+
for tool_class in tools:
|
112
|
+
tool_name = getattr(tool_class(), "tool_name", None)
|
113
|
+
if tool_name:
|
114
|
+
self.tools_adapter.unregister_tool(tool_name)
|
115
|
+
|
116
|
+
# Cleanup plugin
|
117
|
+
plugin.cleanup()
|
118
|
+
|
119
|
+
# Remove from registry
|
120
|
+
del self.plugins[plugin_name]
|
121
|
+
if plugin_name in self.plugin_configs:
|
122
|
+
del self.plugin_configs[plugin_name]
|
123
|
+
|
124
|
+
logger.info(f"Successfully unloaded plugin: {plugin_name}")
|
125
|
+
return True
|
126
|
+
|
127
|
+
except Exception as e:
|
128
|
+
logger.error(f"Failed to unload plugin {plugin_name}: {e}")
|
129
|
+
return False
|
130
|
+
|
131
|
+
def list_plugins(self) -> List[str]:
|
132
|
+
"""Return list of loaded plugin names."""
|
133
|
+
return list(self.plugins.keys())
|
134
|
+
|
135
|
+
def get_plugin(self, plugin_name: str) -> Optional[Plugin]:
|
136
|
+
"""Get a loaded plugin by name."""
|
137
|
+
return self.plugins.get(plugin_name)
|
138
|
+
|
139
|
+
def get_plugin_metadata(self, plugin_name: str) -> Optional[PluginMetadata]:
|
140
|
+
"""Get metadata for a loaded plugin."""
|
141
|
+
plugin = self.plugins.get(plugin_name)
|
142
|
+
return plugin.metadata if plugin else None
|
143
|
+
|
144
|
+
def load_plugins_from_config(self, config: Dict[str, Any]) -> None:
|
145
|
+
"""
|
146
|
+
Load plugins from configuration.
|
147
|
+
|
148
|
+
Args:
|
149
|
+
config: Configuration dict with plugin settings
|
150
|
+
"""
|
151
|
+
plugins_config = config.get("plugins", {})
|
152
|
+
|
153
|
+
# Add plugin paths
|
154
|
+
for path in plugins_config.get("paths", []):
|
155
|
+
self.add_plugin_path(path)
|
156
|
+
|
157
|
+
# Load plugins
|
158
|
+
for plugin_name, plugin_config in plugins_config.get("load", {}).items():
|
159
|
+
if isinstance(plugin_config, bool):
|
160
|
+
if plugin_config:
|
161
|
+
self.load_plugin(plugin_name)
|
162
|
+
else:
|
163
|
+
self.load_plugin(plugin_name, plugin_config)
|
164
|
+
|
165
|
+
def load_plugins_from_user_config(self) -> None:
|
166
|
+
"""
|
167
|
+
Load plugins from user configuration directory.
|
168
|
+
Uses ~/.janito/plugins.json instead of janito.json
|
169
|
+
"""
|
170
|
+
config = load_plugins_config()
|
171
|
+
self.load_plugins_from_config(config)
|
172
|
+
|
173
|
+
def reload_plugin(self, plugin_name: str) -> bool:
|
174
|
+
"""
|
175
|
+
Reload a plugin.
|
176
|
+
|
177
|
+
Args:
|
178
|
+
plugin_name: Name of the plugin to reload
|
179
|
+
|
180
|
+
Returns:
|
181
|
+
True if plugin reloaded successfully
|
182
|
+
"""
|
183
|
+
config = self.plugin_configs.get(plugin_name)
|
184
|
+
self.unload_plugin(plugin_name)
|
185
|
+
return self.load_plugin(plugin_name, config)
|
186
|
+
|
187
|
+
def get_loaded_plugins_info(self) -> Dict[str, Dict[str, Any]]:
|
188
|
+
"""Get information about all loaded plugins."""
|
189
|
+
info = {}
|
190
|
+
for name, plugin in self.plugins.items():
|
191
|
+
info[name] = {
|
192
|
+
"metadata": plugin.metadata,
|
193
|
+
"tools": [tool.__name__ for tool in plugin.get_tools()],
|
194
|
+
"commands": list(plugin.get_commands().keys()),
|
195
|
+
"config": self.plugin_configs.get(name, {}),
|
196
|
+
"builtin": BuiltinPluginRegistry.is_builtin(name),
|
197
|
+
"resources": [
|
198
|
+
{
|
199
|
+
"name": resource.name,
|
200
|
+
"type": resource.type,
|
201
|
+
"description": resource.description,
|
202
|
+
"schema": resource.schema,
|
203
|
+
}
|
204
|
+
for resource in plugin.get_resources()
|
205
|
+
],
|
206
|
+
}
|
207
|
+
return info
|
208
|
+
|
209
|
+
def get_plugin_resources(self, plugin_name: str) -> List[Dict[str, Any]]:
|
210
|
+
"""
|
211
|
+
Get resources provided by a specific plugin.
|
212
|
+
|
213
|
+
Args:
|
214
|
+
plugin_name: Name of the plugin
|
215
|
+
|
216
|
+
Returns:
|
217
|
+
List of resource dictionaries
|
218
|
+
"""
|
219
|
+
plugin = self.plugins.get(plugin_name)
|
220
|
+
if not plugin:
|
221
|
+
return []
|
222
|
+
|
223
|
+
return [
|
224
|
+
{
|
225
|
+
"name": resource.name,
|
226
|
+
"type": resource.type,
|
227
|
+
"description": resource.description,
|
228
|
+
"schema": resource.schema,
|
229
|
+
}
|
230
|
+
for resource in plugin.get_resources()
|
231
|
+
]
|
232
|
+
|
233
|
+
def list_all_resources(self) -> Dict[str, List[Dict[str, Any]]]:
|
234
|
+
"""
|
235
|
+
List all resources from all loaded plugins.
|
236
|
+
|
237
|
+
Returns:
|
238
|
+
Dict mapping plugin names to their resources
|
239
|
+
"""
|
240
|
+
all_resources = {}
|
241
|
+
for plugin_name in self.plugins:
|
242
|
+
all_resources[plugin_name] = self.get_plugin_resources(plugin_name)
|
243
|
+
return all_resources
|
@@ -15,6 +15,7 @@ class DeleteTextInFile(ToolBase):
|
|
15
15
|
path (str): Path to the file to modify.
|
16
16
|
start_marker (str): The starting delimiter string.
|
17
17
|
end_marker (str): The ending delimiter string.
|
18
|
+
backup (bool, optional): Deprecated. No backups are created anymore and this flag is ignored. Defaults to False.
|
18
19
|
|
19
20
|
Returns:
|
20
21
|
str: Status message indicating the result.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import re
|
2
|
+
from typing import List, Dict
|
3
|
+
|
4
|
+
|
5
|
+
def parse_java_outline(lines: List[str]) -> List[Dict]:
|
6
|
+
"""
|
7
|
+
Parses Java source code lines and extracts classes and methods with their signatures.
|
8
|
+
Returns a list of outline items: {type, name, return_type, parameters, generics, line}
|
9
|
+
"""
|
10
|
+
outline = []
|
11
|
+
class_pattern = re.compile(r"\bclass\s+(\w+)(\s*<[^>]+>)?")
|
12
|
+
# Match methods with or without visibility modifiers (including package-private)
|
13
|
+
method_pattern = re.compile(
|
14
|
+
r"^(?:\s*(public|protected|private)\s+)?(?:static\s+)?([\w<>\[\]]+)\s+(\w+)\s*\(([^)]*)\)"
|
15
|
+
)
|
16
|
+
current_class = None
|
17
|
+
for idx, line in enumerate(lines, 1):
|
18
|
+
class_match = class_pattern.search(line)
|
19
|
+
if class_match:
|
20
|
+
class_name = class_match.group(1)
|
21
|
+
generics = class_match.group(2) or ""
|
22
|
+
outline.append(
|
23
|
+
{
|
24
|
+
"type": "class",
|
25
|
+
"name": class_name,
|
26
|
+
"generics": generics.strip("<>") if generics else None,
|
27
|
+
"line": idx,
|
28
|
+
}
|
29
|
+
)
|
30
|
+
current_class = class_name
|
31
|
+
else:
|
32
|
+
method_match = method_pattern.search(line)
|
33
|
+
if method_match:
|
34
|
+
return_type = method_match.group(2)
|
35
|
+
method_name = method_match.group(3)
|
36
|
+
params = method_match.group(4)
|
37
|
+
outline.append(
|
38
|
+
{
|
39
|
+
"type": "method",
|
40
|
+
"class": current_class,
|
41
|
+
"name": method_name,
|
42
|
+
"return_type": return_type,
|
43
|
+
"parameters": params.strip(),
|
44
|
+
"line": idx,
|
45
|
+
}
|
46
|
+
)
|
47
|
+
return outline
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import re
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
|
5
|
+
def parse_markdown_outline(lines: List[str]):
|
6
|
+
header_pat = re.compile(r"^(#+)\s+(.*)")
|
7
|
+
outline = []
|
8
|
+
for idx, line in enumerate(lines):
|
9
|
+
match = header_pat.match(line)
|
10
|
+
if match:
|
11
|
+
level = len(match.group(1))
|
12
|
+
title = match.group(2).strip()
|
13
|
+
outline.append({"level": level, "title": title, "line": idx + 1})
|
14
|
+
return outline
|
@@ -0,0 +1,303 @@
|
|
1
|
+
import re
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
|
5
|
+
def handle_assignment(idx, assign_match, outline):
|
6
|
+
var_name = assign_match.group(2)
|
7
|
+
var_type = "const" if var_name.isupper() else "var"
|
8
|
+
outline.append(
|
9
|
+
{
|
10
|
+
"type": var_type,
|
11
|
+
"name": var_name,
|
12
|
+
"start": idx + 1,
|
13
|
+
"end": idx + 1,
|
14
|
+
"parent": "",
|
15
|
+
"docstring": "",
|
16
|
+
}
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
def handle_main(idx, outline):
|
21
|
+
outline.append(
|
22
|
+
{
|
23
|
+
"type": "main",
|
24
|
+
"name": "__main__",
|
25
|
+
"start": idx + 1,
|
26
|
+
"end": idx + 1,
|
27
|
+
"parent": "",
|
28
|
+
"docstring": "",
|
29
|
+
}
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
def close_stack_objects(idx, indent, stack, obj_ranges):
|
34
|
+
while stack and indent < stack[-1][2]:
|
35
|
+
popped = stack.pop()
|
36
|
+
obj_ranges.append((popped[0], popped[1], popped[3], idx, popped[4], popped[2]))
|
37
|
+
|
38
|
+
|
39
|
+
def close_last_top_obj(idx, last_top_obj, stack, obj_ranges):
|
40
|
+
if last_top_obj and last_top_obj in stack:
|
41
|
+
stack.remove(last_top_obj)
|
42
|
+
obj_ranges.append(
|
43
|
+
(
|
44
|
+
last_top_obj[0],
|
45
|
+
last_top_obj[1],
|
46
|
+
last_top_obj[3],
|
47
|
+
idx,
|
48
|
+
last_top_obj[4],
|
49
|
+
last_top_obj[2],
|
50
|
+
)
|
51
|
+
)
|
52
|
+
return None
|
53
|
+
return last_top_obj
|
54
|
+
|
55
|
+
|
56
|
+
def handle_class(idx, class_match, indent, stack, last_top_obj):
|
57
|
+
name = class_match.group(2)
|
58
|
+
parent = stack[-1][1] if stack and stack[-1][0] == "class" else ""
|
59
|
+
obj = ("class", name, indent, idx + 1, parent)
|
60
|
+
stack.append(obj)
|
61
|
+
if indent == 0:
|
62
|
+
last_top_obj = obj
|
63
|
+
return last_top_obj
|
64
|
+
|
65
|
+
|
66
|
+
def handle_function(idx, func_match, indent, stack, last_top_obj):
|
67
|
+
name = func_match.group(2)
|
68
|
+
parent = ""
|
69
|
+
for s in reversed(stack):
|
70
|
+
if s[0] == "class" and indent > s[2]:
|
71
|
+
parent = s[1]
|
72
|
+
break
|
73
|
+
obj = ("function", name, indent, idx + 1, parent)
|
74
|
+
stack.append(obj)
|
75
|
+
if indent == 0:
|
76
|
+
last_top_obj = obj
|
77
|
+
return last_top_obj
|
78
|
+
|
79
|
+
|
80
|
+
def process_line(idx, line, regexes, stack, obj_ranges, outline, last_top_obj):
|
81
|
+
class_pat, func_pat, assign_pat, main_pat = regexes
|
82
|
+
class_match = class_pat.match(line)
|
83
|
+
func_match = func_pat.match(line)
|
84
|
+
assign_match = assign_pat.match(line)
|
85
|
+
indent = len(line) - len(line.lstrip())
|
86
|
+
# If a new top-level class or function starts, close the previous one
|
87
|
+
if (class_match or func_match) and indent == 0 and last_top_obj:
|
88
|
+
last_top_obj = close_last_top_obj(idx, last_top_obj, stack, obj_ranges)
|
89
|
+
if class_match:
|
90
|
+
last_top_obj = handle_class(idx, class_match, indent, stack, last_top_obj)
|
91
|
+
elif func_match:
|
92
|
+
last_top_obj = handle_function(idx, func_match, indent, stack, last_top_obj)
|
93
|
+
elif assign_match and indent == 0:
|
94
|
+
handle_assignment(idx, assign_match, outline)
|
95
|
+
main_match = main_pat.match(line)
|
96
|
+
if main_match:
|
97
|
+
handle_main(idx, outline)
|
98
|
+
close_stack_objects(idx, indent, stack, obj_ranges)
|
99
|
+
return last_top_obj
|
100
|
+
|
101
|
+
|
102
|
+
def extract_signature_and_decorators(lines, start_idx):
|
103
|
+
"""
|
104
|
+
Extracts the signature line and leading decorators for a given function/class/method.
|
105
|
+
Returns (signature:str, decorators:List[str], signature_lineno:int)
|
106
|
+
"""
|
107
|
+
decorators = []
|
108
|
+
sig_line = None
|
109
|
+
sig_lineno = start_idx
|
110
|
+
for i in range(start_idx - 1, -1, -1):
|
111
|
+
striped = lines[i].strip()
|
112
|
+
if striped.startswith("@"):
|
113
|
+
decorators.insert(0, striped)
|
114
|
+
sig_lineno = i
|
115
|
+
elif not striped:
|
116
|
+
continue
|
117
|
+
else:
|
118
|
+
break
|
119
|
+
# Find the signature line itself
|
120
|
+
for k in range(start_idx, len(lines)):
|
121
|
+
striped = lines[k].strip()
|
122
|
+
if striped.startswith("def ") or striped.startswith("class "):
|
123
|
+
sig_line = striped
|
124
|
+
sig_lineno = k
|
125
|
+
break
|
126
|
+
return sig_line, decorators, sig_lineno
|
127
|
+
|
128
|
+
|
129
|
+
def extract_docstring(lines, start_idx, end_idx):
|
130
|
+
"""Extracts a docstring from lines[start_idx:end_idx] if present."""
|
131
|
+
for i in range(start_idx, min(end_idx, len(lines))):
|
132
|
+
line = lines[i].lstrip()
|
133
|
+
if not line:
|
134
|
+
continue
|
135
|
+
if line.startswith('"""') or line.startswith("'''"):
|
136
|
+
quote = line[:3]
|
137
|
+
doc = line[3:]
|
138
|
+
if doc.strip().endswith(quote):
|
139
|
+
return doc.strip()[:-3].strip()
|
140
|
+
docstring_lines = [doc]
|
141
|
+
for j in range(i + 1, min(end_idx, len(lines))):
|
142
|
+
line = lines[j]
|
143
|
+
if line.strip().endswith(quote):
|
144
|
+
docstring_lines.append(line.strip()[:-3])
|
145
|
+
return "\n".join([d.strip() for d in docstring_lines]).strip()
|
146
|
+
docstring_lines.append(line)
|
147
|
+
break
|
148
|
+
else:
|
149
|
+
break
|
150
|
+
return ""
|
151
|
+
|
152
|
+
|
153
|
+
def build_outline_entry(obj, lines, outline):
|
154
|
+
obj_type, name, start, end, parent, indent = obj
|
155
|
+
# Determine if this is a method
|
156
|
+
if obj_type == "function" and parent:
|
157
|
+
outline_type = "method"
|
158
|
+
elif obj_type == "function":
|
159
|
+
outline_type = "function"
|
160
|
+
else:
|
161
|
+
outline_type = obj_type
|
162
|
+
docstring = extract_docstring(lines, start, end)
|
163
|
+
outline.append(
|
164
|
+
{
|
165
|
+
"type": outline_type,
|
166
|
+
"name": name,
|
167
|
+
"start": start,
|
168
|
+
"end": end,
|
169
|
+
"parent": parent,
|
170
|
+
"docstring": docstring,
|
171
|
+
}
|
172
|
+
)
|
173
|
+
|
174
|
+
|
175
|
+
def process_lines(lines, regexes):
|
176
|
+
outline = []
|
177
|
+
stack = []
|
178
|
+
obj_ranges = []
|
179
|
+
last_top_obj = None
|
180
|
+
for idx, line in enumerate(lines):
|
181
|
+
last_top_obj = process_line(
|
182
|
+
idx, line, regexes, stack, obj_ranges, outline, last_top_obj
|
183
|
+
)
|
184
|
+
# Close any remaining open objects
|
185
|
+
for popped in stack:
|
186
|
+
obj_ranges.append(
|
187
|
+
(popped[0], popped[1], popped[3], len(lines), popped[4], popped[2])
|
188
|
+
)
|
189
|
+
return outline, obj_ranges
|
190
|
+
|
191
|
+
|
192
|
+
def build_outline(obj_ranges, lines, outline):
|
193
|
+
for obj in obj_ranges:
|
194
|
+
build_outline_entry(obj, lines, outline)
|
195
|
+
return outline
|
196
|
+
|
197
|
+
|
198
|
+
def parse_python_outline(lines: List[str]):
|
199
|
+
class_pat = re.compile(r"^(\s*)class\s+(\w+)")
|
200
|
+
func_pat = re.compile(r"^(\s*)def\s+(\w+)")
|
201
|
+
assign_pat = re.compile(r"^(\s*)([A-Za-z_][A-Za-z0-9_]*)\s*=.*")
|
202
|
+
main_pat = re.compile(r"^\s*if\s+__name__\s*==\s*[\'\"]__main__[\'\"]\s*:")
|
203
|
+
outline = []
|
204
|
+
stack = []
|
205
|
+
obj_ranges = []
|
206
|
+
last_top_obj = None
|
207
|
+
for idx, line in enumerate(lines):
|
208
|
+
class_match = class_pat.match(line)
|
209
|
+
func_match = func_pat.match(line)
|
210
|
+
assign_match = assign_pat.match(line)
|
211
|
+
indent = len(line) - len(line.lstrip())
|
212
|
+
parent = ""
|
213
|
+
for s in reversed(stack):
|
214
|
+
if s[0] == "class" and indent > s[2]:
|
215
|
+
parent = s[1]
|
216
|
+
break
|
217
|
+
if class_match:
|
218
|
+
obj = ("class", class_match.group(2), idx + 1, None, parent, indent)
|
219
|
+
stack.append(obj)
|
220
|
+
last_top_obj = obj
|
221
|
+
elif func_match:
|
222
|
+
obj = ("function", func_match.group(2), idx + 1, None, parent, indent)
|
223
|
+
stack.append(obj)
|
224
|
+
last_top_obj = obj
|
225
|
+
elif assign_match and indent == 0:
|
226
|
+
outline.append(
|
227
|
+
{
|
228
|
+
"type": "const" if assign_match.group(2).isupper() else "var",
|
229
|
+
"name": assign_match.group(2),
|
230
|
+
"start": idx + 1,
|
231
|
+
"end": idx + 1,
|
232
|
+
"parent": "",
|
233
|
+
"signature": line.strip(),
|
234
|
+
"decorators": [],
|
235
|
+
"docstring": "",
|
236
|
+
}
|
237
|
+
)
|
238
|
+
if line.strip().startswith("if __name__ == "):
|
239
|
+
outline.append(
|
240
|
+
{
|
241
|
+
"type": "main",
|
242
|
+
"name": "__main__",
|
243
|
+
"start": idx + 1,
|
244
|
+
"end": idx + 1,
|
245
|
+
"parent": "",
|
246
|
+
"signature": line.strip(),
|
247
|
+
"decorators": [],
|
248
|
+
"docstring": "",
|
249
|
+
}
|
250
|
+
)
|
251
|
+
# Close stack objects if indent falls back
|
252
|
+
while stack and indent <= stack[-1][5] and idx + 1 > stack[-1][2]:
|
253
|
+
finished = stack.pop()
|
254
|
+
outline_entry = finished[:2] + (
|
255
|
+
finished[2],
|
256
|
+
idx + 1,
|
257
|
+
finished[4],
|
258
|
+
finished[5],
|
259
|
+
)
|
260
|
+
build_outline_entry(outline_entry, lines, outline)
|
261
|
+
# Close any remaining objects
|
262
|
+
while stack:
|
263
|
+
finished = stack.pop()
|
264
|
+
outline_entry = finished[:2] + (
|
265
|
+
finished[2],
|
266
|
+
len(lines),
|
267
|
+
finished[4],
|
268
|
+
finished[5],
|
269
|
+
)
|
270
|
+
build_outline_entry(outline_entry, lines, outline)
|
271
|
+
return outline
|
272
|
+
|
273
|
+
class_pat = re.compile(r"^(\s*)class\s+(\w+)")
|
274
|
+
func_pat = re.compile(r"^(\s*)def\s+(\w+)")
|
275
|
+
assign_pat = re.compile(r"^(\s*)([A-Za-z_][A-Za-z0-9_]*)\s*=.*")
|
276
|
+
main_pat = re.compile(r"^\s*if\s+__name__\s*==\s*[\'\"]__main__[\'\"]\s*:")
|
277
|
+
regexes = (class_pat, func_pat, assign_pat, main_pat)
|
278
|
+
outline, obj_ranges = process_lines(lines, regexes)
|
279
|
+
return build_outline(obj_ranges, lines, outline)
|
280
|
+
|
281
|
+
|
282
|
+
def extract_docstring(lines, start_idx, end_idx):
|
283
|
+
"""Extracts a docstring from lines[start_idx:end_idx] if present."""
|
284
|
+
for i in range(start_idx, min(end_idx, len(lines))):
|
285
|
+
line = lines[i].lstrip()
|
286
|
+
if not line:
|
287
|
+
continue
|
288
|
+
if line.startswith('"""') or line.startswith("'''"):
|
289
|
+
quote = line[:3]
|
290
|
+
doc = line[3:]
|
291
|
+
if doc.strip().endswith(quote):
|
292
|
+
return doc.strip()[:-3].strip()
|
293
|
+
docstring_lines = [doc]
|
294
|
+
for j in range(i + 1, min(end_idx, len(lines))):
|
295
|
+
line = lines[j]
|
296
|
+
if line.strip().endswith(quote):
|
297
|
+
docstring_lines.append(line.strip()[:-3])
|
298
|
+
return "\n".join([d.strip() for d in docstring_lines]).strip()
|
299
|
+
docstring_lines.append(line)
|
300
|
+
break
|
301
|
+
else:
|
302
|
+
break
|
303
|
+
return ""
|
@@ -0,0 +1,36 @@
|
|
1
|
+
from janito.tools.tool_base import ToolBase, ToolPermissions
|
2
|
+
from janito.report_events import ReportAction
|
3
|
+
from janito.tools.loop_protection_decorator import protect_against_loops
|
4
|
+
|
5
|
+
|
6
|
+
class SearchOutlineTool(ToolBase):
|
7
|
+
"""
|
8
|
+
Tool for searching outlines in files.
|
9
|
+
|
10
|
+
Args:
|
11
|
+
path (str): Path to the file for which to generate an outline.
|
12
|
+
Returns:
|
13
|
+
str: Outline search result or status message.
|
14
|
+
"""
|
15
|
+
|
16
|
+
permissions = ToolPermissions(read=True)
|
17
|
+
tool_name = "search_outline"
|
18
|
+
|
19
|
+
@protect_against_loops(max_calls=5, time_window=10.0, key_field="path")
|
20
|
+
def run(self, path: str) -> str:
|
21
|
+
from janito.tools.tool_utils import display_path
|
22
|
+
from janito.i18n import tr
|
23
|
+
|
24
|
+
self.report_action(
|
25
|
+
tr(
|
26
|
+
"🔍 Searching for outline in '{disp_path}'",
|
27
|
+
disp_path=display_path(path),
|
28
|
+
),
|
29
|
+
ReportAction.READ,
|
30
|
+
)
|
31
|
+
# ... rest of implementation ...
|
32
|
+
# Example warnings and successes:
|
33
|
+
# self.report_warning(tr("No files found with supported extensions."))
|
34
|
+
# self.report_warning(tr("Error reading {path}: {error}", path=path, error=e))
|
35
|
+
# self.report_success(tr("✅ {count} {match_word} found", count=len(output), match_word=pluralize('match', len(output))))
|
36
|
+
pass
|