janito 3.6.1__py3-none-any.whl → 3.8.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 (172) hide show
  1. janito/README.md +4 -7
  2. janito/cli/chat_mode/bindings.py +0 -50
  3. janito/cli/chat_mode/session.py +1 -12
  4. janito/cli/chat_mode/shell/commands/multi.py +0 -5
  5. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +33 -47
  6. janito/cli/cli_commands/list_plugins.py +43 -52
  7. janito/cli/cli_commands/list_tools.py +1 -6
  8. janito/cli/core/getters.py +0 -3
  9. janito/cli/core/model_guesser.py +24 -40
  10. janito/cli/main_cli.py +13 -10
  11. janito/cli/prompt_core.py +9 -47
  12. janito/cli/rich_terminal_reporter.py +4 -4
  13. janito/docs/GETTING_STARTED.md +9 -8
  14. janito/drivers/openai/driver.py +0 -1
  15. janito/drivers/zai/driver.py +0 -1
  16. janito/i18n/it.py +46 -46
  17. janito/llm/agent.py +16 -32
  18. janito/llm/auth_utils.py +5 -14
  19. janito/llm/driver.py +0 -8
  20. janito/plugins/__init__.py +12 -31
  21. janito/plugins/auto_loader.py +11 -12
  22. janito/plugins/auto_loader_fixed.py +11 -12
  23. janito/{plugin_system → plugins}/base.py +2 -5
  24. janito/plugins/builtin.py +1 -15
  25. janito/plugins/core_adapter.py +11 -89
  26. janito/plugins/core_loader.py +120 -0
  27. janito/plugins/core_loader_fixed.py +125 -0
  28. janito/plugins/discovery.py +5 -5
  29. janito/plugins/discovery_core.py +9 -14
  30. janito/plugins/manager.py +1 -1
  31. janito/providers/__init__.py +0 -1
  32. janito/providers/alibaba/model_info.py +10 -0
  33. janito/providers/alibaba/provider.py +1 -3
  34. janito/providers/moonshot/model_info.py +7 -7
  35. janito/providers/moonshot/provider.py +1 -1
  36. janito/tools/__init__.py +7 -41
  37. janito/tools/adapters/__init__.py +1 -6
  38. janito/tools/adapters/local/__init__.py +70 -7
  39. janito/{plugins/tools/core → tools/adapters/local}/ask_user.py +3 -3
  40. janito/tools/adapters/local/copy_file.py +87 -0
  41. janito/tools/adapters/local/create_file.py +138 -0
  42. janito/{plugins/tools/core → tools/adapters/local}/fetch_url.py +23 -20
  43. janito/tools/adapters/local/move_file.py +131 -0
  44. janito/{plugins/tools/core → tools/adapters/local}/python_code_run.py +7 -23
  45. janito/{plugins/tools/core → tools/adapters/local}/python_command_run.py +5 -21
  46. janito/{plugins/tools/core → tools/adapters/local}/python_file_run.py +5 -21
  47. janito/tools/adapters/local/remove_file.py +58 -0
  48. janito/{plugins/tools/core → tools/adapters/local}/replace_text_in_file.py +4 -4
  49. janito/{plugins/tools/core → tools/adapters/local}/run_bash_command.py +3 -3
  50. janito/{plugins/tools/core → tools/adapters/local}/run_powershell_command.py +3 -3
  51. janito/{plugins/tools/core → tools/adapters/local}/show_image.py +6 -15
  52. janito/{plugins/core/imagedisplay/tools → tools/adapters/local}/show_image_grid.py +5 -13
  53. janito/tools/adapters/local/view_file.py +172 -0
  54. janito/tools/function_adapter.py +65 -0
  55. janito/tools/loop_protection_decorator.py +117 -114
  56. janito-3.8.0.dist-info/METADATA +84 -0
  57. janito-3.8.0.dist-info/RECORD +264 -0
  58. janito/cli/cli_commands/check_tools.py +0 -212
  59. janito/data/blocked.txt +0 -96
  60. janito/llm/cancellation_manager.py +0 -63
  61. janito/llm/enter_cancellation.py +0 -107
  62. janito/plugin_system/__init__.py +0 -10
  63. janito/plugin_system/core_loader.py +0 -217
  64. janito/plugin_system/core_loader_fixed.py +0 -225
  65. janito/plugins/core/__init__.py +0 -7
  66. janito/plugins/core/codeanalyzer/__init__.py +0 -43
  67. janito/plugins/core/filemanager/__init__.py +0 -124
  68. janito/plugins/core/filemanager/tools/copy_file.py +0 -87
  69. janito/plugins/core/filemanager/tools/create_file.py +0 -87
  70. janito/plugins/core/filemanager/tools/move_file.py +0 -131
  71. janito/plugins/core/filemanager/tools/remove_file.py +0 -58
  72. janito/plugins/core/filemanager/tools/replace_text_in_file.py +0 -270
  73. janito/plugins/core/filemanager/tools/view_file.py +0 -172
  74. janito/plugins/core/imagedisplay/__init__.py +0 -14
  75. janito/plugins/core/imagedisplay/plugin.py +0 -51
  76. janito/plugins/core/imagedisplay/tools/__init__.py +0 -1
  77. janito/plugins/core/imagedisplay/tools/show_image.py +0 -83
  78. janito/plugins/core/system/__init__.py +0 -23
  79. janito/plugins/core/system/tools/run_bash_command.py +0 -204
  80. janito/plugins/core/system/tools/run_powershell_command.py +0 -234
  81. janito/plugins/dev/__init__.py +0 -7
  82. janito/plugins/dev/pythondev/__init__.py +0 -37
  83. janito/plugins/dev/visualization/__init__.py +0 -23
  84. janito/plugins/example_plugin.py +0 -108
  85. janito/plugins/tools/__init__.py +0 -39
  86. janito/plugins/tools/core/__init__.py +0 -65
  87. janito/plugins/tools/core/copy_file.py +0 -87
  88. janito/plugins/tools/core/create_directory.py +0 -70
  89. janito/plugins/tools/core/create_file.py +0 -138
  90. janito/plugins/tools/core/decorators.py +0 -19
  91. janito/plugins/tools/core/delete_text_in_file.py +0 -134
  92. janito/plugins/tools/core/find_files.py +0 -143
  93. janito/plugins/tools/core/get_file_outline/__init__.py +0 -7
  94. janito/plugins/tools/core/get_file_outline/core.py +0 -122
  95. janito/plugins/tools/core/get_file_outline/java_outline.py +0 -47
  96. janito/plugins/tools/core/get_file_outline/markdown_outline.py +0 -14
  97. janito/plugins/tools/core/get_file_outline/python_outline.py +0 -303
  98. janito/plugins/tools/core/get_file_outline/search_outline.py +0 -36
  99. janito/plugins/tools/core/move_file.py +0 -131
  100. janito/plugins/tools/core/open_html_in_browser.py +0 -51
  101. janito/plugins/tools/core/open_url.py +0 -37
  102. janito/plugins/tools/core/read_chart.py +0 -259
  103. janito/plugins/tools/core/read_files.py +0 -58
  104. janito/plugins/tools/core/remove_directory.py +0 -55
  105. janito/plugins/tools/core/remove_file.py +0 -58
  106. janito/plugins/tools/core/search_text/__init__.py +0 -7
  107. janito/plugins/tools/core/search_text/core.py +0 -205
  108. janito/plugins/tools/core/search_text/match_lines.py +0 -67
  109. janito/plugins/tools/core/search_text/pattern_utils.py +0 -73
  110. janito/plugins/tools/core/search_text/traverse_directory.py +0 -145
  111. janito/plugins/tools/core/show_image_grid.py +0 -85
  112. janito/plugins/tools/core/validate_file_syntax/__init__.py +0 -7
  113. janito/plugins/tools/core/validate_file_syntax/core.py +0 -114
  114. janito/plugins/tools/core/validate_file_syntax/css_validator.py +0 -35
  115. janito/plugins/tools/core/validate_file_syntax/html_validator.py +0 -100
  116. janito/plugins/tools/core/validate_file_syntax/jinja2_validator.py +0 -50
  117. janito/plugins/tools/core/validate_file_syntax/js_validator.py +0 -27
  118. janito/plugins/tools/core/validate_file_syntax/json_validator.py +0 -6
  119. janito/plugins/tools/core/validate_file_syntax/markdown_validator.py +0 -109
  120. janito/plugins/tools/core/validate_file_syntax/ps1_validator.py +0 -32
  121. janito/plugins/tools/core/validate_file_syntax/python_validator.py +0 -5
  122. janito/plugins/tools/core/validate_file_syntax/xml_validator.py +0 -11
  123. janito/plugins/tools/core/validate_file_syntax/yaml_validator.py +0 -6
  124. janito/plugins/tools/core/view_file.py +0 -172
  125. janito/plugins/ui/__init__.py +0 -7
  126. janito/plugins/ui/userinterface/__init__.py +0 -16
  127. janito/plugins/ui/userinterface/tools/ask_user.py +0 -110
  128. janito/plugins/web/__init__.py +0 -7
  129. janito/plugins/web/webtools/__init__.py +0 -23
  130. janito/providers/together/__init__.py +0 -1
  131. janito/providers/together/model_info.py +0 -69
  132. janito/providers/together/provider.py +0 -108
  133. janito/tools/blocked_sites.py +0 -74
  134. janito/tools/cli_initializer.py +0 -88
  135. janito/tools/initialize.py +0 -70
  136. janito-3.6.1.dist-info/METADATA +0 -228
  137. janito-3.6.1.dist-info/RECORD +0 -339
  138. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/create_directory.py +0 -0
  139. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/delete_text_in_file.py +0 -0
  140. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/find_files.py +0 -0
  141. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
  142. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/core.py +0 -0
  143. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/java_outline.py +0 -0
  144. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
  145. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/python_outline.py +0 -0
  146. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/search_outline.py +0 -0
  147. /janito/{plugins/web/webtools/tools → tools/adapters/local}/open_html_in_browser.py +0 -0
  148. /janito/{plugins/web/webtools/tools → tools/adapters/local}/open_url.py +0 -0
  149. /janito/{plugins/dev/visualization/tools → tools/adapters/local}/read_chart.py +0 -0
  150. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/read_files.py +0 -0
  151. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/remove_directory.py +0 -0
  152. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/__init__.py +0 -0
  153. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/core.py +0 -0
  154. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/match_lines.py +0 -0
  155. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/pattern_utils.py +0 -0
  156. /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/traverse_directory.py +0 -0
  157. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
  158. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/core.py +0 -0
  159. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +0 -0
  160. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +0 -0
  161. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/jinja2_validator.py +0 -0
  162. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +0 -0
  163. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +0 -0
  164. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +0 -0
  165. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +0 -0
  166. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +0 -0
  167. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +0 -0
  168. /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +0 -0
  169. {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/WHEEL +0 -0
  170. {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/entry_points.txt +0 -0
  171. {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/licenses/LICENSE +0 -0
  172. {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,138 @@
1
+ import os
2
+ from janito.tools.path_utils import expand_path
3
+ from janito.tools.adapters.local.adapter import register_local_tool
4
+
5
+ from janito.tools.tool_utils import display_path
6
+ from janito.tools.tool_base import ToolBase, ToolPermissions
7
+ from janito.report_events import ReportAction
8
+ from janito.i18n import tr
9
+ from janito.tools.loop_protection_decorator import protect_against_loops
10
+
11
+ from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
12
+
13
+
14
+ @register_local_tool
15
+ class CreateFileTool(ToolBase):
16
+ """
17
+ Create a new file with specified content at the given path.
18
+
19
+ This tool provides comprehensive file creation capabilities with built-in safety features,
20
+ automatic syntax validation, and detailed feedback. It handles path expansion, directory
21
+ creation, encoding issues, and provides clear status messages for both success and failure cases.
22
+
23
+ Key Features:
24
+ - Automatic directory creation for nested paths
25
+ - UTF-8 encoding with error handling for special characters
26
+ - Built-in syntax validation for common file types (Python, JavaScript, JSON, YAML, etc.)
27
+ - Loop protection to prevent excessive file creation
28
+ - Detailed error messages with context
29
+ - Safe overwrite protection with preview of existing content
30
+ - Cross-platform path handling (Windows, macOS, Linux)
31
+
32
+ Args:
33
+ path (str): Target file path. Supports relative and absolute paths, with automatic
34
+ expansion of user home directory (~) and environment variables.
35
+ Examples: "src/main.py", "~/Documents/config.json", "$HOME/.env"
36
+ content (str): File content to write. Empty string creates empty file.
37
+ Supports any text content including Unicode characters, newlines,
38
+ and binary-safe text representation.
39
+ overwrite (bool, optional): If True, allows overwriting existing files. Default: False.
40
+ When False, prevents accidental overwrites by checking
41
+ file existence and showing current content. Always review
42
+ existing content before enabling overwrite.
43
+
44
+ Returns:
45
+ str: Detailed status message including:
46
+ - Success confirmation with line count
47
+ - File path (display-friendly format)
48
+ - Syntax validation results
49
+ - Existing content preview (when overwrite blocked)
50
+ - Error details (when creation fails)
51
+
52
+ Raises:
53
+ No direct exceptions - all errors are caught and returned as user-friendly messages.
54
+ Common error cases include: permission denied, invalid path format, disk full,
55
+ or file exists (when overwrite=False).
56
+
57
+ Security Features:
58
+ - Loop protection: Maximum 5 calls per 10 seconds for the same file path
59
+ - Path traversal prevention: Validates and sanitizes file paths
60
+ - Permission checking: Respects file system permissions
61
+ - Atomic writes: Prevents partial file creation on errors
62
+
63
+ Examples:
64
+ Basic file creation:
65
+ >>> create_file("hello.py", "print('Hello, World!')")
66
+ ✅ Created file 1 lines.
67
+ ✅ Syntax OK
68
+
69
+ Creating nested directories:
70
+ >>> create_file("src/utils/helpers.py", "def helper(): pass")
71
+ ✅ Created file 2 lines.
72
+ ✅ Syntax OK
73
+
74
+ Overwrite protection:
75
+ >>> create_file("existing.txt", "new content")
76
+ ❗ Cannot create file: file already exists at 'existing.txt'.
77
+ --- Current file content ---
78
+ old content
79
+
80
+ Note: After successful creation, automatic syntax validation is performed based on
81
+ file extension. Results are appended to the return message for immediate feedback.
82
+ """
83
+
84
+ permissions = ToolPermissions(write=True)
85
+ tool_name = "create_file"
86
+
87
+ @protect_against_loops(max_calls=5, time_window=10.0, key_field="path")
88
+ def run(self, path: str, content: str, overwrite: bool = False) -> str:
89
+ path = expand_path(path)
90
+ disp_path = display_path(path)
91
+ if os.path.exists(path) and not overwrite:
92
+ try:
93
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
94
+ existing_content = f.read()
95
+ except Exception as e:
96
+ existing_content = f"[Error reading file: {e}]"
97
+ return tr(
98
+ "❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
99
+ disp_path=disp_path,
100
+ existing_content=existing_content,
101
+ )
102
+ # Determine if we are overwriting an existing file
103
+ is_overwrite = os.path.exists(path) and overwrite
104
+ if is_overwrite:
105
+ # Overwrite branch: log only overwrite warning (no create message)
106
+ self.report_action(
107
+ tr("⚠️ Overwriting file '{disp_path}'", disp_path=disp_path),
108
+ ReportAction.CREATE,
109
+ )
110
+ dir_name = os.path.dirname(path)
111
+ if dir_name:
112
+ os.makedirs(dir_name, exist_ok=True)
113
+ if not is_overwrite:
114
+ # Create branch: log file creation message
115
+ self.report_action(
116
+ tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
117
+ ReportAction.CREATE,
118
+ )
119
+ with open(path, "w", encoding="utf-8", errors="replace") as f:
120
+ f.write(content)
121
+ new_lines = content.count("\n") + 1 if content else 0
122
+ self.report_success(
123
+ tr("✅ {new_lines} lines", new_lines=new_lines), ReportAction.CREATE
124
+ )
125
+ # Perform syntax validation and append result
126
+ validation_result = validate_file_syntax(path)
127
+ if is_overwrite:
128
+ # Overwrite branch: return minimal overwrite info to user
129
+ return (
130
+ tr("✅ {new_lines} lines", new_lines=new_lines)
131
+ + f"\n{validation_result}"
132
+ )
133
+ else:
134
+ # Create branch: return detailed create success to user
135
+ return (
136
+ tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
137
+ + f"\n{validation_result}"
138
+ )
@@ -5,7 +5,7 @@ import json
5
5
  from pathlib import Path
6
6
  from bs4 import BeautifulSoup
7
7
  from typing import Dict, Any, Optional
8
- from .decorators import register_core_tool
8
+ from janito.tools.adapters.local.adapter import register_local_tool
9
9
  from janito.tools.tool_base import ToolBase, ToolPermissions
10
10
  from janito.report_events import ReportAction
11
11
  from janito.i18n import tr
@@ -13,8 +13,8 @@ from janito.tools.tool_utils import pluralize
13
13
  from janito.tools.loop_protection_decorator import protect_against_loops
14
14
 
15
15
 
16
- @register_core_tool
17
- class FetchUrl(ToolBase):
16
+ @register_local_tool
17
+ class FetchUrlTool(ToolBase):
18
18
  """
19
19
  Fetch the content of a web page and extract its text.
20
20
 
@@ -190,23 +190,11 @@ class FetchUrl(ToolBase):
190
190
 
191
191
  Also implements URL whitelist checking and browser-like behavior.
192
192
  """
193
- # Check URL whitelist and blocked sites
193
+ # Check URL whitelist
194
194
  from janito.tools.url_whitelist import get_url_whitelist_manager
195
- from janito.tools.blocked_sites import get_blocked_sites_manager
196
195
 
197
196
  whitelist_manager = get_url_whitelist_manager()
198
- blocked_manager = get_blocked_sites_manager()
199
197
 
200
- # Check blocked sites first
201
- if blocked_manager.is_url_blocked(url):
202
- error_message = tr("Blocked: Site is in blocked list")
203
- self.report_error(
204
- tr("❗ Blocked: Site is in blocked list"),
205
- ReportAction.READ,
206
- )
207
- return error_message
208
-
209
- # Then check whitelist
210
198
  if not whitelist_manager.is_url_allowed(url):
211
199
  error_message = tr("Blocked")
212
200
  self.report_error(
@@ -257,7 +245,7 @@ class FetchUrl(ToolBase):
257
245
  self.session_cache[url] = content
258
246
  return content
259
247
  except requests.exceptions.HTTPError as http_err:
260
- status_code = http_err.response.status_code if http_err.response is not None else None
248
+ status_code = http_err.response.status_code if http_err.response else None
261
249
 
262
250
  # Map status codes to descriptions
263
251
  status_descriptions = {
@@ -303,7 +291,14 @@ class FetchUrl(ToolBase):
303
291
  return error_message
304
292
  else:
305
293
  status_code_str = str(status_code) if status_code else "Error"
306
- description = status_descriptions.get(status_code, "Error")
294
+ description = status_descriptions.get(
295
+ status_code,
296
+ (
297
+ "Server Error"
298
+ if status_code and status_code >= 500
299
+ else "Client Error"
300
+ ),
301
+ )
307
302
  self.report_error(
308
303
  f"❗ HTTP {status_code_str} {description}",
309
304
  ReportAction.READ,
@@ -406,7 +401,11 @@ class FetchUrl(ToolBase):
406
401
  cookies=cookies,
407
402
  follow_redirects=follow_redirects,
408
403
  )
409
- if html_content.startswith("HTTP") or "Error" in html_content or html_content == "Blocked":
404
+ if (
405
+ html_content.startswith("HTTP Error ")
406
+ or html_content == "Error"
407
+ or html_content == "Blocked"
408
+ ):
410
409
  return html_content
411
410
 
412
411
  try:
@@ -435,7 +434,11 @@ class FetchUrl(ToolBase):
435
434
  cookies=cookies,
436
435
  follow_redirects=follow_redirects,
437
436
  )
438
- if html_content.startswith("HTTP") or "Error" in html_content or html_content == "Blocked":
437
+ if (
438
+ html_content.startswith("HTTP Error ")
439
+ or html_content == "Error"
440
+ or html_content == "Blocked"
441
+ ):
439
442
  return html_content
440
443
 
441
444
  # Extract and clean text
@@ -0,0 +1,131 @@
1
+ import os
2
+ from janito.tools.path_utils import expand_path
3
+ import shutil
4
+ from janito.tools.adapters.local.adapter import register_local_tool
5
+ from janito.tools.tool_utils import display_path
6
+ from janito.tools.tool_base import ToolBase, ToolPermissions
7
+ from janito.report_events import ReportAction
8
+ from janito.i18n import tr
9
+
10
+
11
+ @register_local_tool
12
+ class MoveFileTool(ToolBase):
13
+ """
14
+ Move a file or directory from src_path to dest_path.
15
+
16
+ Args:
17
+ src_path (str): Source file or directory path.
18
+ dest_path (str): Destination file or directory path.
19
+ overwrite (bool, optional): Whether to overwrite if the destination exists. Defaults to False.
20
+ backup (bool, optional): Deprecated. No backups are created anymore. This flag is ignored. Defaults to False.
21
+ Returns:
22
+ str: Status message indicating the result.
23
+ """
24
+
25
+ permissions = ToolPermissions(read=True, write=True)
26
+ tool_name = "move_file"
27
+
28
+ def run(
29
+ self,
30
+ src_path: str,
31
+ dest_path: str,
32
+ overwrite: bool = False,
33
+ backup: bool = False,
34
+ ) -> str:
35
+ src = expand_path(src_path)
36
+ dest = expand_path(dest_path)
37
+ original_src = src_path
38
+ original_dest = dest_path
39
+ disp_src = display_path(original_src)
40
+ disp_dest = display_path(original_dest)
41
+ backup_path = None
42
+
43
+ valid, is_src_file, is_src_dir, err_msg = self._validate_source(src, disp_src)
44
+ if not valid:
45
+ return err_msg
46
+
47
+ dest_result = self._handle_destination(dest, disp_dest, overwrite, backup)
48
+ if dest_result is not None:
49
+ backup_path, err_msg = dest_result
50
+ if err_msg:
51
+ return err_msg
52
+
53
+ try:
54
+ self.report_action(
55
+ tr(
56
+ "📝 Moving from '{disp_src}' to '{disp_dest}' ...",
57
+ disp_src=disp_src,
58
+ disp_dest=disp_dest,
59
+ ),
60
+ ReportAction.UPDATE,
61
+ )
62
+ shutil.move(src, dest)
63
+ self.report_success(tr("✅ Move complete."))
64
+ msg = tr("✅ Move complete.")
65
+
66
+ return msg
67
+ except Exception as e:
68
+ self.report_error(tr("❌ Error moving: {error}", error=e))
69
+ return tr("❌ Error moving: {error}", error=e)
70
+
71
+ def _validate_source(self, src, disp_src):
72
+ if not os.path.exists(src):
73
+ self.report_error(
74
+ tr("❌ Source '{disp_src}' does not exist.", disp_src=disp_src)
75
+ )
76
+ return (
77
+ False,
78
+ False,
79
+ False,
80
+ tr("❌ Source '{disp_src}' does not exist.", disp_src=disp_src),
81
+ )
82
+ is_src_file = os.path.isfile(src)
83
+ is_src_dir = os.path.isdir(src)
84
+ if not (is_src_file or is_src_dir):
85
+ self.report_error(
86
+ tr(
87
+ "❌ Source path '{disp_src}' is neither a file nor a directory.",
88
+ disp_src=disp_src,
89
+ )
90
+ )
91
+ return (
92
+ False,
93
+ False,
94
+ False,
95
+ tr(
96
+ "❌ Source path '{disp_src}' is neither a file nor a directory.",
97
+ disp_src=disp_src,
98
+ ),
99
+ )
100
+ return True, is_src_file, is_src_dir, None
101
+
102
+ def _handle_destination(self, dest, disp_dest, overwrite, backup):
103
+ backup_path = None
104
+ if os.path.exists(dest):
105
+ if not overwrite:
106
+ self.report_error(
107
+ tr(
108
+ "❗ Destination '{disp_dest}' exists and overwrite is False.",
109
+ disp_dest=disp_dest,
110
+ ),
111
+ ReportAction.UPDATE,
112
+ )
113
+ return None, tr(
114
+ "❗ Destination '{disp_dest}' already exists and overwrite is False.",
115
+ disp_dest=disp_dest,
116
+ )
117
+
118
+ try:
119
+ if os.path.isfile(dest):
120
+ os.remove(dest)
121
+ elif os.path.isdir(dest):
122
+ shutil.rmtree(dest)
123
+ except Exception as e:
124
+ self.report_error(
125
+ tr("❌ Error removing destination before move: {error}", error=e),
126
+ ReportAction.UPDATE,
127
+ )
128
+ return None, tr(
129
+ "❌ Error removing destination before move: {error}", error=e
130
+ )
131
+ return backup_path, None
@@ -5,12 +5,12 @@ import tempfile
5
5
  import threading
6
6
  from janito.tools.tool_base import ToolBase, ToolPermissions
7
7
  from janito.report_events import ReportAction
8
- from .decorators import register_core_tool
8
+ from janito.tools.adapters.local.adapter import register_local_tool
9
9
  from janito.i18n import tr
10
10
 
11
11
 
12
- @register_core_tool
13
- class PythonCodeRun(ToolBase):
12
+ @register_local_tool
13
+ class PythonCodeRunTool(ToolBase):
14
14
  """
15
15
  Tool to execute Python code by passing it to the interpreter via standard input (stdin).
16
16
 
@@ -93,9 +93,6 @@ class PythonCodeRun(ToolBase):
93
93
  def stream_output(stream, file_obj, report_func, count_func):
94
94
  nonlocal stdout_lines, stderr_lines
95
95
  for line in stream:
96
- # Check for cancellation
97
- if hasattr(self, '_cancel_event') and self._cancel_event.is_set():
98
- break
99
96
  file_obj.write(line)
100
97
  file_obj.flush()
101
98
  report_func(line.rstrip("\r\n"))
@@ -104,11 +101,6 @@ class PythonCodeRun(ToolBase):
104
101
  else:
105
102
  stderr_lines += 1
106
103
 
107
- # Set up cancellation event
108
- from janito.llm.cancellation_manager import get_cancellation_manager
109
- cancel_manager = get_cancellation_manager()
110
- self._cancel_event = cancel_manager.get_current_cancel_event()
111
-
112
104
  stdout_thread = threading.Thread(
113
105
  target=stream_output,
114
106
  args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
@@ -119,18 +111,10 @@ class PythonCodeRun(ToolBase):
119
111
  )
120
112
  stdout_thread.start()
121
113
  stderr_thread.start()
122
-
123
- try:
124
- process.stdin.write(code)
125
- process.stdin.close()
126
- stdout_thread.join()
127
- stderr_thread.join()
128
- except Exception as e:
129
- # Handle cancellation or other errors
130
- if self._cancel_event and self._cancel_event.is_set():
131
- process.kill()
132
- self.report_warning(tr("Code execution cancelled by user"), ReportAction.EXECUTE)
133
- raise
114
+ process.stdin.write(code)
115
+ process.stdin.close()
116
+ stdout_thread.join()
117
+ stderr_thread.join()
134
118
  return stdout_lines, stderr_lines
135
119
 
136
120
  def _wait_for_process(self, process, timeout):
@@ -5,12 +5,12 @@ import tempfile
5
5
  import threading
6
6
  from janito.tools.tool_base import ToolBase, ToolPermissions
7
7
  from janito.report_events import ReportAction
8
- from .decorators import register_core_tool
8
+ from janito.tools.adapters.local.adapter import register_local_tool
9
9
  from janito.i18n import tr
10
10
 
11
11
 
12
- @register_core_tool
13
- class PythonCommandRun(ToolBase):
12
+ @register_local_tool
13
+ class PythonCommandRunTool(ToolBase):
14
14
  """
15
15
  Tool to execute Python code using the `python -c` command-line flag.
16
16
 
@@ -92,9 +92,6 @@ class PythonCommandRun(ToolBase):
92
92
  def stream_output(stream, file_obj, report_func, count_func):
93
93
  nonlocal stdout_lines, stderr_lines
94
94
  for line in stream:
95
- # Check for cancellation
96
- if hasattr(self, '_cancel_event') and self._cancel_event.is_set():
97
- break
98
95
  file_obj.write(line)
99
96
  file_obj.flush()
100
97
  from janito.tools.tool_base import ReportAction
@@ -105,11 +102,6 @@ class PythonCommandRun(ToolBase):
105
102
  else:
106
103
  stderr_lines += 1
107
104
 
108
- # Set up cancellation event
109
- from janito.llm.cancellation_manager import get_cancellation_manager
110
- cancel_manager = get_cancellation_manager()
111
- self._cancel_event = cancel_manager.get_current_cancel_event()
112
-
113
105
  stdout_thread = threading.Thread(
114
106
  target=stream_output,
115
107
  args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
@@ -120,16 +112,8 @@ class PythonCommandRun(ToolBase):
120
112
  )
121
113
  stdout_thread.start()
122
114
  stderr_thread.start()
123
-
124
- try:
125
- stdout_thread.join()
126
- stderr_thread.join()
127
- except Exception as e:
128
- # Handle cancellation
129
- if self._cancel_event and self._cancel_event.is_set():
130
- process.kill()
131
- self.report_warning(tr("Code execution cancelled by user"), ReportAction.EXECUTE)
132
- raise
115
+ stdout_thread.join()
116
+ stderr_thread.join()
133
117
  return stdout_lines, stderr_lines
134
118
 
135
119
  def _wait_for_process(self, process, timeout):
@@ -5,12 +5,12 @@ import tempfile
5
5
  import threading
6
6
  from janito.tools.tool_base import ToolBase, ToolPermissions
7
7
  from janito.report_events import ReportAction
8
- from .decorators import register_core_tool
8
+ from janito.tools.adapters.local.adapter import register_local_tool
9
9
  from janito.i18n import tr
10
10
 
11
11
 
12
- @register_core_tool
13
- class PythonFileRun(ToolBase):
12
+ @register_local_tool
13
+ class PythonFileRunTool(ToolBase):
14
14
  """
15
15
  Tool to execute a specified Python script file.
16
16
 
@@ -92,9 +92,6 @@ class PythonFileRun(ToolBase):
92
92
  def stream_output(stream, file_obj, report_func, count_func):
93
93
  nonlocal stdout_lines, stderr_lines
94
94
  for line in stream:
95
- # Check for cancellation
96
- if hasattr(self, '_cancel_event') and self._cancel_event.is_set():
97
- break
98
95
  file_obj.write(line)
99
96
  file_obj.flush()
100
97
  # Always supply a default action for stdout/stderr reporting
@@ -106,11 +103,6 @@ class PythonFileRun(ToolBase):
106
103
  else:
107
104
  stderr_lines += 1
108
105
 
109
- # Set up cancellation event
110
- from janito.llm.cancellation_manager import get_cancellation_manager
111
- cancel_manager = get_cancellation_manager()
112
- self._cancel_event = cancel_manager.get_current_cancel_event()
113
-
114
106
  stdout_thread = threading.Thread(
115
107
  target=stream_output,
116
108
  args=(process.stdout, stdout_file, self.report_stdout, "stdout"),
@@ -121,16 +113,8 @@ class PythonFileRun(ToolBase):
121
113
  )
122
114
  stdout_thread.start()
123
115
  stderr_thread.start()
124
-
125
- try:
126
- stdout_thread.join()
127
- stderr_thread.join()
128
- except Exception as e:
129
- # Handle cancellation
130
- if self._cancel_event and self._cancel_event.is_set():
131
- process.kill()
132
- self.report_warning(tr("File execution cancelled by user"), ReportAction.EXECUTE)
133
- raise
116
+ stdout_thread.join()
117
+ stderr_thread.join()
134
118
  return stdout_lines, stderr_lines
135
119
 
136
120
  def _wait_for_process(self, process, timeout):
@@ -0,0 +1,58 @@
1
+ import os
2
+ from janito.tools.path_utils import expand_path
3
+ import shutil
4
+ from janito.tools.adapters.local.adapter import register_local_tool
5
+
6
+ from janito.tools.tool_utils import display_path
7
+ from janito.tools.tool_base import ToolBase, ToolPermissions
8
+ from janito.report_events import ReportAction
9
+ from janito.i18n import tr
10
+
11
+
12
+ @register_local_tool
13
+ class RemoveFileTool(ToolBase):
14
+ """
15
+ Remove a file at the specified path.
16
+
17
+ Args:
18
+ path (str): Path to the file to remove.
19
+ backup (bool, optional): Deprecated. Backups are no longer created. Flag ignored.
20
+ Returns:
21
+ str: Status message indicating the result. Example:
22
+ - " Successfully removed the file at ..."
23
+ - " Cannot remove file: ..."
24
+ """
25
+
26
+ permissions = ToolPermissions(write=True)
27
+ tool_name = "remove_file"
28
+
29
+ def run(self, path: str, backup: bool = False) -> str:
30
+ path = expand_path(path)
31
+ disp_path = display_path(path)
32
+
33
+ # Report initial info about what is going to be removed
34
+ self.report_action(
35
+ tr("🗑️ Remove file '{disp_path}' ...", disp_path=disp_path),
36
+ ReportAction.DELETE,
37
+ )
38
+ if not os.path.exists(path):
39
+ self.report_error(tr("❌ File does not exist."), ReportAction.DELETE)
40
+ return tr("❌ File does not exist.")
41
+ if not os.path.isfile(path):
42
+ self.report_error(tr("❌ Path is not a file."), ReportAction.DELETE)
43
+ return tr("❌ Path is not a file.")
44
+ try:
45
+
46
+ os.remove(path)
47
+ self.report_success(tr("✅ File removed"), ReportAction.DELETE)
48
+ msg = tr(
49
+ "✅ Successfully removed the file at '{disp_path}'.",
50
+ disp_path=disp_path,
51
+ )
52
+
53
+ return msg
54
+ except Exception as e:
55
+ self.report_error(
56
+ tr("❌ Error removing file: {error}", error=e), ReportAction.DELETE
57
+ )
58
+ return tr("❌ Error removing file: {error}", error=e)
@@ -1,14 +1,14 @@
1
1
  from janito.tools.tool_base import ToolBase, ToolPermissions
2
2
  from janito.report_events import ReportAction
3
- from .decorators import register_core_tool
3
+ from janito.tools.adapters.local.adapter import register_local_tool
4
4
  from janito.i18n import tr
5
5
  import shutil
6
6
  import re
7
- from .validate_file_syntax.core import validate_file_syntax
7
+ from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
8
8
 
9
9
 
10
- @register_core_tool
11
- class ReplaceTextInFile(ToolBase):
10
+ @register_local_tool
11
+ class ReplaceTextInFileTool(ToolBase):
12
12
  """
13
13
  Replace exact occurrences of a given text in a file.
14
14
 
@@ -1,6 +1,6 @@
1
1
  from janito.tools.tool_base import ToolBase, ToolPermissions
2
2
  from janito.report_events import ReportAction
3
- from .decorators import register_core_tool
3
+ from janito.tools.adapters.local.adapter import register_local_tool
4
4
  from janito.i18n import tr
5
5
  import subprocess
6
6
  import tempfile
@@ -9,8 +9,8 @@ import os
9
9
  import threading
10
10
 
11
11
 
12
- @register_core_tool
13
- class RunBashCommand(ToolBase):
12
+ @register_local_tool
13
+ class RunBashCommandTool(ToolBase):
14
14
  """
15
15
  Execute a non-interactive command using the bash shell and capture live output.
16
16
  This tool explicitly invokes the 'bash' shell (not just the system default shell), so it requires bash to be installed and available in the system PATH. On Windows, this will only work if bash is available (e.g., via WSL, Git Bash, or similar).
@@ -1,6 +1,6 @@
1
1
  from janito.tools.tool_base import ToolBase, ToolPermissions
2
2
  from janito.report_events import ReportAction
3
- from .decorators import register_core_tool
3
+ from janito.tools.adapters.local.adapter import register_local_tool
4
4
  from janito.i18n import tr
5
5
  import subprocess
6
6
  import os
@@ -9,8 +9,8 @@ import tempfile
9
9
  import threading
10
10
 
11
11
 
12
- @register_core_tool
13
- class RunPowershellCommand(ToolBase):
12
+ @register_local_tool
13
+ class RunPowershellCommandTool(ToolBase):
14
14
  """
15
15
  Execute a non-interactive command using the PowerShell shell and capture live output.
16
16
  This tool explicitly invokes 'powershell.exe' (on Windows) or 'pwsh' (on other platforms if available).