janito 2.26.0__py3-none-any.whl → 2.27.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -130,25 +130,39 @@ class ChatSession:
130
130
  profile = "Market Analyst"
131
131
 
132
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
149
- )
150
- except ImportError:
151
- profile = "Raw Model Session (no tools, no context)"
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
137
+ 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
+
144
+ if skip_profile_selection:
145
+ profile = "Developer with Python Tools" # Default for non-interactive commands
146
+ else:
147
+ try:
148
+ from janito.cli.chat_mode.session_profile_select import select_profile
149
+
150
+ result = select_profile()
151
+ if isinstance(result, dict):
152
+ profile = result.get("profile")
153
+ profile_system_prompt = result.get("profile_system_prompt")
154
+ no_tools_mode = result.get("no_tools_mode", False)
155
+ elif isinstance(result, str) and result.startswith("role:"):
156
+ role = result[len("role:") :].strip()
157
+ profile = "Developer with Python Tools"
158
+ else:
159
+ profile = (
160
+ "Developer with Python Tools"
161
+ if result == "Developer"
162
+ else result
163
+ )
164
+ except ImportError:
165
+ profile = "Raw Model Session (no tools, no context)"
152
166
  if role_arg is not None:
153
167
  role = role_arg
154
168
  if profile is None:
@@ -209,9 +223,7 @@ class ChatSession:
209
223
 
210
224
  self.console.print(f"[bold green]Janito Chat Mode v{__version__}[/bold green]")
211
225
  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
- )
226
+
215
227
  import os
216
228
 
217
229
  cwd = os.getcwd()
@@ -226,7 +238,7 @@ class ChatSession:
226
238
 
227
239
  priv_status = get_privilege_status_message()
228
240
  self.console.print(
229
- f"[green]Working Dir:[/green] {cwd_display} | {priv_status}"
241
+ f"[green]Working Dir:[/green] [cyan]{cwd_display}[/cyan] | {priv_status}"
230
242
  )
231
243
 
232
244
  if self.multi_line_mode:
@@ -282,6 +294,8 @@ class ChatSession:
282
294
 
283
295
  def _process_prompt(self, cmd_input):
284
296
  try:
297
+ # Clear screen before processing new prompt
298
+ self.console.clear()
285
299
  import time
286
300
 
