janito 2.5.1__py3-none-any.whl → 2.6.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 (61) hide show
  1. janito/agent/setup_agent.py +231 -223
  2. janito/agent/templates/profiles/system_prompt_template_software_developer.txt.j2 +39 -0
  3. janito/cli/chat_mode/bindings.py +1 -26
  4. janito/cli/chat_mode/session.py +282 -294
  5. janito/cli/chat_mode/session_profile_select.py +125 -55
  6. janito/cli/chat_mode/shell/commands/tools.py +51 -48
  7. janito/cli/chat_mode/toolbar.py +42 -68
  8. janito/cli/cli_commands/list_tools.py +41 -56
  9. janito/cli/cli_commands/show_system_prompt.py +70 -49
  10. janito/cli/core/runner.py +6 -1
  11. janito/cli/core/setters.py +43 -34
  12. janito/cli/main_cli.py +25 -1
  13. janito/cli/prompt_core.py +76 -69
  14. janito/cli/rich_terminal_reporter.py +22 -1
  15. janito/cli/single_shot_mode/handler.py +95 -94
  16. janito/drivers/driver_registry.py +27 -29
  17. janito/drivers/openai/driver.py +436 -494
  18. janito/llm/agent.py +54 -68
  19. janito/provider_registry.py +178 -178
  20. janito/providers/anthropic/model_info.py +41 -22
  21. janito/providers/anthropic/provider.py +80 -67
  22. janito/providers/provider_static_info.py +18 -17
  23. janito/tools/adapters/local/__init__.py +66 -65
  24. janito/tools/adapters/local/adapter.py +79 -18
  25. janito/tools/adapters/local/create_directory.py +9 -9
  26. janito/tools/adapters/local/create_file.py +12 -12
  27. janito/tools/adapters/local/delete_text_in_file.py +16 -16
  28. janito/tools/adapters/local/find_files.py +2 -2
  29. janito/tools/adapters/local/get_file_outline/core.py +5 -5
  30. janito/tools/adapters/local/get_file_outline/search_outline.py +4 -4
  31. janito/tools/adapters/local/open_html_in_browser.py +15 -15
  32. janito/tools/adapters/local/python_file_run.py +4 -4
  33. janito/tools/adapters/local/read_files.py +40 -0
  34. janito/tools/adapters/local/remove_directory.py +5 -5
  35. janito/tools/adapters/local/remove_file.py +4 -4
  36. janito/tools/adapters/local/replace_text_in_file.py +21 -21
  37. janito/tools/adapters/local/run_bash_command.py +1 -1
  38. janito/tools/adapters/local/search_text/pattern_utils.py +2 -2
  39. janito/tools/adapters/local/search_text/traverse_directory.py +10 -10
  40. janito/tools/adapters/local/validate_file_syntax/core.py +7 -7
  41. janito/tools/adapters/local/validate_file_syntax/css_validator.py +2 -2
  42. janito/tools/adapters/local/validate_file_syntax/html_validator.py +7 -7
  43. janito/tools/adapters/local/validate_file_syntax/js_validator.py +2 -2
  44. janito/tools/adapters/local/validate_file_syntax/json_validator.py +2 -2
  45. janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +2 -2
  46. janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +2 -2
  47. janito/tools/adapters/local/validate_file_syntax/python_validator.py +2 -2
  48. janito/tools/adapters/local/validate_file_syntax/xml_validator.py +2 -2
  49. janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +2 -2
  50. janito/tools/adapters/local/view_file.py +12 -12
  51. janito/tools/path_security.py +204 -0
  52. janito/tools/tool_use_tracker.py +12 -12
  53. janito/tools/tools_adapter.py +66 -34
  54. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/METADATA +412 -412
  55. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/RECORD +59 -58
  56. janito/drivers/anthropic/driver.py +0 -113
  57. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +0 -156
  58. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/WHEEL +0 -0
  59. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/entry_points.txt +0 -0
  60. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/licenses/LICENSE +0 -0
  61. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,103 @@
