janito 2.3.1__py3-none-any.whl → 2.4.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 (96) hide show
  1. janito/__init__.py +1 -1
  2. janito/_version.py +57 -0
  3. janito/agent/setup_agent.py +92 -18
  4. janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +44 -0
  5. janito/cli/chat_mode/bindings.py +21 -2
  6. janito/cli/chat_mode/chat_entry.py +2 -3
  7. janito/cli/chat_mode/prompt_style.py +5 -0
  8. janito/cli/chat_mode/session.py +80 -94
  9. janito/cli/chat_mode/session_profile_select.py +80 -0
  10. janito/cli/chat_mode/shell/commands/__init__.py +13 -7
  11. janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
  12. janito/cli/chat_mode/shell/commands/conversation_restart.py +30 -0
  13. janito/cli/chat_mode/shell/commands/execute.py +42 -0
  14. janito/cli/chat_mode/shell/commands/help.py +6 -3
  15. janito/cli/chat_mode/shell/commands/model.py +28 -0
  16. janito/cli/chat_mode/shell/commands/read.py +37 -0
  17. janito/cli/chat_mode/shell/commands/tools.py +45 -18
  18. janito/cli/chat_mode/shell/commands/write.py +37 -0
  19. janito/cli/chat_mode/shell/commands.bak.zip +0 -0
  20. janito/cli/chat_mode/shell/session.bak.zip +0 -0
  21. janito/cli/chat_mode/toolbar.py +44 -27
  22. janito/cli/cli_commands/list_tools.py +44 -11
  23. janito/cli/cli_commands/model_utils.py +95 -95
  24. janito/cli/cli_commands/show_system_prompt.py +57 -14
  25. janito/cli/config.py +5 -6
  26. janito/cli/core/getters.py +33 -33
  27. janito/cli/core/runner.py +25 -18
  28. janito/cli/core/setters.py +10 -1
  29. janito/cli/main_cli.py +28 -5
  30. janito/cli/prompt_core.py +18 -2
  31. janito/cli/prompt_setup.py +56 -0
  32. janito/cli/single_shot_mode/handler.py +14 -73
  33. janito/cli/verbose_output.py +1 -1
  34. janito/config_manager.py +125 -112
  35. janito/drivers/dashscope.bak.zip +0 -0
  36. janito/drivers/openai/README.md +20 -0
  37. janito/drivers/openai_responses.bak.zip +0 -0
  38. janito/event_bus/event.py +2 -2
  39. janito/i18n/pt.py +0 -1
  40. janito/llm/README.md +23 -0
  41. janito/llm/agent.py +80 -16
  42. janito/llm/auth.py +63 -63
  43. janito/llm/driver.py +8 -0
  44. janito/provider_registry.py +178 -176
  45. janito/providers/azure_openai/model_info.py +16 -16
  46. janito/providers/dashscope.bak.zip +0 -0
  47. janito/providers/registry.py +26 -26
  48. janito/shell.bak.zip +0 -0
  49. janito/tools/DOCSTRING_STANDARD.txt +33 -0
  50. janito/tools/README.md +3 -0
  51. janito/tools/__init__.py +20 -6
  52. janito/tools/adapters/local/__init__.py +65 -62
  53. janito/tools/adapters/local/adapter.py +18 -35
  54. janito/tools/adapters/local/ask_user.py +3 -4
  55. janito/tools/adapters/local/copy_file.py +2 -2
  56. janito/tools/adapters/local/create_directory.py +2 -2
  57. janito/tools/adapters/local/create_file.py +2 -2
  58. janito/tools/adapters/local/delete_text_in_file.py +2 -2
  59. janito/tools/adapters/local/fetch_url.py +2 -2
  60. janito/tools/adapters/local/find_files.py +2 -1
  61. janito/tools/adapters/local/get_file_outline/core.py +2 -2
  62. janito/tools/adapters/local/get_file_outline/search_outline.py +2 -2
  63. janito/tools/adapters/local/move_file.py +2 -2
  64. janito/tools/adapters/local/open_html_in_browser.py +2 -1
  65. janito/tools/adapters/local/open_url.py +2 -2
  66. janito/tools/adapters/local/python_code_run.py +3 -3
  67. janito/tools/adapters/local/python_command_run.py +3 -3
  68. janito/tools/adapters/local/python_file_run.py +3 -3
  69. janito/tools/adapters/local/remove_directory.py +2 -2
  70. janito/tools/adapters/local/remove_file.py +2 -2
  71. janito/tools/adapters/local/replace_text_in_file.py +2 -2
  72. janito/tools/adapters/local/run_bash_command.py +3 -3
  73. janito/tools/adapters/local/run_powershell_command.py +3 -3
  74. janito/tools/adapters/local/search_text/core.py +2 -2
  75. janito/tools/adapters/local/validate_file_syntax/core.py +2 -2
  76. janito/tools/adapters/local/view_file.py +2 -1
  77. janito/tools/outline_file.bak.zip +0 -0
  78. janito/tools/permissions.py +45 -0
  79. janito/tools/permissions_parse.py +12 -0
  80. janito/tools/tool_base.py +14 -1
  81. janito/tools/tool_utils.py +4 -6
  82. janito/tools/tools_adapter.py +25 -20
  83. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/METADATA +51 -16
  84. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/RECORD +88 -74
  85. janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +0 -13
  86. janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +0 -37
  87. janito/cli/chat_mode/shell/commands/edit.py +0 -25
  88. janito/cli/chat_mode/shell/commands/exec.py +0 -27
  89. janito/cli/chat_mode/shell/commands/termweb_log.py +0 -92
  90. janito/cli/termweb_starter.py +0 -122
  91. janito/termweb/app.py +0 -95
  92. janito/version.py +0 -4
  93. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/WHEEL +0 -0
  94. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/entry_points.txt +0 -0
  95. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/licenses/LICENSE +0 -0
  96. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,80 @@
