janito 3.4.0__py3-none-any.whl → 3.5.1__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 (159) hide show
  1. janito/README.md +3 -0
  2. janito/cli/chat_mode/bindings.py +50 -0
  3. janito/cli/chat_mode/session.py +12 -1
  4. janito/cli/chat_mode/shell/commands/multi.py +5 -0
  5. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +47 -33
  6. janito/cli/cli_commands/check_tools.py +212 -0
  7. janito/cli/cli_commands/list_plugins.py +52 -43
  8. janito/cli/core/getters.py +3 -0
  9. janito/cli/core/model_guesser.py +40 -24
  10. janito/cli/main_cli.py +9 -12
  11. janito/cli/prompt_core.py +47 -9
  12. janito/cli/rich_terminal_reporter.py +2 -2
  13. janito/drivers/openai/driver.py +1 -0
  14. janito/drivers/zai/driver.py +1 -0
  15. janito/i18n/it.py +46 -46
  16. janito/llm/agent.py +32 -16
  17. janito/llm/auth_utils.py +14 -5
  18. janito/llm/cancellation_manager.py +63 -0
  19. janito/llm/driver.py +8 -0
  20. janito/llm/enter_cancellation.py +107 -0
  21. janito/plugin_system/__init__.py +10 -0
  22. janito/{plugins → plugin_system}/base.py +5 -2
  23. janito/plugin_system/core_loader.py +217 -0
  24. janito/plugin_system/core_loader_fixed.py +225 -0
  25. janito/plugins/__init__.py +31 -12
  26. janito/plugins/auto_loader.py +12 -11
  27. janito/plugins/auto_loader_fixed.py +12 -11
  28. janito/plugins/builtin.py +15 -1
  29. janito/plugins/core/__init__.py +7 -0
  30. janito/plugins/core/codeanalyzer/__init__.py +43 -0
  31. janito/plugins/core/filemanager/__init__.py +124 -0
  32. janito/plugins/core/filemanager/tools/create_file.py +87 -0
  33. janito/plugins/core/filemanager/tools/replace_text_in_file.py +270 -0
  34. janito/plugins/core/imagedisplay/__init__.py +14 -0
  35. janito/plugins/core/imagedisplay/plugin.py +51 -0
  36. janito/plugins/core/imagedisplay/tools/__init__.py +1 -0
  37. janito/plugins/core/imagedisplay/tools/show_image.py +83 -0
  38. janito/{tools/adapters/local → plugins/core/imagedisplay/tools}/show_image_grid.py +13 -5
  39. janito/plugins/core/system/__init__.py +23 -0
  40. janito/plugins/core/system/tools/run_bash_command.py +204 -0
  41. janito/plugins/core/system/tools/run_powershell_command.py +234 -0
  42. janito/plugins/core_adapter.py +89 -11
  43. janito/plugins/dev/__init__.py +7 -0
  44. janito/plugins/dev/pythondev/__init__.py +37 -0
  45. janito/plugins/dev/visualization/__init__.py +23 -0
  46. janito/plugins/discovery.py +5 -5
  47. janito/plugins/discovery_core.py +14 -9
  48. janito/plugins/example_plugin.py +108 -0
  49. janito/plugins/manager.py +1 -1
  50. janito/plugins/tools/__init__.py +10 -0
  51. janito/{tools/adapters/local → plugins/tools}/ask_user.py +3 -3
  52. janito/plugins/tools/copy_file.py +87 -0
  53. janito/plugins/tools/core_tools_plugin.py +87 -0
  54. janito/plugins/tools/create_directory.py +70 -0
  55. janito/{tools/adapters/local → plugins/tools}/create_file.py +6 -6
  56. janito/plugins/tools/decorators.py +19 -0
  57. janito/plugins/tools/delete_text_in_file.py +134 -0
  58. janito/{tools/adapters/local → plugins/tools}/fetch_url.py +3 -3
  59. janito/plugins/tools/find_files.py +143 -0
  60. janito/plugins/tools/get_file_outline/__init__.py +7 -0
  61. janito/plugins/tools/get_file_outline/core.py +122 -0
  62. janito/plugins/tools/get_file_outline/java_outline.py +47 -0
  63. janito/plugins/tools/get_file_outline/markdown_outline.py +14 -0
  64. janito/plugins/tools/get_file_outline/python_outline.py +303 -0
  65. janito/plugins/tools/get_file_outline/search_outline.py +36 -0
  66. janito/plugins/tools/move_file.py +131 -0
  67. janito/plugins/tools/open_html_in_browser.py +51 -0
  68. janito/plugins/tools/open_url.py +37 -0
  69. janito/{tools/adapters/local → plugins/tools}/python_code_run.py +23 -7
  70. janito/{tools/adapters/local → plugins/tools}/python_command_run.py +21 -5
  71. janito/{tools/adapters/local → plugins/tools}/python_file_run.py +21 -5
  72. janito/plugins/tools/read_chart.py +259 -0
  73. janito/plugins/tools/read_files.py +58 -0
  74. janito/plugins/tools/remove_directory.py +55 -0
  75. janito/plugins/tools/remove_file.py +58 -0
  76. janito/{tools/adapters/local → plugins/tools}/replace_text_in_file.py +4 -4
  77. janito/{tools/adapters/local → plugins/tools}/run_bash_command.py +3 -3
  78. janito/{tools/adapters/local → plugins/tools}/run_powershell_command.py +3 -3
  79. janito/plugins/tools/search_text/__init__.py +7 -0
  80. janito/plugins/tools/search_text/core.py +205 -0
  81. janito/plugins/tools/search_text/match_lines.py +67 -0
  82. janito/plugins/tools/search_text/pattern_utils.py +73 -0
  83. janito/plugins/tools/search_text/traverse_directory.py +145 -0
  84. janito/{tools/adapters/local → plugins/tools}/show_image.py +15 -6
  85. janito/plugins/tools/show_image_grid.py +85 -0
  86. janito/plugins/tools/validate_file_syntax/__init__.py +7 -0
  87. janito/plugins/tools/validate_file_syntax/core.py +114 -0
  88. janito/plugins/tools/validate_file_syntax/css_validator.py +35 -0
  89. janito/plugins/tools/validate_file_syntax/html_validator.py +100 -0
  90. janito/plugins/tools/validate_file_syntax/jinja2_validator.py +50 -0
  91. janito/plugins/tools/validate_file_syntax/js_validator.py +27 -0
  92. janito/plugins/tools/validate_file_syntax/json_validator.py +6 -0
  93. janito/plugins/tools/validate_file_syntax/markdown_validator.py +109 -0
  94. janito/plugins/tools/validate_file_syntax/ps1_validator.py +32 -0
  95. janito/plugins/tools/validate_file_syntax/python_validator.py +5 -0
  96. janito/plugins/tools/validate_file_syntax/xml_validator.py +11 -0
  97. janito/plugins/tools/validate_file_syntax/yaml_validator.py +6 -0
  98. janito/plugins/tools/view_file.py +172 -0
  99. janito/plugins/ui/__init__.py +7 -0
  100. janito/plugins/ui/userinterface/__init__.py +16 -0
  101. janito/plugins/ui/userinterface/tools/ask_user.py +110 -0
  102. janito/plugins/web/__init__.py +7 -0
  103. janito/plugins/web/webtools/__init__.py +33 -0
  104. janito/plugins/web/webtools/tools/fetch_url.py +458 -0
  105. janito/providers/__init__.py +1 -0
  106. janito/providers/together/__init__.py +1 -0
  107. janito/providers/together/model_info.py +69 -0
  108. janito/providers/together/provider.py +108 -0
  109. janito/tools/__init__.py +31 -7
  110. janito/tools/adapters/__init__.py +6 -1
  111. janito/tools/adapters/local/__init__.py +7 -70
  112. janito/tools/cli_initializer.py +88 -0
  113. janito/tools/initialize.py +70 -0
  114. janito/tools/loop_protection_decorator.py +114 -117
  115. janito-3.5.1.dist-info/METADATA +229 -0
  116. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/RECORD +155 -86
  117. janito/plugins/core_loader.py +0 -120
  118. janito/plugins/core_loader_fixed.py +0 -125
  119. janito/tools/function_adapter.py +0 -65
  120. janito-3.4.0.dist-info/METADATA +0 -84
  121. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/__init__.py +0 -0
  122. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/core.py +0 -0
  123. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/java_outline.py +0 -0
  124. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/markdown_outline.py +0 -0
  125. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/python_outline.py +0 -0
  126. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/get_file_outline/search_outline.py +0 -0
  127. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/__init__.py +0 -0
  128. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/core.py +0 -0
  129. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/match_lines.py +0 -0
  130. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/pattern_utils.py +0 -0
  131. /janito/{tools/adapters/local → plugins/core/codeanalyzer/tools}/search_text/traverse_directory.py +0 -0
  132. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/copy_file.py +0 -0
  133. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/create_directory.py +0 -0
  134. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/delete_text_in_file.py +0 -0
  135. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/find_files.py +0 -0
  136. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/move_file.py +0 -0
  137. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/read_files.py +0 -0
  138. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/remove_directory.py +0 -0
  139. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/remove_file.py +0 -0
  140. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/__init__.py +0 -0
  141. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/core.py +0 -0
  142. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/css_validator.py +0 -0
  143. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/html_validator.py +0 -0
  144. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/jinja2_validator.py +0 -0
  145. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/js_validator.py +0 -0
  146. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/json_validator.py +0 -0
  147. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/markdown_validator.py +0 -0
  148. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/ps1_validator.py +0 -0
  149. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/python_validator.py +0 -0
  150. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/xml_validator.py +0 -0
  151. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/validate_file_syntax/yaml_validator.py +0 -0
  152. /janito/{tools/adapters/local → plugins/core/filemanager/tools}/view_file.py +0 -0
  153. /janito/{tools/adapters/local → plugins/dev/visualization/tools}/read_chart.py +0 -0
  154. /janito/{tools/adapters/local → plugins/web/webtools/tools}/open_html_in_browser.py +0 -0
  155. /janito/{tools/adapters/local → plugins/web/webtools/tools}/open_url.py +0 -0
  156. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/WHEEL +0 -0
  157. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/entry_points.txt +0 -0
  158. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/licenses/LICENSE +0 -0
  159. {janito-3.4.0.dist-info → janito-3.5.1.dist-info}/top_level.txt +0 -0
