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,167 +1,168 @@
1
- from janito.tools.tool_base import ToolBase
2
- from janito.report_events import ReportAction
3
- from janito.tools.adapters.local.adapter import register_local_tool
4
- from janito.tools.tool_utils import pluralize
5
- from janito.i18n import tr
6
-
7
-
8
- @register_local_tool
9
- class ViewFileTool(ToolBase):
10
- """
11
- Read lines from a file. You can specify a line range, or read the entire file by simply omitting the from_line and to_line parameters.
12
-
13
- Args:
14
- file_path (str): Path to the file to read lines from.
15
- from_line (int, optional): Starting line number (1-based). Omit to start from the first line.
16
- to_line (int, optional): Ending line number (1-based). Omit to read to the end of the file.
17
-
18
- To read the full file, just provide file_path and leave from_line and to_line unset.
19
-
20
- Returns:
21
- str: File content with a header indicating the file name and line range. Example:
22
- - "---\nFile: /path/to/file.py | Lines: 1-10 (of 100)\n---\n<lines...>"
23
- - "---\nFile: /path/to/file.py | All lines (total: 100 (all))\n---\n<all lines...>"
24
- - "Error reading file: <error message>"
25
- - "❗ not found"
26
- """
27
-
28
- tool_name = "view_file"
29
-
30
- def run(self, file_path: str, from_line: int = None, to_line: int = None) -> str:
31
- import os
32
- from janito.tools.tool_utils import display_path
33
-
34
- disp_path = display_path(file_path)
35
- self.report_action(
36
- tr("📖 View '{disp_path}'", disp_path=disp_path),
37
- ReportAction.READ,
38
- )
39
- try:
40
- if os.path.isdir(file_path):
41
- return self._list_directory(file_path, disp_path)
42
- lines = self._read_file_lines(file_path)
43
- selected, selected_len, total_lines = self._select_lines(
44
- lines, from_line, to_line
45
- )
46
- self._report_success(selected_len, from_line, to_line, total_lines)
47
- header = self._format_header(
48
- disp_path, from_line, to_line, selected_len, total_lines
49
- )
50
- return header + "".join(selected)
51
- except FileNotFoundError as e:
52
- self.report_warning(tr("❗ not found"))
53
- return f"Error reading file: {e}"
54
- except Exception as e:
55
- self.report_error(tr(" Error: {error}", error=e))
56
- return tr("Error reading file: {error}", error=e)
57
-
58
- def _list_directory(self, file_path, disp_path):
59
- import os
60
-
61
- try:
62
- entries = os.listdir(file_path)
63
- entries.sort()
64
- # Suffix subdirectories with '/'
65
- formatted_entries = []
66
- for entry in entries:
67
- full_path = os.path.join(file_path, entry)
68
- if os.path.isdir(full_path):
69
- formatted_entries.append(entry + "/")
70
- else:
71
- formatted_entries.append(entry)
72
- header = (
73
- f"--- view_file: {disp_path} [directory, {len(entries)} entries] ---\n"
74
- )
75
- listing = "\n".join(formatted_entries)
76
- self.report_success(tr("📁 Directory ({count} items)", count=len(entries)))
77
- return header + listing + "\n"
78
- except Exception as e:
79
- self.report_error(tr(" Error listing directory: {error}", error=e))
80
- return tr("Error listing directory: {error}", error=e)
81
-
82
- def _read_file_lines(self, file_path):
83
- """Read all lines from the file."""
84
- with open(file_path, "r", encoding="utf-8", errors="replace") as f:
85
- return f.readlines()
86
-
87
- def _select_lines(self, lines, from_line, to_line):
88
- """Select the requested lines and return them with their count and total lines."""
89
- selected = lines[
90
- (from_line - 1 if from_line else 0) : (to_line if to_line else None)
91
- ]
92
- selected_len = len(selected)
93
- total_lines = len(lines)
94
- return selected, selected_len, total_lines
95
-
96
- def _report_success(self, selected_len, from_line, to_line, total_lines):
97
- """Report the success message after reading lines."""
98
- if from_line and to_line:
99
- requested = to_line - from_line + 1
100
- at_end = to_line >= total_lines or selected_len < requested
101
- if at_end:
102
- self.report_success(
103
- tr(
104
- " ✅ {selected_len} {line_word} (end)",
105
- selected_len=selected_len,
106
- line_word=pluralize("line", selected_len),
107
- )
108
- )
109
- elif to_line < total_lines:
110
- self.report_success(
111
- tr(
112
- " ✅ {selected_len} {line_word} ({remaining} to end)",
113
- selected_len=selected_len,
114
- line_word=pluralize("line", selected_len),
115
- remaining=total_lines - to_line,
116
- )
117
- )
118
- else:
119
- self.report_success(
120
- tr(
121
- " ✅ {selected_len} {line_word} (all)",
122
- selected_len=selected_len,
123
- line_word=pluralize("line", selected_len),
124
- )
125
- )
126
-
127
- def _format_header(self, disp_path, from_line, to_line, selected_len, total_lines):
128
- """Format the header for the output."""
129
- if from_line and to_line:
130
- requested = to_line - from_line + 1
131
- at_end = selected_len < requested or to_line >= total_lines
132
- if at_end:
133
- return tr(
134
- "---\n{disp_path} {from_line}-{to_line} (end)\n---\n",
135
- disp_path=disp_path,
136
- from_line=from_line,
137
- to_line=to_line,
138
- )
139
- else:
140
- return tr(
141
- "---\n{disp_path} {from_line}-{to_line} (of {total_lines})\n---\n",
142
- disp_path=disp_path,
143
- from_line=from_line,
144
- to_line=to_line,
145
- total_lines=total_lines,
146
- )
147
- elif from_line:
148
- return tr(
149
- "---\n{disp_path} {from_line}-END (of {total_lines})\n---\n",
150
- disp_path=disp_path,
151
- from_line=from_line,
152
- total_lines=total_lines,
153
- )
154
- else:
155
- return tr(
156
- "---\n{disp_path} All lines (total: {total_lines} (all))\n---\n",
157
- disp_path=disp_path,
158
- total_lines=total_lines,
159
- )
160
-
161
- def _handle_read_error(self, e):
162
- """Handle file read errors and report appropriately."""
163
- if isinstance(e, FileNotFoundError):
164
- self.report_error(tr("❗ not found"), ReportAction.READ)
165
- return tr("❗ not found")
166
- self.report_error(tr(" Error: {error}", error=e), ReportAction.READ)
167
- return tr("Error reading file: {error}", error=e)
1
+ from janito.tools.tool_base import ToolBase, ToolPermissions
2
+ from janito.report_events import ReportAction
3
+ from janito.tools.adapters.local.adapter import register_local_tool
4
+ from janito.tools.tool_utils import pluralize
5
+ from janito.i18n import tr
6
+
7
+
8
+ @register_local_tool
9
+ class ViewFileTool(ToolBase):
10
+ """
11
+ Read lines from a file. You can specify a line range, or read the entire file by simply omitting the from_line and to_line parameters.
12
+
13
+ Args:
14
+ file_path (str): Path to the file to read lines from.
15
+ from_line (int, optional): Starting line number (1-based). Omit to start from the first line.
16
+ to_line (int, optional): Ending line number (1-based). Omit to read to the end of the file.
17
+
18
+ To read the full file, just provide file_path and leave from_line and to_line unset.
19
+
20
+ Returns:
21
+ str: File content with a header indicating the file name and line range. Example:
22
+ - "---\nFile: /path/to/file.py | Lines: 1-10 (of 100)\n---\n<lines...>"
23
+ - "---\nFile: /path/to/file.py | All lines (total: 100 (all))\n---\n<all lines...>"
24
+ - "Error reading file: <error message>"
25
+ - "❗ not found"
26
+ """
27
+
28
+ permissions = ToolPermissions(read=True)
29
+ tool_name = "view_file"
30
+
31
+ def run(self, file_path: str, from_line: int = None, to_line: int = None) -> str:
32
+ import os
33
+ from janito.tools.tool_utils import display_path
34
+
35
+ disp_path = display_path(file_path)
36
+ self.report_action(
37
+ tr("📖 View '{disp_path}'", disp_path=disp_path),
38
+ ReportAction.READ,
39
+ )
40
+ try:
41
+ if os.path.isdir(file_path):
42
+ return self._list_directory(file_path, disp_path)
43
+ lines = self._read_file_lines(file_path)
44
+ selected, selected_len, total_lines = self._select_lines(
45
+ lines, from_line, to_line
46
+ )
47
+ self._report_success(selected_len, from_line, to_line, total_lines)
48
+ header = self._format_header(
49
+ disp_path, from_line, to_line, selected_len, total_lines
50
+ )
51
+ return header + "".join(selected)
52
+ except FileNotFoundError as e:
53
+ self.report_warning(tr(" not found"))
54
+ return f"Error reading file: {e}"
55
+ except Exception as e:
56
+ self.report_error(tr(" Error: {error}", error=e))
57
+ return tr("Error reading file: {error}", error=e)
58
+
59
+ def _list_directory(self, file_path, disp_path):
60
+ import os
61
+
62
+ try:
63
+ entries = os.listdir(file_path)
64
+ entries.sort()
65
+ # Suffix subdirectories with '/'
66
+ formatted_entries = []
67
+ for entry in entries:
68
+ full_path = os.path.join(file_path, entry)
69
+ if os.path.isdir(full_path):
70
+ formatted_entries.append(entry + "/")
71
+ else:
72
+ formatted_entries.append(entry)
73
+ header = (
74
+ f"--- view_file: {disp_path} [directory, {len(entries)} entries] ---\n"
75
+ )
76
+ listing = "\n".join(formatted_entries)
77
+ self.report_success(tr("📁 Directory ({count} items)", count=len(entries)))
78
+ return header + listing + "\n"
79
+ except Exception as e:
80
+ self.report_error(tr("Error listing directory: {error}", error=e))
81
+ return tr("Error listing directory: {error}", error=e)
82
+
83
+ def _read_file_lines(self, file_path):
84
+ """Read all lines from the file."""
85
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
86
+ return f.readlines()
87
+
88
+ def _select_lines(self, lines, from_line, to_line):
89
+ """Select the requested lines and return them with their count and total lines."""
90
+ selected = lines[
91
+ (from_line - 1 if from_line else 0) : (to_line if to_line else None)
92
+ ]
93
+ selected_len = len(selected)
94
+ total_lines = len(lines)
95
+ return selected, selected_len, total_lines
96
+
97
+ def _report_success(self, selected_len, from_line, to_line, total_lines):
98
+ """Report the success message after reading lines."""
99
+ if from_line and to_line:
100
+ requested = to_line - from_line + 1
101
+ at_end = to_line >= total_lines or selected_len < requested
102
+ if at_end:
103
+ self.report_success(
104
+ tr(
105
+ " ✅ {selected_len} {line_word} (end)",
106
+ selected_len=selected_len,
107
+ line_word=pluralize("line", selected_len),
108
+ )
109
+ )
110
+ elif to_line < total_lines:
111
+ self.report_success(
112
+ tr(
113
+ " ✅ {selected_len} {line_word} ({remaining} to end)",
114
+ selected_len=selected_len,
115
+ line_word=pluralize("line", selected_len),
116
+ remaining=total_lines - to_line,
117
+ )
118
+ )
119
+ else:
120
+ self.report_success(
121
+ tr(
122
+ " ✅ {selected_len} {line_word} (all)",
123
+ selected_len=selected_len,
124
+ line_word=pluralize("line", selected_len),
125
+ )
126
+ )
127
+
128
+ def _format_header(self, disp_path, from_line, to_line, selected_len, total_lines):
129
+ """Format the header for the output."""
130
+ if from_line and to_line:
131
+ requested = to_line - from_line + 1
132
+ at_end = selected_len < requested or to_line >= total_lines
133
+ if at_end:
134
+ return tr(
135
+ "---\n{disp_path} {from_line}-{to_line} (end)\n---\n",
136
+ disp_path=disp_path,
137
+ from_line=from_line,
138
+ to_line=to_line,
139
+ )
140
+ else:
141
+ return tr(
142
+ "---\n{disp_path} {from_line}-{to_line} (of {total_lines})\n---\n",
143
+ disp_path=disp_path,
144
+ from_line=from_line,
145
+ to_line=to_line,
146
+ total_lines=total_lines,
147
+ )
148
+ elif from_line:
149
+ return tr(
150
+ "---\n{disp_path} {from_line}-END (of {total_lines})\n---\n",
151
+ disp_path=disp_path,
152
+ from_line=from_line,
153
+ total_lines=total_lines,
154
+ )
155
+ else:
156
+ return tr(
157
+ "---\n{disp_path} All lines (total: {total_lines} (all))\n---\n",
158
+ disp_path=disp_path,
159
+ total_lines=total_lines,
160
+ )
161
+
162
+ def _handle_read_error(self, e):
163
+ """Handle file read errors and report appropriately."""
164
+ if isinstance(e, FileNotFoundError):
165
+ self.report_error(tr("❗ not found"), ReportAction.READ)
166
+ return tr(" not found")
167
+ self.report_error(tr(" Error: {error}", error=e), ReportAction.READ)
168
+ return tr("Error reading file: {error}", error=e)
@@ -1,17 +1,17 @@
1
- def check_tools_registry():
2
- # Import and use the singleton tools adapter instance
3
- from janito.tools.adapters.local import local_tools_adapter
4
-
5
- print("Available tool names:", local_tools_adapter.list_tools())
6
- print(
7
- "Available tool classes:",
8
- [cls.__name__ for cls in local_tools_adapter.get_tool_classes()],
9
- )
10
- print(
11
- "Available tool instances:",
12
- [tool.name for tool in local_tools_adapter.get_tools()],
13
- )
14
-
15
-
16
- if __name__ == "__main__":
17
- check_tools_registry()
1
+ def check_tools_registry():
2
+ # Import and use the singleton tools adapter instance
3
+ from janito.tools.adapters.local import local_tools_adapter
4
+
5
+ print("Available tool names:", local_tools_adapter.list_tools())
6
+ print(
7
+ "Available tool classes:",
8
+ [cls.__name__ for cls in local_tools_adapter.get_tool_classes()],
9
+ )
10
+ print(
11
+ "Available tool instances:",
12
+ [tool.name for tool in local_tools_adapter.get_tools()],
13
+ )
14
+
15
+
16
+ if __name__ == "__main__":
17
+ check_tools_registry()
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
+ )