janito 2.27.0__py3-none-any.whl → 2.28.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 (88) hide show
  1. janito/README.md +9 -9
  2. janito/agent/setup_agent.py +29 -16
  3. janito/cli/chat_mode/script_runner.py +1 -1
  4. janito/cli/chat_mode/session.py +50 -24
  5. janito/cli/chat_mode/session_profile_select.py +8 -2
  6. janito/cli/chat_mode/shell/commands/__init__.py +2 -0
  7. janito/cli/chat_mode/shell/commands/execute.py +4 -2
  8. janito/cli/chat_mode/shell/commands/help.py +8 -1
  9. janito/cli/chat_mode/shell/commands/privileges.py +6 -2
  10. janito/cli/chat_mode/shell/commands/provider.py +28 -0
  11. janito/cli/chat_mode/shell/commands/read.py +4 -2
  12. janito/cli/chat_mode/shell/commands/security/__init__.py +1 -1
  13. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +16 -13
  14. janito/cli/chat_mode/shell/commands/security_command.py +14 -10
  15. janito/cli/chat_mode/shell/commands/tools.py +4 -2
  16. janito/cli/chat_mode/shell/commands/unrestricted.py +17 -12
  17. janito/cli/chat_mode/shell/commands/write.py +4 -2
  18. janito/cli/chat_mode/toolbar.py +15 -1
  19. janito/cli/cli_commands/enable_disable_plugin.py +87 -0
  20. janito/cli/cli_commands/list_models.py +2 -2
  21. janito/cli/cli_commands/list_plugins.py +35 -19
  22. janito/cli/cli_commands/list_profiles.py +6 -6
  23. janito/cli/cli_commands/list_providers.py +1 -1
  24. janito/cli/cli_commands/model_utils.py +45 -20
  25. janito/cli/cli_commands/ping_providers.py +10 -10
  26. janito/cli/cli_commands/set_api_key.py +5 -3
  27. janito/cli/cli_commands/show_config.py +13 -7
  28. janito/cli/cli_commands/show_system_prompt.py +13 -6
  29. janito/cli/core/getters.py +7 -0
  30. janito/cli/core/model_guesser.py +18 -15
  31. janito/cli/core/runner.py +28 -6
  32. janito/cli/core/setters.py +21 -6
  33. janito/cli/main_cli.py +14 -12
  34. janito/cli/prompt_core.py +2 -0
  35. janito/cli/prompt_setup.py +4 -4
  36. janito/cli/single_shot_mode/handler.py +2 -0
  37. janito/config_manager.py +2 -0
  38. janito/docs/GETTING_STARTED.md +9 -9
  39. janito/drivers/cerebras/__init__.py +1 -1
  40. janito/exceptions.py +6 -4
  41. janito/plugins/__init__.py +2 -2
  42. janito/plugins/base.py +48 -40
  43. janito/plugins/builtin.py +88 -0
  44. janito/plugins/config.py +16 -19
  45. janito/plugins/discovery.py +129 -40
  46. janito/plugins/manager.py +63 -59
  47. janito/provider_registry.py +10 -10
  48. janito/providers/__init__.py +1 -1
  49. janito/providers/alibaba/model_info.py +3 -5
  50. janito/providers/alibaba/provider.py +3 -1
  51. janito/providers/cerebras/__init__.py +1 -1
  52. janito/providers/cerebras/model_info.py +12 -27
  53. janito/providers/cerebras/provider.py +11 -9
  54. janito/providers/mistral/__init__.py +1 -1
  55. janito/providers/mistral/model_info.py +1 -1
  56. janito/providers/mistral/provider.py +1 -1
  57. janito/providers/moonshot/__init__.py +1 -0
  58. janito/providers/{moonshotai → moonshot}/model_info.py +3 -3
  59. janito/providers/{moonshotai → moonshot}/provider.py +8 -8
  60. janito/providers/openai/provider.py +3 -1
  61. janito/report_events.py +0 -1
  62. janito/tools/adapters/local/ask_user.py +7 -1
  63. janito/tools/adapters/local/create_file.py +1 -1
  64. janito/tools/adapters/local/fetch_url.py +45 -29
  65. janito/tools/adapters/local/python_command_run.py +2 -1
  66. janito/tools/adapters/local/python_file_run.py +1 -0
  67. janito/tools/adapters/local/run_powershell_command.py +1 -1
  68. janito/tools/adapters/local/search_text/core.py +1 -1
  69. janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +14 -11
  70. janito/tools/base.py +12 -0
  71. janito/tools/loop_protection.py +24 -22
  72. janito/tools/path_utils.py +7 -7
  73. janito/tools/tool_base.py +0 -2
  74. janito/tools/tools_adapter.py +15 -5
  75. janito/tools/url_whitelist.py +27 -26
  76. {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/METADATA +3 -1
  77. {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/RECORD +81 -82
  78. janito-2.28.0.dist-info/top_level.txt +1 -0
  79. janito/providers/moonshotai/__init__.py +0 -1
  80. janito-2.27.0.dist-info/top_level.txt +0 -2
  81. janito-coder/janito_coder/__init__.py +0 -9
  82. janito-coder/janito_coder/plugins/__init__.py +0 -27
  83. janito-coder/janito_coder/plugins/code_navigator.py +0 -618
  84. janito-coder/janito_coder/plugins/git_analyzer.py +0 -273
  85. janito-coder/pyproject.toml +0 -347
  86. {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/WHEEL +0 -0
  87. {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/entry_points.txt +0 -0
  88. {janito-2.27.0.dist-info → janito-2.28.0.dist-info}/licenses/LICENSE +0 -0
janito/README.md CHANGED
@@ -15,17 +15,17 @@ pip install janito
15
15
  1. **Get your API key**: Sign up at [Moonshot AI](https://platform.moonshot.cn/) and get your API key
16
16
  2. **Set your API key**:
17
17
  ```bash
18
- janito --set-api-key YOUR_MOONSHOT_API_KEY -p moonshotai
18
+ janito --set-api-key YOUR_MOONSHOT_API_KEY -p moonshot
19
19
  ```
20
20
 
21
21
  ### Basic Usage
22
22
 
23
- **MoonshotAI (Recommended - Default Provider)**
23
+ **Moonshot (Recommended - Default Provider)**
24
24
  ```bash
25
- # Using the default provider (moonshotai) and model
25
+ # Using the default provider (moonshot) and model
26
26
  janito "Create a Python script that reads a CSV file"
27
27
 
28
- # Using a specific MoonshotAI model
28
+ # Using a specific Moonshot model
29
29
  janito -m kimi-k1-8k "Explain quantum computing"
30
30
  ```
31
31
 
@@ -71,13 +71,13 @@ In chat mode, you can:
71
71
 
72
72
  Set default provider and model:
73
73
  ```bash
74
- janito --set provider=moonshotai
74
+ janito --set provider=moonshot
75
75
  janito --set model=kimi-k1-8k
76
76
  ```
77
77
 
78
78
  ## Providers
79
79
 
80
- ### MoonshotAI (Recommended)
80
+ ### Moonshot (Recommended)
81
81
 
82
82
  - **Models**: kimi-k1-8k, kimi-k1-32k, kimi-k1-128k, kimi-k2-turbo-preview
83
83
  - **Strengths**: Excellent Chinese/English support, competitive pricing, fast responses
@@ -126,10 +126,10 @@ janito --role python-expert "Optimize this algorithm"
126
126
  ### Environment Variables
127
127
  You can also configure via environment variables:
128
128
 
129
- **MoonshotAI:**
129
+ **Moonshot:**
130
130
  ```bash
131
- export MOONSHOTAI_API_KEY=your_key_here
132
- export JANITO_PROVIDER=moonshotai
131
+ export MOONSHOT_API_KEY=your_key_here
132
+ export JANITO_PROVIDER=moonshot
133
133
  export JANITO_MODEL=kimi-k1-8k
134
134
  ```
135
135
 
@@ -53,38 +53,48 @@ def _load_template_content(profile, templates_dir):
53
53
  return file.read(), user_template_path
54
54
 
55
55
  # If nothing matched, list available profiles and raise an informative error
56
- from janito.cli.cli_commands.list_profiles import _gather_default_profiles, _gather_user_profiles
57
-
56
+ from janito.cli.cli_commands.list_profiles import (
57
+ _gather_default_profiles,
58
+ _gather_user_profiles,
59
+ )
60
+
58
61
  default_profiles = _gather_default_profiles()
59
62
  user_profiles = _gather_user_profiles()
60
-
63
+
61
64
  available_profiles = []
62
65
  if default_profiles:
63
66
  available_profiles.extend([(p, "default") for p in default_profiles])
64
67
  if user_profiles:
65
68
  available_profiles.extend([(p, "user") for p in user_profiles])
66
-
69
+
67
70
  # Normalize the input profile for better matching suggestions
68
71
  normalized_input = re.sub(r"\s+", " ", profile.strip().lower())
69
-
72
+
70
73
  if available_profiles:
71
- profile_list = "\n".join([f" - {name} ({source})" for name, source in available_profiles])
72
-
74
+ profile_list = "\n".join(
75
+ [f" - {name} ({source})" for name, source in available_profiles]
76
+ )
77
+
73
78
  # Find close matches
74
79
  close_matches = []
75
80
  for name, source in available_profiles:
76
81
  normalized_name = name.lower()
77
- if normalized_input in normalized_name or normalized_name in normalized_input:
82
+ if (
83
+ normalized_input in normalized_name
84
+ or normalized_name in normalized_input
85
+ ):
78
86
  close_matches.append(name)
79
-
87
+
80
88
  suggestion = ""
81
89
  if close_matches:
82
90
  suggestion = f"\nDid you mean: {', '.join(close_matches)}?"
83
-
91
+
84
92
  error_msg = f"[janito] Could not find profile '{profile}'. Available profiles:\n{profile_list}{suggestion}"
85
93
  else:
86
- error_msg = f"[janito] Could not find profile '{profile}'. No profiles available."
87
-
94
+ error_msg = (
95
+ f"[janito] Could not find profile '{profile}'. No profiles available."
96
+ )
97
+
88
98
  raise FileNotFoundError(error_msg)
89
99
  # Replace spaces in profile name with underscores for filename resolution
90
100
  sanitized_profile = re.sub(r"\\s+", "_", profile.strip()) if profile else profile
@@ -144,20 +154,23 @@ def _prepare_template_context(role, profile, allowed_permissions):
144
154
  context["platform"] = pd.get_platform_name()
145
155
  context["python_version"] = pd.get_python_version()
146
156
  context["shell_info"] = pd.detect_shell()
147
-
157
+
148
158
  # Add allowed sites for market analyst profile
149
159
  if profile == "market-analyst":
150
160
  from janito.tools.url_whitelist import get_url_whitelist_manager
161
+
151
162
  whitelist_manager = get_url_whitelist_manager()
152
163
  allowed_sites = whitelist_manager.get_allowed_sites()
153
164
  context["allowed_sites"] = allowed_sites
154
-
165
+
155
166
  # Add market data sources documentation
156
167
  if not allowed_sites:
157
- context["allowed_sites_info"] = "No whitelist restrictions - all sites allowed"
168
+ context["allowed_sites_info"] = (
169
+ "No whitelist restrictions - all sites allowed"
170
+ )
158
171
  else:
159
172
  context["allowed_sites_info"] = f"Restricted to: {', '.join(allowed_sites)}"
160
-
173
+
161
174
  return context
162
175
 
163
176
 
@@ -52,7 +52,7 @@ class ChatScriptRunner:
52
52
  inputs: List[str],
53
53
  *,
54
54
  console: Optional[Console] = None,
55
- provider: str = "moonshotai",
55
+ provider: str = "moonshot",
56
56
  model: str = "kimi-k1-8k",
57
57
  use_real_agent: bool = True,
58
58
  **chat_session_kwargs,
@@ -116,7 +116,9 @@ class ChatSession:
116
116
  def _select_profile_and_role(self, args, role):
117
117
  profile = getattr(args, "profile", None) if args is not None else None
118
118
  role_arg = getattr(args, "role", None) if args is not None else None
119
- python_profile = getattr(args, "developer", False) if args is not None else False
119
+ python_profile = (
120
+ getattr(args, "developer", False) if args is not None else False
121
+ )
120
122
  market_profile = getattr(args, "market", False) if args is not None else False
121
123
  profile_system_prompt = None
122
124
  no_tools_mode = False
@@ -129,26 +131,47 @@ class ChatSession:
129
131
  if market_profile and profile is None and role_arg is None:
130
132
  profile = "Market Analyst"
131
133
 
132
- if profile is None and role_arg is None and not python_profile and not market_profile:
133
- try:
134
- from janito.cli.chat_mode.session_profile_select import select_profile
135
-
136
- result = select_profile()
137
- if isinstance(result, dict):
138
- profile = result.get("profile")
139
- profile_system_prompt = result.get("profile_system_prompt")
140
- no_tools_mode = result.get("no_tools_mode", False)
141
- elif isinstance(result, str) and result.startswith("role:"):
142
- role = result[len("role:") :].strip()
143
- profile = "Developer with Python Tools"
144
- else:
145
- profile = (
146
- "Developer with Python Tools"
147
- if result == "Developer"
148
- else result
134
+ if (
135
+ profile is None
136
+ and role_arg is None
137
+ and not python_profile
138
+ and not market_profile
139
+ ):
140
+ # Skip interactive profile selection for list commands
141
+ from janito.cli.core.getters import GETTER_KEYS
142
+
143
+ # Check if any getter command is active - these don't need interactive mode
144
+ skip_profile_selection = False
145
+ if args is not None:
146
+ for key in GETTER_KEYS:
147
+ if getattr(args, key, False):
148
+ skip_profile_selection = True
149
+ break
150
+
151
+ if skip_profile_selection:
152
+ profile = "Developer with Python Tools" # Default for non-interactive commands
153
+ else:
154
+ try:
155
+ from janito.cli.chat_mode.session_profile_select import (
156
+ select_profile,
149
157
  )
150
- except ImportError:
151
- profile = "Raw Model Session (no tools, no context)"
158
+
159
+ result = select_profile()
160
+ if isinstance(result, dict):
161
+ profile = result.get("profile")
162
+ profile_system_prompt = result.get("profile_system_prompt")
163
+ no_tools_mode = result.get("no_tools_mode", False)
164
+ elif isinstance(result, str) and result.startswith("role:"):
165
+ role = result[len("role:") :].strip()
166
+ profile = "Developer with Python Tools"
167
+ else:
168
+ profile = (
169
+ "Developer with Python Tools"
170
+ if result == "Developer"
171
+ else result
172
+ )
173
+ except ImportError:
174
+ profile = "Raw Model Session (no tools, no context)"
152
175
  if role_arg is not None:
153
176
  role = role_arg
154
177
  if profile is None:
@@ -209,9 +232,7 @@ class ChatSession:
209
232
 
210
233
  self.console.print(f"[bold green]Janito Chat Mode v{__version__}[/bold green]")
211
234
  self.console.print(f"[dim]Profile: {self.profile}[/dim]")
212
- self.console.print(
213
- "[green]/help for commands /exit or Ctrl+C to quit[/green]"
214
- )
235
+
215
236
  import os
216
237
 
217
238
  cwd = os.getcwd()
@@ -226,7 +247,7 @@ class ChatSession:
226
247
 
227
248
  priv_status = get_privilege_status_message()
228
249
  self.console.print(
229
- f"[green]Working Dir:[/green] {cwd_display} | {priv_status}"
250
+ f"[green]Working Dir:[/green] [cyan]{cwd_display}[/cyan] | {priv_status}"
230
251
  )
231
252
 
232
253
  if self.multi_line_mode:
@@ -282,6 +303,8 @@ class ChatSession:
282
303
 
283
304
  def _process_prompt(self, cmd_input):
284
305
  try:
306
+ # Clear screen before processing new prompt
307
+ self.console.clear()
285
308
  import time
286
309
 
287
310
  final_event = (
@@ -324,6 +347,7 @@ class ChatSession:
324
347
  if top_base:
325
348
  candidates.append(top_base)
326
349
  from urllib.parse import urlparse
350
+
327
351
  for candidate in candidates:
328
352
  try:
329
353
  if not candidate:
@@ -353,6 +377,8 @@ class ChatSession:
353
377
  print_token_message_summary(
354
378
  self.console, self.msg_count, usage, elapsed=elapsed
355
379
  )
380
+ # Send terminal bell character to trigger TUI bell after printing token summary
381
+ print("\a", end="", flush=True)
356
382
  if final_event and hasattr(final_event, "metadata"):
357
383
  exit_reason = (
358
384
  final_event.metadata.get("exit_reason")
@@ -149,7 +149,10 @@ def select_profile():
149
149
 
150
150
  # Get the absolute path relative to the current script location
151
151
  current_dir = Path(__file__).parent
152
- template_path = current_dir / "../../agent/templates/profiles/system_prompt_template_developer.txt.j2"
152
+ template_path = (
153
+ current_dir
154
+ / "../../agent/templates/profiles/system_prompt_template_developer.txt.j2"
155
+ )
153
156
  with open(template_path, "r", encoding="utf-8") as f:
154
157
  template_content = f.read()
155
158
 
@@ -165,7 +168,10 @@ def select_profile():
165
168
 
166
169
  # Get the absolute path relative to the current script location
167
170
  current_dir = Path(__file__).parent
168
- template_path = current_dir / "../../agent/templates/profiles/system_prompt_template_market_analyst.txt.j2"
171
+ template_path = (
172
+ current_dir
173
+ / "../../agent/templates/profiles/system_prompt_template_market_analyst.txt.j2"
174
+ )
169
175
  with open(template_path, "r", encoding="utf-8") as f:
170
176
  template_content = f.read()
171
177
 
@@ -1,6 +1,7 @@
1
1
  from .base import ShellCmdHandler
2
2
  from .history_view import ViewShellHandler
3
3
  from .lang import LangShellHandler
4
+ from .provider import ProviderCmdHandler
4
5
 
5
6
  from .prompt import PromptShellHandler, RoleShellHandler
6
7
  from .multi import MultiShellHandler
@@ -43,6 +44,7 @@ COMMAND_HANDLERS = {
43
44
  "/multi": MultiShellHandler,
44
45
  "/help": HelpShellHandler,
45
46
  "/security": SecurityCommand,
47
+ "/provider": ProviderCmdHandler,
46
48
  }
47
49
 
48
50
 
@@ -6,8 +6,10 @@ class ExecuteShellHandler(ShellCmdHandler):
6
6
  help_text = "/execute on|off: Enable or disable code and command execution tools. Usage: /execute on or /execute off."
7
7
 
8
8
  def run(self):
9
- if self.shell_state and getattr(self.shell_state, 'no_tools_mode', False):
10
- shared_console.print("[yellow]No tools are available in this mode (no tools, no context).[/yellow]")
9
+ if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
10
+ shared_console.print(
11
+ "[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
12
+ )
11
13
  return
12
14
  if not self.shell_state:
13
15
  shared_console.print("[red]Shell state unavailable.[/red]")
@@ -7,7 +7,14 @@ class HelpShellHandler(ShellCmdHandler):
7
7
  help_text = "Show this help message"
8
8
 
9
9
  def run(self):
10
- from . import COMMAND_HANDLERS
10
+ # Ensure /provider command is registered before showing help
11
+ from janito.cli.chat_mode.shell.commands import COMMAND_HANDLERS
12
+
13
+ if "/provider" not in COMMAND_HANDLERS:
14
+ from janito.cli.chat_mode.shell.commands.provider import ProviderCmdHandler
15
+
16
+ COMMAND_HANDLERS["/provider"] = ProviderCmdHandler
17
+
11
18
  from ._priv_check import user_has_any_privileges
12
19
 
13
20
  shared_console.print("[bold magenta]Available commands:[/bold magenta]")
@@ -1,16 +1,20 @@
1
1
  from janito.cli.console import shared_console
2
2
  from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
3
3
 
4
+
4
5
  class PrivilegesShellHandler(ShellCmdHandler):
5
6
  help_text = "Show current tool privileges or availability."
6
7
 
7
8
  def run(self):
8
9
  # Check for no_tools_mode in shell_state
9
- if self.shell_state and getattr(self.shell_state, 'no_tools_mode', False):
10
- shared_console.print("[yellow]No tools are available in this mode (no tools, no context).[/yellow]")
10
+ if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
11
+ shared_console.print(
12
+ "[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
13
+ )
11
14
  return
12
15
  try:
13
16
  from janito.tools.permissions import get_global_allowed_permissions
17
+
14
18
  perms = get_global_allowed_permissions()
15
19
  lines = ["[bold]Current tool privileges:[/bold]"]
16
20
  lines.append(f"Read: {'✅' if perms.read else '❌'}")
@@ -0,0 +1,28 @@
1
+ from janito.cli.core.getters import get_current_provider
2
+ from janito.cli.core.setters import set_provider
3
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
4
+ from janito.cli.console import shared_console as console
5
+
6
+
7
+ class ProviderCmdHandler(ShellCmdHandler):
8
+ """Handler for the /provider command to view or change the current provider."""
9
+
10
+ help_text = "Manage the current LLM provider. Usage: /provider [provider_name]"
11
+
12
+ def run(self):
13
+ """Execute the provider command."""
14
+ if not self.after_cmd_line.strip():
15
+ # No argument provided, show current provider
16
+ current = get_current_provider()
17
+ console.print(f"[bold]Current provider:[/bold] {current}")
18
+ return
19
+
20
+ # Argument provided, attempt to change provider
21
+ new_provider = self.after_cmd_line.strip()
22
+ try:
23
+ set_provider(new_provider)
24
+ console.print(
25
+ f"[bold green]Provider changed to:[/bold green] {new_provider}"
26
+ )
27
+ except ValueError as e:
28
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
@@ -6,8 +6,10 @@ class ReadShellHandler(ShellCmdHandler):
6
6
  help_text = "/read on|off: Enable or disable read permissions for tools. Usage: /read on or /read off."
7
7
 
8
8
  def run(self):
9
- if self.shell_state and getattr(self.shell_state, 'no_tools_mode', False):
10
- shared_console.print("[yellow]No tools are available in this mode (no tools, no context).[/yellow]")
9
+ if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
10
+ shared_console.print(
11
+ "[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
12
+ )
11
13
  return
12
14
  if not self.shell_state:
13
15
  shared_console.print("[red]Shell state unavailable.[/red]")
@@ -1 +1 @@
1
- """Security management commands for chat mode."""
1
+ """Security management commands for chat mode."""
@@ -6,15 +6,17 @@ from janito.tools.url_whitelist import get_url_whitelist_manager
6
6
 
7
7
  class SecurityAllowedSitesCommand(BaseCommand):
8
8
  """Manage allowed sites for fetch_url tool."""
9
-
9
+
10
10
  def get_name(self) -> str:
11
11
  return "allowed-sites"
12
-
12
+
13
13
  def get_description(self) -> str:
14
14
  return "Manage allowed sites for the fetch_url tool"
15
-
15
+
16
16
  def get_usage(self):
17
- return self.get_description() + """
17
+ return (
18
+ self.get_description()
19
+ + """
18
20
  Usage: /security allowed-sites [command] [site]
19
21
 
20
22
  Commands:
@@ -29,6 +31,7 @@ Examples:
29
31
  /security allowed-sites remove yahoo.com
30
32
  /security allowed-sites clear
31
33
  """
34
+ )
32
35
  return """
33
36
  Usage: /security allowed-sites [command] [site]
34
37
 
@@ -44,18 +47,18 @@ Examples:
44
47
  /security allowed-sites remove yahoo.com
45
48
  /security allowed-sites clear
46
49
  """
47
-
50
+
48
51
  def run(self):
49
52
  """Execute the allowed-sites command."""
50
53
  args = self.after_cmd_line.strip().split()
51
-
54
+
52
55
  if not args:
53
56
  print(self.get_usage())
54
57
  return
55
-
58
+
56
59
  command = args[0].lower()
57
60
  whitelist_manager = get_url_whitelist_manager()
58
-
61
+
59
62
  if command == "list":
60
63
  sites = whitelist_manager.get_allowed_sites()
61
64
  if sites:
@@ -64,7 +67,7 @@ Examples:
64
67
  print(f" • {site}")
65
68
  else:
66
69
  print("No sites are whitelisted (all sites are allowed)")
67
-
70
+
68
71
  elif command == "add":
69
72
  if len(args) < 2:
70
73
  print("Error: Please specify a site to add")
@@ -74,7 +77,7 @@ Examples:
74
77
  print(f"✅ Added '{site}' to allowed sites")
75
78
  else:
76
79
  print(f"ℹ️ '{site}' is already in allowed sites")
77
-
80
+
78
81
  elif command == "remove":
79
82
  if len(args) < 2:
80
83
  print("Error: Please specify a site to remove")
@@ -84,11 +87,11 @@ Examples:
84
87
  print(f"✅ Removed '{site}' from allowed sites")
85
88
  else:
86
89
  print(f"ℹ️ '{site}' was not in allowed sites")
87
-
90
+
88
91
  elif command == "clear":
89
92
  whitelist_manager.clear_whitelist()
90
93
  print("✅ Cleared all allowed sites (all sites are now allowed)")
91
-
94
+
92
95
  else:
93
96
  print(f"Error: Unknown command '{command}'")
94
- print(self.get_usage())
97
+ print(self.get_usage())
@@ -1,18 +1,20 @@
1
1
  """Security command group for chat mode."""
2
2
 
3
3
  from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler as BaseCommand
4
- from janito.cli.chat_mode.shell.commands.security.allowed_sites import SecurityAllowedSitesCommand
4
+ from janito.cli.chat_mode.shell.commands.security.allowed_sites import (
5
+ SecurityAllowedSitesCommand,
6
+ )
5
7
 
6
8
 
7
9
  class SecurityCommand(BaseCommand):
8
10
  """Security management command group."""
9
-
11
+
10
12
  def get_name(self) -> str:
11
13
  return "security"
12
-
14
+
13
15
  def get_description(self) -> str:
14
16
  return "Security management commands"
15
-
17
+
16
18
  def get_usage(self):
17
19
  return """
18
20
  Usage: /security <subcommand> [args...]
@@ -25,21 +27,23 @@ Examples:
25
27
  /security allowed-sites add tradingview.com
26
28
  /security allowed-sites remove yahoo.com
27
29
  """
28
-
30
+
29
31
  def __init__(self, after_cmd_line=None, shell_state=None):
30
32
  super().__init__(after_cmd_line=after_cmd_line, shell_state=shell_state)
31
33
  self.subcommands = {
32
- "allowed-sites": SecurityAllowedSitesCommand(after_cmd_line=after_cmd_line, shell_state=shell_state)
34
+ "allowed-sites": SecurityAllowedSitesCommand(
35
+ after_cmd_line=after_cmd_line, shell_state=shell_state
36
+ )
33
37
  }
34
-
38
+
35
39
  def run(self):
36
40
  """Execute the security command."""
37
41
  args = self.after_cmd_line.strip().split()
38
-
42
+
39
43
  if not args:
40
44
  print(self.get_usage())
41
45
  return
42
-
46
+
43
47
  subcommand = args[0].lower()
44
48
  if subcommand in self.subcommands:
45
49
  # Pass the remaining args to the subcommand
@@ -48,4 +52,4 @@ Examples:
48
52
  self.subcommands[subcommand].run()
49
53
  else:
50
54
  print(f"Error: Unknown security subcommand '{subcommand}'")
51
- print(self.get_usage())
55
+ print(self.get_usage())
@@ -56,8 +56,10 @@ class ToolsShellHandler(ShellCmdHandler):
56
56
 
57
57
  def run(self):
58
58
  # Check for no_tools_mode in shell_state
59
- if self.shell_state and getattr(self.shell_state, 'no_tools_mode', False):
60
- shared_console.print("[yellow]No tools are available in this mode (no tools, no context).[/yellow]")
59
+ if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
60
+ shared_console.print(
61
+ "[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
62
+ )
61
63
  return
62
64
  try:
63
65
  import janito.tools # Ensure all tools are registered
@@ -6,7 +6,7 @@ from janito.cli.console import shared_console
6
6
 
7
7
  class UnrestrictedShellHandler(ShellCmdHandler):
8
8
  """Toggle unrestricted mode (equivalent to -u CLI flag)."""
9
-
9
+
10
10
  help_text = "Toggle unrestricted mode (disable path security and URL whitelist)"
11
11
 
12
12
  def run(self):
@@ -14,27 +14,32 @@ class UnrestrictedShellHandler(ShellCmdHandler):
14
14
  if not self.shell_state:
15
15
  shared_console.print("[red]Error: Shell state not available[/red]")
16
16
  return
17
-
17
+
18
18
  # Toggle unrestricted mode
19
- current_unrestricted = getattr(self.shell_state, 'unrestricted_mode', False)
19
+ current_unrestricted = getattr(self.shell_state, "unrestricted_mode", False)
20
20
  new_unrestricted = not current_unrestricted
21
-
21
+
22
22
  # Update shell state
23
23
  self.shell_state.unrestricted_mode = new_unrestricted
24
-
24
+
25
25
  # Update tools adapter
26
- if hasattr(self.shell_state, 'tools_adapter'):
27
- setattr(self.shell_state.tools_adapter, 'unrestricted_paths', new_unrestricted)
28
-
26
+ if hasattr(self.shell_state, "tools_adapter"):
27
+ setattr(
28
+ self.shell_state.tools_adapter, "unrestricted_paths", new_unrestricted
29
+ )
30
+
29
31
  # Update URL whitelist manager
30
32
  from janito.tools.url_whitelist import get_url_whitelist_manager
33
+
31
34
  whitelist_manager = get_url_whitelist_manager()
32
35
  whitelist_manager.set_unrestricted_mode(new_unrestricted)
33
-
36
+
34
37
  status = "enabled" if new_unrestricted else "disabled"
35
- warning = " (DANGEROUS - no path or URL restrictions)" if new_unrestricted else ""
36
-
38
+ warning = (
39
+ " (DANGEROUS - no path or URL restrictions)" if new_unrestricted else ""
40
+ )
41
+
37
42
  shared_console.print(
38
43
  f"[bold {'red' if new_unrestricted else 'green'}]"
39
44
  f"Unrestricted mode {status}{warning}[/bold {'red' if new_unrestricted else 'green'}]"
40
- )
45
+ )
@@ -6,8 +6,10 @@ class WriteShellHandler(ShellCmdHandler):
6
6
  help_text = "/write on|off: Enable or disable write permissions for tools. Usage: /write on or /write off."
7
7
 
8
8
  def run(self):
9
- if self.shell_state and getattr(self.shell_state, 'no_tools_mode', False):
10
- shared_console.print("[yellow]No tools are available in this mode (no tools, no context).[/yellow]")
9
+ if self.shell_state and getattr(self.shell_state, "no_tools_mode", False):
10
+ shared_console.print(
11
+ "[yellow]No tools are available in this mode (no tools, no context).[/yellow]"
12
+ )
11
13
  return
12
14
  if not self.shell_state:
13
15
  shared_console.print("[red]Shell state unavailable.[/red]")