janito/README.md CHANGED
@@ -106,6 +106,9 @@ janito --set model=kimi-k1-8k
106
106
 
107
107
  ## Advanced Features
108
108
 
109
+ ### 🚀 New in v3.1.0: Enter Key Cancellation
110
+ **Chat Mode Enhancement**: Press **Enter** at any time to instantly cancel long-running requests in interactive chat mode. No more waiting for stuck requests!
111
+
109
112
  ### Tool Usage
110
113
 
111
114
  Janito includes powerful built-in tools for:
@@ -35,4 +35,54 @@ class KeyBindingsFactory:
35
35
  buf.text = "Do It"
36
36
  buf.validate_and_handle()
37
37
 
38
+ @bindings.add("c-c")
39
+ def _(event):
40
+ """Handle Ctrl+C to interrupt current request or exit chat."""
41
+ # Use global cancellation manager for robust cancellation
42
+ from janito.llm.cancellation_manager import get_cancellation_manager
43
+
44
+ cancel_manager = get_cancellation_manager()
45
+
46
+ cancelled = cancel_manager.cancel_current_request()
47
+ if cancelled:
48
+ # Provide user feedback
49
+ from rich.console import Console
50
+
51
+ console = Console()
52
+ console.print("[red]Request cancelled by Ctrl+C[/red]")
53
+
54
+ # Prevent the Ctrl+C from being processed as input
55
+ event.app.output.flush()
56
+ return
57
+ else:
58
+ # No active request to cancel, exit the chat
59
+ from rich.console import Console
60
+ console = Console()
61
+ console.print("[yellow]Goodbye![/yellow]")
62
+ event.app.exit()
63
+
64
+ @bindings.add("escape", eager=True)
65
+ def _(event):
66
+ """Handle ESC key to interrupt current request (like Ctrl+C)."""
67
+ import threading
68
+
69
+ # Use global cancellation manager for robust cancellation
70
+ from janito.llm.cancellation_manager import get_cancellation_manager
71
+
72
+ cancel_manager = get_cancellation_manager()
73
+
74
+ cancelled = cancel_manager.cancel_current_request()
75
+ if cancelled:
76
+ # Provide user feedback
77
+ from rich.console import Console
78
+
79
+ console = Console()
80
+ console.print("[red]Request cancelled by ESC key[/red]")
81
+
82
+ # Prevent the ESC key from being processed as input
83
+ event.app.output.flush()
84
+ return
85
+
86
+ # If no active request to cancel, ESC does nothing
87
+
38
88
  return bindings
