janito 2.27.1__py3-none-any.whl → 2.29.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/README.md +9 -9
- janito/agent/setup_agent.py +29 -16
- janito/cli/chat_mode/script_runner.py +1 -1
- janito/cli/chat_mode/session.py +160 -56
- janito/cli/chat_mode/session_profile_select.py +8 -2
- janito/cli/chat_mode/shell/commands/execute.py +4 -2
- janito/cli/chat_mode/shell/commands/help.py +2 -0
- janito/cli/chat_mode/shell/commands/privileges.py +6 -2
- janito/cli/chat_mode/shell/commands/provider.py +7 -4
- janito/cli/chat_mode/shell/commands/read.py +4 -2
- janito/cli/chat_mode/shell/commands/security/__init__.py +1 -1
- janito/cli/chat_mode/shell/commands/security/allowed_sites.py +16 -13
- janito/cli/chat_mode/shell/commands/security_command.py +14 -10
- janito/cli/chat_mode/shell/commands/tools.py +4 -2
- janito/cli/chat_mode/shell/commands/unrestricted.py +17 -12
- janito/cli/chat_mode/shell/commands/write.py +4 -2
- janito/cli/chat_mode/toolbar.py +4 -4
- janito/cli/cli_commands/enable_disable_plugin.py +48 -25
- janito/cli/cli_commands/list_models.py +2 -2
- janito/cli/cli_commands/list_plugins.py +18 -18
- janito/cli/cli_commands/list_profiles.py +6 -6
- janito/cli/cli_commands/list_providers.py +1 -1
- janito/cli/cli_commands/model_utils.py +45 -20
- janito/cli/cli_commands/ping_providers.py +10 -10
- janito/cli/cli_commands/set_api_key.py +5 -3
- janito/cli/cli_commands/show_config.py +13 -7
- janito/cli/cli_commands/show_system_prompt.py +13 -6
- janito/cli/core/getters.py +1 -0
- janito/cli/core/model_guesser.py +18 -15
- janito/cli/core/runner.py +15 -7
- janito/cli/core/setters.py +9 -6
- janito/cli/main_cli.py +15 -12
- janito/cli/prompt_setup.py +4 -4
- janito/cli/rich_terminal_reporter.py +2 -1
- janito/config_manager.py +2 -0
- janito/docs/GETTING_STARTED.md +9 -9
- janito/drivers/cerebras/__init__.py +1 -1
- janito/exceptions.py +6 -4
- janito/plugins/__init__.py +2 -2
- janito/plugins/base.py +48 -40
- janito/plugins/builtin.py +13 -9
- janito/plugins/config.py +16 -19
- janito/plugins/discovery.py +73 -66
- janito/plugins/manager.py +62 -60
- janito/provider_registry.py +10 -10
- janito/providers/__init__.py +1 -1
- janito/providers/alibaba/model_info.py +3 -5
- janito/providers/alibaba/provider.py +3 -1
- janito/providers/cerebras/__init__.py +1 -1
- janito/providers/cerebras/model_info.py +12 -27
- janito/providers/cerebras/provider.py +11 -9
- janito/providers/mistral/__init__.py +1 -1
- janito/providers/mistral/model_info.py +1 -1
- janito/providers/mistral/provider.py +1 -1
- janito/providers/moonshot/__init__.py +1 -0
- janito/providers/{moonshotai → moonshot}/model_info.py +3 -3
- janito/providers/{moonshotai → moonshot}/provider.py +8 -8
- janito/providers/openai/provider.py +3 -1
- janito/report_events.py +0 -1
- janito/tools/adapters/local/create_file.py +1 -1
- janito/tools/adapters/local/fetch_url.py +45 -29
- janito/tools/adapters/local/python_command_run.py +2 -1
- janito/tools/adapters/local/python_file_run.py +1 -0
- janito/tools/adapters/local/run_powershell_command.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +14 -11
- janito/tools/base.py +4 -3
- janito/tools/loop_protection.py +24 -22
- janito/tools/path_utils.py +7 -7
- janito/tools/tool_base.py +0 -2
- janito/tools/tools_adapter.py +15 -5
- janito/tools/url_whitelist.py +27 -26
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/METADATA +1 -1
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/RECORD +77 -77
- janito/providers/moonshotai/__init__.py +0 -1
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/WHEEL +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/entry_points.txt +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/top_level.txt +0 -0
janito/plugins/builtin.py
CHANGED
@@ -12,24 +12,24 @@ from janito.plugins.base import Plugin
|
|
12
12
|
|
13
13
|
class BuiltinPluginRegistry:
|
14
14
|
"""Registry for builtin plugins that come packaged with janito."""
|
15
|
-
|
15
|
+
|
16
16
|
_plugins: Dict[str, Type[Plugin]] = {}
|
17
|
-
|
17
|
+
|
18
18
|
@classmethod
|
19
19
|
def register(cls, name: str, plugin_class: Type[Plugin]) -> None:
|
20
20
|
"""Register a builtin plugin."""
|
21
21
|
cls._plugins[name] = plugin_class
|
22
|
-
|
22
|
+
|
23
23
|
@classmethod
|
24
24
|
def get_plugin_class(cls, name: str) -> Optional[Type[Plugin]]:
|
25
25
|
"""Get the plugin class for a builtin plugin."""
|
26
26
|
return cls._plugins.get(name)
|
27
|
-
|
27
|
+
|
28
28
|
@classmethod
|
29
29
|
def list_builtin_plugins(cls) -> List[str]:
|
30
30
|
"""List all registered builtin plugins."""
|
31
31
|
return list(cls._plugins.keys())
|
32
|
-
|
32
|
+
|
33
33
|
@classmethod
|
34
34
|
def is_builtin(cls, name: str) -> bool:
|
35
35
|
"""Check if a plugin is builtin."""
|
@@ -38,9 +38,11 @@ class BuiltinPluginRegistry:
|
|
38
38
|
|
39
39
|
def register_builtin_plugin(name: str):
|
40
40
|
"""Decorator to register a plugin as builtin."""
|
41
|
+
|
41
42
|
def decorator(plugin_class: Type[Plugin]) -> Type[Plugin]:
|
42
43
|
BuiltinPluginRegistry.register(name, plugin_class)
|
43
44
|
return plugin_class
|
45
|
+
|
44
46
|
return decorator
|
45
47
|
|
46
48
|
|
@@ -66,7 +68,7 @@ try:
|
|
66
68
|
SecurityScannerPlugin,
|
67
69
|
DocumentationGeneratorPlugin,
|
68
70
|
)
|
69
|
-
|
71
|
+
|
70
72
|
# Register all janito-coder plugins as builtin
|
71
73
|
BuiltinPluginRegistry.register("git_analyzer", GitAnalyzerPlugin)
|
72
74
|
BuiltinPluginRegistry.register("code_navigator", CodeNavigatorPlugin)
|
@@ -77,8 +79,10 @@ try:
|
|
77
79
|
BuiltinPluginRegistry.register("debugger", DebuggerPlugin)
|
78
80
|
BuiltinPluginRegistry.register("performance_profiler", PerformanceProfilerPlugin)
|
79
81
|
BuiltinPluginRegistry.register("security_scanner", SecurityScannerPlugin)
|
80
|
-
BuiltinPluginRegistry.register(
|
81
|
-
|
82
|
+
BuiltinPluginRegistry.register(
|
83
|
+
"documentation_generator", DocumentationGeneratorPlugin
|
84
|
+
)
|
85
|
+
|
82
86
|
except ImportError:
|
83
87
|
# janito-coder not available, skip registration
|
84
|
-
pass
|
88
|
+
pass
|
janito/plugins/config.py
CHANGED
@@ -21,35 +21,32 @@ def get_plugins_config_path() -> Path:
|
|
21
21
|
def load_plugins_config() -> Dict[str, Any]:
|
22
22
|
"""
|
23
23
|
Load plugins configuration from user directory.
|
24
|
-
|
24
|
+
|
25
25
|
Returns:
|
26
26
|
Dict containing plugins configuration
|
27
27
|
"""
|
28
28
|
config_path = get_plugins_config_path()
|
29
|
-
|
29
|
+
|
30
30
|
if not config_path.exists():
|
31
31
|
# Create default config if it doesn't exist
|
32
32
|
default_config = {
|
33
33
|
"plugins": {
|
34
|
-
"paths": [
|
35
|
-
|
36
|
-
"./plugins"
|
37
|
-
],
|
38
|
-
"load": {}
|
34
|
+
"paths": [str(Path.home() / ".janito" / "plugins"), "./plugins"],
|
35
|
+
"load": {},
|
39
36
|
}
|
40
37
|
}
|
41
|
-
|
38
|
+
|
42
39
|
# Ensure directory exists
|
43
40
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
44
|
-
|
41
|
+
|
45
42
|
# Save default config
|
46
|
-
with open(config_path,
|
43
|
+
with open(config_path, "w") as f:
|
47
44
|
json.dump(default_config, f, indent=2)
|
48
|
-
|
45
|
+
|
49
46
|
return default_config
|
50
|
-
|
47
|
+
|
51
48
|
try:
|
52
|
-
with open(config_path,
|
49
|
+
with open(config_path, "r") as f:
|
53
50
|
return json.load(f)
|
54
51
|
except (json.JSONDecodeError, IOError) as e:
|
55
52
|
print(f"Warning: Failed to load plugins config from {config_path}: {e}")
|
@@ -59,20 +56,20 @@ def load_plugins_config() -> Dict[str, Any]:
|
|
59
56
|
def save_plugins_config(config: Dict[str, Any]) -> bool:
|
60
57
|
"""
|
61
58
|
Save plugins configuration to user directory.
|
62
|
-
|
59
|
+
|
63
60
|
Args:
|
64
61
|
config: Configuration dict to save
|
65
|
-
|
62
|
+
|
66
63
|
Returns:
|
67
64
|
True if saved successfully
|
68
65
|
"""
|
69
66
|
config_path = get_plugins_config_path()
|
70
|
-
|
67
|
+
|
71
68
|
try:
|
72
69
|
# Ensure directory exists
|
73
70
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
74
|
-
|
75
|
-
with open(config_path,
|
71
|
+
|
72
|
+
with open(config_path, "w") as f:
|
76
73
|
json.dump(config, f, indent=2)
|
77
74
|
return True
|
78
75
|
except IOError as e:
|
@@ -84,4 +81,4 @@ def get_user_plugins_dir() -> Path:
|
|
84
81
|
"""Get the user plugins directory."""
|
85
82
|
plugins_dir = get_user_config_dir() / "plugins"
|
86
83
|
plugins_dir.mkdir(parents=True, exist_ok=True)
|
87
|
-
return plugins_dir
|
84
|
+
return plugins_dir
|
janito/plugins/discovery.py
CHANGED
@@ -37,36 +37,38 @@ from .builtin import load_builtin_plugin, BuiltinPluginRegistry
|
|
37
37
|
logger = logging.getLogger(__name__)
|
38
38
|
|
39
39
|
|
40
|
-
def discover_plugins(
|
40
|
+
def discover_plugins(
|
41
|
+
plugin_name: str, search_paths: List[Path] = None
|
42
|
+
) -> Optional[Plugin]:
|
41
43
|
"""
|
42
44
|
Discover and load a plugin by name.
|
43
|
-
|
45
|
+
|
44
46
|
Supports multiple plugin formats:
|
45
47
|
- Single .py files
|
46
48
|
- Python package directories
|
47
49
|
- Package-based plugins (e.g., core.filemanager)
|
48
50
|
- Installed Python packages
|
49
51
|
- ZIP files containing packages
|
50
|
-
|
52
|
+
|
51
53
|
Args:
|
52
54
|
plugin_name: Name of the plugin to discover
|
53
55
|
search_paths: List of directories to search for plugins
|
54
|
-
|
56
|
+
|
55
57
|
Returns:
|
56
58
|
Plugin instance if found, None otherwise
|
57
59
|
"""
|
58
60
|
if search_paths is None:
|
59
61
|
search_paths = []
|
60
|
-
|
62
|
+
|
61
63
|
# Add default search paths
|
62
64
|
default_paths = [
|
63
65
|
Path.cwd() / "plugins",
|
64
66
|
Path.home() / ".janito" / "plugins",
|
65
67
|
Path(sys.prefix) / "share" / "janito" / "plugins",
|
66
68
|
]
|
67
|
-
|
69
|
+
|
68
70
|
all_paths = search_paths + default_paths
|
69
|
-
|
71
|
+
|
70
72
|
# Handle package-based plugins (e.g., core.filemanager)
|
71
73
|
if "." in plugin_name:
|
72
74
|
parts = plugin_name.split(".")
|
@@ -76,33 +78,33 @@ def discover_plugins(plugin_name: str, search_paths: List[Path] = None) -> Optio
|
|
76
78
|
package_path = base_path / package_name / submodule_name / "__init__.py"
|
77
79
|
if package_path.exists():
|
78
80
|
return _load_plugin_from_file(package_path, plugin_name=plugin_name)
|
79
|
-
|
81
|
+
|
80
82
|
plugin_path = base_path / package_name / submodule_name / "plugin.py"
|
81
83
|
if plugin_path.exists():
|
82
84
|
return _load_plugin_from_file(plugin_path, plugin_name=plugin_name)
|
83
|
-
|
85
|
+
|
84
86
|
# Try to find plugin in search paths
|
85
87
|
for base_path in all_paths:
|
86
88
|
plugin_path = base_path / plugin_name
|
87
89
|
if plugin_path.exists():
|
88
90
|
return _load_plugin_from_directory(plugin_path)
|
89
|
-
|
91
|
+
|
90
92
|
# Try as Python module
|
91
93
|
module_path = base_path / f"{plugin_name}.py"
|
92
94
|
if module_path.exists():
|
93
95
|
return _load_plugin_from_file(module_path)
|
94
|
-
|
96
|
+
|
95
97
|
# Check for builtin plugins
|
96
98
|
builtin_plugin = load_builtin_plugin(plugin_name)
|
97
99
|
if builtin_plugin:
|
98
100
|
return builtin_plugin
|
99
|
-
|
101
|
+
|
100
102
|
# Try importing as installed package
|
101
103
|
try:
|
102
104
|
return _load_plugin_from_package(plugin_name)
|
103
105
|
except ImportError:
|
104
106
|
pass
|
105
|
-
|
107
|
+
|
106
108
|
return None
|
107
109
|
|
108
110
|
|
@@ -112,74 +114,78 @@ def _load_plugin_from_directory(plugin_path: Path) -> Optional[Plugin]:
|
|
112
114
|
# Look for __init__.py or plugin.py
|
113
115
|
init_file = plugin_path / "__init__.py"
|
114
116
|
plugin_file = plugin_path / "plugin.py"
|
115
|
-
|
117
|
+
|
116
118
|
if init_file.exists():
|
117
119
|
return _load_plugin_from_file(init_file, plugin_name=plugin_path.name)
|
118
120
|
elif plugin_file.exists():
|
119
121
|
return _load_plugin_from_file(plugin_file, plugin_name=plugin_path.name)
|
120
|
-
|
122
|
+
|
121
123
|
except Exception as e:
|
122
124
|
logger.error(f"Failed to load plugin from directory {plugin_path}: {e}")
|
123
|
-
|
125
|
+
|
124
126
|
return None
|
125
127
|
|
126
128
|
|
127
|
-
def _load_plugin_from_file(
|
129
|
+
def _load_plugin_from_file(
|
130
|
+
file_path: Path, plugin_name: str = None
|
131
|
+
) -> Optional[Plugin]:
|
128
132
|
"""Load a plugin from a Python file."""
|
129
133
|
try:
|
130
134
|
if plugin_name is None:
|
131
135
|
plugin_name = file_path.stem
|
132
|
-
|
136
|
+
|
133
137
|
spec = importlib.util.spec_from_file_location(plugin_name, file_path)
|
134
138
|
if spec is None or spec.loader is None:
|
135
139
|
return None
|
136
|
-
|
140
|
+
|
137
141
|
module = importlib.util.module_from_spec(spec)
|
138
142
|
spec.loader.exec_module(module)
|
139
|
-
|
143
|
+
|
140
144
|
# Look for Plugin class
|
141
145
|
for attr_name in dir(module):
|
142
146
|
attr = getattr(module, attr_name)
|
143
|
-
if
|
144
|
-
issubclass(attr, Plugin) and
|
145
|
-
attr != Plugin):
|
147
|
+
if isinstance(attr, type) and issubclass(attr, Plugin) and attr != Plugin:
|
146
148
|
return attr()
|
147
|
-
|
149
|
+
|
148
150
|
# Check for package-based plugin with __plugin_name__ metadata
|
149
|
-
if hasattr(module,
|
151
|
+
if hasattr(module, "__plugin_name__"):
|
150
152
|
from janito.plugins.base import PluginMetadata
|
151
|
-
|
153
|
+
|
152
154
|
# Create a dynamic plugin class
|
153
155
|
class PackagePlugin(Plugin):
|
154
156
|
def __init__(self):
|
155
157
|
super().__init__()
|
156
158
|
self._module = module
|
157
|
-
|
159
|
+
|
158
160
|
def get_metadata(self) -> PluginMetadata:
|
159
161
|
return PluginMetadata(
|
160
|
-
name=getattr(module,
|
161
|
-
version=getattr(module,
|
162
|
-
description=getattr(
|
163
|
-
|
164
|
-
|
162
|
+
name=getattr(module, "__plugin_name__", plugin_name),
|
163
|
+
version=getattr(module, "__plugin_version__", "1.0.0"),
|
164
|
+
description=getattr(
|
165
|
+
module,
|
166
|
+
"__plugin_description__",
|
167
|
+
f"Package plugin: {plugin_name}",
|
168
|
+
),
|
169
|
+
author=getattr(module, "__plugin_author__", "Unknown"),
|
170
|
+
license=getattr(module, "__plugin_license__", "MIT"),
|
165
171
|
)
|
166
|
-
|
172
|
+
|
167
173
|
def get_tools(self):
|
168
|
-
return getattr(module,
|
169
|
-
|
174
|
+
return getattr(module, "__plugin_tools__", [])
|
175
|
+
|
170
176
|
def initialize(self):
|
171
|
-
if hasattr(module,
|
177
|
+
if hasattr(module, "initialize"):
|
172
178
|
module.initialize()
|
173
|
-
|
179
|
+
|
174
180
|
def cleanup(self):
|
175
|
-
if hasattr(module,
|
181
|
+
if hasattr(module, "cleanup"):
|
176
182
|
module.cleanup()
|
177
|
-
|
183
|
+
|
178
184
|
return PackagePlugin()
|
179
|
-
|
185
|
+
|
180
186
|
except Exception as e:
|
181
187
|
logger.error(f"Failed to load plugin from file {file_path}: {e}")
|
182
|
-
|
188
|
+
|
183
189
|
return None
|
184
190
|
|
185
191
|
|
@@ -187,54 +193,52 @@ def _load_plugin_from_package(package_name: str) -> Optional[Plugin]:
|
|
187
193
|
"""Load a plugin from an installed package."""
|
188
194
|
try:
|
189
195
|
module = importlib.import_module(package_name)
|
190
|
-
|
196
|
+
|
191
197
|
# Look for Plugin class
|
192
198
|
for attr_name in dir(module):
|
193
199
|
attr = getattr(module, attr_name)
|
194
|
-
if
|
195
|
-
issubclass(attr, Plugin) and
|
196
|
-
attr != Plugin):
|
200
|
+
if isinstance(attr, type) and issubclass(attr, Plugin) and attr != Plugin:
|
197
201
|
return attr()
|
198
|
-
|
202
|
+
|
199
203
|
except ImportError as e:
|
200
204
|
logger.debug(f"Could not import package {package_name}: {e}")
|
201
|
-
|
205
|
+
|
202
206
|
return None
|
203
207
|
|
204
208
|
|
205
209
|
def list_available_plugins(search_paths: List[Path] = None) -> List[str]:
|
206
210
|
"""
|
207
211
|
List all available plugins in search paths.
|
208
|
-
|
212
|
+
|
209
213
|
Scans for plugins in multiple formats:
|
210
214
|
- .py files (excluding __init__.py)
|
211
215
|
- Directories with __init__.py or plugin.py
|
212
216
|
- Package directories with plugin metadata (__plugin_name__)
|
213
217
|
- Any valid plugin structure in search paths
|
214
|
-
|
218
|
+
|
215
219
|
Args:
|
216
220
|
search_paths: List of directories to search for plugins
|
217
|
-
|
221
|
+
|
218
222
|
Returns:
|
219
223
|
List of plugin names found
|
220
224
|
"""
|
221
225
|
if search_paths is None:
|
222
226
|
search_paths = []
|
223
|
-
|
227
|
+
|
224
228
|
# Add default search paths
|
225
229
|
default_paths = [
|
226
230
|
Path.cwd() / "plugins",
|
227
|
-
Path.home() / ".janito" / "plugins",
|
231
|
+
Path.home() / ".janito" / "plugins",
|
228
232
|
Path(sys.prefix) / "share" / "janito" / "plugins",
|
229
233
|
]
|
230
|
-
|
234
|
+
|
231
235
|
all_paths = search_paths + default_paths
|
232
236
|
plugins = []
|
233
|
-
|
237
|
+
|
234
238
|
for base_path in all_paths:
|
235
239
|
if not base_path.exists():
|
236
240
|
continue
|
237
|
-
|
241
|
+
|
238
242
|
# Look for directories with __init__.py or plugin.py
|
239
243
|
for item in base_path.iterdir():
|
240
244
|
if item.is_dir():
|
@@ -245,30 +249,33 @@ def list_available_plugins(search_paths: List[Path] = None) -> List[str]:
|
|
245
249
|
if subitem.is_dir() and (subitem / "__init__.py").exists():
|
246
250
|
try:
|
247
251
|
import importlib.util
|
252
|
+
|
248
253
|
spec = importlib.util.spec_from_file_location(
|
249
|
-
f"{item.name}.{subitem.name}",
|
250
|
-
subitem / "__init__.py"
|
254
|
+
f"{item.name}.{subitem.name}",
|
255
|
+
subitem / "__init__.py",
|
251
256
|
)
|
252
257
|
if spec and spec.loader:
|
253
258
|
module = importlib.util.module_from_spec(spec)
|
254
259
|
spec.loader.exec_module(module)
|
255
|
-
|
260
|
+
|
256
261
|
# Check for plugin metadata
|
257
|
-
if hasattr(module,
|
258
|
-
plugins.append(
|
262
|
+
if hasattr(module, "__plugin_name__"):
|
263
|
+
plugins.append(
|
264
|
+
getattr(module, "__plugin_name__")
|
265
|
+
)
|
259
266
|
except Exception:
|
260
267
|
pass
|
261
|
-
|
268
|
+
|
262
269
|
# Also check for plugin.py files
|
263
270
|
plugin_file = item / "plugin.py"
|
264
271
|
if plugin_file.exists():
|
265
272
|
plugins.append(item.name)
|
266
|
-
|
267
|
-
elif item.suffix ==
|
273
|
+
|
274
|
+
elif item.suffix == ".py" and item.stem != "__init__":
|
268
275
|
plugins.append(item.stem)
|
269
|
-
|
276
|
+
|
270
277
|
# Add builtin plugins
|
271
278
|
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
272
279
|
plugins.extend(builtin_plugins)
|
273
|
-
|
274
|
-
return sorted(set(plugins))
|
280
|
+
|
281
|
+
return sorted(set(plugins))
|