janito 2.27.1__py3-none-any.whl → 2.29.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 (78) 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 +160 -56
  5. janito/cli/chat_mode/session_profile_select.py +8 -2
  6. janito/cli/chat_mode/shell/commands/execute.py +4 -2
  7. janito/cli/chat_mode/shell/commands/help.py +2 -0
  8. janito/cli/chat_mode/shell/commands/privileges.py +6 -2
  9. janito/cli/chat_mode/shell/commands/provider.py +7 -4
  10. janito/cli/chat_mode/shell/commands/read.py +4 -2
  11. janito/cli/chat_mode/shell/commands/security/__init__.py +1 -1
  12. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +16 -13
  13. janito/cli/chat_mode/shell/commands/security_command.py +14 -10
  14. janito/cli/chat_mode/shell/commands/tools.py +4 -2
  15. janito/cli/chat_mode/shell/commands/unrestricted.py +17 -12
  16. janito/cli/chat_mode/shell/commands/write.py +4 -2
  17. janito/cli/chat_mode/toolbar.py +4 -4
  18. janito/cli/cli_commands/enable_disable_plugin.py +48 -25
  19. janito/cli/cli_commands/list_models.py +2 -2
  20. janito/cli/cli_commands/list_plugins.py +18 -18
  21. janito/cli/cli_commands/list_profiles.py +6 -6
  22. janito/cli/cli_commands/list_providers.py +1 -1
  23. janito/cli/cli_commands/model_utils.py +45 -20
  24. janito/cli/cli_commands/ping_providers.py +10 -10
  25. janito/cli/cli_commands/set_api_key.py +5 -3
  26. janito/cli/cli_commands/show_config.py +13 -7
  27. janito/cli/cli_commands/show_system_prompt.py +13 -6
  28. janito/cli/core/getters.py +1 -0
  29. janito/cli/core/model_guesser.py +18 -15
  30. janito/cli/core/runner.py +15 -7
  31. janito/cli/core/setters.py +9 -6
  32. janito/cli/main_cli.py +15 -12
  33. janito/cli/prompt_setup.py +4 -4
  34. janito/cli/rich_terminal_reporter.py +2 -1
  35. janito/config_manager.py +2 -0
  36. janito/docs/GETTING_STARTED.md +9 -9
  37. janito/drivers/cerebras/__init__.py +1 -1
  38. janito/exceptions.py +6 -4
  39. janito/plugins/__init__.py +2 -2
  40. janito/plugins/base.py +48 -40
  41. janito/plugins/builtin.py +13 -9
  42. janito/plugins/config.py +16 -19
  43. janito/plugins/discovery.py +73 -66
  44. janito/plugins/manager.py +62 -60
  45. janito/provider_registry.py +10 -10
  46. janito/providers/__init__.py +1 -1
  47. janito/providers/alibaba/model_info.py +3 -5
  48. janito/providers/alibaba/provider.py +3 -1
  49. janito/providers/cerebras/__init__.py +1 -1
  50. janito/providers/cerebras/model_info.py +12 -27
  51. janito/providers/cerebras/provider.py +11 -9
  52. janito/providers/mistral/__init__.py +1 -1
  53. janito/providers/mistral/model_info.py +1 -1
  54. janito/providers/mistral/provider.py +1 -1
  55. janito/providers/moonshot/__init__.py +1 -0
  56. janito/providers/{moonshotai → moonshot}/model_info.py +3 -3
  57. janito/providers/{moonshotai → moonshot}/provider.py +8 -8
  58. janito/providers/openai/provider.py +3 -1
  59. janito/report_events.py +0 -1
  60. janito/tools/adapters/local/create_file.py +1 -1
  61. janito/tools/adapters/local/fetch_url.py +45 -29
  62. janito/tools/adapters/local/python_command_run.py +2 -1
  63. janito/tools/adapters/local/python_file_run.py +1 -0
  64. janito/tools/adapters/local/run_powershell_command.py +1 -1
  65. janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +14 -11
  66. janito/tools/base.py +4 -3
  67. janito/tools/loop_protection.py +24 -22
  68. janito/tools/path_utils.py +7 -7
  69. janito/tools/tool_base.py +0 -2
  70. janito/tools/tools_adapter.py +15 -5
  71. janito/tools/url_whitelist.py +27 -26
  72. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/METADATA +1 -1
  73. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/RECORD +77 -77
  74. janito/providers/moonshotai/__init__.py +0 -1
  75. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/WHEEL +0 -0
  76. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/entry_points.txt +0 -0
  77. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/licenses/LICENSE +0 -0
  78. {janito-2.27.1.dist-info → janito-2.29.0.dist-info}/top_level.txt +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,
@@ -63,6 +63,7 @@ class ChatSession:
63
63
  allowed_permissions=None,
64
64
  ):
65
65
  self.console = console
66
+ self.session_start_time = time.time()
66
67
  self.user_input_history = UserInputHistory()
67
68
  self.input_dicts = self.user_input_history.load()
68
69
  self.mem_history = InMemoryHistory()
@@ -114,38 +115,31 @@ class ChatSession:
114
115
  self.multi_line_mode = getattr(args, "multi", False) if args else False
115
116
 
116
117
  def _select_profile_and_role(self, args, role):
117
- profile = getattr(args, "profile", None) if args is not None else None
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
120
- market_profile = getattr(args, "market", False) if args is not None else False
118
+ profile, role_arg, python_profile, market_profile = self._extract_args(args)
121
119
  profile_system_prompt = None
122
120
  no_tools_mode = False
123
121
 
124
- # Handle --developer flag
125
- if python_profile and profile is None and role_arg is None:
126
- profile = "Developer with Python Tools"
127
-
128
- # Handle --market flag
129
- if market_profile and profile is None and role_arg is None:
130
- profile = "Market Analyst"
122
+ profile = self._determine_profile(
123
+ profile, role_arg, python_profile, market_profile
124
+ )
131
125
 
132
- if profile is None and role_arg is None and not python_profile and not market_profile:
133
- # Skip interactive profile selection for list commands
134
- from janito.cli.core.getters import GETTER_KEYS
135
-
136
- # Check if any getter command is active - these don't need interactive mode
126
+ if (
127
+ profile is None
128
+ and role_arg is None
129
+ and not python_profile
130
+ and not market_profile
131
+ ):
132
+ skip_profile_selection = self._should_skip_profile_selection(args)
133
+ else:
137
134
  skip_profile_selection = False
138
- if args is not None:
139
- for key in GETTER_KEYS:
140
- if getattr(args, key, False):
141
- skip_profile_selection = True
142
- break
143
-
135
+
144
136
  if skip_profile_selection:
145
137
  profile = "Developer with Python Tools" # Default for non-interactive commands
146
138
  else:
147
139
  try:
148
- from janito.cli.chat_mode.session_profile_select import select_profile
140
+ from janito.cli.chat_mode.session_profile_select import (
141
+ select_profile,
142
+ )
149
143
 
150
144
  result = select_profile()
151
145
  if isinstance(result, dict):
@@ -305,39 +299,15 @@ class ChatSession:
305
299
  )
306
300
  start_time = time.time()
307
301
 