@@ -22,6 +22,7 @@ import time
22
22
 
23
23
  # Shared prompt/agent factory
24
24
  from janito.cli.prompt_setup import setup_agent_and_prompt_handler
25
+ from janito.llm.cancellation_manager import get_cancellation_manager
25
26
 
26
27
  import time
27
28
 
@@ -113,6 +114,10 @@ class ChatSession:
113
114
 
114
115
  # Check if multi-line mode should be enabled by default
115
116
  self.multi_line_mode = getattr(args, "multi", False) if args else False
117
+
118
+ # Default to single-line mode (Enter submits) unless explicitly enabled
119
+ if not self.multi_line_mode:
120
+ self.multi_line_mode = False
116
121
 
117
122
  def _select_profile_and_role(self, args, role):
118
123
  profile, role_arg, python_profile, market_profile = self._extract_args(args)
@@ -283,7 +288,13 @@ class ChatSession:
283
288
  )
284
289
  )
285
290
 
286
- self._prompt_handler.run_prompt(cmd_input)
291
+ try:
292
+ self._prompt_handler.run_prompt(cmd_input)
293
+ finally:
294
+ # Ensure cancellation manager is cleared
295
+ cancel_manager = get_cancellation_manager()
296
+ cancel_manager.clear_current_request()
297
+
287
298
  end_time = time.time()