1
+ """
2
+ Profile selection logic for Janito Chat CLI using questionary.
3
+ """
4
+ import questionary
5
+ from questionary import Style
6
+
7
+ def select_profile():
8
+ choices = [
9
+ "helpful assistant",
10
+ "developer",
11
+ "using role...",
12
+ "full custom system prompt..."
13
+ ]
14
+ custom_style = Style([
15
+ ("highlighted", "bg:#00aaff #ffffff"), # background for item under cursor
16
+ ("question", "fg:#00aaff bold"),
17
+ ])
18
+ answer = questionary.select(
19
+ "Select a profile to use:",
20
+ choices=choices,
21
+ default=None,
22
+ style=custom_style
23
+ ).ask()
24
+ if answer == "helpful assistant":
25
+ return {"profile": None, "profile_system_prompt": "You are an helpful assistant"}
26
+ if answer == "using role...":
27
+ role_name = questionary.text("Enter the role name:").ask()
28
+ return f"role:{role_name}"
29
+ 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}
80
+ return answer
@@ -1,21 +1,26 @@
1
1
  from .base import ShellCmdHandler
2
- from .edit import EditShellHandler
3
2
  from .history_view import ViewShellHandler
4
3
  from .lang import LangShellHandler
5
4
  from .livelogs import LivelogsShellHandler
6
5
  from .prompt import PromptShellHandler, RoleShellHandler, ProfileShellHandler
7
6
  from .multi import MultiShellHandler
7
+ from .model import ModelShellHandler
8
8
  from .role import RoleCommandShellHandler
9
9
  from .session import HistoryShellHandler
10
- from .termweb_log import TermwebLogTailShellHandler
11
10
  from .tools import ToolsShellHandler
12
11
  from .help import HelpShellHandler
13
12
  from janito.cli.console import shared_console
14
13
 