287
301
  final_event = (
@@ -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
 
@@ -7,7 +7,12 @@ 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
+ if "/provider" not in COMMAND_HANDLERS:
13
+ from janito.cli.chat_mode.shell.commands.provider import ProviderCmdHandler
14
+ COMMAND_HANDLERS["/provider"] = ProviderCmdHandler
15
+
11
16
  from ._priv_check import user_has_any_privileges
12
17
 
13
18
  shared_console.print("[bold magenta]Available commands:[/bold magenta]")
@@ -0,0 +1,25 @@
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
+ class ProviderCmdHandler(ShellCmdHandler):
7
+ """Handler for the /provider command to view or change the current provider."""
8
+
9
+ help_text = "Manage the current LLM provider. Usage: /provider [provider_name]"
10
+
11
+ def run(self):
12
+ """Execute the provider command."""
13
+ if not self.after_cmd_line.strip():
14
+ # No argument provided, show current provider
15
+ current = get_current_provider()
16
+ console.print(f"[bold]Current provider:[/bold] {current}")
17
+ return
18
+
19
+ # Argument provided, attempt to change provider
20
+ new_provider = self.after_cmd_line.strip()
21
+ try:
22
+ set_provider(new_provider)
23
+ console.print(f"[bold green]Provider changed to:[/bold green] {new_provider}")
24
+ except ValueError as e:
25
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from prompt_toolkit.formatted_text import HTML
2
3
  from janito.performance_collector import PerformanceCollector
3
4
  from janito.cli.config import config
@@ -17,7 +18,20 @@ def format_tokens(n, tag=None):
17
18
 
18
19
 
19
20
  def assemble_first_line(provider_name, model_name, role, agent=None):
20
- return f" Janito {VERSION} | Provider: <provider>{provider_name}</provider> | Model: <model>{model_name}</model> | Role: <role>{role}</role>"
21
+ cwd = os.getcwd()
22
+ home = os.path.expanduser("~")
23
+
24
+ # Convert to relative path if under home directory
25
+ if cwd.startswith(home):
26
+ cwd_display = "~" + cwd[len(home):]
27
+ else:
28
+ cwd_display = cwd
29
+
30
+ # Shorten long paths for display
31
+ if len(cwd_display) > 50:
32
+ cwd_display = "..." + cwd_display[-47:]
33
+
34
+ return f" Janito {VERSION} | Provider: <provider>{provider_name}</provider> | Model: <model>{model_name}</model> | Dir: <model>{cwd_display}</model>"
21
35
 
22
36
 
23
37
  def assemble_bindings_line(width, permissions=None):
@@ -0,0 +1,64 @@
1
+ """
2
+ CLI command to enable/disable plugins by modifying plugins.json configuration.
3
+ """
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Dict, Any
9
+ from janito.plugins.config import load_plugins_config, save_plugins_config, get_plugins_config_path
10
+ from janito.plugins.manager import PluginManager
11
+
12
+
13
+ def handle_enable_plugin(args: argparse.Namespace) -> None:
14
+ """Enable a plugin by adding it to plugins.json."""
15
+ config = load_plugins_config()
16
+
17
+ if 'plugins' not in config:
18
+ config['plugins'] = {}
19
+ if 'load' not in config['plugins']:
20
+ config['plugins']['load'] = {}
21
+
22
+ # Set the plugin to enabled (True)
23
+ config['plugins']['load'][args.plugin_name] = True
24
+
25
+ if save_plugins_config(config):
26
+ print(f"Plugin '{args.plugin_name}' has been enabled in {get_plugins_config_path()}")
27
+ print("Note: You may need to reload the plugin in the current session with 'plugin reload'")
28
+ else:
29
+ print(f"Error: Failed to enable plugin '{args.plugin_name}'")
30
+
31
+
32
+ def handle_disable_plugin(args: argparse.Namespace) -> None:
33
+ """Disable a plugin by removing it from plugins.json or setting it to False."""
34
+ config = load_plugins_config()
35
+
36
+ if 'plugins' in config and 'load' in config['plugins'] and args.plugin_name in config['plugins']['load']:
37
+ # Remove the plugin entry or set it to False
38
+ if args.remove:
39
+ del config['plugins']['load'][args.plugin_name]
40
+ action = "removed"
41
+ else:
42
+ config['plugins']['load'][args.plugin_name] = False
43
+ action = "disabled"
44
+
45
+ if save_plugins_config(config):
46
+ print(f"Plugin '{args.plugin_name}' has been {action} in {get_plugins_config_path()}")
47
+ print("Note: You may need to unload the plugin in the current session with 'plugin unload'")
48
+ else:
49
+ print(f"Error: Failed to {action} plugin '{args.plugin_name}'")
50
+ else:
51
+ print(f"Plugin '{args.plugin_name}' is not currently configured in plugins.json")
52
+ print("It may still be loaded in the current session, but won't be loaded on restart")
53
+
54
+
55
+ def add_enable_plugin_args(parser: argparse.ArgumentParser) -> None:
56
+ """Add enable-plugin arguments to argument parser."""
57
+ parser.add_argument('plugin_name', help='Name of the plugin to enable')
58
+
59
+
60
+ def add_disable_plugin_args(parser: argparse.ArgumentParser) -> None:
61
+ """Add disable-plugin arguments to argument parser."""
62
+ parser.add_argument('plugin_name', help='Name of the plugin to disable')
63
+ parser.add_argument('--remove', action='store_true',
64
+ help='Completely remove the plugin from config instead of setting to False')
@@ -7,6 +7,7 @@ from typing import List, Dict, Any
7
7
  from janito.plugins.discovery import list_available_plugins, discover_plugins
8
8
  import os
9
9
  from janito.plugins.manager import PluginManager
10
+ from janito.plugins.builtin import BuiltinPluginRegistry
10
11
 
11
12
 
12
13
  def handle_list_plugins(args: argparse.Namespace) -> None:
@@ -15,10 +16,23 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
15
16
  if getattr(args, 'list_plugins_available', False):
16
17
  # List available plugins
17
18
  available = list_available_plugins()
18
- if available:
19
+ builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
20
+
21
+ if available or builtin_plugins:
19
22
  print("Available plugins:")
20
- for plugin in available:
21
- print(f" - {plugin}")
23
+
24
+ # Show builtin plugins first
25
+ if builtin_plugins:
26
+ print(" Builtin plugins:")
27
+ for plugin in builtin_plugins:
28
+ print(f" - {plugin} [BUILTIN]")
29
+
30
+ # Show other available plugins
31
+ other_plugins = [p for p in available if p not in builtin_plugins]
32
+ if other_plugins:
33
+ print(" External plugins:")
34
+ for plugin in other_plugins:
35
+ print(f" - {plugin}")
22
36
  else:
23
37
  print("No plugins found in search paths")
24
38
  print("Search paths:")
@@ -65,8 +79,10 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
65
79
  print("Loaded plugins:")
66
80
  for plugin_name in loaded:
67
81
  metadata = manager.get_plugin_metadata(plugin_name)
82
+ is_builtin = BuiltinPluginRegistry.is_builtin(plugin_name)
68
83
  if metadata:
69
- print(f" - {metadata.name} v{metadata.version}")
84
+ builtin_tag = " [BUILTIN]" if is_builtin else ""
85
+ print(f" - {metadata.name} v{metadata.version}{builtin_tag}")
70
86
  print(f" {metadata.description}")
71
87
  if metadata.author:
72
88
  print(f" Author: {metadata.author}")
@@ -42,6 +42,30 @@ def handle_show_config(args):
42
42
  console.print(f"[bold yellow]Current provider:[/bold yellow] {provider!r}\n")
43
43
  if model is not None:
44
44
  console.print(f"[bold yellow]Global model:[/bold yellow] {model!r}\n")
45
+
46
+ # Show all configuration values
47
+ console.print("[bold green]Configuration values:[/bold green]")
48
+ all_config = config.all()
49
+ if all_config:
50
+ for key, value in sorted(all_config.items()):
51
+ # Hide sensitive values like API keys
52
+ if 'api_key' in key.lower() and value:
53
+ masked_value = value[:8] + '***' + value[-4:] if len(value) > 12 else '***'
54
+ console.print(f" {key}: {masked_value!r}")
55
+ elif key == 'providers' and isinstance(value, dict):
56
+ # Handle nested provider configs with API keys
57
+ masked_providers = {}
58
+ for provider_name, provider_config in value.items():
59
+ masked_config = dict(provider_config)
60
+ if 'api_key' in masked_config and masked_config['api_key']:
61
+ api_key = masked_config['api_key']
62
+ masked_config['api_key'] = api_key[:8] + '***' + api_key[-4:] if len(api_key) > 12 else '***'
63
+ masked_providers[provider_name] = masked_config
64
+ console.print(f" {key}: {masked_providers!r}")
65
+ else:
66
+ console.print(f" {key}: {value!r}")
67
+ else:
68
+ console.print(" (no configuration values set)")
45
69
 
46
70
  # Show disabled tools
47
71
  from janito.tools.disabled_tools import load_disabled_tools_from_config
@@ -13,6 +13,7 @@ from janito.cli.cli_commands.list_providers_region import handle_list_providers_
13
13
  from janito.cli.cli_commands.list_plugins import handle_list_plugins
14
14
  from functools import partial
15
15
  from janito.provider_registry import ProviderRegistry
16
+ from janito.config import config as global_config
16
17
 
17
18
  GETTER_KEYS = [
18
19
  "show_config",
@@ -30,6 +31,10 @@ GETTER_KEYS = [
30
31
  ]
31
32
 
32
33
 
34
+ def get_current_provider():
35
+ """Get the current provider from the global config."""
36
+ return global_config.get("provider", "none")
37
+
33
38
  def handle_getter(args, config_mgr=None):
34
39
  provider_instance = None
35
40
  if getattr(args, "list_models", False):
@@ -56,6 +61,7 @@ def handle_getter(args, config_mgr=None):
56
61
  "region_info": partial(handle_region_info, args),
57
62
  "list_providers_region": partial(handle_list_providers_region, args),
58
63
  "list_plugins": partial(handle_list_plugins, args),
64
+ "list_plugins_available": partial(handle_list_plugins, args),
59
65
  "list_resources": partial(handle_list_plugins, args),
60
66
  }
61
67
  for arg in GETTER_KEYS:
janito/cli/core/runner.py CHANGED
@@ -174,7 +174,21 @@ def handle_runner(
174
174
  "Active LLMDriverConfig (after provider)", llm_driver_config, style="green"
175
175
  )
176
176
  print_verbose_info("Agent role", agent_role, style="green")
177
- if mode == "single_shot":
177
+
178
+ # Skip chat mode for list commands - handle them directly
179
+ from janito.cli.core.getters import GETTER_KEYS
180
+ skip_chat_mode = False
181
+ if args is not None:
182
+ for key in GETTER_KEYS:
183
+ if getattr(args, key, False):
184
+ skip_chat_mode = True
185
+ break
186
+
187
+ if skip_chat_mode:
188
+ # Handle list commands directly without prompt
189
+ from janito.cli.core.getters import handle_getter
190
+ handle_getter(args)
191
+ elif mode == "single_shot":
178
192
  from janito.cli.single_shot_mode.handler import (
179
193
  PromptHandler as SingleShotPromptHandler,
180
194
  )
@@ -101,17 +101,29 @@ def _handle_set_base_url(value):
101
101
  return True
102
102
 
103
103
 
104
- def _handle_set_config_provider(value):
104
+ def set_provider(value):
105
+ """Set the current provider.
106
+
107
+ Args:
108
+ value (str): The provider name to set
109
+
110
+ Raises:
111
+ ValueError: If the provider is not supported
112
+ """
105
113
  try:
106
114
  supported = ProviderRegistry().get_provider(value)
107
115
  except Exception:
108
- print(
109
- f"Error: Provider '{value}' is not supported. Run '--list-providers' to see the supported list."
110
- )
111
- return True
116
+ raise ValueError(f"Provider '{value}' is not supported. Run '--list-providers' to see the supported list.")
112
117
  from janito.provider_config import set_config_provider
113
-
114
118
  set_config_provider(value)
119
+
120
+
121
+ def _handle_set_config_provider(value):
122
+ try:
123
+ set_provider(value)
124
+ except ValueError as e:
125
+ print(f"Error: {str(e)}")
126
+ return True
115
127
  print(f"Provider set to '{value}'.")
116
128
  return True
117
129
 
janito/cli/main_cli.py CHANGED
@@ -293,7 +293,8 @@ class JanitoCLI:
293
293
  self.parser = argparse.ArgumentParser(
294
294
  description="Janito CLI - A tool for running LLM-powered workflows from the command line."
295
295
  "\n\nExample usage: janito -p moonshotai -m kimi-k1-8k 'Your prompt here'\n\n"
296
- "Use -m or --model to set the model for the session."
296
+ "Use -m or --model to set the model for the session.",
297
+
297
298
  )
298
299
  self._define_args()
299
300
  self.args = self.parser.parse_args()
@@ -404,15 +405,27 @@ class JanitoCLI:
404
405
  or self.args.list_drivers
405
406
  or self.args.list_plugins
406
407
  or self.args.list_plugins_available
408
+ or self.args.list_resources
407
409
  or self.args.ping
408
410
  ):