288
299
  elapsed = end_time - start_time
289
300
  self.msg_count += 1
@@ -23,6 +23,11 @@ class MultiShellHandler(ShellCmdHandler):
23
23
  submitted["value"] = buffer.text
24
24
  event.app.exit(result=buffer.text)
25
25
 
26
+ # Support single ESC to cancel/interrupt (like Ctrl+C)
27
+ @bindings.add("escape")
28
+ def _(event):
29
+ raise KeyboardInterrupt
30
+
26
31
  # Support Ctrl+D
27
32
  @bindings.add("c-d")
28
33
  def _(event):
@@ -59,39 +59,53 @@ Examples:
59
59
  command = args[0].lower()
60
60
  whitelist_manager = get_url_whitelist_manager()
61
61
 
62
- if command == "list":
63
- sites = whitelist_manager.get_allowed_sites()
64
- if sites:
65
- print("Allowed sites:")
66
- for site in sites:
67
- print(f" • {site}")
68
- else:
69
- print("No sites are whitelisted (all sites are allowed)")
70
-
71
- elif command == "add":
72
- if len(args) < 2:
73
- print("Error: Please specify a site to add")
74
- return
75
- site = args[1]
76
- if whitelist_manager.add_allowed_site(site):
77
- print(f"✅ Added '{site}' to allowed sites")
78
- else:
79
- print(f"ℹ️ '{site}' is already in allowed sites")
80
-
81
- elif command == "remove":
82
- if len(args) < 2:
83
- print("Error: Please specify a site to remove")
84
- return
85
- site = args[1]
86
- if whitelist_manager.remove_allowed_site(site):
87
- print(f"✅ Removed '{site}' from allowed sites")
88
- else:
89
- print(f"ℹ️ '{site}' was not in allowed sites")
90
-
91
- elif command == "clear":
92
- whitelist_manager.clear_whitelist()
93
- print("✅ Cleared all allowed sites (all sites are now allowed)")
94
-
62
+ handlers = {
63
+ "list": self._handle_list,
64
+ "add": self._handle_add,
65
+ "remove": self._handle_remove,
66
+ "clear": self._handle_clear,
67
+ }
68
+
69
+ handler = handlers.get(command)
70
+ if handler:
71
+ handler(args, whitelist_manager)
95
72
  else:
96
73
  print(f"Error: Unknown command '{command}'")
97
74
  print(self.get_usage())
