janito 3.2.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.
Files changed (166) hide show
  1. janito/README.md +0 -3
  2. janito/cli/chat_mode/bindings.py +0 -26
  3. janito/cli/chat_mode/session.py +1 -12
  4. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +33 -47
  5. janito/cli/cli_commands/list_plugins.py +8 -13
  6. janito/cli/core/model_guesser.py +24 -40
  7. janito/cli/prompt_core.py +9 -20
  8. janito/i18n/it.py +46 -46
  9. janito/llm/agent.py +16 -32
  10. janito/llm/driver.py +0 -8
  11. janito/{plugin_system → plugin_system_backup_20250825_070018}/core_loader.py +3 -76
  12. janito/{plugin_system → plugin_system_backup_20250825_070018}/core_loader_fixed.py +3 -79
  13. janito/plugins/__init__.py +21 -29
  14. janito/plugins/__main__.py +85 -0
  15. janito/plugins/base.py +57 -0
  16. janito/plugins/builtin.py +1 -1
  17. janito/plugins/core/filemanager/tools/copy_file.py +25 -1
  18. janito/plugins/core/filemanager/tools/create_directory.py +28 -1
  19. janito/plugins/core/filemanager/tools/create_file.py +27 -3
  20. janito/plugins/core/filemanager/tools/delete_text_in_file.py +1 -0
  21. janito/plugins/core/imagedisplay/plugin.py +1 -1
  22. janito/plugins/core_loader.py +144 -0
  23. janito/plugins/discovery.py +3 -3
  24. janito/plugins/example_plugin.py +1 -1
  25. janito/plugins/manager.py +1 -1
  26. janito/plugins_backup_20250825_070018/__init__.py +36 -0
  27. janito/{plugins → plugins_backup_20250825_070018}/auto_loader.py +11 -12
  28. janito/plugins_backup_20250825_070018/builtin.py +102 -0
  29. janito/plugins_backup_20250825_070018/config.py +84 -0
  30. janito/plugins_backup_20250825_070018/core/__init__.py +7 -0
  31. janito/plugins_backup_20250825_070018/core/codeanalyzer/__init__.py +43 -0
  32. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/__init__.py +1 -0
  33. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/get_file_outline/core.py +122 -0
  34. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/__init__.py +1 -0
  35. janito/plugins_backup_20250825_070018/core/codeanalyzer/tools/search_text/core.py +205 -0
  36. janito/plugins_backup_20250825_070018/core/filemanager/__init__.py +124 -0
  37. janito/plugins_backup_20250825_070018/core/filemanager/tools/copy_file.py +87 -0
  38. janito/plugins_backup_20250825_070018/core/filemanager/tools/create_directory.py +70 -0
  39. janito/plugins_backup_20250825_070018/core/filemanager/tools/create_file.py +87 -0
  40. janito/plugins_backup_20250825_070018/core/filemanager/tools/delete_text_in_file.py +135 -0
  41. janito/plugins_backup_20250825_070018/core/filemanager/tools/find_files.py +143 -0
  42. janito/plugins_backup_20250825_070018/core/filemanager/tools/move_file.py +131 -0
  43. janito/plugins_backup_20250825_070018/core/filemanager/tools/read_files.py +58 -0
  44. janito/plugins_backup_20250825_070018/core/filemanager/tools/remove_directory.py +55 -0
  45. janito/plugins_backup_20250825_070018/core/filemanager/tools/remove_file.py +58 -0
  46. janito/plugins_backup_20250825_070018/core/filemanager/tools/replace_text_in_file.py +270 -0
  47. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/__init__.py +1 -0
  48. janito/plugins_backup_20250825_070018/core/filemanager/tools/validate_file_syntax/core.py +114 -0
  49. janito/plugins_backup_20250825_070018/core/filemanager/tools/view_file.py +172 -0
  50. janito/plugins_backup_20250825_070018/core/imagedisplay/__init__.py +14 -0
  51. janito/plugins_backup_20250825_070018/core/imagedisplay/plugin.py +51 -0
  52. janito/plugins_backup_20250825_070018/core/imagedisplay/tools/__init__.py +1 -0
  53. janito/plugins_backup_20250825_070018/core/imagedisplay/tools/show_image.py +83 -0
  54. janito/plugins_backup_20250825_070018/core/imagedisplay/tools/show_image_grid.py +84 -0
  55. janito/plugins_backup_20250825_070018/core/system/__init__.py +23 -0
  56. janito/plugins_backup_20250825_070018/core/system/tools/run_bash_command.py +183 -0
  57. janito/plugins_backup_20250825_070018/core/system/tools/run_powershell_command.py +218 -0
  58. janito/plugins_backup_20250825_070018/core_adapter.py +55 -0
  59. janito/plugins_backup_20250825_070018/dev/__init__.py +7 -0
  60. janito/plugins_backup_20250825_070018/dev/pythondev/__init__.py +37 -0
  61. janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_code_run.py +172 -0
  62. janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_command_run.py +171 -0
  63. janito/plugins_backup_20250825_070018/dev/pythondev/tools/python_file_run.py +172 -0
  64. janito/plugins_backup_20250825_070018/dev/visualization/__init__.py +23 -0
  65. janito/plugins_backup_20250825_070018/dev/visualization/tools/read_chart.py +259 -0
  66. janito/plugins_backup_20250825_070018/discovery.py +289 -0
  67. janito/{plugins → plugins_backup_20250825_070018}/discovery_core.py +9 -14
  68. janito/plugins_backup_20250825_070018/example_plugin.py +108 -0
  69. janito/plugins_backup_20250825_070018/manager.py +243 -0
  70. janito/{plugins → plugins_backup_20250825_070018}/tools/core_tools_plugin.py +10 -9
  71. janito/{plugins → plugins_backup_20250825_070018}/tools/create_file.py +2 -2
  72. janito/{plugins → plugins_backup_20250825_070018}/tools/delete_text_in_file.py +1 -0
  73. janito/plugins_backup_20250825_070018/tools/get_file_outline/java_outline.py +47 -0
  74. janito/plugins_backup_20250825_070018/tools/get_file_outline/markdown_outline.py +14 -0
  75. janito/plugins_backup_20250825_070018/tools/get_file_outline/python_outline.py +303 -0
  76. janito/plugins_backup_20250825_070018/tools/get_file_outline/search_outline.py +36 -0
  77. janito/plugins_backup_20250825_070018/tools/search_text/match_lines.py +67 -0
  78. janito/plugins_backup_20250825_070018/tools/search_text/pattern_utils.py +73 -0
  79. janito/plugins_backup_20250825_070018/tools/search_text/traverse_directory.py +145 -0
  80. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/css_validator.py +35 -0
  81. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/html_validator.py +100 -0
  82. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/jinja2_validator.py +50 -0
  83. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/js_validator.py +27 -0
  84. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/json_validator.py +6 -0
  85. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/markdown_validator.py +109 -0
  86. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/ps1_validator.py +32 -0
  87. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/python_validator.py +5 -0
  88. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/xml_validator.py +11 -0
  89. janito/plugins_backup_20250825_070018/tools/validate_file_syntax/yaml_validator.py +6 -0
  90. janito/plugins_backup_20250825_070018/ui/__init__.py +7 -0
  91. janito/plugins_backup_20250825_070018/ui/userinterface/__init__.py +16 -0
  92. janito/plugins_backup_20250825_070018/ui/userinterface/tools/ask_user.py +110 -0
  93. janito/plugins_backup_20250825_070018/web/__init__.py +7 -0
  94. janito/plugins_backup_20250825_070018/web/webtools/__init__.py +33 -0
  95. janito/plugins_backup_20250825_070018/web/webtools/tools/fetch_url.py +458 -0
  96. janito/plugins_backup_20250825_070018/web/webtools/tools/open_html_in_browser.py +51 -0
  97. janito/plugins_backup_20250825_070018/web/webtools/tools/open_url.py +37 -0
  98. janito/providers/__init__.py +0 -1
  99. janito/tools/base.py +31 -1
  100. janito/tools/cli_initializer.py +1 -1
  101. janito/tools/function_adapter.py +176 -0
  102. janito/tools/initialize.py +1 -1
  103. janito/tools/loop_protection_decorator.py +117 -114
  104. janito/tools/tool_base.py +142 -114
  105. janito/tools/tools_schema.py +12 -6
  106. {janito-3.2.0.dist-info → janito-3.3.0.dist-info}/METADATA +1 -1
  107. {janito-3.2.0.dist-info → janito-3.3.0.dist-info}/RECORD +160 -95
  108. janito/llm/cancellation_manager.py +0 -63
  109. janito/llm/enter_cancellation.py +0 -107
  110. janito/plugins/core_adapter.py +0 -131
  111. janito/providers/together/__init__.py +0 -1
  112. janito/providers/together/model_info.py +0 -69
  113. janito/providers/together/provider.py +0 -108
  114. /janito/{plugin_system → plugin_system_backup_20250825_070018}/__init__.py +0 -0
  115. /janito/{plugin_system → plugin_system_backup_20250825_070018}/base.py +0 -0
  116. /janito/{plugins → plugins_backup_20250825_070018}/auto_loader_fixed.py +0 -0
  117. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/java_outline.py +0 -0
  118. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/markdown_outline.py +0 -0
  119. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/python_outline.py +0 -0
  120. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/get_file_outline/search_outline.py +0 -0
  121. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/search_text/match_lines.py +0 -0
  122. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/search_text/pattern_utils.py +0 -0
  123. /janito/{plugins → plugins_backup_20250825_070018/core/codeanalyzer}/tools/search_text/traverse_directory.py +0 -0
  124. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/css_validator.py +0 -0
  125. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/html_validator.py +0 -0
  126. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/jinja2_validator.py +0 -0
  127. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/js_validator.py +0 -0
  128. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/json_validator.py +0 -0
  129. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/markdown_validator.py +0 -0
  130. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/ps1_validator.py +0 -0
  131. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/python_validator.py +0 -0
  132. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/xml_validator.py +0 -0
  133. /janito/{plugins → plugins_backup_20250825_070018/core/filemanager}/tools/validate_file_syntax/yaml_validator.py +0 -0
  134. /janito/{plugins → plugins_backup_20250825_070018}/tools/__init__.py +0 -0
  135. /janito/{plugins → plugins_backup_20250825_070018}/tools/ask_user.py +0 -0
  136. /janito/{plugins → plugins_backup_20250825_070018}/tools/copy_file.py +0 -0
  137. /janito/{plugins → plugins_backup_20250825_070018}/tools/create_directory.py +0 -0
  138. /janito/{plugins → plugins_backup_20250825_070018}/tools/decorators.py +0 -0
  139. /janito/{plugins → plugins_backup_20250825_070018}/tools/fetch_url.py +0 -0
  140. /janito/{plugins → plugins_backup_20250825_070018}/tools/find_files.py +0 -0
  141. /janito/{plugins → plugins_backup_20250825_070018}/tools/get_file_outline/__init__.py +0 -0
  142. /janito/{plugins → plugins_backup_20250825_070018}/tools/get_file_outline/core.py +0 -0
  143. /janito/{plugins → plugins_backup_20250825_070018}/tools/move_file.py +0 -0
  144. /janito/{plugins → plugins_backup_20250825_070018}/tools/open_html_in_browser.py +0 -0
  145. /janito/{plugins → plugins_backup_20250825_070018}/tools/open_url.py +0 -0
  146. /janito/{plugins → plugins_backup_20250825_070018}/tools/python_code_run.py +0 -0
  147. /janito/{plugins → plugins_backup_20250825_070018}/tools/python_command_run.py +0 -0
  148. /janito/{plugins → plugins_backup_20250825_070018}/tools/python_file_run.py +0 -0
  149. /janito/{plugins → plugins_backup_20250825_070018}/tools/read_chart.py +0 -0
  150. /janito/{plugins → plugins_backup_20250825_070018}/tools/read_files.py +0 -0
  151. /janito/{plugins → plugins_backup_20250825_070018}/tools/remove_directory.py +0 -0
  152. /janito/{plugins → plugins_backup_20250825_070018}/tools/remove_file.py +0 -0
  153. /janito/{plugins → plugins_backup_20250825_070018}/tools/replace_text_in_file.py +0 -0
  154. /janito/{plugins → plugins_backup_20250825_070018}/tools/run_bash_command.py +0 -0
  155. /janito/{plugins → plugins_backup_20250825_070018}/tools/run_powershell_command.py +0 -0
  156. /janito/{plugins → plugins_backup_20250825_070018}/tools/search_text/__init__.py +0 -0
  157. /janito/{plugins → plugins_backup_20250825_070018}/tools/search_text/core.py +0 -0
  158. /janito/{plugins → plugins_backup_20250825_070018}/tools/show_image.py +0 -0
  159. /janito/{plugins → plugins_backup_20250825_070018}/tools/show_image_grid.py +0 -0
  160. /janito/{plugins → plugins_backup_20250825_070018}/tools/validate_file_syntax/__init__.py +0 -0
  161. /janito/{plugins → plugins_backup_20250825_070018}/tools/validate_file_syntax/core.py +0 -0
  162. /janito/{plugins → plugins_backup_20250825_070018}/tools/view_file.py +0 -0
  163. {janito-3.2.0.dist-info → janito-3.3.0.dist-info}/WHEEL +0 -0
  164. {janito-3.2.0.dist-info → janito-3.3.0.dist-info}/entry_points.txt +0 -0
  165. {janito-3.2.0.dist-info → janito-3.3.0.dist-info}/licenses/LICENSE +0 -0
  166. {janito-3.2.0.dist-info → janito-3.3.0.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ from janito.plugins.discovery import list_available_plugins
13
13
  # List of core plugins that should be enabled by default
14
14
  CORE_PLUGINS = [
15
15
  "core.filemanager",
16
- "core.codeanalyzer",
16
+ "core.codeanalyzer",
17
17
  "core.system",
18
18
  "core.imagedisplay",
19
19
  "dev.pythondev",
@@ -26,23 +26,23 @@ CORE_PLUGINS = [
26
26
  def load_core_plugins(pm: PluginManager = None) -> List[str]:
27
27
  """
28
28
  Load all core plugins.
29
-
29
+
30
30
  Args:
31
31
  pm: PluginManager instance. If None, creates a new one.
32
-
32
+
33
33
  Returns:
34
34
  List of successfully loaded plugin names
35
35
  """
36
36
  if pm is None:
37
37
  pm = PluginManager()
38
-
38
+
39
39
  # Ensure plugins directory is in search path
40
40
  plugins_dir = Path.cwd() / "plugins"
41
41
  if plugins_dir.exists():
42
42
  pm.add_plugin_path(str(plugins_dir))
43
-
43
+
44
44
  loaded = []
45
-
45
+
46
46
  # Load core plugins
47
47
  for plugin_name in CORE_PLUGINS:
48
48
  try:
@@ -50,14 +50,14 @@ def load_core_plugins(pm: PluginManager = None) -> List[str]:
50
50
  loaded.append(plugin_name)
51
51
  except Exception as e:
52
52
  print(f"Warning: Failed to load core plugin {plugin_name}: {e}")
53
-
53
+
54
54
  return loaded
55
55
 
56
56
 
57
57
  def get_loaded_core_plugins() -> List[str]:
58
58
  """
59
59
  Get list of currently loaded core plugins.
60
-
60
+
61
61
  Returns:
62
62
  List of loaded core plugin names
63
63
  """
@@ -69,10 +69,10 @@ def get_loaded_core_plugins() -> List[str]:
69
69
  def is_core_plugin(plugin_name: str) -> bool:
70
70
  """
71
71
  Check if a plugin is a core plugin.
72
-
72
+
73
73
  Args:
74
74
  plugin_name: Name of the plugin to check
75
-
75
+
76
76
  Returns:
77
77
  True if it's a core plugin
78
78
  """
@@ -82,11 +82,10 @@ def is_core_plugin(plugin_name: str) -> bool:
82
82
  # Auto-load core plugins when module is imported
83
83
  _plugin_manager = None
84
84
 
85
-
86
85
  def get_plugin_manager() -> PluginManager:
87
86
  """Get the global plugin manager with core plugins loaded."""
88
87
  global _plugin_manager
89
88
  if _plugin_manager is None:
90
89
  _plugin_manager = PluginManager()
91
90
  load_core_plugins(_plugin_manager)
92
- return _plugin_manager
91
+ return _plugin_manager
@@ -0,0 +1,102 @@
1
+ """
2
+ Builtin plugin system for janito-packaged plugins.
3
+
4
+ This module provides the infrastructure for plugins that are bundled
5
+ with janito and available by default without requiring external installation.
6
+ """
7
+
8
+ import importlib
9
+ from typing import Dict, List, Optional, Type
10
+ from janito.plugin_system.base import Plugin
11
+
12
+
13
+ class BuiltinPluginRegistry:
14
+ """Registry for builtin plugins that come packaged with janito."""
15
+
16
+ _plugins: Dict[str, Type[Plugin]] = {}
17
+
18
+ @classmethod
19
+ def register(cls, name: str, plugin_class: Type[Plugin]) -> None:
20
+ """Register a builtin plugin."""
21
+ cls._plugins[name] = plugin_class
22
+
23
+ @classmethod
24
+ def get_plugin_class(cls, name: str) -> Optional[Type[Plugin]]:
25
+ """Get the plugin class for a builtin plugin."""
26
+ return cls._plugins.get(name)
27
+
28
+ @classmethod
29
+ def list_builtin_plugins(cls) -> List[str]:
30
+ """List all registered builtin plugins."""
31
+ return list(cls._plugins.keys())
32
+
33
+ @classmethod
34
+ def is_builtin(cls, name: str) -> bool:
35
+ """Check if a plugin is builtin."""
36
+ return name in cls._plugins
37
+
38
+
39
+ def register_builtin_plugin(name: str):
40
+ """Decorator to register a plugin as builtin."""
41
+
42
+ def decorator(plugin_class: Type[Plugin]) -> Type[Plugin]:
43
+ BuiltinPluginRegistry.register(name, plugin_class)
44
+ return plugin_class
45
+
46
+ return decorator
47
+
48
+
49
+ def load_builtin_plugin(name: str) -> Optional[Plugin]:
50
+ """Load a builtin plugin by name."""
51
+ plugin_class = BuiltinPluginRegistry.get_plugin_class(name)
52
+ if plugin_class:
53
+ return plugin_class()
54
+ return None
55
+
56
+
57
+ # Auto-register janito-coder plugins as builtin
58
+ try:
59
+ from janito_coder.plugins import (
60
+ GitAnalyzerPlugin,
61
+ CodeNavigatorPlugin,
62
+ DependencyAnalyzerPlugin,
63
+ CodeFormatterPlugin,
64
+ TestRunnerPlugin,
65
+ LinterPlugin,
66
+ DebuggerPlugin,
67
+ PerformanceProfilerPlugin,
68
+ SecurityScannerPlugin,
69
+ DocumentationGeneratorPlugin,
70
+ )
71
+
72
+ # Register all janito-coder plugins as builtin
73
+ BuiltinPluginRegistry.register("git_analyzer", GitAnalyzerPlugin)
74
+ BuiltinPluginRegistry.register("code_navigator", CodeNavigatorPlugin)
75
+ BuiltinPluginRegistry.register("dependency_analyzer", DependencyAnalyzerPlugin)
76
+ BuiltinPluginRegistry.register("code_formatter", CodeFormatterPlugin)
77
+ BuiltinPluginRegistry.register("test_runner", TestRunnerPlugin)
78
+ BuiltinPluginRegistry.register("linter", LinterPlugin)
79
+ BuiltinPluginRegistry.register("debugger", DebuggerPlugin)
80
+ BuiltinPluginRegistry.register("performance_profiler", PerformanceProfilerPlugin)
81
+ BuiltinPluginRegistry.register("security_scanner", SecurityScannerPlugin)
82
+ BuiltinPluginRegistry.register(
83
+ "documentation_generator", DocumentationGeneratorPlugin
84
+ )
85
+
86
+ # Register core tools plugin
87
+ from janito.plugins.tools import CoreToolsPlugin
88
+
89
+ BuiltinPluginRegistry.register("core_tools", CoreToolsPlugin)
90
+
91
+ except ImportError:
92
+ # janito-coder not available, skip registration
93
+ pass
94
+
95
+ # Register core tools plugin (always available)
96
+ try:
97
+ from janito.plugins.tools import CoreToolsPlugin
98
+
99
+ BuiltinPluginRegistry.register("core_tools", CoreToolsPlugin)
100
+ except ImportError:
101
+ # Should not happen, but handle gracefully
102
+ pass
@@ -0,0 +1,84 @@
1
+ """
2
+ Configuration management for plugins using user directory.
3
+ """
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Dict, Any, Optional
9
+
10
+
11
+ def get_user_config_dir() -> Path:
12
+ """Get the user configuration directory."""
13
+ return Path.home() / ".janito"
14
+
15
+
16
+ def get_plugins_config_path() -> Path:
17
+ """Get the path to the plugins configuration file."""
18
+ return get_user_config_dir() / "plugins.json"
19
+
20
+
21
+ def load_plugins_config() -> Dict[str, Any]:
22
+ """
23
+ Load plugins configuration from user directory.
24
+
25
+ Returns:
26
+ Dict containing plugins configuration
27
+ """
28
+ config_path = get_plugins_config_path()
29
+
30
+ if not config_path.exists():
31
+ # Create default config if it doesn't exist
32
+ default_config = {
33
+ "plugins": {
34
+ "paths": [str(Path.home() / ".janito" / "plugins"), "./plugins"],
35
+ "load": {},
36
+ }
37
+ }
38
+
39
+ # Ensure directory exists
40
+ config_path.parent.mkdir(parents=True, exist_ok=True)
41
+
42
+ # Save default config
43
+ with open(config_path, "w") as f:
44
+ json.dump(default_config, f, indent=2)
45
+
46
+ return default_config
47
+
48
+ try:
49
+ with open(config_path, "r") as f:
50
+ return json.load(f)
51
+ except (json.JSONDecodeError, IOError) as e:
52
+ print(f"Warning: Failed to load plugins config from {config_path}: {e}")
53
+ return {"plugins": {"paths": [], "load": {}}}
54
+
55
+
56
+ def save_plugins_config(config: Dict[str, Any]) -> bool:
57
+ """
58
+ Save plugins configuration to user directory.
59
+
60
+ Args:
61
+ config: Configuration dict to save
62
+
63
+ Returns:
64
+ True if saved successfully
65
+ """
66
+ config_path = get_plugins_config_path()
67
+
68
+ try:
69
+ # Ensure directory exists
70
+ config_path.parent.mkdir(parents=True, exist_ok=True)
71
+
72
+ with open(config_path, "w") as f:
73
+ json.dump(config, f, indent=2)
74
+ return True
75
+ except IOError as e:
76
+ print(f"Error: Failed to save plugins config to {config_path}: {e}")
77
+ return False
78
+
79
+
80
+ def get_user_plugins_dir() -> Path:
81
+ """Get the user plugins directory."""
82
+ plugins_dir = get_user_config_dir() / "plugins"
83
+ plugins_dir.mkdir(parents=True, exist_ok=True)
84
+ return plugins_dir
@@ -0,0 +1,7 @@
1
+ """
2
+ Core Plugin Package
3
+
4
+ Contains essential system and file management plugins.
5
+ """
6
+
7
+ __all__ = ["filemanager", "codeanalyzer", "system"]
@@ -0,0 +1,43 @@
1
+ """
2
+ Code Analyzer Plugin
3
+
4
+ Tools for understanding and searching code structure.
5
+ """
6
+
7
+ from typing import List, Optional
8
+
9
+
10
+ def get_file_outline(path: str) -> str:
11
+ """Get file structure (classes, functions, etc.)"""
12
+ return f"get_file_outline(path='{path}')"
13
+
14
+
15
+ get_file_outline.tool_name = "get_file_outline"
16
+
17
+
18
+ def search_outline(path: str) -> str:
19
+ """Search within file outlines"""
20
+ return f"search_outline(path='{path}')"
21
+
22
+
23
+ search_outline.tool_name = "search_outline"
24
+
25
+
26
+ def search_text(
27
+ paths: str,
28
+ query: str,
29
+ use_regex: bool = False,
30
+ case_sensitive: bool = True,
31
+ max_depth: Optional[int] = None,
32
+ ) -> str:
33
+ """Full-text search across files with regex support"""
34
+ return f"search_text(paths='{paths}', query='{query}', regex={use_regex})"
35
+
36
+
37
+ search_text.tool_name = "search_text"
38
+
39
+
40
+ # Plugin metadata
41
+ __plugin_name__ = "core.codeanalyzer"
42
+ __plugin_description__ = "Code analysis and structure understanding"
43
+ __plugin_tools__ = [get_file_outline, search_outline, search_text]
@@ -0,0 +1 @@
1
+ # Outline tools and parsers package
@@ -0,0 +1,122 @@
1
+ from janito.tools.adapters.local.adapter import register_local_tool
2
+ from .python_outline import parse_python_outline
3
+ from .markdown_outline import parse_markdown_outline
4
+ from janito.formatting import OutlineFormatter
5
+ from .java_outline import parse_java_outline
6
+ import os
7
+ from janito.tools.path_utils import expand_path
8
+ from janito.tools.tool_base import ToolBase, ToolPermissions
9
+ from janito.report_events import ReportAction
10
+ from janito.tools.tool_utils import display_path, pluralize
11
+ from janito.i18n import tr
12
+
13
+ from janito.tools.adapters.local.adapter import register_local_tool as register_tool
14
+ from janito.tools.loop_protection_decorator import protect_against_loops
15
+
16
+
17
+ @register_tool
18
+ class GetFileOutlineTool(ToolBase):
19
+ """
20
+ Get an outline of a file's structure. Supports Python and Markdown files.
21
+
22
+ Args:
23
+ path (str): Path to the file to outline.
24
+ """
25
+
26
+ permissions = ToolPermissions(read=True)
27
+ tool_name = "get_file_outline"
28
+
29
+ @protect_against_loops(max_calls=5, time_window=10.0, key_field="path")
30
+ def run(self, path: str) -> str:
31
+ try:
32
+ path = expand_path(path)
33
+ self.report_action(
34
+ tr(
35
+ "📄 Outline file '{disp_path}' ...",
36
+ disp_path=display_path(path),
37
+ ),
38
+ ReportAction.READ,
39
+ )
40
+ ext = os.path.splitext(path)[1].lower()
41
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
42
+ lines = f.readlines()
43
+ return self._outline_by_extension(ext, lines)
44
+ except Exception as e:
45
+ self.report_error(
46
+ tr("❌ Error reading file: {error}", error=e),
47
+ ReportAction.READ,
48
+ )
49
+ return tr("Error reading file: {error}", error=e)
50
+
51
+ def _outline_by_extension(self, ext, lines):
52
+ if ext == ".py":
53
+ outline_items = parse_python_outline(lines)
54
+ outline_type = "python"
55
+ table = OutlineFormatter.format_outline_table(outline_items)
56
+ self.report_success(
57
+ tr(
58
+ "✅ Outlined {count} {item_word}",
59
+ count=len(outline_items),
60
+ item_word=pluralize("item", len(outline_items)),
61
+ ),
62
+ ReportAction.READ,
63
+ )
64
+ return (
65
+ tr(
66
+ "Outline: {count} items ({outline_type})\n",
67
+ count=len(outline_items),
68
+ outline_type=outline_type,
69
+ )
70
+ + table
71
+ )
72
+ elif ext == ".md":
73
+ outline_items = parse_markdown_outline(lines)
74
+ outline_type = "markdown"
75
+ table = OutlineFormatter.format_markdown_outline_table(outline_items)
76
+ self.report_success(
77
+ tr(
78
+ "✅ Outlined {count} {item_word}",
79
+ count=len(outline_items),
80
+ item_word=pluralize("item", len(outline_items)),
81
+ ),
82
+ ReportAction.READ,
83
+ )
84
+ return (
85
+ tr(
86
+ "Outline: {count} items ({outline_type})\n",
87
+ count=len(outline_items),
88
+ outline_type=outline_type,
89
+ )
90
+ + table
91
+ )
92
+ elif ext == ".java":
93
+ outline_items = parse_java_outline(lines)
94
+ outline_type = "java"
95
+ table = OutlineFormatter.format_outline_table(outline_items)
96
+ self.report_success(
97
+ tr(
98
+ "✅ Outlined {count} {item_word}",
99
+ count=len(outline_items),
100
+ item_word=pluralize("item", len(outline_items)),
101
+ ),
102
+ ReportAction.READ,
103
+ )
104
+ return (
105
+ tr(
106
+ "Outline: {count} items ({outline_type})\n",
107
+ count=len(outline_items),
108
+ outline_type=outline_type,
109
+ )
110
+ + table
111
+ )
112
+ else:
113
+ outline_type = "default"
114
+ self.report_success(
115
+ tr("✅ Outlined {count} items", count=len(lines)),
116
+ ReportAction.READ,
117
+ )
118
+ return tr(
119
+ "Outline: {count} lines ({outline_type})\nFile has {count} lines.",
120
+ count=len(lines),
121
+ outline_type=outline_type,
122
+ )
@@ -0,0 +1 @@
1
+ from .core import SearchTextTool
@@ -0,0 +1,205 @@
1
+ from janito.tools.tool_base import ToolBase, ToolPermissions
2
+ from janito.report_events import ReportAction
3
+ from janito.tools.adapters.local.adapter import register_local_tool
4
+ from janito.tools.tool_utils import pluralize, display_path
5
+ from janito.i18n import tr
6
+ import os
7
+ from janito.tools.path_utils import expand_path
8
+ from .pattern_utils import prepare_pattern, format_result, summarize_total
9
+ from .match_lines import read_file_lines
10
+ from .traverse_directory import traverse_directory
11
+ from janito.tools.loop_protection_decorator import protect_against_loops
12
+
13
+
14
+ from janito.tools.adapters.local.adapter import register_local_tool as register_tool
15
+
16
+
17
+ @register_tool
18
+ class SearchTextTool(ToolBase):
19
+ """
20
+ Search for a text query in all files within one or more directories or file paths and return matching lines or counts. Respects .gitignore.
21
+ Args:
22
+ paths (str): String of one or more paths (space-separated) to search in. Each path can be a directory or a file.
23
+ query (str): Text or regular expression to search for in files. Must not be empty. When use_regex=True, this is treated as a regex pattern; otherwise as plain text.
24
+ use_regex (bool): If True, treat query as a regular expression. If False, treat as plain text (default).
25
+ case_sensitive (bool): If False, perform a case-insensitive search. Default is True (case sensitive).
26
+ max_depth (int, optional): Maximum directory depth to search. If 0 (default), search is recursive with no depth limit. If >0, limits recursion to that depth. Setting max_depth=1 disables recursion (only top-level directory). Ignored for file paths.
27
+ max_results (int, optional): Maximum number of results to return. Defaults to 100. 0 means no limit.
28
+ count_only (bool): If True, return only the count of matches per file and total, not the matching lines. Default is False.
29
+ Returns:
30
+ str: If count_only is False, matching lines from files as a newline-separated string, each formatted as 'filepath:lineno: line'.
31
+ If count_only is True, returns per-file and total match counts.
32
+ If max_results is reached, appends a note to the output.
33
+ """
34
+
35
+ permissions = ToolPermissions(read=True)
36
+ tool_name = "search_text"
37
+
38
+ def _handle_file(
39
+ self,
40
+ search_path,
41
+ query,
42
+ regex,
43
+ use_regex,
44
+ case_sensitive,
45
+ max_results,
46
+ total_results,
47
+ count_only,
48
+ ):
49
+ if count_only:
50
+ match_count, dir_limit_reached, _ = read_file_lines(
51
+ search_path,
52
+ query,
53
+ regex,
54
+ use_regex,
55
+ case_sensitive,
56
+ True,
57
+ max_results,
58
+ total_results,
59
+ )
60
+ per_file_counts = [(search_path, match_count)] if match_count > 0 else []
61
+ return [], dir_limit_reached, per_file_counts
62
+ else:
63
+ dir_output, dir_limit_reached, match_count_list = read_file_lines(
64
+ search_path,
65
+ query,
66
+ regex,
67
+ use_regex,
68
+ case_sensitive,
69
+ False,
70
+ max_results,
71
+ total_results,
72
+ )
73
+ per_file_counts = (
74
+ [(search_path, len(match_count_list))]
75
+ if match_count_list and len(match_count_list) > 0
76
+ else []
77
+ )
78
+ return dir_output, dir_limit_reached, per_file_counts
79
+
80
+ def _handle_path(
81
+ self,
82
+ search_path,
83
+ query,
84
+ regex,
85
+ use_regex,
86
+ case_sensitive,
87
+ max_depth,
88
+ max_results,
89
+ total_results,
90
+ count_only,
91
+ ):
92
+ info_str = tr(
93
+ "🔍 Search {search_type} '{query}' in '{disp_path}'",
94
+ search_type=("regex" if use_regex else "text"),
95
+ query=query,
96
+ disp_path=display_path(search_path),
97
+ )
98
+ if max_depth > 0:
99
+ info_str += tr(" [max_depth={max_depth}]", max_depth=max_depth)
100
+ if count_only:
101
+ info_str += " [count]"
102
+ self.report_action(info_str, ReportAction.READ)
103
+ if os.path.isfile(search_path):
104
+ dir_output, dir_limit_reached, per_file_counts = self._handle_file(
105
+ search_path,
106
+ query,
107
+ regex,
108
+ use_regex,
109
+ case_sensitive,
110
+ max_results,
111
+ total_results,
112
+ count_only,
113
+ )
114
+ else:
115
+ if count_only:
116
+ per_file_counts, dir_limit_reached, _ = traverse_directory(
117
+ search_path,
118
+ query,
119
+ regex,
120
+ use_regex,
121
+ case_sensitive,
122
+ max_depth,
123
+ max_results,
124
+ total_results,
125
+ True,
126
+ )
127
+ dir_output = []
128
+ else:
129
+ dir_output, dir_limit_reached, per_file_counts = traverse_directory(
130
+ search_path,
131
+ query,
132
+ regex,
133
+ use_regex,
134
+ case_sensitive,
135
+ max_depth,
136
+ max_results,
137
+ total_results,
138
+ False,
139
+ )
140
+ count = sum(count for _, count in per_file_counts)
141
+ file_word = pluralize("match", count)
142
+ num_files = len(per_file_counts)
143
+ file_label = pluralize("file", num_files)
144
+ file_word_max = file_word + (" (max)" if dir_limit_reached else "")
145
+ self.report_success(
146
+ tr(
147
+ " ✅ {count} {file_word}/{num_files} {file_label}",
148
+ count=count,
149
+ file_word=file_word_max,
150
+ num_files=num_files,
151
+ file_label=file_label,
152
+ ),
153
+ ReportAction.READ,
154
+ )
155
+ return info_str, dir_output, dir_limit_reached, per_file_counts
156
+
157
+ @protect_against_loops(max_calls=5, time_window=10.0, key_field="paths")
158
+ def run(
159
+ self,
160
+ paths: str,
161
+ query: str,
162
+ use_regex: bool = False,
163
+ case_sensitive: bool = False,
164
+ max_depth: int = 0,
165
+ max_results: int = 100,
166
+ count_only: bool = False,
167
+ ) -> str:
168
+ regex, use_regex, error_msg = prepare_pattern(
169
+ query, use_regex, case_sensitive, self.report_error, self.report_warning
170
+ )
171
+ if error_msg:
172
+ return error_msg
173
+ paths_list = [expand_path(p) for p in paths.split()]
174
+ results = []
175
+ all_per_file_counts = []
176
+ for search_path in paths_list:
177
+ info_str, dir_output, dir_limit_reached, per_file_counts = (
178
+ self._handle_path(
179
+ search_path,
180
+ query,
181
+ regex,
182
+ use_regex,
183
+ case_sensitive,
184
+ max_depth,
185
+ max_results,
186
+ 0,
187
+ count_only,
188
+ )
189
+ )
190
+ if count_only:
191
+ all_per_file_counts.extend(per_file_counts)
192
+ result_str = format_result(
193
+ query,
194
+ use_regex,
195
+ dir_output,
196
+ dir_limit_reached,
197
+ count_only,
198
+ per_file_counts,
199
+ )
200
+ results.append(info_str + "\n" + result_str)
201
+ if dir_limit_reached:
202
+ break
203
+ if count_only:
204
+ results.append(summarize_total(all_per_file_counts))
205
+ return "\n\n".join(results)