409
411
  self._maybe_print_verbose_provider_model()
410
412
  handle_getter(self.args)
411
413
  return
414
+ # Handle /rwx prefix for enabling all permissions
415
+ if self.args.user_prompt and self.args.user_prompt[0] == '/rwx':
416
+ self.args.read = True
417
+ self.args.write = True
418
+ self.args.exec = True
419
+ # Remove the /rwx prefix from the prompt
420
+ self.args.user_prompt = self.args.user_prompt[1:]
421
+ elif self.args.user_prompt and self.args.user_prompt[0].startswith('/'):
422
+ # Skip LLM processing for other commands that start with /
423
+ return
424
+
412
425
  # If running in single shot mode and --profile is not provided, default to 'developer' profile
413
- if get_prompt_mode(self.args) == "single_shot" and not getattr(
414
- self.args, "profile", None
415
- ):
426
+ # Skip profile selection for list commands that don't need it
427
+ if (get_prompt_mode(self.args) == "single_shot" and
428
+ not getattr(self.args, "profile", None)):
416
429
  self.args.profile = "developer"
417
430
  provider = self._get_provider_or_default()
418
431
  if provider is None:
@@ -441,8 +454,6 @@ class JanitoCLI:
441
454
  agent_role,
442
455
  verbose_tools=self.args.verbose_tools,