75
+
76
+ def _handle_list(self, args, whitelist_manager):
77
+ """Handle list command."""
78
+ sites = whitelist_manager.get_allowed_sites()
79
+ if sites:
80
+ print("Allowed sites:")
81
+ for site in sites:
82
+ print(f" • {site}")
83
+ else:
84
+ print("No sites are whitelisted (all sites are allowed)")
85
+
86
+ def _handle_add(self, args, whitelist_manager):
87
+ """Handle add command."""
88
+ if len(args) < 2:
89
+ print("Error: Please specify a site to add")
90
+ return
91
+ site = args[1]
92
+ if whitelist_manager.add_allowed_site(site):
93
+ print(f"✅ Added '{site}' to allowed sites")
94
+ else:
95
+ print(f"ℹ️ '{site}' is already in allowed sites")
96
+
97
+ def _handle_remove(self, args, whitelist_manager):
98
+ """Handle remove command."""
99
+ if len(args) < 2:
100
+ print("Error: Please specify a site to remove")
101
+ return
102
+ site = args[1]
103
+ if whitelist_manager.remove_allowed_site(site):
104
+ print(f"✅ Removed '{site}' from allowed sites")
105
+ else:
106
+ print(f"ℹ️ '{site}' was not in allowed sites")
107
+
108
+ def _handle_clear(self, args, whitelist_manager):
109
+ """Handle clear command."""
110
+ whitelist_manager.clear_whitelist()
111
+ print("✅ Cleared all allowed sites (all sites are now allowed)")
@@ -0,0 +1,212 @@
1
+ """
2
+ CLI Command: Check all registered tools for signature validation and availability
3
+ """
4
+
5
+ import inspect
6
+ import sys
7
+ from typing import Dict, List, Tuple, Any
8
+
9
+
10
+ def _validate_tool_signature(tool_instance) -> Tuple[bool, List[str]]:
11
+ """Validate the signature of a tool's run method."""
12
+ errors = []
13
+
14
+ if not hasattr(tool_instance, "run"):
15
+ errors.append("Missing 'run' method")
16
+ return False, errors
17
+
18
+ try:
19
+ sig = inspect.signature(tool_instance.run)
20
+ except ValueError as e:
21
+ errors.append(f"Invalid signature: {e}")
22
+ return False, errors
23
+
24
+ # Basic signature validation - just check if it's callable
25
+ if not callable(getattr(tool_instance, "run")):
26
+ errors.append("'run' method is not callable")
27
+
28
+ return len(errors) == 0, errors
29
+
30
+
31
+ def _check_tool_availability(tool_instance) -> Tuple[bool, List[str]]:
32
+ """Check if a tool is available for use."""
33
+ errors = []
34
+
35
+ # Check if tool has required attributes
36
+ required_attrs = ["tool_name"]
37
+ for attr in required_attrs:
38
+ if not hasattr(tool_instance, attr):
39
+ errors.append(f"Missing required attribute: {attr}")
40
+
41
+ # Check description (optional for now)
42
+ if not hasattr(tool_instance, "description"):
43
+ pass # Allow missing description
44
+
45
+ # Check permissions
46
+ if not hasattr(tool_instance, "permissions"):
47
+ # Function-based tools use permissions from function decorators
48
+ pass # Allow missing permissions for function-based tools
49
+ else:
50
+ perms = tool_instance.permissions
51
+ if (
52
+ not hasattr(perms, "read")
53
+ or not hasattr(perms, "write")
54
+ or not hasattr(perms, "execute")
55
+ ):
56
+ errors.append("Invalid permissions structure")
57
+
58
+ return len(errors) == 0, errors
59
+
60
+
61
+ def _get_tool_status_summary(
62
+ tools: List[Any], disabled_tools: List[str]
63
+ ) -> Dict[str, Any]:
64
+ """Get a comprehensive status summary for all tools."""
65
+ summary = {
66
+ "total": len(tools),
67
+ "available": 0,
68
+ "disabled": 0,
69
+ "invalid": 0,
70
+ "details": [],
71
+ }
72
+
73
+ for tool in tools:
74
+ tool_name = getattr(tool, "tool_name", str(tool))
75
+
76
+ # Check if disabled
77
+ is_disabled = tool_name in disabled_tools
78
+
79
+ # Validate signature and availability
80
+ sig_valid, sig_errors = _validate_tool_signature(tool)
81
+ avail_valid, avail_errors = _check_tool_availability(tool)
82
+
83
+ status = {
84
+ "name": tool_name,
85
+ "disabled": is_disabled,
86
+ "signature_valid": sig_valid,
87
+ "available": avail_valid,
88
+ "signature_errors": sig_errors,
89
+ "availability_errors": avail_errors,
90
+ }
91
+
92
+ summary["details"].append(status)
93
+
94
+ if is_disabled:
95
+ summary["disabled"] += 1
96
+ elif not sig_valid or not avail_valid:
97
+ summary["invalid"] += 1
98
+ else:
99
+ summary["available"] += 1
100
+
101
+ return summary
102
+
103
+
104
+ def _print_check_results(console, summary: Dict[str, Any], verbose: bool = False):
105
+ """Print the tool check results in a formatted way."""
106
+ from rich.table import Table
107
+ from rich.panel import Panel
108
+ from rich.text import Text
109
+
110
+ # Overall summary
111
+ summary_text = Text()
112
+ summary_text.append(f"Total tools: {summary['total']}", style="cyan")
113
+ summary_text.append(" | ")
114
+ summary_text.append(f"Available: {summary['available']}", style="green")
115
+ summary_text.append(" | ")
116
+ summary_text.append(f"Disabled: {summary['disabled']}", style="yellow")
117
+ summary_text.append(" | ")
118
+ summary_text.append(f"Invalid: {summary['invalid']}", style="red")
119
+
120
+ console.print(Panel(summary_text, title="Tool Check Summary", style="bold"))
121
+
122
+ # Always show the table for check-tools
123
+ table = Table(title="Tool Details", show_header=True, header_style="bold")
124
+ table.add_column("Tool", style="cyan", no_wrap=True)
125
+ table.add_column("Status", style="green")
126
+ table.add_column("Issues", style="red")
127
+
128
+ for detail in summary["details"]:
129
+ name = detail["name"]
130
+
131
+ if detail["disabled"]:
132
+ status = "[yellow]Disabled[/yellow]"
133
+ issues = "-"
134
+ elif not detail["available"] or not detail["signature_valid"]:
135
+ status = "[red]Invalid[/red]"
136
+ all_issues = detail["signature_errors"] + detail["availability_errors"]
137
+ issues = "\n".join(all_issues)
138
+ else:
139
+ status = "[green]Available[/green]"
140
+ issues = "-"
141
+
142
+ table.add_row(name, status, issues)
143
+
144
+ console.print(table)
145
+
146
+
147
+ def handle_check_tools(args=None):
148
+ """Handle the --check-tools CLI command."""
149
+ from janito.tools.adapters.local.adapter import LocalToolsAdapter
150
+ import janito.tools # Ensure all tools are registered
151
+
152
+ # Load disabled tools from config
153
+ from janito.tools.disabled_tools import DisabledToolsState
154
+ from janito.config import config
155
+
156
+ disabled_str = config.get("disabled_tools", "")
157
+ if disabled_str:
158
+ DisabledToolsState.set_disabled_tools(disabled_str)
159
+ disabled_tools = DisabledToolsState.get_disabled_tools()
160
+
161
+ # Initialize tools properly using the same approach as list_tools.py
162
+ from janito.tools.adapters.local.adapter import LocalToolsAdapter
163
+ from janito.tools.tool_base import ToolPermissions
164
+ import janito.tools # Ensure all tools are registered
165
+
166
+ read = getattr(args, "read", False) if args else False
167
+ write = getattr(args, "write", False) if args else False
168
+ execute = getattr(args, "exec", False) if args else False
169
+ if not (read or write or execute):
170
+ read = write = execute = True
171
+ from janito.tools.permissions import set_global_allowed_permissions
172
+
173
+ set_global_allowed_permissions(
174
+ ToolPermissions(read=read, write=write, execute=execute)
175
+ )
176
+
177
+ # Load disabled tools from config
178
+ from janito.tools.disabled_tools import DisabledToolsState
179
+ from janito.config import config
180
+
181
+ disabled_str = config.get("disabled_tools", "")
182
+ if disabled_str:
183
+ DisabledToolsState.set_disabled_tools(disabled_str)
184
+ disabled_tools = DisabledToolsState.get_disabled_tools()
185
+
186
+ # Initialize tools using the same method as list_tools.py
187
+ from janito.tools.initialize import initialize_tools
188
+
189
+ registry = initialize_tools()
190
+
191
+ # Get actual tool instances
192
+ tool_instances = []
193
+ for name, tool_info in registry._tools.items():
194
+ if "instance" in tool_info:
195
+ tool_instances.append(tool_info["instance"])
196
+
197
+ if not tool_instances:
198
+ print("No tools registered.")
199
+ return
200
+
201
+ from rich.console import Console
202
+
203
+ console = Console()
204
+
205
+ verbose = getattr(args, "verbose", False) if args else False
206
+
207
+ summary = _get_tool_status_summary(tool_instances, disabled_tools)
208
+ _print_check_results(console, summary, verbose=verbose)
209
+
210
+ # Exit with error code if there are invalid tools
211
+ if summary["invalid"] > 0:
212
+ sys.exit(1)
@@ -8,7 +8,11 @@ from janito.plugins.discovery import list_available_plugins
8
8
  import os