15
14
  COMMAND_HANDLERS = {
16
- "/exec": __import__(
17
- "janito.cli.chat_mode.shell.commands.exec", fromlist=["ExecShellHandler"]
18
- ).ExecShellHandler,
15
+ "/execute": __import__(
16
+ "janito.cli.chat_mode.shell.commands.execute", fromlist=["ExecuteShellHandler"]
17
+ ).ExecuteShellHandler,
18
+ "/read": __import__(
19
+ "janito.cli.chat_mode.shell.commands.read", fromlist=["ReadShellHandler"]
20
+ ).ReadShellHandler,
21
+ "/write": __import__(
22
+ "janito.cli.chat_mode.shell.commands.write", fromlist=["WriteShellHandler"]
23
+ ).WriteShellHandler,
19
24
  "/clear": __import__(
20
25
  "janito.cli.chat_mode.shell.commands.clear", fromlist=["ClearShellHandler"]
21
26
  ).ClearShellHandler,
@@ -23,7 +28,7 @@ COMMAND_HANDLERS = {
23
28
  "janito.cli.chat_mode.shell.commands.conversation_restart",
24
29
  fromlist=["RestartShellHandler"],
25
30
  ).RestartShellHandler,
26
- "/edit": EditShellHandler,
31
+
27
32
  "/view": ViewShellHandler,
28
33
  "/lang": LangShellHandler,
29
34
  "/livelogs": LivelogsShellHandler,
@@ -31,8 +36,9 @@ COMMAND_HANDLERS = {
31
36
  "/role": RoleShellHandler,
32
37
  "/profile": ProfileShellHandler,
33
38
  "/history": HistoryShellHandler,
34
- "/termweb-logs": TermwebLogTailShellHandler,
39
+
35
40
  "/tools": ToolsShellHandler,
41
+ "/model": ModelShellHandler,
36
42
  "/multi": MultiShellHandler,
37
43
  "/help": HelpShellHandler,
38
44
  }
@@ -0,0 +1,5 @@
1
+ from janito.tools.permissions import get_global_allowed_permissions
2
+
3
+ def user_has_any_privileges():
4
+ perms = get_global_allowed_permissions()
5
+ return perms.read or perms.write or perms.execute
@@ -37,6 +37,12 @@ def handle_restart(shell_state=None):
37
37
  # Reset conversation history using the agent's method
38
38
  if hasattr(shell_state, "agent") and shell_state.agent:
39
39
  shell_state.agent.reset_conversation_history()
40
+ # Reset system prompt to original template context if available
41
+ if hasattr(shell_state.agent, "_original_template_vars"):
42
+ shell_state.agent._template_vars = shell_state.agent._original_template_vars.copy()
43
+ if hasattr(shell_state.agent, "_refresh_system_prompt_from_template"):
44
+ shell_state.agent._refresh_system_prompt_from_template()
45
+ # No need to print the system prompt after restart
40
46
 
41
47
  # Reset tool use tracker
42
48
  try:
@@ -63,6 +69,30 @@ def handle_restart(shell_state=None):
63
69
  f"[bold yellow]Warning: Failed to reset PerformanceCollector token info:[/bold yellow] {e}"
64
70
  )
65
71
 
72
+ # Restore tool permissions to the CLI default on restart
73
+ try:
74
+ from janito.tools.permissions import set_global_allowed_permissions, get_default_allowed_permissions
75
+ import janito.tools
76
+ default_perms = get_default_allowed_permissions()
77
+ if default_perms is not None:
78
+ set_global_allowed_permissions(default_perms)
79
+ janito.tools.local_tools_adapter.set_allowed_permissions(default_perms)
80
+ msg = None
81
+
82
+ else:
83
+ from janito.tools.tool_base import ToolPermissions
84
+ set_global_allowed_permissions(ToolPermissions(read=False, write=False, execute=False))
85
+ janito.tools.local_tools_adapter.set_allowed_permissions(ToolPermissions(read=False, write=False, execute=False))
86
+ msg = "[green]All tool permissions have been set to OFF (read, write, execute = False).[/green]"
87
+ # Refresh system prompt to reflect new permissions
88
+ if hasattr(shell_state, "agent") and shell_state.agent and hasattr(shell_state.agent, "_refresh_system_prompt_from_template"):
89
+ shell_state.agent._refresh_system_prompt_from_template()
90
+ if msg:
91
+ shared_console.print(msg)
92
+
93
+ except Exception as e:
94
+ shared_console.print(f"[yellow]Warning: Failed to restore tool permissions: {e}[/yellow]")
95
+
66
96
  shared_console.print(
67
97
  "[bold green]Conversation history has been started (context reset).[/bold green]"
68
98
  )
