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.
Files changed (78) hide show
  1. janito/README.md +9 -9
  2. janito/agent/setup_agent.py +29 -16
  3. janito/cli/chat_mode/script_runner.py +1 -1
  4. janito/cli/chat_mode/session.py +160 -56
  5. janito/cli/chat_mode/session_profile_select.py +8 -2
  6. janito/cli/chat_mode/shell/commands/execute.py +4 -2
  7. janito/cli/chat_mode/shell/commands/help.py +2 -0
  8. janito/cli/chat_mode/shell/commands/privileges.py +6 -2
  9. janito/cli/chat_mode/shell/commands/provider.py +7 -4
  10. janito/cli/chat_mode/shell/commands/read.py +4 -2
  11. janito/cli/chat_mode/shell/commands/security/__init__.py +1 -1
  12. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +16 -13
  13. janito/cli/chat_mode/shell/commands/security_command.py +14 -10
  14. janito/cli/chat_mode/shell/commands/tools.py +4 -2
  15. janito/cli/chat_mode/shell/commands/unrestricted.py +17 -12
  16. janito/cli/chat_mode/shell/commands/write.py +4 -2
  17. janito/cli/chat_mode/toolbar.py +4 -4
  18. janito/cli/cli_commands/enable_disable_plugin.py +48 -25
  19. janito/cli/cli_commands/list_models.py +2 -2
  20. janito/cli/cli_commands/list_plugins.py +18 -18
  21. janito/cli/cli_commands/list_profiles.py +6 -6
  22. janito/cli/cli_commands/list_providers.py +1 -1
  23. janito/cli/cli_commands/model_utils.py +45 -20
  24. janito/cli/cli_commands/ping_providers.py +10 -10
  25. janito/cli/cli_commands/set_api_key.py +5 -3
  26. janito/cli/cli_commands/show_config.py +13 -7
  27. janito/cli/cli_commands/show_system_prompt.py +13 -6
  28. janito/cli/core/getters.py +1 -0
  29. janito/cli/core/model_guesser.py +18 -15
  30. janito/cli/core/runner.py +15 -7
  31. janito/cli/core/setters.py +9 -6
  32. janito/cli/main_cli.py +15 -12
  33. janito/cli/prompt_setup.py +4 -4
  34. janito/cli/rich_terminal_reporter.py +2 -1
  35. janito/config_manager.py +2 -0
  36. janito/docs/GETTING_STARTED.md +9 -9
  37. janito/drivers/cerebras/__init__.py +1 -1
  38. janito/exceptions.py +6 -4
  39. janito/plugins/__init__.py +2 -2
  40. janito/plugins/base.py +48 -40
  41. janito/plugins/builtin.py +13 -9
  42. janito/plugins/config.py +16 -19
  43. janito/plugins/discovery.py +73 -66
  44. janito/plugins/manager.py +62 -60
  45. janito/provider_registry.py +10 -10
  46. janito/providers/__init__.py +1 -1
  47. janito/providers/alibaba/model_info.py +3 -5
  48. janito/providers/alibaba/provider.py +3 -1
  49. janito/providers/cerebras/__init__.py +1 -1
  50. janito/providers/cerebras/model_info.py +12 -27
  51. janito/providers/cerebras/provider.py +11 -9
  52. janito/providers/mistral/__init__.py +1 -1
  53. janito/providers/mistral/model_info.py +1 -1
  54. janito/providers/mistral/provider.py +1 -1
  55. janito/providers/moonshot/__init__.py +1 -0
  56. janito/providers/{moonshotai → moonshot}/model_info.py +3 -3
  57. janito/providers/{moonshotai → moonshot}/provider.py +8 -8
  58. janito/providers/openai/provider.py +3 -1
  59. janito/report_events.py +0 -1
  60. janito/tools/adapters/local/create_file.py +1 -1
  61. janito/tools/adapters/local/fetch_url.py +45 -29
  62. janito/tools/adapters/local/python_command_run.py +2 -1
  63. janito/tools/adapters/local/python_file_run.py +1 -0
  64. janito/tools/adapters/local/run_powershell_command.py +1 -1
  65. janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +14 -11
  66. janito/tools/base.py +4 -3
  67. janito/tools/loop_protection.py +24 -22
  68. janito/tools/path_utils.py +7 -7
  69. janito/tools/tool_base.py +0 -2
  70. janito/tools/tools_adapter.py +15 -5
  71. janito/tools/url_whitelist.py +27 -26
  72. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/METADATA +1 -1
  73. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/RECORD +77 -77
  74. janito/providers/moonshotai/__init__.py +0 -1
  75. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/WHEEL +0 -0
  76. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/entry_points.txt +0 -0
  77. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/licenses/LICENSE +0 -0
  78. {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("documentation_generator", DocumentationGeneratorPlugin)
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
- str(Path.home() / ".janito" / "plugins"),
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, 'w') as f:
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, 'r') as f:
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, 'w') as f:
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
@@ -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(plugin_name: str, search_paths: List[Path] = None) -> Optional[Plugin]:
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(file_path: Path, plugin_name: str = None) -> Optional[Plugin]:
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 (isinstance(attr, type) and
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, '__plugin_name__'):
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, '__plugin_name__', plugin_name),
161
- version=getattr(module, '__plugin_version__', '1.0.0'),
162
- description=getattr(module, '__plugin_description__', f'Package plugin: {plugin_name}'),
163
- author=getattr(module, '__plugin_author__', 'Unknown'),
164
- license=getattr(module, '__plugin_license__', 'MIT')
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, '__plugin_tools__', [])
169
-
174
+ return getattr(module, "__plugin_tools__", [])
175
+
170
176
  def initialize(self):
171
- if hasattr(module, 'initialize'):
177
+ if hasattr(module, "initialize"):
172
178
  module.initialize()
173
-
179
+
174
180
  def cleanup(self):
175
- if hasattr(module, 'cleanup'):
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 (isinstance(attr, type) and
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, '__plugin_name__'):
258
- plugins.append(getattr(module, '__plugin_name__'))
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 == '.py' and item.stem != '__init__':
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))