1
+ import os
2
+ from pathlib import Path
3
+ import questionary
4
+ from questionary import Style
5
+ import os
6
+ from pathlib import Path
7
+ from prompt_toolkit.formatted_text import HTML
8
+ from prompt_toolkit import PromptSession
9
+ from prompt_toolkit.key_binding import KeyBindings
10
+ from prompt_toolkit.enums import EditingMode
11
+ from prompt_toolkit.formatted_text import HTML
12
+ from .prompt_style import chat_shell_style
13
+
14
+
1
15
  """
2
16
  Profile selection logic for Janito Chat CLI using questionary.
3
17
  """
4
- import questionary
5
- from questionary import Style
18
+
19
+ def _handle_helpful_assistant():
20
+ return {"profile": "assistant", "profile_system_prompt": None}
21
+
22
+ def _handle_using_role():
23
+ role_name = questionary.text("Enter the role name:").ask()
24
+ return f"role:{role_name}"
25
+
26
+ def _get_toolbar(mode):
27
+ if mode["multiline"]:
28
+ return HTML("<b>Multiline mode (Esc+Enter to submit). Type /single to switch.</b>")
29
+ else:
30
+ return HTML("<b>Single-line mode (Enter to submit). Type /multi for multiline.</b>")
31
+
32
+ def _handle_custom_system_prompt():
33
+ mode = {"multiline": False}
34
+ bindings = KeyBindings()
35
+
36
+ @bindings.add("c-r")
37
+ def _(event):
38
+ pass
39
+
40
+ @bindings.add("f12")
41
+ def _(event):
42
+ buf = event.app.current_buffer
43
+ buf.text = "Do It"
44
+ buf.validate_and_handle()
45
+
46
+ session = PromptSession(
47
+ multiline=False,
48
+ key_bindings=bindings,
49
+ editing_mode=EditingMode.EMACS,
50
+ bottom_toolbar=lambda: _get_toolbar(mode),
51
+ style=chat_shell_style,
52
+ )
53
+ prompt_icon = HTML("<inputline>📝 </inputline>")
54
+ while True:
55
+ response = session.prompt(prompt_icon)
56
+ if not mode["multiline"] and response.strip() == "/multi":
57
+ mode["multiline"] = True
58
+ session.multiline = True
59
+ continue
60
+ elif mode["multiline"] and response.strip() == "/single":
61
+ mode["multiline"] = False
62
+ session.multiline = False
63
+ continue
64
+ else:
65
+ sanitized = response.strip()
66
+ try:
67
+ sanitized.encode("utf-8")
68
+ except UnicodeEncodeError:
69
+ sanitized = sanitized.encode("utf-8", errors="replace").decode("utf-8")
70
+ return {"profile": None, "profile_system_prompt": sanitized}
71
+
72
+
73
+ def _load_user_profiles():
74
+ user_profiles_dir = Path.home() / ".janito" / "profiles"
75
+ profiles = {}
76
+ if user_profiles_dir.exists() and user_profiles_dir.is_dir():
77
+ for profile_file in user_profiles_dir.glob("*"):
78
+ if profile_file.is_file():
79
+ try:
80
+ with open(profile_file, "r", encoding="utf-8") as f:
81
+ profiles[profile_file.stem] = f.read().strip()
82
+ except Exception:
83
+ # Ignore unreadable files
84
+ pass
85
+ return profiles
86
+
6
87
 
7
88
  def select_profile():
89
+ user_profiles = _load_user_profiles()
8
90
  choices = [
9
91
  "helpful assistant",
10
92
  "developer",
93
+ "software developer",
11
94
  "using role...",
12
95
  "full custom system prompt..."
13
96
  ]