@@ -0,0 +1,42 @@
1
+ from janito.cli.console import shared_console
2
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
3
+
4
+ class ExecuteShellHandler(ShellCmdHandler):
5
+ help_text = "/execute on|off: Enable or disable code and command execution tools. Usage: /execute on or /execute off."
6
+
7
+ def run(self):
8
+ if not self.shell_state:
9
+ shared_console.print("[red]Shell state unavailable.[/red]")
10
+ return
11
+ arg = (self.after_cmd_line or "").strip().lower()
12
+ if arg not in ("on", "off"):
13
+ shared_console.print("[yellow]Usage: /execute on|off[/yellow]")
14
+ return
15
+ enable = arg == "on"
16
+ # Dynamically enable/disable execution tools in the registry
17
+ try:
18
+ from janito.tools.permissions import set_global_allowed_permissions, get_global_allowed_permissions
19
+ from janito.tools.tool_base import ToolPermissions
20
+ current_perms = get_global_allowed_permissions()
21
+ new_perms = ToolPermissions(
22
+ read=getattr(current_perms, 'read', False),
23
+ write=getattr(current_perms, 'write', False),
24
+ execute=enable
25
+ )
26
+ set_global_allowed_permissions(new_perms)
27
+ # Also update the singleton tools registry permissions
28
+ import janito.tools
29
+ janito.tools.local_tools_adapter.set_allowed_permissions(new_perms)
30
+
31
+ except Exception as e:
32
+ shared_console.print(f"[yellow]Warning: Could not update execution tools dynamically: {e}[/yellow]")
33
+ # Refresh system prompt if agent is available
34
+ agent = getattr(self.shell_state, "agent", None)
35
+ if agent:
36
+ if hasattr(agent, "_refresh_system_prompt_from_template"):
37
+ agent._refresh_system_prompt_from_template()
38
+ # No need to print the system prompt after permission change
39
+ if enable:
40
+ shared_console.print("[green]Execution tools ENABLED. Tools can now execute code and commands.[/green]")
41
+ else:
42
+ shared_console.print("[yellow]Execution tools DISABLED. Tools cannot run code or commands.[/yellow]")
@@ -6,11 +6,14 @@ class HelpShellHandler(ShellCmdHandler):
6
6
  help_text = "Show this help message"
7
7
 
8
8
  def run(self):
9
- from . import (
10
- COMMAND_HANDLERS,
11
- ) # Import moved inside method to avoid circular import
9
+ from . import COMMAND_HANDLERS
10
+ from ._priv_check import user_has_any_privileges
12
11
 
13
12
  shared_console.print("[bold magenta]Available commands:[/bold magenta]")
14
13
  for cmd, handler_cls in sorted(COMMAND_HANDLERS.items()):
15
14
  help_text = getattr(handler_cls, "help_text", "")
16
15
  shared_console.print(f"[cyan]{cmd}[/cyan]: {help_text}")
16
+
17
+ # After help, print privilege info if user has no privileges
18
+ if not user_has_any_privileges():
19
+ shared_console.print("[yellow]Note: You currently have no privileges enabled. If you need to interact with files or the system, enable permissions using /read on, /write on, or /execute on.[/yellow]")
@@ -0,0 +1,28 @@
1
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
2
+ from janito.cli.console import shared_console
3
+ from janito.cli.config import config
4
+
5
+ from janito.cli.cli_commands.show_config import resolve_effective_model
6
+
7
+ class ModelShellHandler(ShellCmdHandler):
8
+ help_text = "Change or show the current LLM model (usage: /model [MODEL_NAME])"
9
+
10
+ def run(self):
11
+ model_name = self.after_cmd_line.strip()
12
+ if not model_name:
13
+ # Show effective model
14
+ provider = config.get("provider")
15
+ effective_model = resolve_effective_model(provider) if provider else None
16
+ if effective_model:
17
+ shared_console.print(f"[bold green]Current effective model:[/bold green] {effective_model}")
18
+ else:
19
+ shared_console.print("[bold yellow]No model is currently set.[/bold yellow]")
20
+ return
21
+ # Set new model (global override)
22
+ config.runtime_set("model", model_name)
23
+ # Update agent's model in shell_state if possible
24
+ agent = getattr(self.shell_state, "agent", None)
25
+ if agent is not None and hasattr(agent, "reset_driver_config_to_model_defaults"):
26
+ agent.reset_driver_config_to_model_defaults(model_name)
27
+ shared_console.print(f"[bold green]Model and config reset to defaults for:[/bold green] {model_name}")
28
+
@@ -0,0 +1,37 @@
1
+ from janito.cli.console import shared_console
2
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
3
+
4
+ class ReadShellHandler(ShellCmdHandler):
5
+ help_text = "/read on|off: Enable or disable read permissions for tools. Usage: /read on or /read off."
6
+
7
+ def run(self):
8
+ if not self.shell_state:
9
+ shared_console.print("[red]Shell state unavailable.[/red]")
10
+ return
11
+ arg = (self.after_cmd_line or "").strip().lower()
12
+ if arg not in ("on", "off"):
13
+ shared_console.print("[yellow]Usage: /read on|off[/yellow]")
14
+ return
15
+ enable = arg == "on"
16
+ try:
17
+ from janito.tools.permissions import set_global_allowed_permissions, get_global_allowed_permissions
18
+ from janito.tools.tool_base import ToolPermissions
19
+ current = get_global_allowed_permissions()
20
+ new_perms = ToolPermissions(read=enable, write=current.write, execute=current.execute)
21
+ set_global_allowed_permissions(new_perms)
22
+ # Also update the singleton tools registry permissions
23
+ import janito.tools
24
+ janito.tools.local_tools_adapter.set_allowed_permissions(new_perms)
25
+
26
+ except Exception as e:
27
+ shared_console.print(f"[yellow]Warning: Could not update read permissions dynamically: {e}[/yellow]")
28
+ # Refresh system prompt if agent is available
29
+ agent = getattr(self.shell_state, "agent", None)
30
+ if agent:
31
+ if hasattr(agent, "_refresh_system_prompt_from_template"):
32
+ agent._refresh_system_prompt_from_template()
33
+ # No need to print the system prompt after permission change
34
+ if enable:
35
+ shared_console.print("[green]Read permissions ENABLED. Tools can now read files and data.[/green]")
36
+ else:
37
+ shared_console.print("[yellow]Read permissions DISABLED. Tools cannot read files/data.[/yellow]")
@@ -6,37 +6,64 @@ class ToolsShellHandler(ShellCmdHandler):
6
6
 