9
9
  from janito.plugins.manager import PluginManager
10
10
  from janito.plugins.builtin import BuiltinPluginRegistry
11
- from janito.plugins.auto_loader_fixed import load_core_plugins, get_loaded_core_plugins, is_core_plugin
11
+ from janito.plugins.auto_loader_fixed import (
12
+ load_core_plugins,
13
+ get_loaded_core_plugins,
14
+ is_core_plugin,
15
+ )
12
16
  from rich.console import Console
13
17
  from rich.table import Table
14
18
  from rich.panel import Panel
@@ -51,7 +55,8 @@ def _list_available_plugins():
51
55
  console.print(table)
52
56
 
53
57
  # Show core plugins
54
- from janito.plugins.core_loader_fixed import get_core_plugins
58
+ from janito.plugin_system.core_loader_fixed import get_core_plugins
59
+
55
60
  core_plugins = get_core_plugins()
56
61
  core_table = Table(title="Core Plugins (Enabled by Default)")
57
62
  core_table.add_column("Plugin Name", style="cyan", no_wrap=True)
@@ -62,14 +67,16 @@ def _list_available_plugins():
62
67
 
63
68
  console.print(core_table)
64
69
  else:
65
- console.print(Panel(
66
- "No plugins found in search paths\n"
67
- f"[dim]Search paths:[/dim]\n"
68
- f" {os.getcwd()}/plugins\n"
69
- f" • {os.path.expanduser('~')}/.janito/plugins",
70
- title="No Plugins Found",
71
- style="yellow"
72
- ))
70
+ console.print(
71
+ Panel(
72
+ "No plugins found in search paths\n"
73
+ f"[dim]Search paths:[/dim]\n"
74
+ f" • {os.getcwd()}/plugins\n"
75
+ f" {os.path.expanduser('~')}/.janito/plugins",
76
+ title="No Plugins Found",
77
+ style="yellow",
78
+ )
79
+ )
73
80
 
74
81
 
75
82
  def _print_builtin_plugins(builtin_plugins):
@@ -92,7 +99,7 @@ def _print_external_plugins(available, builtin_plugins):
92
99
  def _list_plugin_resources():
93
100
  """List all resources from loaded plugins using rich formatting."""
94
101
  from janito.plugins.auto_loader_fixed import get_plugin_manager
95
-
102
+
96
103
  console = Console()
97
104
  manager = get_plugin_manager()
98
105
  all_resources = manager.list_all_resources()
@@ -100,11 +107,11 @@ def _list_plugin_resources():
100
107
  if all_resources:
101
108
  for plugin_name, resources in all_resources.items():
102
109
  metadata = manager.get_plugin_metadata(plugin_name)
103
- version = metadata.version if metadata else 'unknown'
104
-
110
+ version = metadata.version if metadata else "unknown"
111
+
105
112
  # Create panel for each plugin
106
113
  panel_content = []
107
-
114
+
108
115
  tools = [r for r in resources if r["type"] == "tool"]
109
116
  commands = [r for r in resources if r["type"] == "command"]
110
117
  configs = [r for r in resources if r["type"] == "config"]
@@ -122,19 +129,25 @@ def _list_plugin_resources():
122
129
  if configs:
123
130
  panel_content.append("[bold yellow]Configuration:[/bold yellow]")
124
131
  for config in configs:
125
- panel_content.append(f" • {config['name']}: {config['description']}")
126
-
127
- console.print(Panel(
128
- "\n".join(panel_content),
129
- title=f"{plugin_name} v{version}",
130
- style="cyan"
131
- ))
132
+ panel_content.append(
133
+ f" • {config['name']}: {config['description']}"
134
+ )
135
+
136
+ console.print(
137
+ Panel(
138
+ "\n".join(panel_content),
139
+ title=f"{plugin_name} v{version}",
140
+ style="cyan",
141
+ )
142
+ )
132
143
  else:
133
- console.print(Panel(
134
- "No plugins are currently loaded.",
135
- title="No Plugin Resources",
136
- style="yellow"
137
- ))
144
+ console.print(
145
+ Panel(
146
+ "No plugins are currently loaded.",
147
+ title="No Plugin Resources",
148
+ style="yellow",
149
+ )
150
+ )
138
151
 
139
152
 
140
153
  def _print_resources_by_type(resources):
@@ -162,7 +175,7 @@ def _print_resources_by_type(resources):
162
175
  def _list_loaded_plugins():
163
176
  """List loaded plugins using rich formatting."""
164
177
  from janito.plugins.auto_loader_fixed import get_plugin_manager
165
-
178
+
166
179
  console = Console()
167
180
  manager = get_plugin_manager()
168
181
  loaded = manager.list_plugins()
@@ -177,42 +190,38 @@ def _list_loaded_plugins():
177
190
 
178
191
  core_plugins = []
179
192
  other_plugins = []
180
-
193
+
181
194
  for plugin_name in loaded:
182
195
  if is_core_plugin(plugin_name):
183
196
  core_plugins.append(plugin_name)
184
197
  else:
185
198
  other_plugins.append(plugin_name)
186
-
199
+
187
200
  # Add core plugins
188
201
  for plugin_name in core_plugins:
189
202
  metadata = manager.get_plugin_metadata(plugin_name)
190
203
  if metadata:
191
204
  table.add_row(
192
- metadata.name,
193
- metadata.version,
194
- metadata.description,
195
- "🔵 Core"
205
+ metadata.name, metadata.version, metadata.description, "🔵 Core"
196
206
  )
197
-
207
+
198
208
  # Add other plugins
199
209
  for plugin_name in other_plugins:
200
210
  metadata = manager.get_plugin_metadata(plugin_name)
201
211
  if metadata:
202
212
  table.add_row(
203
- metadata.name,
204
- metadata.version,
205
- metadata.description,
206
- "🔶 External"
213
+ metadata.name, metadata.version, metadata.description, "🔶 External"
207
214
  )