308
- # Print rule line with model info before processing prompt
309
- model_name = (
310
- self.agent.get_model_name()
311
- if hasattr(self.agent, "get_model_name")
312
- else "Unknown"
313
- )
314
- provider_name = (
315
- self.agent.get_provider_name()
316
- if hasattr(self.agent, "get_provider_name")
317
- else "Unknown"
302
+ model_name, provider_name = self._get_model_info()
303
+ backend_hostname = self._get_backend_hostname()
304
+
305
+ self.console.print(
306
+ Rule(
307
+ f"[bold blue]Model: {model_name} ({provider_name}) | Backend: {backend_hostname}[/bold blue]"
308
+ )
318
309
  )
319
310
 
320
- backend_hostname = "Unknown"
321
- candidates = []
322
- drv = getattr(self.agent, "driver", None)
323
- if drv is not None:
324
- cfg = getattr(drv, "config", None)
325
- if cfg is not None:
326
- b = getattr(cfg, "base_url", None)
327
- if b:
328
- candidates.append(b)
329
- direct_base = getattr(drv, "base_url", None)
330
- if direct_base:
331
- candidates.append(direct_base)
332
- cfg2 = getattr(self.agent, "config", None)
333
- if cfg2 is not None:
334
- b2 = getattr(cfg2, "base_url", None)
335
- if b2:
336
- candidates.append(b2)
337
- top_base = getattr(self.agent, "base_url", None)
338
- if top_base:
339
- candidates.append(top_base)
340
- from urllib.parse import urlparse
341
311
  for candidate in candidates:
342
312
  try:
343
313
  if not candidate:
@@ -383,6 +353,102 @@ class ChatSession:
383
353
 
384
354
  self.console.print(traceback.format_exc())
385
355
 
356
+ def _extract_args(self, args):
357
+ """Extract profile and role arguments from args."""
358
+ profile = getattr(args, "profile", None) if args is not None else None
359
+ role_arg = getattr(args, "role", None) if args is not None else None
360
+ python_profile = (
361
+ getattr(args, "developer", False) if args is not None else False
362
+ )
363
+ market_profile = getattr(args, "market", False) if args is not None else False
364
+ return profile, role_arg, python_profile, market_profile
365
+
366
+ def _determine_profile(self, profile, role_arg, python_profile, market_profile):
367
+ """Determine the profile based on flags and arguments."""
368
+ if python_profile and profile is None and role_arg is None:
369
+ return "Developer with Python Tools"
370
+ if market_profile and profile is None and role_arg is None:
371
+ return "Market Analyst"
372
+ return profile
373
+
374
+ def _should_skip_profile_selection(self, args):
375
+ """Check if profile selection should be skipped for getter commands."""
376
+ from janito.cli.core.getters import GETTER_KEYS
377
+
378
+ if args is None:
379
+ return False
380
+
381
+ for key in GETTER_KEYS:
382
+ if getattr(args, key, False):
383
+ return True
384
+ return False
385
+
386
+ def _get_model_info(self):
387
+ """Get model and provider information."""
388
+ model_name = (
389
+ self.agent.get_model_name()
390
+ if hasattr(self.agent, "get_model_name")
391
+ else "Unknown"
392
+ )
393
+ provider_name = (
394
+ self.agent.get_provider_name()
395
+ if hasattr(self.agent, "get_provider_name")
396
+ else "Unknown"
397
+ )
398
+ return model_name, provider_name
399
+
400
+ def _get_backend_hostname(self):
401
+ """Extract backend hostname from agent configuration."""
402
+ candidates = self._collect_base_urls()
403
+ return self._parse_hostname_from_urls(candidates)
404
+
405
+ def _collect_base_urls(self):
406
+ """Collect all possible base URLs from agent configuration."""
407
+ candidates = []
408
+
409
+ # Collect from driver
410
+ drv = getattr(self.agent, "driver", None)
411
+ if drv is not None:
412
+ cfg = getattr(drv, "config", None)
413
+ if cfg is not None:
414
+ b = getattr(cfg, "base_url", None)
415
+ if b:
416
+ candidates.append(b)
417
+ direct_base = getattr(drv, "base_url", None)
418
+ if direct_base:
419
+ candidates.append(direct_base)
420
+
421
+ # Collect from agent config
422
+ cfg2 = getattr(self.agent, "config", None)
423
+ if cfg2 is not None:
424
+ b2 = getattr(cfg2, "base_url", None)
425
+ if b2:
426
+ candidates.append(b2)
427
+
428
+ # Collect from agent directly
429
+ top_base = getattr(self.agent, "base_url", None)
430
+ if top_base:
431
+ candidates.append(top_base)
432
+
433
+ return candidates
434
+
435
+ def _parse_hostname_from_urls(self, candidates):
436
+ """Parse hostname from a list of URL candidates."""
437
+ from urllib.parse import urlparse
438
+
439
+ for candidate in candidates:
440
+ try:
441
+ if not candidate:
442
+ continue
443
+ parsed = urlparse(str(candidate))
444
+ host = parsed.netloc or parsed.path
445
+ if host:
446
+ return host
447
+ except Exception:
448
+ return str(candidate)
449
+
450
+ return "Unknown"
451
+
386
452
  def _create_prompt_session(self):
387
453
  return PromptSession(
388
454
  style=chat_shell_style,
@@ -404,7 +470,25 @@ class ChatSession:
404
470
  else:
405
471
  try:
406
472
  cmd_input = session.prompt(HTML("<inputline>💬 </inputline>"))
407
- except (KeyboardInterrupt, EOFError):
473
+ except KeyboardInterrupt:
474
+ # Ask for confirmation on Ctrl+C
475
+ from prompt_toolkit import prompt
476
+
477
+ try:
478
+ confirm = prompt(
479
+ "Are you sure you want to exit? (y/n): ",
480
+ style=self._create_prompt_session().style,
481
+ )
482
+ if confirm.lower() == "y":
483
+ self._handle_exit()
484
+ return None
485
+ else:
486
+ return "" # Return empty string to continue
487
+ except (KeyboardInterrupt, EOFError):
488
+ # Handle second Ctrl+C or Ctrl+D as immediate exit
489
+ self._handle_exit()
490
+ return None
491
+ except EOFError:
408
492
  self._handle_exit()
409
493
  return None
410
494
  sanitized = cmd_input.strip()
@@ -418,7 +502,27 @@ class ChatSession:
418
502
  return sanitized
419
503
 
420
504
  def _handle_exit(self):
421
- self.console.print("[bold yellow]Exiting chat. Goodbye![/bold yellow]")
505
+ session_duration = time.time() - self.session_start_time
506
+
507
+ # Get total token usage from performance collector
508
+ from janito.perf_singleton import performance_collector
509
+
510
+ total_tokens = performance_collector.get_token_usage().get("total_tokens", 0)
511
+
512
+ # Format session duration
513
+ if session_duration < 60:
514
+ duration_str = f"{session_duration:.1f}s"
515
+ elif session_duration < 3600:
516
+ duration_str = f"{session_duration/60:.1f}m"
517
+ else:
518
+ duration_str = f"{session_duration/3600:.1f}h"
519
+
520
+ self.console.print(f"[bold yellow]Session completed![/bold yellow]")
521
+ self.console.print(
522
+ f"[dim]Session time: {duration_str} | Total tokens: {total_tokens:,}[/dim]"
523
+ )
524
+ self.console.print("[bold yellow]Goodbye![/bold yellow]")
525
+
422
526
  if hasattr(self, "agent") and hasattr(self.agent, "join_driver"):
423
527
  if (
424
528
  hasattr(self.agent, "input_queue")
@@ -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
 
@@ -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]")
@@ -9,8 +9,10 @@ class HelpShellHandler(ShellCmdHandler):
9
9
  def run(self):
10
10
  # Ensure /provider command is registered before showing help
11
11
  from janito.cli.chat_mode.shell.commands import COMMAND_HANDLERS
12
+
12
13
  if "/provider" not in COMMAND_HANDLERS:
13
14
  from janito.cli.chat_mode.shell.commands.provider import ProviderCmdHandler
15
+
14
16
  COMMAND_HANDLERS["/provider"] = ProviderCmdHandler
15
17
 
16
18
  from ._priv_check import user_has_any_privileges
@@ -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 '❌'}")
@@ -3,11 +3,12 @@ from janito.cli.core.setters import set_provider
3
3
  from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler
4
4
  from janito.cli.console import shared_console as console
5
5
 
6
+
6
7
  class ProviderCmdHandler(ShellCmdHandler):
7
8
  """Handler for the /provider command to view or change the current provider."""
8
-
9
+
9
10
  help_text = "Manage the current LLM provider. Usage: /provider [provider_name]"
10
-
11
+
11
12
  def run(self):
12
13
  """Execute the provider command."""
13
14
  if not self.after_cmd_line.strip():
@@ -15,11 +16,13 @@ class ProviderCmdHandler(ShellCmdHandler):
15
16
  current = get_current_provider()
16
17
  console.print(f"[bold]Current provider:[/bold] {current}")
17
18
  return
18
-
19
+
19
20
  # Argument provided, attempt to change provider
20
21
  new_provider = self.after_cmd_line.strip()
21
22
  try:
22
23
  set_provider(new_provider)
23
- console.print(f"[bold green]Provider changed to:[/bold green] {new_provider}")
24
+ console.print(
25
+ f"[bold green]Provider changed to:[/bold green] {new_provider}"
26
+ )
24
27
  except ValueError as e:
25
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())