7
7
  def run(self):
8
8
  try:
9
- # Initialize allow_execution before use
10
- allow_execution = False
11
- if hasattr(self, 'shell_state') and self.shell_state is not None:
12
- allow_execution = getattr(self.shell_state, 'allow_execution', False)
13
9
 
14
10
  import janito.tools # Ensure all tools are registered
11
+ from janito.tools.permissions import get_global_allowed_permissions
15
12
  registry = janito.tools.get_local_tools_adapter()
16
13
  tools = registry.list_tools()
17
- shared_console.print("Registered tools:" if tools else "No tools registered.")
18
- # Get tool instances for annotation
14
+ from rich.table import Table
15
+ shared_console.print("Registered tools:")
19
16
  tool_instances = {t.tool_name: t for t in registry.get_tools()}
17
+ if not tools:
18
+ shared_console.print("No tools are enabled under the current permission settings.")
19
+ 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"
20
40
  for tool in tools:
21
41
  inst = tool_instances.get(tool, None)
22
- is_exec = getattr(inst, 'provides_execution', False) if inst else False
23
- if is_exec and not allow_execution:
24
- shared_console.print(f"- {tool} (disabled)")
42
+ perms = getattr(inst, 'permissions', None)
43
+ if not perms:
44
+ group = "unknown"
25
45
  else:
26
- shared_console.print(f"- {tool}")
27
-
28
- if allow_execution:
29
- shared_console.print("[green]Execution tools are ENABLED.[/green]")
30
- else:
31
- shared_console.print("[yellow]Execution tools are DISABLED. Use /exec on to enable them.[/yellow]")
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)
32
55
 
33
- # Find all possible execution tools (by convention: provides_execution = True)
56
+
57
+ # Find all possible execution tools (by permission: execute=True)
34
58
  exec_tools = []
35
59
  for tool_instance in registry.get_tools():
36
- if getattr(tool_instance, 'provides_execution', False):
60
+ perms = getattr(tool_instance, 'permissions', None)
61
+ if perms and perms.execute:
37
62
  exec_tools.append(tool_instance.tool_name)
38
63
 
39
- if not allow_execution and exec_tools:
64
+ from janito.tools.permissions import get_global_allowed_permissions
65
+ perms = get_global_allowed_permissions()
66
+ if not perms.execute and exec_tools:
40
67
  shared_console.print("[yellow]⚠️ Warning: Execution tools (e.g., commands, code execution) are disabled. Use -x to enable them.[/yellow]")
41
68
 
42
69
  except Exception as e:
@@ -0,0 +1,37 @@
1
+ from janito.cli.console import shared_console
2
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
3
+
4
+ class WriteShellHandler(ShellCmdHandler):
5
+ help_text = "/write on|off: Enable or disable write permissions for tools. Usage: /write on or /write off."
6
+
7
+ def run(self):
8
+ if not self.shell_state:
9
+ shared_console.print("[red]Shell state unavailable.[/red]")
10
+ return
11
+ arg = (self.after_cmd_line or "").strip().lower()
12
+ if arg not in ("on", "off"):
13
+ shared_console.print("[yellow]Usage: /write on|off[/yellow]")
14
+ return
15
+ enable = arg == "on"
16
+ try:
17
+ from janito.tools.permissions import set_global_allowed_permissions, get_global_allowed_permissions
18
+ from janito.tools.tool_base import ToolPermissions
19
+ current = get_global_allowed_permissions()
20
+ new_perms = ToolPermissions(read=current.read, write=enable, execute=current.execute)
21
+ set_global_allowed_permissions(new_perms)
22
+ # Also update the singleton tools registry permissions
23
+ import janito.tools
24
+ janito.tools.local_tools_adapter.set_allowed_permissions(new_perms)
25
+
26
+ except Exception as e:
27
+ shared_console.print(f"[yellow]Warning: Could not update write permissions dynamically: {e}[/yellow]")
28
+ # Refresh system prompt if agent is available
29
+ agent = getattr(self.shell_state, "agent", None)
30
+ if agent:
31
+ if hasattr(agent, "_refresh_system_prompt_from_template"):
32
+ agent._refresh_system_prompt_from_template()
33
+ # No need to print the system prompt after permission change
34
+ if enable:
35
+ shared_console.print("[green]Write permissions ENABLED. Tools can now write files and data.[/green]")
36
+ else:
37
+ shared_console.print("[yellow]Write permissions DISABLED. Tools cannot write files/data.[/yellow]")
@@ -1,7 +1,7 @@
1
1
  from prompt_toolkit.formatted_text import HTML
2
2
  from janito.performance_collector import PerformanceCollector
3
3
  from janito.cli.config import config
4
- from janito.version import __version__ as VERSION
4
+ from janito import __version__ as VERSION
5
5
 
6
6
 
7
7
  def format_tokens(n, tag=None):
@@ -20,11 +20,22 @@ def assemble_first_line(provider_name, model_name, role, agent=None):
20
20
  return f" Janito {VERSION} | Provider: <provider>{provider_name}</provider> | Model: <model>{model_name}</model> | Role: <role>{role}</role>"
21
21
 
22
22
 
23
- def assemble_bindings_line(width):
23
+ def assemble_bindings_line(width, permissions=None):
24
+ # permissions: ToolPermissions or None
25
+ def color_state(state):
26
+ if state == "on":
27
+ return 'on '
28
+ else:
29
+ return 'off'
30
+ read_state = color_state("on" if getattr(permissions, "read", False) else "off")
31
+ write_state = color_state("on" if getattr(permissions, "write", False) else "off")
32
+ execute_state = color_state("on" if getattr(permissions, "execute", False) else "off")
24
33
  return (
25
- f" <key-label>CTRL-C</key-label>: Interrupt Request/Exit Shell | "
26
- f"<key-label>F1</key-label>: Restart conversation | "
27
- f"<key-label>F2</key-label>: Exec | "
34
+ 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
39
  f"<b>/help</b>: Help | "
29
40
  f"<key-label>F12</key-label>: Do It "
30
41
  )
@@ -40,29 +51,29 @@ def get_toolbar_func(perf: PerformanceCollector, msg_count: int, shell_state):
40
51
  model_name = "?"
41
52
  role = "?"
42
53
  agent = shell_state.agent if hasattr(shell_state, "agent") else None