443
456
  )
444
- elif run_mode == RunMode.GET:
445
- handle_getter(self.args)
446
457
 
447
458
  def _run_set_mode(self):
448
459
  if handle_api_key_set(self.args):
@@ -0,0 +1,84 @@
1
+ """
2
+ Builtin plugin system for janito-packaged plugins.
3
+
4
+ This module provides the infrastructure for plugins that are bundled
5
+ with janito and available by default without requiring external installation.
6
+ """
7
+
8
+ import importlib
9
+ from typing import Dict, List, Optional, Type
10
+ from janito.plugins.base import Plugin
11
+
12
+
13
+ class BuiltinPluginRegistry:
14
+ """Registry for builtin plugins that come packaged with janito."""
15
+
16
+ _plugins: Dict[str, Type[Plugin]] = {}
17
+
18
+ @classmethod
19
+ def register(cls, name: str, plugin_class: Type[Plugin]) -> None:
20
+ """Register a builtin plugin."""
21
+ cls._plugins[name] = plugin_class
22
+
23
+ @classmethod
24
+ def get_plugin_class(cls, name: str) -> Optional[Type[Plugin]]:
25
+ """Get the plugin class for a builtin plugin."""
26
+ return cls._plugins.get(name)
27
+
28
+ @classmethod
29
+ def list_builtin_plugins(cls) -> List[str]:
30
+ """List all registered builtin plugins."""
31
+ return list(cls._plugins.keys())
32
+
33
+ @classmethod
34
+ def is_builtin(cls, name: str) -> bool:
35
+ """Check if a plugin is builtin."""
36
+ return name in cls._plugins
37
+
38
+
39
+ def register_builtin_plugin(name: str):
40
+ """Decorator to register a plugin as builtin."""
41
+ def decorator(plugin_class: Type[Plugin]) -> Type[Plugin]:
42
+ BuiltinPluginRegistry.register(name, plugin_class)
43
+ return plugin_class
44
+ return decorator
45
+
46
+
47
+ def load_builtin_plugin(name: str) -> Optional[Plugin]:
48
+ """Load a builtin plugin by name."""
49
+ plugin_class = BuiltinPluginRegistry.get_plugin_class(name)
50
+ if plugin_class:
51
+ return plugin_class()
52
+ return None
53
+
54
+
55
+ # Auto-register janito-coder plugins as builtin
56
+ try:
57
+ from janito_coder.plugins import (
58
+ GitAnalyzerPlugin,
59
+ CodeNavigatorPlugin,
60
+ DependencyAnalyzerPlugin,
61
+ CodeFormatterPlugin,
62
+ TestRunnerPlugin,
63
+ LinterPlugin,
64
+ DebuggerPlugin,
65
+ PerformanceProfilerPlugin,
66
+ SecurityScannerPlugin,
67
+ DocumentationGeneratorPlugin,
68
+ )
69
+
70
+ # Register all janito-coder plugins as builtin
71
+ BuiltinPluginRegistry.register("git_analyzer", GitAnalyzerPlugin)
72
+ BuiltinPluginRegistry.register("code_navigator", CodeNavigatorPlugin)
73
+ BuiltinPluginRegistry.register("dependency_analyzer", DependencyAnalyzerPlugin)
74
+ BuiltinPluginRegistry.register("code_formatter", CodeFormatterPlugin)
75
+ BuiltinPluginRegistry.register("test_runner", TestRunnerPlugin)
76
+ BuiltinPluginRegistry.register("linter", LinterPlugin)
77
+ BuiltinPluginRegistry.register("debugger", DebuggerPlugin)
78
+ BuiltinPluginRegistry.register("performance_profiler", PerformanceProfilerPlugin)
79
+ BuiltinPluginRegistry.register("security_scanner", SecurityScannerPlugin)
80
+ BuiltinPluginRegistry.register("documentation_generator", DocumentationGeneratorPlugin)
81
+
82
+ except ImportError:
83
+ # janito-coder not available, skip registration
84
+ pass