208
215
 
209
216
  console.print(table)
210
217
  else:
211
- console.print(Panel(
212
- "No plugins are currently loaded.",
213
- title="No Plugins Loaded",
214
- style="yellow"
215
- ))
218
+ console.print(
219
+ Panel(
220
+ "No plugins are currently loaded.",
221
+ title="No Plugins Loaded",
222
+ style="yellow",
223
+ )
224
+ )
216
225
 
217
226
 
218
227
  def _print_plugin_details(manager, plugin_name):
@@ -5,6 +5,7 @@ import sys
5
5
  from janito.cli.cli_commands.list_providers import handle_list_providers
6
6
  from janito.cli.cli_commands.list_models import handle_list_models
7
7
  from janito.cli.cli_commands.list_tools import handle_list_tools
8
+ from janito.cli.cli_commands.check_tools import handle_check_tools
8
9
  from janito.cli.cli_commands.show_config import handle_show_config
9
10
  from janito.cli.cli_commands.list_config import handle_list_config
10
11
  from janito.cli.cli_commands.list_drivers import handle_list_drivers
@@ -28,6 +29,7 @@ GETTER_KEYS = [
28
29
  "list_plugins",
29
30
  "list_plugins_available",
30
31
  "list_resources",
32
+ "check_tools",
31
33
  ]
32
34
 
33
35
 
@@ -64,6 +66,7 @@ def handle_getter(args, config_mgr=None):
64
66
  "list_plugins": partial(handle_list_plugins, args),
65
67
  "list_plugins_available": partial(handle_list_plugins, args),
66
68
  "list_resources": partial(handle_list_plugins, args),
69
+ "check_tools": partial(handle_check_tools, args),
67
70
  }
68
71
  for arg in GETTER_KEYS:
69
72
  if getattr(args, arg, False) and arg in GETTER_DISPATCH:
@@ -21,34 +21,50 @@ def guess_provider_from_model(model_name: str) -> str:
21
21
  model_name = model_name.lower()
22
22
 
23
23
  # Check each provider's models
24
+ return _find_provider_for_model(model_name)
25
+
26
+
27
+ def _find_provider_for_model(model_name: str) -> str:
28
+ """Find provider for given model name."""
24
29
  for provider_name in LLMProviderRegistry.list_providers():
25
30
  provider_class = LLMProviderRegistry.get(provider_name)
26
31
  if not provider_class:
27
32
  continue
28
33
 
29
- # Get model specs for this provider
30
- try:
31
- if hasattr(provider_class, "MODEL_SPECS"):
32
- model_specs = provider_class.MODEL_SPECS
33
- for spec_model_name in model_specs.keys():
34
- if spec_model_name.lower() == model_name:
35
- return provider_name
36
-
37
- # Handle special cases like moonshot
38
- if provider_name == "moonshot":
39
- try:
40
- from janito.providers.moonshot.model_info import (
41
- MOONSHOT_MODEL_SPECS,
42
- )
43
-
44
- for spec_model_name in MOONSHOT_MODEL_SPECS.keys():
45
- if spec_model_name.lower() == model_name:
46
- return "moonshot"
47
- except ImportError:
48
- pass
49
-
50
- except Exception:
51
- # Skip providers that have issues accessing model specs
52
- continue
34
+ if _check_provider_models(provider_name, provider_class, model_name):
35
+ return provider_name
53
36
 
54
37
  return None
38
+
39
+
40
+ def _check_provider_models(provider_name: str, provider_class, model_name: str) -> bool:
41
+ """Check if provider has matching model."""
42
+ try:
43
+ if hasattr(provider_class, "MODEL_SPECS"):
44
+ model_specs = provider_class.MODEL_SPECS
45
+ for spec_model_name in model_specs.keys():
46
+ if spec_model_name.lower() == model_name:
47
+ return True
48
+
49
+ # Handle special cases like moonshot
50
+ if provider_name == "moonshot":
51
+ return _check_moonshot_models(model_name)
52
+
53
+ except Exception:
54
+ # Skip providers that have issues accessing model specs
55
+ pass
56
+
57
+ return False
58
+
59
+
60
+ def _check_moonshot_models(model_name: str) -> bool:
61
+ """Check moonshot models specifically."""
62
+ try:
63
+ from janito.providers.moonshot.model_info import MOONSHOT_MODEL_SPECS
64
+
65
+ for spec_model_name in MOONSHOT_MODEL_SPECS.keys():
66
+ if spec_model_name.lower() == model_name:
67
+ return True
68
+ except ImportError:
69
+ pass
70
+ return False