43
- termweb_support = getattr(shell_state, "termweb_support", False)
44
- termweb_port = (
45
- shell_state.termweb_port if hasattr(shell_state, "termweb_port") else None
54
+ _support = getattr(shell_state, "_support", False)
55
+ _port = (
56
+ shell_state._port if hasattr(shell_state, "_port") else None
46
57
  )
47
- termweb_status = (
48
- shell_state.termweb_status
49
- if hasattr(shell_state, "termweb_status")
58
+ _status = (
59
+ shell_state._status
60
+ if hasattr(shell_state, "_status")
50
61
  else None
51
62
  )
52
63
  # Use cached liveness check only (set by background thread in shell_state)
53
- this_termweb_status = termweb_status
54
- if not termweb_support:
55
- this_termweb_status = None
56
- elif termweb_status == "starting" or termweb_status is None:
57
- this_termweb_status = termweb_status
64
+ this__status = _status
65
+ if not _support:
66
+ this__status = None
67
+ elif _status == "starting" or _status is None:
68
+ this__status = _status
58
69
  else:
59
70
  live_status = (
60
- shell_state.termweb_live_status
61
- if hasattr(shell_state, "termweb_live_status")
71
+ shell_state._live_status
72
+ if hasattr(shell_state, "_live_status")
62
73
  else None
63
74
  )
64
75
  if live_status is not None:
65
- this_termweb_status = live_status
76
+ this__status = live_status
66
77
  if agent is not None:
67
78
  # Use agent API to get provider and model name
68
79
  provider_name = (
@@ -78,15 +89,21 @@ def get_toolbar_func(perf: PerformanceCollector, msg_count: int, shell_state):
78
89
  usage = perf.get_last_request_usage()
79
90
  first_line = assemble_first_line(provider_name, model_name, role, agent=agent)
80
91
 
81
- bindings_line = assemble_bindings_line(width)
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
98
+ bindings_line = assemble_bindings_line(width, permissions)
82
99
  toolbar_text = first_line + "\n" + bindings_line
83
- # Add termweb status if available, after the F12 line
84
- if this_termweb_status == "online" and termweb_port:
85
- toolbar_text += f"\n<termweb> Termweb </termweb>Online<termweb> at <u>http://localhost:{termweb_port}</u></termweb>"
86
- elif this_termweb_status == "starting":
87
- toolbar_text += "\n<termweb> Termweb </termweb>Starting"
88
- elif this_termweb_status == "offline":
89
- toolbar_text += "\n<termweb> Termweb </termweb>Offline"
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"
90
107
  return HTML(toolbar_text)
91
108
 
92
109
  return get_toolbar
@@ -7,6 +7,16 @@ def handle_list_tools(args=None):
7
7
  from janito.tools.adapters.local.adapter import LocalToolsAdapter
8
8
  import janito.tools # Ensure all tools are registered
9
9
 
10
+ # Determine permissions from args (default: all False)
11
+ from janito.tools.tool_base import ToolPermissions
12
+ read = getattr(args, "read", False) if args else False
13
+ write = getattr(args, "write", False) if args else False
14
+ execute = getattr(args, "exec", False) if args else False
15
+ # If no permissions are specified, assume user wants to list all tools
16
+ if not (read or write or execute):
17
+ read = write = execute = True
18
+ from janito.tools.permissions import set_global_allowed_permissions
19
+ set_global_allowed_permissions(ToolPermissions(read=read, write=write, execute=execute))
10
20
  registry = janito.tools.get_local_tools_adapter()
11
21
  tools = registry.list_tools()
12
22
  if tools:
@@ -15,7 +25,9 @@ def handle_list_tools(args=None):
15
25
  console = Console()
16
26
  # Get tool instances to check provides_execution and get info
17
27
  tool_instances = {t.tool_name: t for t in registry.get_tools()}
18
- normal_tools = []
28
+ read_only_tools = []
29
+ write_only_tools = []
30
+ read_write_tools = []
19
31
  exec_tools = []
20
32
  for tool in tools:
21
33
  inst = tool_instances.get(tool, None)
@@ -29,18 +41,39 @@ def handle_list_tools(args=None):
29
41
  "name": tool,
30
42
  "params": ", ".join(param_names),
31
43
  }
32
- if getattr(inst, "provides_execution", False):
44
+ perms = getattr(inst, "permissions", None)
45
+ if perms and perms.execute:
33
46
  exec_tools.append(info)
34
- else:
35
- normal_tools.append(info)
36
- table = Table(title="Registered tools", show_header=True, header_style="bold", show_lines=False, box=None)
37
- table.add_column("Name", style="cyan", no_wrap=True)
38
- table.add_column("Parameters", style="yellow")
39
- for info in normal_tools:
40
- table.add_row(info["name"], info["params"] or "-")
41
- console.print(table)
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
54
+ 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
+ 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)
68
+ 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)
42
75
  if exec_tools:
43
- exec_table = Table(title="Execution tools (only available with -x)", show_header=True, header_style="bold", show_lines=False, box=None)
76
+ exec_table = Table(title="Execution tools (-x)", show_header=True, header_style="bold", show_lines=False, box=None)
44
77
  exec_table.add_column("Name", style="cyan", no_wrap=True)
45
78
  exec_table.add_column("Parameters", style="yellow")
46
79
  for info in exec_tools: