janito 2.3.0__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 (150) hide show
  1. janito/__init__.py +6 -6
  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/autocomplete.py +21 -21
  11. janito/cli/chat_mode/shell/commands/__init__.py +13 -7
  12. janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
  13. janito/cli/chat_mode/shell/commands/clear.py +12 -12
  14. janito/cli/chat_mode/shell/commands/conversation_restart.py +30 -0
  15. janito/cli/chat_mode/shell/commands/execute.py +42 -0
  16. janito/cli/chat_mode/shell/commands/help.py +6 -3
  17. janito/cli/chat_mode/shell/commands/model.py +28 -0
  18. janito/cli/chat_mode/shell/commands/multi.py +51 -51
  19. janito/cli/chat_mode/shell/commands/read.py +37 -0
  20. janito/cli/chat_mode/shell/commands/tools.py +45 -18
  21. janito/cli/chat_mode/shell/commands/write.py +37 -0
  22. janito/cli/chat_mode/shell/commands.bak.zip +0 -0
  23. janito/cli/chat_mode/shell/input_history.py +62 -62
  24. janito/cli/chat_mode/shell/session.bak.zip +0 -0
  25. janito/cli/chat_mode/toolbar.py +44 -27
  26. janito/cli/cli_commands/list_models.py +35 -35
  27. janito/cli/cli_commands/list_providers.py +9 -9
  28. janito/cli/cli_commands/list_tools.py +86 -53
  29. janito/cli/cli_commands/model_selection.py +50 -50
  30. janito/cli/cli_commands/set_api_key.py +19 -19
  31. janito/cli/cli_commands/show_config.py +51 -51
  32. janito/cli/cli_commands/show_system_prompt.py +105 -62
  33. janito/cli/config.py +5 -6
  34. janito/cli/core/__init__.py +4 -4
  35. janito/cli/core/event_logger.py +59 -59
  36. janito/cli/core/runner.py +25 -18
  37. janito/cli/core/setters.py +10 -1
  38. janito/cli/core/unsetters.py +54 -54
  39. janito/cli/main_cli.py +28 -5
  40. janito/cli/prompt_core.py +18 -2
  41. janito/cli/prompt_setup.py +56 -0
  42. janito/cli/single_shot_mode/__init__.py +6 -6
  43. janito/cli/single_shot_mode/handler.py +14 -73
  44. janito/cli/verbose_output.py +1 -1
  45. janito/config.py +5 -5
  46. janito/config_manager.py +13 -0
  47. janito/drivers/anthropic/driver.py +113 -113
  48. janito/drivers/dashscope.bak.zip +0 -0
  49. janito/drivers/openai/README.md +20 -0
  50. janito/drivers/openai_responses.bak.zip +0 -0
  51. janito/event_bus/event.py +2 -2
  52. janito/formatting_token.py +54 -54
  53. janito/i18n/__init__.py +35 -35
  54. janito/i18n/messages.py +23 -23
  55. janito/i18n/pt.py +46 -47
  56. janito/llm/README.md +23 -0
  57. janito/llm/__init__.py +5 -5
  58. janito/llm/agent.py +507 -443
  59. janito/llm/driver.py +8 -0
  60. janito/llm/driver_config_builder.py +34 -34
  61. janito/llm/driver_input.py +12 -12
  62. janito/llm/message_parts.py +60 -60
  63. janito/llm/model.py +38 -38
  64. janito/llm/provider.py +196 -196
  65. janito/provider_registry.py +8 -6
  66. janito/providers/anthropic/model_info.py +22 -22
  67. janito/providers/anthropic/provider.py +2 -0
  68. janito/providers/azure_openai/provider.py +3 -0
  69. janito/providers/dashscope.bak.zip +0 -0
  70. janito/providers/deepseek/__init__.py +1 -1
  71. janito/providers/deepseek/model_info.py +16 -16
  72. janito/providers/deepseek/provider.py +94 -91
  73. janito/providers/google/provider.py +3 -0
  74. janito/providers/mistralai/provider.py +3 -0
  75. janito/providers/openai/provider.py +4 -0
  76. janito/providers/registry.py +26 -26
  77. janito/shell.bak.zip +0 -0
  78. janito/tools/DOCSTRING_STANDARD.txt +33 -0
  79. janito/tools/README.md +3 -0
  80. janito/tools/__init__.py +20 -6
  81. janito/tools/adapters/__init__.py +1 -1
  82. janito/tools/adapters/local/__init__.py +65 -62
  83. janito/tools/adapters/local/adapter.py +18 -35
  84. janito/tools/adapters/local/ask_user.py +101 -102
  85. janito/tools/adapters/local/copy_file.py +84 -84
  86. janito/tools/adapters/local/create_directory.py +69 -69
  87. janito/tools/adapters/local/create_file.py +82 -82
  88. janito/tools/adapters/local/delete_text_in_file.py +2 -2
  89. janito/tools/adapters/local/fetch_url.py +97 -97
  90. janito/tools/adapters/local/find_files.py +139 -138
  91. janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
  92. janito/tools/adapters/local/get_file_outline/core.py +117 -117
  93. janito/tools/adapters/local/get_file_outline/java_outline.py +40 -40
  94. janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
  95. janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
  96. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
  97. janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
  98. janito/tools/adapters/local/move_file.py +2 -2
  99. janito/tools/adapters/local/open_html_in_browser.py +2 -1
  100. janito/tools/adapters/local/open_url.py +2 -2
  101. janito/tools/adapters/local/python_code_run.py +166 -166
  102. janito/tools/adapters/local/python_command_run.py +164 -164
  103. janito/tools/adapters/local/python_file_run.py +163 -163
  104. janito/tools/adapters/local/remove_directory.py +2 -2
  105. janito/tools/adapters/local/remove_file.py +2 -2
  106. janito/tools/adapters/local/replace_text_in_file.py +2 -2
  107. janito/tools/adapters/local/run_bash_command.py +176 -176
  108. janito/tools/adapters/local/run_powershell_command.py +219 -219
  109. janito/tools/adapters/local/search_text/__init__.py +1 -1
  110. janito/tools/adapters/local/search_text/core.py +201 -201
  111. janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
  112. janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
  113. janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
  114. janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
  115. janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
  116. janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
  117. janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
  118. janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
  119. janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
  120. janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
  121. janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
  122. janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
  123. janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
  124. janito/tools/adapters/local/view_file.py +168 -167
  125. janito/tools/inspect_registry.py +17 -17
  126. janito/tools/outline_file.bak.zip +0 -0
  127. janito/tools/permissions.py +45 -0
  128. janito/tools/permissions_parse.py +12 -0
  129. janito/tools/tool_base.py +118 -105
  130. janito/tools/tool_events.py +58 -58
  131. janito/tools/tool_run_exception.py +12 -12
  132. janito/tools/tool_use_tracker.py +81 -81
  133. janito/tools/tool_utils.py +43 -45
  134. janito/tools/tools_adapter.py +25 -20
  135. janito/tools/tools_schema.py +104 -104
  136. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/METADATA +425 -388
  137. janito-2.4.0.dist-info/RECORD +195 -0
  138. janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +0 -13
  139. janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +0 -37
  140. janito/cli/chat_mode/shell/commands/edit.py +0 -25
  141. janito/cli/chat_mode/shell/commands/exec.py +0 -27
  142. janito/cli/chat_mode/shell/commands/termweb_log.py +0 -92
  143. janito/cli/termweb_starter.py +0 -122
  144. janito/termweb/app.py +0 -95
  145. janito/version.py +0 -4
  146. janito-2.3.0.dist-info/RECORD +0 -181
  147. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/WHEEL +0 -0
  148. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/entry_points.txt +0 -0
  149. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/licenses/LICENSE +0 -0
  150. {janito-2.3.0.dist-info → janito-2.4.0.dist-info}/top_level.txt +0 -0