97
+ # Add user profiles to choices
98
+ if user_profiles:
99
+ choices.extend(user_profiles.keys())
100
+
14
101
  custom_style = Style([
15
102
  ("highlighted", "bg:#00aaff #ffffff"), # background for item under cursor
16
103
  ("question", "fg:#00aaff bold"),
@@ -21,60 +108,43 @@ def select_profile():
21
108
  default=None,
22
109
  style=custom_style
23
110
  ).ask()
111
+
24
112
  if answer == "helpful assistant":
25
- return {"profile": "assistant", "profile_system_prompt": None}
113
+ return _handle_helpful_assistant()
26
114
  if answer == "using role...":
27
- role_name = questionary.text("Enter the role name:").ask()
28
- return f"role:{role_name}"
115
+ return _handle_using_role()
29
116
  elif answer == "full custom system prompt...":
30
- from prompt_toolkit import PromptSession
31
- from prompt_toolkit.key_binding import KeyBindings
32
- from prompt_toolkit.enums import EditingMode
33
- from prompt_toolkit.formatted_text import HTML
34
- from .prompt_style import chat_shell_style
35
-
36
- mode = {"multiline": False}
37
- bindings = KeyBindings()
38
-
39
- @bindings.add("c-r")
40
- def _(event):
41
- pass
42
-
43
- @bindings.add("f12")
44
- def _(event):
45
- buf = event.app.current_buffer
46
- buf.text = "Do It"
47
- buf.validate_and_handle()
48
-
49
- def get_toolbar():
50
- if mode["multiline"]:
51
- return HTML("<b>Multiline mode (Esc+Enter to submit). Type /single to switch.</b>")
52
- else:
53
- return HTML("<b>Single-line mode (Enter to submit). Type /multi for multiline.</b>")
54
-
55
- session = PromptSession(
56
- multiline=False,
57
- key_bindings=bindings,
58
- editing_mode=EditingMode.EMACS,
59
- bottom_toolbar=get_toolbar,
60
- style=chat_shell_style,
61
- )
62
- prompt_icon = HTML("<inputline>📝 </inputline>")
63
- while True:
64
- response = session.prompt(prompt_icon)
65
- if not mode["multiline"] and response.strip() == "/multi":
66
- mode["multiline"] = True
67
- session.multiline = True
68
- continue
69
- elif mode["multiline"] and response.strip() == "/single":
70
- mode["multiline"] = False
71
- session.multiline = False
72
- continue
73
- else:
74
- sanitized = response.strip()
75
- try:
76
- sanitized.encode("utf-8")
77
- except UnicodeEncodeError:
78
- sanitized = sanitized.encode("utf-8", errors="replace").decode("utf-8")
79
- return {"profile": None, "profile_system_prompt": sanitized}
117
+ return _handle_custom_system_prompt()
118
+ elif answer in user_profiles:
119
+ # Return the content of the user profile as a custom system prompt
120
+ return {"profile": None, "profile_system_prompt": user_profiles[answer]}
121
+ elif answer == "software developer":
122
+ # Return the content of the built-in software developer profile prompt
123
+ with open("./janito/agent/templates/profiles/system_prompt_template_software_developer.txt.j2", "r", encoding="utf-8") as f:
124
+ prompt = f.read().strip()
125
+ return {"profile": "software developer", "profile_system_prompt": prompt}
126
+ return answer
127
+
128
+ choices = [
129
+ "helpful assistant",
130
+ "developer",
131
+ "using role...",
132
+ "full custom system prompt..."
133
+ ]
134
+ custom_style = Style([
135
+ ("highlighted", "bg:#00aaff #ffffff"), # background for item under cursor
136
+ ("question", "fg:#00aaff bold"),
137
+ ])
138
+ answer = questionary.select(
139
+ "Select a profile to use:",
140
+ choices=choices,
141
+ default=None,
142
+ style=custom_style
143
+ ).ask()
144
+ if answer == "helpful assistant":
145
+ return _handle_helpful_assistant()
146
+ if answer == "using role...":
147
+ return _handle_using_role()
148
+ elif answer == "full custom system prompt...":
149
+ return _handle_custom_system_prompt()
80
150
  return answer
@@ -4,67 +4,70 @@ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
4
4
  class ToolsShellHandler(ShellCmdHandler):
5
5
  help_text = "List available tools"
6
6
 
7
+ def _perm_class(self, perms):
8
+ if perms.execute:
9
+ if perms.read and perms.write:
10
+ return "read-write-execute"
11
+ elif perms.read:
12
+ return "read-execute"
13
+ elif perms.write:
14
+ return "write-execute"
15
+ else:
16
+ return "execute-only"
17
+ elif perms.read and perms.write:
18
+ return "read-write"
19
+ elif perms.read:
20
+ return "read-only"
21
+ elif perms.write:
22
+ return "write-only"
23
+ else:
24
+ return "none"
25
+
26
+ def _group_tools_by_permission(self, tools, tool_instances):
27
+ perm_groups = {}
28
+ for tool in tools:
29
+ inst = tool_instances.get(tool, None)
30
+ perms = getattr(inst, 'permissions', None)
31
+ if not perms:
32
+ group = "unknown"
33
+ else:
34
+ group = self._perm_class(perms)
35
+ perm_groups.setdefault(group, []).append(tool)
36
+ return perm_groups
37
+
38
+ def _print_tools_table(self, perm_groups):
39
+ from rich.table import Table
40
+ table = Table(title="Tools by Permission Class")
41
+ table.add_column("Permission Type", style="cyan", no_wrap=True)
42
+ table.add_column("Tools", style="magenta")
43
+ for group, tool_list in sorted(perm_groups.items()):
44
+ table.add_row(group, " ".join(sorted(tool_list)))
45
+ shared_console.print(table)
46
+
47
+ def _find_exec_tools(self, registry):
48
+ exec_tools = []
49
+ for tool_instance in registry.get_tools():
50
+ perms = getattr(tool_instance, 'permissions', None)
51
+ if perms and perms.execute:
52
+ exec_tools.append(tool_instance.tool_name)
53
+ return exec_tools
54
+
7
55
  def run(self):
8
56
  try:
9
-
10
57
  import janito.tools # Ensure all tools are registered
11
58
  from janito.tools.permissions import get_global_allowed_permissions
12
59
  registry = janito.tools.get_local_tools_adapter()
13
60
  tools = registry.list_tools()
14
- from rich.table import Table
15
61
  shared_console.print("Registered tools:")
16
62
  tool_instances = {t.tool_name: t for t in registry.get_tools()}
17
63
  if not tools:
18
64
  shared_console.print("No tools are enabled under the current permission settings.")
19
65
  return
20
- # Group tools by permission class
21
- perm_groups = {}
22
- def perm_class(perms):
23
- if perms.execute:
24
- if perms.read and perms.write:
25
- return "read-write-execute"
26
- elif perms.read:
27
- return "read-execute"
28
- elif perms.write:
29
- return "write-execute"
30
- else:
31
- return "execute-only"
32
- elif perms.read and perms.write:
33
- return "read-write"
34
- elif perms.read:
35
- return "read-only"
36
- elif perms.write:
37
- return "write-only"
38
- else:
39
- return "none"
40
- for tool in tools:
41
- inst = tool_instances.get(tool, None)
42
- perms = getattr(inst, 'permissions', None)
43
- if not perms:
44
- group = "unknown"
45
- else:
46
- group = perm_class(perms)
47
- perm_groups.setdefault(group, []).append(tool)
48
- # Build and print table
49
- table = Table(title="Tools by Permission Class")
50
- table.add_column("Permission Type", style="cyan", no_wrap=True)
51
- table.add_column("Tools", style="magenta")
52
- for group, tool_list in sorted(perm_groups.items()):
53
- table.add_row(group, " ".join(sorted(tool_list)))
54
- shared_console.print(table)
55
-
56
-
57
- # Find all possible execution tools (by permission: execute=True)
58
- exec_tools = []
59
- for tool_instance in registry.get_tools():
60
- perms = getattr(tool_instance, 'permissions', None)
61
- if perms and perms.execute:
62
- exec_tools.append(tool_instance.tool_name)
63
-
64
- from janito.tools.permissions import get_global_allowed_permissions
66
+ perm_groups = self._group_tools_by_permission(tools, tool_instances)
67
+ self._print_tools_table(perm_groups)
68
+ exec_tools = self._find_exec_tools(registry)
65
69
  perms = get_global_allowed_permissions()
66
70
  if not perms.execute and exec_tools:
67
71
  shared_console.print("[yellow]⚠️ Warning: Execution tools (e.g., commands, code execution) are disabled. Use -x to enable them.[/yellow]")
68
-
69
72
  except Exception as e:
70
73
  shared_console.print(f"[red]Error loading tools: {e}[/red]")
@@ -3,7 +3,6 @@ from janito.performance_collector import PerformanceCollector
3
3
  from janito.cli.config import config
4
4
  from janito import __version__ as VERSION
5
5
 
6
-
7
6
  def format_tokens(n, tag=None):
8
7
  if n is None:
9
8
  return "?"
@@ -15,95 +14,70 @@ def format_tokens(n, tag=None):
15
14
  val = f"{n/1000000:.1f}M"
16
15
  return f"<{tag}>{val}</{tag}>" if tag else val
17
16
 
18
-
19
17
  def assemble_first_line(provider_name, model_name, role, agent=None):
20
18
  return f" Janito {VERSION} | Provider: <provider>{provider_name}</provider> | Model: <model>{model_name}</model> | Role: <role>{role}</role>"
21
19
 
22
-
23
20
  def assemble_bindings_line(width, permissions=None):
24
- # permissions: ToolPermissions or None
25
21
  def color_state(state):
26
- if state == "on":
27
- return 'on '
28
- else:
29
- return 'off'
22
+ return 'on ' if state == "on" else 'off'
30
23
  read_state = color_state("on" if getattr(permissions, "read", False) else "off")
31
24
  write_state = color_state("on" if getattr(permissions, "write", False) else "off")
32
25
  execute_state = color_state("on" if getattr(permissions, "execute", False) else "off")
33
26
  return (
34
27
  f" <key-label>CTRL-C</key-label>: Interrupt/Exit | "
35
- f"<key-label>F1</key-label>: /restart | "
36
- f"<key-label>F2</key-label>: <key-toggle-{('on' if not getattr(permissions, 'read', False) else 'off')}>/read {'on ' if not getattr(permissions, 'read', False) else 'off'}</key-toggle-{('on' if not getattr(permissions, 'read', False) else 'off')}> | "
37
- f"<key-label>F3</key-label>: <key-toggle-{('on' if not getattr(permissions, 'write', False) else 'off')}>/write {'on ' if not getattr(permissions, 'write', False) else 'off'}</key-toggle-{('on' if not getattr(permissions, 'write', False) else 'off')}> | "
38
- f"<key-label>F4</key-label>: <key-toggle-{('on' if not getattr(permissions, 'execute', False) else 'off')}>/execute {'on ' if not getattr(permissions, 'execute', False) else 'off'}</key-toggle-{('on' if not getattr(permissions, 'execute', False) else 'off')}> | "
28
+ f"<key-label>F2</key-label>: /restart | "
39
29
  f"<b>/help</b>: Help | "
40
30
  f"<key-label>F12</key-label>: Do It "
41
31
  )
42
32
 
33
+ def _get_status(shell_state):
34
+ _support = getattr(shell_state, "_support", False)
35
+ _status = getattr(shell_state, "_status", None)
36
+ if not _support:
37
+ return None
38
+ if _status == "starting" or _status is None:
39
+ return _status
40
+ live_status = getattr(shell_state, "_live_status", None)
41
+ if live_status is not None:
42
+ return live_status
43
+ return _status
44
+
45
+ def _get_agent_info(agent):
46
+ provider_name = agent.get_provider_name() if hasattr(agent, "get_provider_name") else "?"
47
+ model_name = agent.get_model_name() if hasattr(agent, "get_model_name") else "?"
48
+ role = agent.template_vars.get("role", "?") if hasattr(agent, "template_vars") else "?"
49
+ return provider_name, model_name, role
50
+
51
+ def _get_permissions():
52
+ try:
53
+ from janito.tools.permissions import get_global_allowed_permissions
54
+ return get_global_allowed_permissions()
55
+ except Exception:
56
+ return None
57
+
58
+ def _get_termweb_status_line(this__status, _port):
59
+ if this__status == "online" and _port:
60
+ return "\n<> Termweb </>Online"
61
+ elif this__status == "starting":
62
+ return "\n<> Termweb </>Starting"
63
+ elif this__status == "offline":
64
+ return "\n<> Termweb </>Offline"
65
+ return ""
43
66
 
44
67
  def get_toolbar_func(perf: PerformanceCollector, msg_count: int, shell_state):
45
68
  from prompt_toolkit.application.current import get_app
46
- import importlib
47
-
48
69
  def get_toolbar():
49
70
  width = get_app().output.get_size().columns
50
- provider_name = "?"
51
- model_name = "?"
52
- role = "?"
53
- agent = shell_state.agent if hasattr(shell_state, "agent") else None
54
- _support = getattr(shell_state, "_support", False)
55
- _port = (
56
- shell_state._port if hasattr(shell_state, "_port") else None
57
- )
58
- _status = (
59
- shell_state._status
60
- if hasattr(shell_state, "_status")
61
- else None
62
- )
63
- # Use cached liveness check only (set by background thread in shell_state)
64
- this__status = _status
65
- if not _support:
66
- this__status = None
67
- elif _status == "starting" or _status is None:
68
- this__status = _status
69
- else:
70
- live_status = (
71
- shell_state._live_status
72
- if hasattr(shell_state, "_live_status")
73
- else None
74
- )
75
- if live_status is not None:
76
- this__status = live_status
77
- if agent is not None:
78
- # Use agent API to get provider and model name
79
- provider_name = (
80
- agent.get_provider_name()
81
- if hasattr(agent, "get_provider_name")
82
- else "?"
83
- )
84
- model_name = (
85
- agent.get_model_name() if hasattr(agent, "get_model_name") else "?"
86
- )
87
- if hasattr(agent, "template_vars"):
88
- role = agent.template_vars.get("role", "?")
71
+ agent = getattr(shell_state, "agent", None)
72
+ this__status = _get_status(shell_state)
73
+ provider_name, model_name, role = _get_agent_info(agent) if agent is not None else ("?", "?", "?")
89
74
  usage = perf.get_last_request_usage()
90
75
  first_line = assemble_first_line(provider_name, model_name, role, agent=agent)
91
-
92
- # Get current permissions for toolbar state
93
- try:
94
- from janito.tools.permissions import get_global_allowed_permissions
95
- permissions = get_global_allowed_permissions()
96
- except Exception:
97
- permissions = None
76
+ permissions = _get_permissions()
98
77
  bindings_line = assemble_bindings_line(width, permissions)
99
78
  toolbar_text = first_line + "\n" + bindings_line
100
- # Add status if available, after the F12 line
101
- if this__status == "online" and _port:
102
- toolbar_text += f"\n<> Termweb </>Online"
103
- elif this__status == "starting":
104
- toolbar_text += "\n<> Termweb </>Starting"
105
- elif this__status == "offline":
106
- toolbar_text += "\n<> Termweb </>Offline"
79
+ _port = getattr(shell_state, "_port", None)
80
+ toolbar_text += _get_termweb_status_line(this__status, _port)
107
81
  return HTML(toolbar_text)
108
-
109
82
  return get_toolbar
83
+
@@ -3,16 +3,49 @@ CLI Command: List available tools
3
3
  """
4
4
 
5
5
 
6
+ def _group_tools_by_permission(tools, tool_instances):
7
+ read_only_tools = []
8
+ write_only_tools = []
9
+ read_write_tools = []
10
+ exec_tools = []
11
+ import inspect
12
+ for tool in tools:
13
+ inst = tool_instances.get(tool, None)
14
+ param_names = []
15
+ if inst and hasattr(inst, "run"):
16
+ sig = inspect.signature(inst.run)
17
+ param_names = [p for p in sig.parameters if p != "self"]
18
+ info = {
19
+ "name": tool,
20
+ "params": ", ".join(param_names),
21
+ }
22
+ perms = getattr(inst, "permissions", None)
23
+ if perms and perms.execute:
24
+ exec_tools.append(info)
25
+ elif perms and perms.read and perms.write:
26
+ read_write_tools.append(info)
27
+ elif perms and perms.read:
28
+ read_only_tools.append(info)
29
+ elif perms and perms.write:
30
+ write_only_tools.append(info)
31
+ return read_only_tools, write_only_tools, read_write_tools, exec_tools
32
+
33
+ def _print_tools_table(console, title, tools_info):
34
+ from rich.table import Table
35
+ table = Table(title=title, show_header=True, header_style="bold", show_lines=False, box=None)
36
+ table.add_column("Name", style="cyan", no_wrap=True)
37
+ table.add_column("Parameters", style="yellow")
38
+ for info in tools_info:
39
+ table.add_row(info["name"], info["params"] or "-")
40
+ console.print(table)
41
+
6
42
  def handle_list_tools(args=None):
7
43
  from janito.tools.adapters.local.adapter import LocalToolsAdapter
8
44
  import janito.tools # Ensure all tools are registered
9
-
10
- # Determine permissions from args (default: all False)
11
45
  from janito.tools.tool_base import ToolPermissions
12
46
  read = getattr(args, "read", False) if args else False
13
47
  write = getattr(args, "write", False) if args else False
14
48
  execute = getattr(args, "exec", False) if args else False
15
- # If no permissions are specified, assume user wants to list all tools
16
49
  if not (read or write or execute):
17
50
  read = write = execute = True
18
51
  from janito.tools.permissions import set_global_allowed_permissions
@@ -20,67 +53,19 @@ def handle_list_tools(args=None):
20
53
  registry = janito.tools.get_local_tools_adapter()
21
54
  tools = registry.list_tools()
22
55
  if tools:
23
- from rich.table import Table
24
56
  from rich.console import Console
25
57
  console = Console()
26
- # Get tool instances to check provides_execution and get info
27
58
  tool_instances = {t.tool_name: t for t in registry.get_tools()}
28
- read_only_tools = []
29
- write_only_tools = []
30
- read_write_tools = []
31
- exec_tools = []
32
- for tool in tools:
33
- inst = tool_instances.get(tool, None)
34
- # Extract parameter names from run signature
35
- param_names = []
36
- if inst and hasattr(inst, "run"):
37
- import inspect
38
- sig = inspect.signature(inst.run)
39
- param_names = [p for p in sig.parameters if p != "self"]
40
- info = {
41
- "name": tool,
42
- "params": ", ".join(param_names),
43
- }
44
- perms = getattr(inst, "permissions", None)
45
- if perms and perms.execute:
46
- exec_tools.append(info)
47
- elif perms and perms.read and perms.write:
48
- read_write_tools.append(info)
49
- elif perms and perms.read:
50
- read_only_tools.append(info)
51
- elif perms and perms.write:
52
- write_only_tools.append(info)
53
- # Print each group if not empty
59
+ read_only_tools, write_only_tools, read_write_tools, exec_tools = _group_tools_by_permission(tools, tool_instances)
54
60
  if read_only_tools:
55
- table = Table(title="Read-only tools (-r)", show_header=True, header_style="bold", show_lines=False, box=None)
56
- table.add_column("Name", style="cyan", no_wrap=True)
57
- table.add_column("Parameters", style="yellow")
58
- for info in read_only_tools:
59
- table.add_row(info["name"], info["params"] or "-")
60
- console.print(table)
61
+ _print_tools_table(console, "Read-only tools (-r)", read_only_tools)
61
62
  if write_only_tools:
62
- table = Table(title="Write-only tools (-w)", show_header=True, header_style="bold", show_lines=False, box=None)
63
- table.add_column("Name", style="cyan", no_wrap=True)
64
- table.add_column("Parameters", style="yellow")
65
- for info in write_only_tools:
66
- table.add_row(info["name"], info["params"] or "-")
67
- console.print(table)
63
+ _print_tools_table(console, "Write-only tools (-w)", write_only_tools)
68
64
  if read_write_tools:
69
- table = Table(title="Read-Write tools (-rw)", show_header=True, header_style="bold", show_lines=False, box=None)
70
- table.add_column("Name", style="cyan", no_wrap=True)
71
- table.add_column("Parameters", style="yellow")
72
- for info in read_write_tools:
73
- table.add_row(info["name"], info["params"] or "-")
74
- console.print(table)
65
+ _print_tools_table(console, "Read-Write tools (-rw)", read_write_tools)
75
66
  if exec_tools:
76
- exec_table = Table(title="Execution tools (-x)", show_header=True, header_style="bold", show_lines=False, box=None)
77
- exec_table.add_column("Name", style="cyan", no_wrap=True)
78
- exec_table.add_column("Parameters", style="yellow")
79
- for info in exec_tools:
80
- exec_table.add_row(info["name"], info["params"] or "-")
81
- console.print(exec_table)
67
+ _print_tools_table(console, "Execution tools (-x)", exec_tools)
82
68
  else:
83
69
  print("No tools registered.")
84
70
  import sys
85
-
86
71
  sys.exit(0)