janito 2.3.1__py3-none-any.whl → 2.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. janito/__init__.py +1 -1
  2. janito/_version.py +57 -0
  3. janito/agent/setup_agent.py +92 -18
  4. janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +44 -0
  5. janito/cli/chat_mode/bindings.py +21 -2
  6. janito/cli/chat_mode/chat_entry.py +2 -3
  7. janito/cli/chat_mode/prompt_style.py +5 -0
  8. janito/cli/chat_mode/session.py +80 -94
  9. janito/cli/chat_mode/session_profile_select.py +80 -0
  10. janito/cli/chat_mode/shell/commands/__init__.py +13 -7
  11. janito/cli/chat_mode/shell/commands/_priv_check.py +5 -0
  12. janito/cli/chat_mode/shell/commands/conversation_restart.py +30 -0
  13. janito/cli/chat_mode/shell/commands/execute.py +42 -0
  14. janito/cli/chat_mode/shell/commands/help.py +6 -3
  15. janito/cli/chat_mode/shell/commands/model.py +28 -0
  16. janito/cli/chat_mode/shell/commands/read.py +37 -0
  17. janito/cli/chat_mode/shell/commands/tools.py +45 -18
  18. janito/cli/chat_mode/shell/commands/write.py +37 -0
  19. janito/cli/chat_mode/shell/commands.bak.zip +0 -0
  20. janito/cli/chat_mode/shell/session.bak.zip +0 -0
  21. janito/cli/chat_mode/toolbar.py +44 -27
  22. janito/cli/cli_commands/list_tools.py +44 -11
  23. janito/cli/cli_commands/model_utils.py +95 -95
  24. janito/cli/cli_commands/show_system_prompt.py +57 -14
  25. janito/cli/config.py +5 -6
  26. janito/cli/core/getters.py +33 -33
  27. janito/cli/core/runner.py +25 -18
  28. janito/cli/core/setters.py +10 -1
  29. janito/cli/main_cli.py +28 -5
  30. janito/cli/prompt_core.py +18 -2
  31. janito/cli/prompt_setup.py +56 -0
  32. janito/cli/single_shot_mode/handler.py +14 -73
  33. janito/cli/verbose_output.py +1 -1
  34. janito/config_manager.py +125 -112
  35. janito/drivers/dashscope.bak.zip +0 -0
  36. janito/drivers/openai/README.md +20 -0
  37. janito/drivers/openai_responses.bak.zip +0 -0
  38. janito/event_bus/event.py +2 -2
  39. janito/i18n/pt.py +0 -1
  40. janito/llm/README.md +23 -0
  41. janito/llm/agent.py +80 -16
  42. janito/llm/auth.py +63 -63
  43. janito/llm/driver.py +8 -0
  44. janito/provider_registry.py +178 -176
  45. janito/providers/azure_openai/model_info.py +16 -16
  46. janito/providers/dashscope.bak.zip +0 -0
  47. janito/providers/registry.py +26 -26
  48. janito/shell.bak.zip +0 -0
  49. janito/tools/DOCSTRING_STANDARD.txt +33 -0
  50. janito/tools/README.md +3 -0
  51. janito/tools/__init__.py +20 -6
  52. janito/tools/adapters/local/__init__.py +65 -62
  53. janito/tools/adapters/local/adapter.py +18 -35
  54. janito/tools/adapters/local/ask_user.py +3 -4
  55. janito/tools/adapters/local/copy_file.py +2 -2
  56. janito/tools/adapters/local/create_directory.py +2 -2
  57. janito/tools/adapters/local/create_file.py +2 -2
  58. janito/tools/adapters/local/delete_text_in_file.py +2 -2
  59. janito/tools/adapters/local/fetch_url.py +2 -2
  60. janito/tools/adapters/local/find_files.py +2 -1
  61. janito/tools/adapters/local/get_file_outline/core.py +2 -2
  62. janito/tools/adapters/local/get_file_outline/search_outline.py +2 -2
  63. janito/tools/adapters/local/move_file.py +2 -2
  64. janito/tools/adapters/local/open_html_in_browser.py +2 -1
  65. janito/tools/adapters/local/open_url.py +2 -2
  66. janito/tools/adapters/local/python_code_run.py +3 -3
  67. janito/tools/adapters/local/python_command_run.py +3 -3
  68. janito/tools/adapters/local/python_file_run.py +3 -3
  69. janito/tools/adapters/local/remove_directory.py +2 -2
  70. janito/tools/adapters/local/remove_file.py +2 -2
  71. janito/tools/adapters/local/replace_text_in_file.py +2 -2
  72. janito/tools/adapters/local/run_bash_command.py +3 -3
  73. janito/tools/adapters/local/run_powershell_command.py +3 -3
  74. janito/tools/adapters/local/search_text/core.py +2 -2
  75. janito/tools/adapters/local/validate_file_syntax/core.py +2 -2
  76. janito/tools/adapters/local/view_file.py +2 -1
  77. janito/tools/outline_file.bak.zip +0 -0
  78. janito/tools/permissions.py +45 -0
  79. janito/tools/permissions_parse.py +12 -0
  80. janito/tools/tool_base.py +14 -1
  81. janito/tools/tool_utils.py +4 -6
  82. janito/tools/tools_adapter.py +25 -20
  83. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/METADATA +51 -16
  84. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/RECORD +88 -74
  85. janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +0 -13
  86. janito/agent/templates/profiles/system_prompt_template_main.txt.j2 +0 -37
  87. janito/cli/chat_mode/shell/commands/edit.py +0 -25
  88. janito/cli/chat_mode/shell/commands/exec.py +0 -27
  89. janito/cli/chat_mode/shell/commands/termweb_log.py +0 -92
  90. janito/cli/termweb_starter.py +0 -122
  91. janito/termweb/app.py +0 -95
  92. janito/version.py +0 -4
  93. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/WHEEL +0 -0
  94. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/entry_points.txt +0 -0
  95. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/licenses/LICENSE +0 -0
  96. {janito-2.3.1.dist-info → janito-2.4.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from janito.i18n import tr
3
- from janito.tools.tool_base import ToolBase
3
+ from janito.tools.tool_base import ToolBase, ToolPermissions
4
4
  from janito.report_events import ReportAction
5
5
  from janito.tools.adapters.local.adapter import register_local_tool
6
6
  from janito.tools.tool_utils import display_path
@@ -82,7 +82,7 @@ class ValidateFileSyntaxTool(ToolBase):
82
82
  - "⚠️ Warning: Syntax error: <error message>"
83
83
  - "⚠️ Warning: Unsupported file extension: <ext>"
84
84
  """
85
-
85
+ permissions = ToolPermissions(read=True)
86
86
  tool_name = "validate_file_syntax"
87
87
 
88
88
  def run(self, file_path: str) -> str:
@@ -1,4 +1,4 @@
1
- from janito.tools.tool_base import ToolBase
1
+ from janito.tools.tool_base import ToolBase, ToolPermissions
2
2
  from janito.report_events import ReportAction
3
3
  from janito.tools.adapters.local.adapter import register_local_tool
4
4
  from janito.tools.tool_utils import pluralize
@@ -25,6 +25,7 @@ class ViewFileTool(ToolBase):
25
25
  - "❗ not found"
26
26
  """
27
27
 
28
+ permissions = ToolPermissions(read=True)
28
29
  tool_name = "view_file"
29
30
 
30
31
  def run(self, file_path: str, from_line: int = None, to_line: int = None) -> str:
Binary file
@@ -0,0 +1,45 @@
1
+ from janito.tools.tool_base import ToolPermissions
2
+
3
+ class AllowedPermissionsState:
4
+ _instance = None
5
+ _permissions = ToolPermissions(read=False, write=False, execute=False)
6
+ _default_permissions = None
7
+
8
+ def __new__(cls):
9
+ if cls._instance is None:
10
+ cls._instance = super().__new__(cls)
11
+ return cls._instance
12
+
13
+ @classmethod
14
+ def get_permissions(cls):
15
+ return cls._permissions
16
+
17
+ @classmethod
18
+ def set_permissions(cls, permissions):
19
+ if not isinstance(permissions, ToolPermissions):
20
+ raise ValueError("permissions must be a ToolPermissions instance")
21
+ cls._permissions = permissions
22
+
23
+ @classmethod
24
+ def set_default_permissions(cls, permissions):
25
+ if not isinstance(permissions, ToolPermissions):
26
+ raise ValueError("permissions must be a ToolPermissions instance")
27
+ cls._default_permissions = permissions
28
+
29
+ @classmethod
30
+ def get_default_permissions(cls):
31
+ return cls._default_permissions
32
+
33
+ # Convenience functions
34
+
35
+ def get_global_allowed_permissions():
36
+ return AllowedPermissionsState.get_permissions()
37
+
38
+ def set_global_allowed_permissions(permissions):
39
+ AllowedPermissionsState.set_permissions(permissions)
40
+
41
+ def set_default_allowed_permissions(permissions):
42
+ AllowedPermissionsState.set_default_permissions(permissions)
43
+
44
+ def get_default_allowed_permissions():
45
+ return AllowedPermissionsState.get_default_permissions()
@@ -0,0 +1,12 @@
1
+ from janito.tools.tool_base import ToolPermissions
2
+
3
+ def parse_permissions_string(perm_str: str) -> ToolPermissions:
4
+ """
5
+ Parse a string like 'rwx', 'rw', 'r', etc. into a ToolPermissions object.
6
+ """
7
+ perm_str = perm_str.lower()
8
+ return ToolPermissions(
9
+ read='r' in perm_str,
10
+ write='w' in perm_str,
11
+ execute='x' in perm_str,
12
+ )
janito/tools/tool_base.py CHANGED
@@ -2,14 +2,27 @@ from janito.report_events import ReportEvent, ReportSubtype, ReportAction
2
2
  from janito.event_bus.bus import event_bus as default_event_bus
3
3
 
4
4
 
5
+ from collections import namedtuple
6
+
7
+ class ToolPermissions(namedtuple('ToolPermissions', ['read', 'write', 'execute'])):
8
+ __slots__ = ()
9
+ def __new__(cls, read=False, write=False, execute=False):
10
+ return super().__new__(cls, read, write, execute)
11
+
12
+ def __repr__(self):
13
+ return f"ToolPermissions(read={self.read}, write={self.write}, execute={self.execute})"
14
+
15
+
5
16
  class ToolBase:
6
17
  """
7
18
  Base class for all tools in the janito project.
8
19
  Extend this class to implement specific tool functionality.
9
20
  """
10
- provides_execution = False # Indicates if the tool provides execution capability (default: False)
21
+ permissions: 'ToolPermissions' = None # Required: must be set by subclasses
11
22
 
12
23
  def __init__(self, name=None, event_bus=None):
24
+ if self.permissions is None or not isinstance(self.permissions, ToolPermissions):
25
+ raise ValueError(f"Tool '{self.__class__.__name__}' must define a 'permissions' attribute of type ToolPermissions.")
13
26
  self.name = name or self.__class__.__name__
14
27
  self._event_bus = event_bus or default_event_bus
15
28
 
@@ -20,9 +20,8 @@ def display_path(path):
20
20
  Returns:
21
21
  str: Display path, as an ANSI hyperlink.
22
22
  """
23
- from janito.cli.config import get_termweb_port
24
-
25
- port = get_termweb_port()
23
+
24
+ port = 8088
26
25
  if os.path.isabs(path):
27
26
  cwd = os.path.abspath(os.getcwd())
28
27
  abs_path = os.path.abspath(path)
@@ -33,9 +32,8 @@ def display_path(path):
33
32
  disp = path
34
33
  else:
35
34
  disp = os.path.relpath(path)
36
- url = f"http://localhost:{port}/?path={urllib.parse.quote(path)}"
37
- # Use Rich markup for hyperlinks
38
- return f"[link={url}]{disp}[/link]"
35
+ # URL injection removed; just return display path
36
+ return disp
39
37
 
40
38
 
41
39
  def pluralize(word: str, count: int) -> str:
@@ -1,8 +1,7 @@
1
1
  from janito.tools.tool_base import ToolBase
2
2
  from janito.tools.tool_events import ToolCallStarted, ToolCallFinished, ToolCallError
3
3
  from janito.exceptions import ToolCallException
4
- from typing import Optional
5
-
4
+ from janito.tools.tool_base import ToolPermissions
6
5
 
7
6
  class ToolsAdapterBase:
8
7
  """
@@ -13,11 +12,10 @@ class ToolsAdapterBase:
13
12
  """
14
13
 
15
14
  def __init__(
16
- self, tools=None, event_bus=None, enabled_tools: Optional[list] = None
15
+ self, tools=None, event_bus=None
17
16
  ):
18
17
  self._tools = tools or []
19
18
  self._event_bus = event_bus # event bus can be set on all adapters
20
- self._enabled_tools = set(enabled_tools) if enabled_tools is not None else None
21
19
  self.verbose_tools = False
22
20
 
23
21
  def set_verbose_tools(self, value: bool):
@@ -31,11 +29,28 @@ class ToolsAdapterBase:
31
29
  def event_bus(self, bus):
32
30
  self._event_bus = bus
33
31
 
32
+ def is_tool_allowed(self, tool):
33
+ """Check if a tool is allowed based on current global AllowedPermissionsState."""
34
+ from janito.tools.permissions import get_global_allowed_permissions
35
+ allowed_permissions = get_global_allowed_permissions()
36
+ perms = tool.permissions # permissions are mandatory and type-checked
37
+ # If all permissions are False, block all tools
38
+ if not (allowed_permissions.read or allowed_permissions.write or allowed_permissions.execute):
39
+ return False
40
+ for perm in ['read', 'write', 'execute']:
41
+ if getattr(perms, perm) and not getattr(allowed_permissions, perm):
42
+ return False
43
+ return True
44
+
34
45
  def get_tools(self):
35
- """Return the list of enabled tools managed by this provider."""
36
- if self._enabled_tools is None:
37
- return self._tools
38
- return [tool for tool in self._tools if getattr(tool, 'tool_name', None) in self._enabled_tools]
46
+ """Return the list of enabled tools managed by this provider, filtered by allowed permissions."""
47
+ tools = [tool for tool in self._tools if self.is_tool_allowed(tool)]
48
+ return tools
49
+
50
+ def set_allowed_permissions(self, allowed_permissions):
51
+ """Set the allowed permissions at runtime. This now updates the global AllowedPermissionsState only."""
52
+ from janito.tools.permissions import set_global_allowed_permissions
53
+ set_global_allowed_permissions(allowed_permissions)
39
54
 
40
55
  def add_tool(self, tool):
41
56
  self._tools.append(tool)
@@ -231,18 +246,8 @@ class ToolsAdapterBase:
231
246
  )
232
247
 
233
248
  def _check_tool_permissions(self, tool_name, request_id, arguments):
234
- if self._enabled_tools is not None and tool_name not in self._enabled_tools:
235
- error_msg = f"Tool '{tool_name}' is not enabled in this adapter."
236
- if self._event_bus:
237
- self._event_bus.publish(
238
- ToolCallError(
239
- tool_name=tool_name,
240
- request_id=request_id,
241
- error=error_msg,
242
- arguments=arguments,
243
- )
244
- )
245
- raise ToolCallException(tool_name, error_msg, arguments=arguments)
249
+ # No enabled_tools check anymore; permission checks are handled by is_tool_allowed
250
+ pass
246
251
 
247
252
  def _ensure_tool_exists(self, tool, tool_name, request_id, arguments):
248
253
  if tool is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 2.3.1
3
+ Version: 2.4.0
4
4
  Summary: A new Python package called janito.
5
5
  Author-email: João Pinto <lamego.pinto@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/janito-dev/janito
@@ -17,7 +17,7 @@ Requires-Dist: prompt_toolkit>=3.0.51
17
17
  Requires-Dist: lxml>=5.4.0
18
18
  Requires-Dist: requests>=2.32.4
19
19
  Requires-Dist: bs4>=0.0.2
20
- Requires-Dist: openai
20
+ Requires-Dist: questionary>=2.0.1
21
21
  Provides-Extra: dev
22
22
  Requires-Dist: pytest; extra == "dev"
23
23
  Requires-Dist: pre-commit; extra == "dev"
@@ -25,7 +25,8 @@ Requires-Dist: ruff==0.11.9; extra == "dev"
25
25
  Requires-Dist: detect-secrets==1.4.0; extra == "dev"
26
26
  Requires-Dist: codespell==2.4.1; extra == "dev"
27
27
  Requires-Dist: black; extra == "dev"
28
- Requires-Dist: openai; extra == "dev"
28
+ Requires-Dist: questionary>=2.0.1; extra == "dev"
29
+ Requires-Dist: setuptools_scm>=8.0; extra == "dev"
29
30
  Dynamic: license-file
30
31
 
31
32
  # Janito
@@ -106,6 +107,21 @@ janito -set provider=PROVIDER
106
107
 
107
108
  After installation, use the `janito` command in your terminal.
108
109
 
110
+ Janito supports both general-purpose and specialized assistance through the use of **profiles**. Profiles allow you to select a specific system prompt template and behavior for the agent, enabling workflows tailored to different roles or tasks (e.g., developer, writer, data analyst), or to use Janito as a generic AI assistant.
111
+
112
+ ### Profiles: General-Purpose and Specialized Assistance
113
+
114
+ - By default, Janito acts as a general-purpose assistant.
115
+ - You can select a specialized profile using the `--profile` option:
116
+ ```bash
117
+ janito --profile developer "Refactor this code for better readability."
118
+ janito --profile writer "Draft a blog post about AI in healthcare."
119
+ ```
120
+ - Profiles change the system prompt and agent behavior to suit the selected role or workflow.
121
+ - To see available profiles or customize them, refer to the documentation or the `agent/templates/profiles/` directory.
122
+
123
+ > **Tip:** Use `--profile` for targeted workflows, or omit it for a general-purpose assistant.
124
+
109
125
  Janito has configuration options, like `--set api-key API_KEY` and `--set provider=PROVIDER`, that create durable configurations and single shoot options, like `-p PROVIDER` and `-m MODEL`, that are active for the single run of the command or session.
110
126
 
111
127
  ### Basic Commands
@@ -153,25 +169,38 @@ Janito has configuration options, like `--set api-key API_KEY` and `--set provid
153
169
 
154
170
  - **Enable Inline Web File Viewer for Clickable Links**
155
171
 
156
- By default, Janito can open referenced files in a browser-based viewer when you click on file links in supported terminals. To enable this feature for your session, use the `-w` or `--web` flag:
172
+ By default, Janito can open referenced files in a browser-based viewer when you click on file links in supported terminals. To enable this feature for your session, use the `--web` flag:
157
173
 
158
174
  ```bash
159
- janito -w
175
+ janito --web
160
176
  ```
161
177
 
162
- This starts the lightweight web file viewer (termweb) in the background, allowing you to inspect files referenced in responses directly in your browser. Combine with interactive mode or prompts as needed.
178
+ This starts the lightweight web file viewer () in the background, allowing you to inspect files referenced in responses directly in your browser. Combine with interactive mode or prompts as needed.
163
179
 
164
180
  > **Tip:** Use with the interactive shell for the best experience with clickable file links.
165
181
 
166
182
 
167
183
  - **Enable Execution Tools (Code/Shell Execution)**
168
184
 
169
- By default, tools that can execute code or shell commands are **disabled** for safety. To enable these tools (such as code execution, shell commands, etc.), use the `--exec` or `-x` flag:
170
-
171
- ```bash
172
- janito -x "Run this code: print('Hello, world!')"
173
- ```
174
- > **Warning:** Enabling execution tools allows running arbitrary code or shell commands. Only use `--exec` if you trust your prompt and environment.
185
+ By default, **all tool privileges (read, write, execute)** are disabled for safety. This means Janito starts with no permissions to run tools that read, write, or execute code/shell commands unless you explicitly enable them.
186
+
187
+ - To enable **read** tools (e.g., file reading, searching): add `-r` or `--read`
188
+ - To enable **write** tools (e.g., file editing): add `-w` or `--write`
189
+ - To enable **execution** tools (code/shell execution): add `-x` or `--exec`
190
+
191
+ You can combine these flags as needed. For example, to enable both read and write tools:
192
+
193
+ ```bash
194
+ janito -r -w "Read and update this file: ..."
195
+ ```
196
+
197
+ To enable all permissions (read, write, execute):
198
+
199
+ ```bash
200
+ janito -r -w -x "Run this code: print('Hello, world!')"
201
+ ```
202
+
203
+ > **Warning:** Enabling execution tools allows running arbitrary code or shell commands. Only use `--exec` if you trust your prompt and environment.
175
204
 
176
205
  - **Set a System Prompt**
177
206
  ```bash
@@ -203,7 +232,7 @@ Janito has configuration options, like `--set api-key API_KEY` and `--set provid
203
232
  ### Core CLI Options
204
233
  | Option | Description |
205
234
  |------------------------|-----------------------------------------------------------------------------|
206
- | `-w`, `--web` | Enable the builtin lightweight web file viewer for clickable file links (termweb). |
235
+ | `--web` | Enable the builtin lightweight web file viewer for clickable file links (). |
207
236
  | `--version` | Show program version |
208
237
  | `--list-tools` | List all registered tools |
209
238
  | `--list-providers` | List all supported LLM providers |
@@ -212,7 +241,7 @@ Janito has configuration options, like `--set api-key API_KEY` and `--set provid
212
241
  | `--set provider=name` | Set the current LLM provider (e.g., `janito --set provider=openai`) |
213
242
  | `--set PROVIDER.model=MODEL` or `--set model=MODEL` | Set the default model for the current/selected provider, or globally. (e.g., `janito --set openai.model=gpt-3.5-turbo`) |
214
243
  | `-s`, `--system` | Set a system prompt (e.g., `janito -s path/to/system_prompt.txt "Your prompt here"`) |
215
- | `-r`, `--role` | Set the role for the agent (overrides config) (e.g., `janito -r "assistant" "Your prompt here"`) |
244
+
216
245
  | `-p`, `--provider` | Select LLM provider (overrides config) (e.g., `janito -p openai "Your prompt here"`) |
217
246
  | `-m`, `--model` | Select model for the provider (e.g., `janito -m gpt-3.5-turbo "Your prompt here"`) |
218
247
  | `-v`, `--verbose` | Print extra information before answering |
@@ -246,8 +275,8 @@ Once inside the interactive chat mode, you can use these slash commands:
246
275
  | Command | Description |
247
276
  |----------------------|----------------------------------------------|
248
277
  | `/tools` | List available tools |
249
- | `/termweb-status` | Show status of termweb server |
250
- | `/termweb-logs` | Show last lines of termweb logs |
278
+ | `/-status` | Show status of server |
279
+ | `/-logs` | Show last lines of logs |
251
280
  | `/livelogs` | Show live updates from server log file |
252
281
  | `/edit <filename>` | Open file in browser-based editor |
253
282
 
@@ -274,6 +303,12 @@ See [docs/supported-providers-models.md](docs/supported-providers-models.md) for
274
303
 
275
304
  Contributions are welcome! Please see the `CONTRIBUTING.md` (if available) or open an issue to get started.
276
305
 
306
+ ---
307
+
308
+ ## Developer Documentation
309
+
310
+ For developer-specific setup, versioning, and contribution guidelines, see [README-dev.md](./README-dev.md).
311
+
277
312
  ## License
278
313
 
279
314
  This project is licensed under the terms of the MIT license.