@@ -1,51 +1,51 @@
1
- from rich.console import Console
2
- from rich.pretty import Pretty
3
-
4
- from janito.config import config
5
- from janito.cli.config import CONFIG_OPTIONS
6
-
7
-
8
- def resolve_effective_model(provider_name):
9
- # Try provider-specific model, then global model, then provider default
10
- provider_cfg = config.get_provider_config(provider_name)
11
- model = provider_cfg.get("model") if provider_cfg else None
12
- if not model:
13
- model = config.get("model")
14
- if not model:
15
- try:
16
- from janito.provider_registry import ProviderRegistry
17
-
18
- provider_class = ProviderRegistry().get_provider(provider_name)
19
- model = getattr(provider_class, "DEFAULT_MODEL", None)
20
- except Exception:
21
- model = None
22
- return model
23
-
24
-
25
- def handle_show_config(args):
26
- console = Console()
27
- provider = config.get("provider")
28
- model = config.get("model")
29
- # Show all providers with their effective model
30
- from janito.provider_registry import ProviderRegistry
31
-
32
- provider_names = []
33
- try:
34
- provider_names = ProviderRegistry()._get_provider_names()
35
- except Exception:
36
- pass
37
- console.print("[bold green]Current configuration:[/bold green]")
38
- console.print(f"[bold yellow]Current provider:[/bold yellow] {provider!r}\n")
39
- if model is not None:
40
- console.print(f"[bold yellow]Global model:[/bold yellow] {model!r}\n")
41
- if provider_names:
42
- console.print("[bold cyan]Provider specific default models:[/bold cyan]")
43
- for pname in provider_names:
44
- eff_model = resolve_effective_model(pname)
45
- prov_cfg = config.get_provider_config(pname)
46
- prov_model = prov_cfg.get("model") if prov_cfg else None
47
- extra = f" (override)" if prov_model else ""
48
- sel = "[default]" if pname == provider else ""
49
- console.print(
50
- f" [bold]{pname}[/bold]{' '+sel if sel else ''}: model = [magenta]{eff_model}[/magenta]{extra}"
51
- )
1
+ from rich.console import Console
2
+ from rich.pretty import Pretty
3
+
4
+ from janito.config import config
5
+ from janito.cli.config import CONFIG_OPTIONS
6
+
7
+
8
+ def resolve_effective_model(provider_name):
9
+ # Try provider-specific model, then global model, then provider default
10
+ provider_cfg = config.get_provider_config(provider_name)
11
+ model = provider_cfg.get("model") if provider_cfg else None
12
+ if not model:
13
+ model = config.get("model")
14
+ if not model:
15
+ try:
16
+ from janito.provider_registry import ProviderRegistry
17
+
18
+ provider_class = ProviderRegistry().get_provider(provider_name)
19
+ model = getattr(provider_class, "DEFAULT_MODEL", None)
20
+ except Exception:
21
+ model = None
22
+ return model
23
+
24
+
25
+ def handle_show_config(args):
26
+ console = Console()
27
+ provider = config.get("provider")
28
+ model = config.get("model")
29
+ # Show all providers with their effective model
30
+ from janito.provider_registry import ProviderRegistry
31
+
32
+ provider_names = []
33
+ try:
34
+ provider_names = ProviderRegistry()._get_provider_names()
35
+ except Exception:
36
+ pass
37
+ console.print("[bold green]Current configuration:[/bold green]")
38
+ console.print(f"[bold yellow]Current provider:[/bold yellow] {provider!r}\n")
39
+ if model is not None:
40
+ console.print(f"[bold yellow]Global model:[/bold yellow] {model!r}\n")
41
+ if provider_names:
42
+ console.print("[bold cyan]Provider specific default models:[/bold cyan]")
43
+ for pname in provider_names:
44
+ eff_model = resolve_effective_model(pname)
45
+ prov_cfg = config.get_provider_config(pname)
46
+ prov_model = prov_cfg.get("model") if prov_cfg else None
47
+ extra = f" (override)" if prov_model else ""
48
+ sel = "[default]" if pname == provider else ""
49
+ console.print(
50
+ f" [bold]{pname}[/bold]{' '+sel if sel else ''}: model = [magenta]{eff_model}[/magenta]{extra}"
51
+ )
@@ -1,62 +1,105 @@
1
- """
2
- CLI Command: Show the resolved system prompt for the main agent (single-shot mode)
3
- """
4
-
5
- from janito.cli.core.runner import prepare_llm_driver_config
6
- from janito.platform_discovery import PlatformDiscovery
7
- from pathlib import Path
8
- from jinja2 import Template
9
- import importlib.resources
10
-
11
-
12
- def handle_show_system_prompt(args):
13
- # Collect modifiers as in JanitoCLI
14
- from janito.cli.main_cli import MODIFIER_KEYS
15
-
16
- modifiers = {
17
- k: getattr(args, k) for k in MODIFIER_KEYS if getattr(args, k, None) is not None
18
- }
19
- provider, llm_driver_config, agent_role = prepare_llm_driver_config(args, modifiers)
20
- if provider is None or llm_driver_config is None:
21
- print("Error: Could not resolve provider or LLM driver config.")
22
- return
23
-
24
- # Prepare context for Jinja2 rendering
25
- context = {}
26
- context["role"] = agent_role or "software developer"
27
- pd = PlatformDiscovery()
28
- context["platform"] = pd.get_platform_name()
29
- context["python_version"] = pd.get_python_version()
30
- context["shell_info"] = pd.detect_shell()
31
-
32
- # Locate and load the system prompt template
33
- templates_dir = (
34
- Path(__file__).parent.parent.parent / "agent" / "templates" / "profiles"
35
- )
36
- template_path = templates_dir / "system_prompt_template_main.txt.j2"
37
- template_content = None
38
- if template_path.exists():
39
- with open(template_path, "r", encoding="utf-8") as file:
40
- template_content = file.read()
41
- else:
42
- # Try package import fallback
43
- try:
44
- with importlib.resources.files("janito.agent.templates.profiles").joinpath(
45
- "system_prompt_template_main.txt.j2"
46
- ).open("r", encoding="utf-8") as file:
47
- template_content = file.read()
48
- except (FileNotFoundError, ModuleNotFoundError, AttributeError):
49
- print(
50
- f"[janito] Could not find system_prompt_template_main.txt.j2 in {template_path} nor in janito.agent.templates.profiles package."
51
- )
52
- print("No system prompt is set or resolved for this configuration.")
53
- return
54
-
55
- template = Template(template_content)
56
- system_prompt = template.render(**context)
57
-
58
- print("\n--- System Prompt (resolved) ---\n")
59
- print(system_prompt)
60
- print("\n-------------------------------\n")
61
- if agent_role:
62
- print(f"[Role: {agent_role}]")
1
+ """
2
+ CLI Command: Show the resolved system prompt for the main agent (single-shot mode)
3
+
4
+ Supports --profile to select a profile-specific system prompt template.
5
+ """
6
+
7
+ from janito.cli.core.runner import prepare_llm_driver_config
8
+ from janito.platform_discovery import PlatformDiscovery
9
+ from pathlib import Path
10
+ from jinja2 import Template
11
+ import importlib.resources
12
+
13
+
14
+ def handle_show_system_prompt(args):
15
+ # Collect modifiers as in JanitoCLI
16
+ from janito.cli.main_cli import MODIFIER_KEYS
17
+
18
+ modifiers = {
19
+ k: getattr(args, k) for k in MODIFIER_KEYS if getattr(args, k, None) is not None
20
+ }
21
+ provider, llm_driver_config, agent_role = prepare_llm_driver_config(args, modifiers)
22
+ if provider is None or llm_driver_config is None:
23
+ print("Error: Could not resolve provider or LLM driver config.")
24
+ return
25
+
26
+ # Prepare context for Jinja2 rendering
27
+ context = {}
28
+ context["role"] = agent_role or "developer"
29
+ context["profile"] = getattr(args, "profile", None)
30
+ # Compute allowed_permissions from CLI args (as in agent setup)
31
+ from janito.tools.tool_base import ToolPermissions
32
+ read = getattr(args, "read", False)
33
+ write = getattr(args, "write", False)
34
+ execute = getattr(args, "exec", False)
35
+ allowed = ToolPermissions(read=read, write=write, execute=execute)
36
+ perm_str = ""
37
+ if allowed.read:
38
+ perm_str += "r"
39
+ if allowed.write:
40
+ perm_str += "w"
41
+ if allowed.execute:
42
+ perm_str += "x"
43
+ allowed_permissions = perm_str or None
44
+ context["allowed_permissions"] = allowed_permissions
45
+ # DEBUG: Show permissions/context before rendering
46
+ from rich import print as rich_print
47
+ debug_flag = False
48
+ import sys
49
+ try:
50
+ debug_flag = (hasattr(sys, 'argv') and ('--debug' in sys.argv or '--verbose' in sys.argv or '-v' in sys.argv))
51
+ except Exception:
52
+ pass
53
+ if debug_flag:
54
+ rich_print(f"[bold magenta][DEBUG][/bold magenta] Rendering system prompt template '[cyan]{template_filename}[/cyan]' with allowed_permissions: [yellow]{allowed_permissions}[/yellow]")
55
+ rich_print(f"[bold magenta][DEBUG][/bold magenta] Template context: [green]{context}[/green]")
56
+ if allowed_permissions and 'x' in allowed_permissions:
57
+ pd = PlatformDiscovery()
58
+ context["platform"] = pd.get_platform_name()
59
+ context["python_version"] = pd.get_python_version()
60
+ context["shell_info"] = pd.detect_shell()
61
+
62
+ # Locate and load the system prompt template
63
+ templates_dir = (
64
+ Path(__file__).parent.parent.parent / "agent" / "templates" / "profiles"
65
+ )
66
+ profile = getattr(args, "profile", None)
67
+ if profile:
68
+ template_filename = f"system_prompt_template_{profile}.txt.j2"
69
+ template_path = templates_dir / template_filename
70
+ else:
71
+ # No profile specified means the main agent has no dedicated system prompt template.
72
+ print("[janito] No profile specified. The main agent runs without a system prompt template.\n"
73
+ "Use --profile PROFILE to view a profile-specific system prompt.")
74
+ return
75
+ template_content = None
76
+ if template_path and template_path.exists():
77
+ with open(template_path, "r", encoding="utf-8") as file:
78
+ template_content = file.read()
79
+ else:
80
+ # Try package import fallback
81
+ try:
82
+ with importlib.resources.files("janito.agent.templates.profiles").joinpath(
83
+ template_filename
84
+ ).open("r", encoding="utf-8") as file:
85
+ template_content = file.read()
86
+ except (FileNotFoundError, ModuleNotFoundError, AttributeError):
87
+ if profile:
88
+ raise FileNotFoundError(
89
+ f"[janito] Could not find profile-specific template '{template_filename}' in {template_path} nor in janito.agent.templates.profiles package."
90
+ )
91
+ else:
92
+ print(
93
+ f"[janito] Could not find {template_filename} in {template_path} nor in janito.agent.templates.profiles package."
94
+ )
95
+ print("No system prompt is set or resolved for this configuration.")
96
+ return
97
+
98
+ template = Template(template_content)
99
+ system_prompt = template.render(**context)
100
+
101
+ print(f"\n--- System Prompt (resolved, profile: {getattr(args, 'profile', 'main')}) ---\n")
102
+ print(system_prompt)
103
+ print("\n-------------------------------\n")
104
+ if agent_role:
105
+ print(f"[Role: {agent_role}]")
janito/cli/config.py CHANGED
@@ -13,16 +13,15 @@ CONFIG_OPTIONS = {
13
13
  "profile": "Agent Profile name (only 'base' is supported)",
14
14
  }
15
15
 
16
- DEFAULT_TERMWEB_PORT = 8088
17
16
 
18
17
 
19
- def get_termweb_port():
20
- port = config.get("termweb_port")
18
+ def get__port():
19
+ port = config.get("_port")
21
20
  try:
22
21
  return int(port)
23
22
  except Exception:
24
- return DEFAULT_TERMWEB_PORT
23
+ pass
25
24
 
26
25
 
27
- def set_termweb_port(port):
28
- config.file_set("termweb_port", int(port))
26
+ def set__port(port):
27
+ config.file_set("_port", int(port))
@@ -1,4 +1,4 @@
1
- """
2
- Core logic, handler classes, and utilities for the main Janito CLI orchestration and sub-command processing.
3
- Contains modules for setters, getters, event logger, and runner pipelines.
4
- """
1
+ """
2
+ Core logic, handler classes, and utilities for the main Janito CLI orchestration and sub-command processing.
3
+ Contains modules for setters, getters, event logger, and runner pipelines.
4
+ """
@@ -1,59 +1,59 @@
1
- """Stub implementation of EventLogger for the CLI event logging system."""
2
-
3
-
4
- class EventLogger:
5
- def __init__(self, debug=False):
6
- self.debug = debug
7
-
8
- def subscribe(self, event_name, callback):
9
- if self.debug:
10
- pass # Add debug subscribe output if needed
11
- # No real subscription logic
12
-
13
- def submit(self, event_name, payload=None):
14
- if self.debug:
15
- pass # Add debug submit output if needed
16
- # No real submission logic
17
-
18
-
19
- def setup_event_logger(args):
20
- debug = getattr(args, "event_debug", False)
21
- event_logger = EventLogger(debug=debug)
22
- print("[EventLog] Event logger is now active (stub implementation)")
23
- return event_logger
24
-
25
-
26
- def setup_event_logger_if_needed(args):
27
- if getattr(args, "event_log", False):
28
- print("[EventLog] Setting up event logger with system bus...")
29
- event_logger = setup_event_logger(args)
30
- from janito.event_bus import event_bus
31
-
32
- def event_logger_handler(event):
33
- from janito.cli.console import shared_console
34
- from rich.pretty import Pretty
35
- from datetime import datetime
36
-
37
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
38
- shared_console.print(
39
- f"[EventLog] [dim]{timestamp}[/] [bold green]{event.__class__.__name__}[/] | [cyan]{getattr(event, 'category', '')}[/]"
40
- )
41
- shared_console.print(Pretty(event, expand_all=True))
42
- shared_console.file.flush()
43
-
44
- from janito.event_bus.event import Event
45
-
46
- event_bus.subscribe(Event, event_logger_handler)
47
-
48
-
49
- def inject_debug_event_bus_if_needed(args):
50
- if getattr(args, "event_debug", False):
51
- from janito.event_bus import event_bus
52
-
53
- orig_publish = event_bus.publish
54
-
55
- def debug_publish(event):
56
- # You can enrich here if needed
57
- return orig_publish(event)
58
-
59
- event_bus.publish = debug_publish
1
+ """Stub implementation of EventLogger for the CLI event logging system."""
2
+
3
+
4
+ class EventLogger:
5
+ def __init__(self, debug=False):
6
+ self.debug = debug
7
+
8
+ def subscribe(self, event_name, callback):
9
+ if self.debug:
10
+ pass # Add debug subscribe output if needed
11
+ # No real subscription logic
12
+
13
+ def submit(self, event_name, payload=None):
14
+ if self.debug:
15
+ pass # Add debug submit output if needed
16
+ # No real submission logic
17
+
18
+
19
+ def setup_event_logger(args):
20
+ debug = getattr(args, "event_debug", False)
21
+ event_logger = EventLogger(debug=debug)
22
+ print("[EventLog] Event logger is now active (stub implementation)")
23
+ return event_logger
24
+
25
+
26
+ def setup_event_logger_if_needed(args):
27
+ if getattr(args, "event_log", False):
28
+ print("[EventLog] Setting up event logger with system bus...")
29
+ event_logger = setup_event_logger(args)
30
+ from janito.event_bus import event_bus
31
+
32
+ def event_logger_handler(event):
33
+ from janito.cli.console import shared_console
34
+ from rich.pretty import Pretty
35
+ from datetime import datetime
36
+
37
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
38
+ shared_console.print(
39
+ f"[EventLog] [dim]{timestamp}[/] [bold green]{event.__class__.__name__}[/] | [cyan]{getattr(event, 'category', '')}[/]"
40
+ )
41
+ shared_console.print(Pretty(event, expand_all=True))
42
+ shared_console.file.flush()
43
+
44
+ from janito.event_bus.event import Event
45
+
46
+ event_bus.subscribe(Event, event_logger_handler)
47
+
48
+
49
+ def inject_debug_event_bus_if_needed(args):
50
+ if getattr(args, "event_debug", False):
51
+ from janito.event_bus import event_bus
52
+
53
+ orig_publish = event_bus.publish
54
+
55
+ def debug_publish(event):
56
+ # You can enrich here if needed
57
+ return orig_publish(event)
58
+
59
+ event_bus.publish = debug_publish
janito/cli/core/runner.py CHANGED
@@ -92,7 +92,7 @@ def prepare_llm_driver_config(args, modifiers):
92
92
  llm_driver_config = LLMDriverConfig(**driver_config_data)
93
93
  if getattr(llm_driver_config, "verbose_api", None):
94
94
  pass
95
- agent_role = modifiers.get("role", "software developer")
95
+ agent_role = modifiers.get("role", "developer")
96
96
  return provider, llm_driver_config, agent_role
97
97
 
98
98
 
@@ -105,23 +105,26 @@ def handle_runner(args, provider, llm_driver_config, agent_role, verbose_tools=F
105
105
 
106
106
  # Patch: disable execution/run tools if not enabled
107
107
  import janito.tools
108
+ from janito.tools.tool_base import ToolPermissions
109
+ read = getattr(args, "read", False)
110
+ write = getattr(args, "write", False)
111
+ execute = exec_enabled
112
+ from janito.tools.permissions import set_global_allowed_permissions
113
+ from janito.tools.tool_base import ToolPermissions
114
+ allowed_permissions = ToolPermissions(read=read, write=write, execute=execute)
115
+ set_global_allowed_permissions(allowed_permissions)
116
+ # Store the default permissions for later restoration (e.g., on /restart)
117
+ from janito.tools.permissions import set_default_allowed_permissions
118
+ set_default_allowed_permissions(allowed_permissions)
108
119
  adapter = janito.tools.get_local_tools_adapter(workdir=getattr(args, "workdir", None))
109
- if not exec_enabled:
110
- if hasattr(adapter, "disable_execution_tools"):
111
- adapter.disable_execution_tools()
112
- else:
113
- # Try to re-register execution tools if possible (print warning if not supported)
114
- if hasattr(adapter, "register_tool"):
115
- # This is a no-op if already registered, but we can attempt to re-register known execution tools
116
- try:
117
- from janito.tools.adapters.local import PythonCodeRunTool, PythonCommandRunTool, PythonFileRunTool, RunBashCommandTool, RunPowershellCommandTool
118
- for tool_cls in [PythonCodeRunTool, PythonCommandRunTool, PythonFileRunTool, RunBashCommandTool, RunPowershellCommandTool]:
119
- try:
120
- adapter.register_tool(tool_cls)
121
- except Exception:
122
- pass # Already registered or error
123
- except Exception:
124
- pass
120
+
121
+ # Print allowed permissions in verbose mode
122
+ if getattr(args, "verbose", False):
123
+ print_verbose_info(
124
+ "Allowed Tool Permissions",
125
+ f"read={read}, write={write}, execute={execute}",
126
+ style="yellow"
127
+ )
125
128
 
126
129
  provider_instance = ProviderRegistry().get_instance(provider, llm_driver_config)
127
130
  if provider_instance is None:
@@ -140,7 +143,10 @@ def handle_runner(args, provider, llm_driver_config, agent_role, verbose_tools=F
140
143
  # DEBUG: Print exec_enabled propagation at runner
141
144
 
142
145
  handler = SingleShotPromptHandler(
143
- args, provider_instance, llm_driver_config, role=agent_role, exec_enabled=exec_enabled
146
+ args, provider_instance, llm_driver_config,
147
+ role=agent_role,
148
+ exec_enabled=exec_enabled,
149
+ allowed_permissions=allowed_permissions,
144
150
  )
145
151
  handler.handle()
146
152
  else:
@@ -157,6 +163,7 @@ def handle_runner(args, provider, llm_driver_config, agent_role, verbose_tools=F
157
163
  verbose_tools=verbose_tools,
158
164
  verbose_agent=getattr(args, "verbose_agent", False),
159
165
  exec_enabled=exec_enabled,
166
+ allowed_permissions=allowed_permissions,
160
167
  )
161
168
  session.run()
162
169
 
@@ -43,8 +43,17 @@ def handle_set(args, config_mgr=None):
43
43
  return True
44
44
  if ".max_tokens" in key or ".base_url" in key:
45
45
  return _handle_set_provider_level_setting(key, value)
46
+ # Tool permissions support: janito set tool_permissions=rwx
47
+ if key == "tool_permissions":
48
+ from janito.tools.permissions_parse import parse_permissions_string
49
+ from janito.tools.permissions import set_global_allowed_permissions
50
+ perms = parse_permissions_string(value)
51
+ global_config.file_set("tool_permissions", value)
52
+ set_global_allowed_permissions(perms)
53
+ print(f"Tool permissions set to '{value}' (parsed: {perms})")
54
+ return True
46
55
  print(
47
- f"Error: Unknown config key '{key}'. Supported: provider, model, <provider>.model, max_tokens, base_url, azure_deployment_name, <provider>.max_tokens, <provider>.base_url, <provider>.<model>.max_tokens, <provider>.<model>.base_url"
56
+ f"Error: Unknown config key '{key}'. Supported: provider, model, <provider>.model, max_tokens, base_url, azure_deployment_name, <provider>.max_tokens, <provider>.base_url, <provider>.<model>.max_tokens, <provider>.<model>.base_url, tool_permissions"
48
57
  )
49
58
  return True
50
59
  except Exception as e:
@@ -1,54 +1,54 @@
1
- from janito.config import config as global_config
2
-
3
-
4
- def handle_unset(args):
5
- unset_arg = getattr(args, "unset", None)
6
- if not unset_arg:
7
- return False
8
- key = unset_arg.strip().replace("-", "_")
9
- if "." in key:
10
- # Provider or model-specific keys
11
- parts = key.split(".")
12
- if len(parts) == 2:
13
- provider, subkey = parts
14
- current_val = (
15
- global_config.file_config.get("providers", {})
16
- .get(provider, {})
17
- .get(subkey)
18
- )
19
- if current_val is not None:
20
- del global_config.file_config["providers"][provider][subkey]
21
- global_config.save()
22
- print(f"{key}={current_val} was removed.")
23
- return True
24
- elif len(parts) == 3:
25
- provider, model, subkey = parts
26
- model_conf = (
27
- global_config.file_config.get("providers", {})
28
- .get(provider, {})
29
- .get("models", {})
30
- .get(model, {})
31
- )
32
- current_val = model_conf.get(subkey)
33
- if current_val is not None:
34
- del global_config.file_config["providers"][provider]["models"][model][
35
- subkey
36
- ]
37
- global_config.save()
38
- print(f"{key}={current_val} was removed.")
39
- return True
40
- else:
41
- current_val = global_config.file_config.get(key)
42
- if current_val is not None:
43
- del global_config.file_config[key]
44
- global_config.save()
45
- print(f"{key}={current_val} was removed.")
46
- return True
47
- if "=" in unset_arg:
48
- provided_key = unset_arg.split("=")[0].strip()
49
- print(
50
- f"Error: --unset expected a key, not key=value. Did you mean: --unset {provided_key}?"
51
- )
52
- else:
53
- print(f"Error: no value set for {key} (cannot remove)")
54
- return True
1
+ from janito.config import config as global_config
2
+
3
+
4
+ def handle_unset(args):
5
+ unset_arg = getattr(args, "unset", None)
6
+ if not unset_arg:
7
+ return False
8
+ key = unset_arg.strip().replace("-", "_")
9
+ if "." in key:
10
+ # Provider or model-specific keys
11
+ parts = key.split(".")
12
+ if len(parts) == 2:
13
+ provider, subkey = parts
14
+ current_val = (
15
+ global_config.file_config.get("providers", {})
16
+ .get(provider, {})
17
+ .get(subkey)
18
+ )
19
+ if current_val is not None:
20
+ del global_config.file_config["providers"][provider][subkey]
21
+ global_config.save()
22
+ print(f"{key}={current_val} was removed.")
23
+ return True
24
+ elif len(parts) == 3:
25
+ provider, model, subkey = parts
26
+ model_conf = (
27
+ global_config.file_config.get("providers", {})
28
+ .get(provider, {})
29
+ .get("models", {})
30
+ .get(model, {})
31
+ )
32
+ current_val = model_conf.get(subkey)
33
+ if current_val is not None:
34
+ del global_config.file_config["providers"][provider]["models"][model][
35
+ subkey
36
+ ]
37
+ global_config.save()
38
+ print(f"{key}={current_val} was removed.")
39
+ return True
40
+ else:
41
+ current_val = global_config.file_config.get(key)
42
+ if current_val is not None:
43
+ del global_config.file_config[key]
44
+ global_config.save()
45
+ print(f"{key}={current_val} was removed.")
46
+ return True
47
+ if "=" in unset_arg:
48
+ provided_key = unset_arg.split("=")[0].strip()
49
+ print(
50
+ f"Error: --unset expected a key, not key=value. Did you mean: --unset {provided_key}?"
51
+ )
52
+ else:
53
+ print(f"Error: no value set for {key} (cannot remove)")
54
+ return True