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.
- janito/README.md +4 -7
- janito/cli/chat_mode/bindings.py +0 -50
- janito/cli/chat_mode/session.py +1 -12
- janito/cli/chat_mode/shell/commands/multi.py +0 -5
- janito/cli/chat_mode/shell/commands/security/allowed_sites.py +33 -47
- janito/cli/cli_commands/list_plugins.py +43 -52
- janito/cli/cli_commands/list_tools.py +1 -6
- janito/cli/core/getters.py +0 -3
- janito/cli/core/model_guesser.py +24 -40
- janito/cli/main_cli.py +13 -10
- janito/cli/prompt_core.py +9 -47
- janito/cli/rich_terminal_reporter.py +4 -4
- janito/docs/GETTING_STARTED.md +9 -8
- janito/drivers/openai/driver.py +0 -1
- janito/drivers/zai/driver.py +0 -1
- janito/i18n/it.py +46 -46
- janito/llm/agent.py +16 -32
- janito/llm/auth_utils.py +5 -14
- janito/llm/driver.py +0 -8
- janito/plugins/__init__.py +12 -31
- janito/plugins/auto_loader.py +11 -12
- janito/plugins/auto_loader_fixed.py +11 -12
- janito/{plugin_system → plugins}/base.py +2 -5
- janito/plugins/builtin.py +1 -15
- janito/plugins/core_adapter.py +11 -89
- janito/plugins/core_loader.py +120 -0
- janito/plugins/core_loader_fixed.py +125 -0
- janito/plugins/discovery.py +5 -5
- janito/plugins/discovery_core.py +9 -14
- janito/plugins/manager.py +1 -1
- janito/providers/__init__.py +0 -1
- janito/providers/alibaba/model_info.py +10 -0
- janito/providers/alibaba/provider.py +1 -3
- janito/providers/moonshot/model_info.py +7 -7
- janito/providers/moonshot/provider.py +1 -1
- janito/tools/__init__.py +7 -41
- janito/tools/adapters/__init__.py +1 -6
- janito/tools/adapters/local/__init__.py +70 -7
- janito/{plugins/tools/core → tools/adapters/local}/ask_user.py +3 -3
- janito/tools/adapters/local/copy_file.py +87 -0
- janito/tools/adapters/local/create_file.py +138 -0
- janito/{plugins/tools/core → tools/adapters/local}/fetch_url.py +23 -20
- janito/tools/adapters/local/move_file.py +131 -0
- janito/{plugins/tools/core → tools/adapters/local}/python_code_run.py +7 -23
- janito/{plugins/tools/core → tools/adapters/local}/python_command_run.py +5 -21
- janito/{plugins/tools/core → tools/adapters/local}/python_file_run.py +5 -21
- janito/tools/adapters/local/remove_file.py +58 -0
- janito/{plugins/tools/core → tools/adapters/local}/replace_text_in_file.py +4 -4
- janito/{plugins/tools/core → tools/adapters/local}/run_bash_command.py +3 -3
- janito/{plugins/tools/core → tools/adapters/local}/run_powershell_command.py +3 -3
- janito/{plugins/tools/core → tools/adapters/local}/show_image.py +6 -15
- janito/{plugins/core/imagedisplay/tools → tools/adapters/local}/show_image_grid.py +5 -13
- janito/tools/adapters/local/view_file.py +172 -0
- janito/tools/function_adapter.py +65 -0
- janito/tools/loop_protection_decorator.py +117 -114
- janito-3.8.0.dist-info/METADATA +84 -0
- janito-3.8.0.dist-info/RECORD +264 -0
- janito/cli/cli_commands/check_tools.py +0 -212
- janito/data/blocked.txt +0 -96
- janito/llm/cancellation_manager.py +0 -63
- janito/llm/enter_cancellation.py +0 -107
- janito/plugin_system/__init__.py +0 -10
- janito/plugin_system/core_loader.py +0 -217
- janito/plugin_system/core_loader_fixed.py +0 -225
- janito/plugins/core/__init__.py +0 -7
- janito/plugins/core/codeanalyzer/__init__.py +0 -43
- janito/plugins/core/filemanager/__init__.py +0 -124
- janito/plugins/core/filemanager/tools/copy_file.py +0 -87
- janito/plugins/core/filemanager/tools/create_file.py +0 -87
- janito/plugins/core/filemanager/tools/move_file.py +0 -131
- janito/plugins/core/filemanager/tools/remove_file.py +0 -58
- janito/plugins/core/filemanager/tools/replace_text_in_file.py +0 -270
- janito/plugins/core/filemanager/tools/view_file.py +0 -172
- janito/plugins/core/imagedisplay/__init__.py +0 -14
- janito/plugins/core/imagedisplay/plugin.py +0 -51
- janito/plugins/core/imagedisplay/tools/__init__.py +0 -1
- janito/plugins/core/imagedisplay/tools/show_image.py +0 -83
- janito/plugins/core/system/__init__.py +0 -23
- janito/plugins/core/system/tools/run_bash_command.py +0 -204
- janito/plugins/core/system/tools/run_powershell_command.py +0 -234
- janito/plugins/dev/__init__.py +0 -7
- janito/plugins/dev/pythondev/__init__.py +0 -37
- janito/plugins/dev/visualization/__init__.py +0 -23
- janito/plugins/example_plugin.py +0 -108
- janito/plugins/tools/__init__.py +0 -39
- janito/plugins/tools/core/__init__.py +0 -65
- janito/plugins/tools/core/copy_file.py +0 -87
- janito/plugins/tools/core/create_directory.py +0 -70
- janito/plugins/tools/core/create_file.py +0 -138
- janito/plugins/tools/core/decorators.py +0 -19
- janito/plugins/tools/core/delete_text_in_file.py +0 -134
- janito/plugins/tools/core/find_files.py +0 -143
- janito/plugins/tools/core/get_file_outline/__init__.py +0 -7
- janito/plugins/tools/core/get_file_outline/core.py +0 -122
- janito/plugins/tools/core/get_file_outline/java_outline.py +0 -47
- janito/plugins/tools/core/get_file_outline/markdown_outline.py +0 -14
- janito/plugins/tools/core/get_file_outline/python_outline.py +0 -303
- janito/plugins/tools/core/get_file_outline/search_outline.py +0 -36
- janito/plugins/tools/core/move_file.py +0 -131
- janito/plugins/tools/core/open_html_in_browser.py +0 -51
- janito/plugins/tools/core/open_url.py +0 -37
- janito/plugins/tools/core/read_chart.py +0 -259
- janito/plugins/tools/core/read_files.py +0 -58
- janito/plugins/tools/core/remove_directory.py +0 -55
- janito/plugins/tools/core/remove_file.py +0 -58
- janito/plugins/tools/core/search_text/__init__.py +0 -7
- janito/plugins/tools/core/search_text/core.py +0 -205
- janito/plugins/tools/core/search_text/match_lines.py +0 -67
- janito/plugins/tools/core/search_text/pattern_utils.py +0 -73
- janito/plugins/tools/core/search_text/traverse_directory.py +0 -145
- janito/plugins/tools/core/show_image_grid.py +0 -85
- janito/plugins/tools/core/validate_file_syntax/__init__.py +0 -7
- janito/plugins/tools/core/validate_file_syntax/core.py +0 -114
- janito/plugins/tools/core/validate_file_syntax/css_validator.py +0 -35
- janito/plugins/tools/core/validate_file_syntax/html_validator.py +0 -100
- janito/plugins/tools/core/validate_file_syntax/jinja2_validator.py +0 -50
- janito/plugins/tools/core/validate_file_syntax/js_validator.py +0 -27
- janito/plugins/tools/core/validate_file_syntax/json_validator.py +0 -6
- janito/plugins/tools/core/validate_file_syntax/markdown_validator.py +0 -109
- janito/plugins/tools/core/validate_file_syntax/ps1_validator.py +0 -32
- janito/plugins/tools/core/validate_file_syntax/python_validator.py +0 -5
- janito/plugins/tools/core/validate_file_syntax/xml_validator.py +0 -11
- janito/plugins/tools/core/validate_file_syntax/yaml_validator.py +0 -6
- janito/plugins/tools/core/view_file.py +0 -172
- janito/plugins/ui/__init__.py +0 -7
- janito/plugins/ui/userinterface/__init__.py +0 -16
- janito/plugins/ui/userinterface/tools/ask_user.py +0 -110
- janito/plugins/web/__init__.py +0 -7
- janito/plugins/web/webtools/__init__.py +0 -23
- janito/providers/together/__init__.py +0 -1
- janito/providers/together/model_info.py +0 -69
- janito/providers/together/provider.py +0 -108
- janito/tools/blocked_sites.py +0 -74
- janito/tools/cli_initializer.py +0 -88
- janito/tools/initialize.py +0 -70
- janito-3.6.1.dist-info/METADATA +0 -228
- janito-3.6.1.dist-info/RECORD +0 -339
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/create_directory.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/delete_text_in_file.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/find_files.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/core.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/java_outline.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/python_outline.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/get_file_outline/search_outline.py +0 -0
- /janito/{plugins/web/webtools/tools → tools/adapters/local}/open_html_in_browser.py +0 -0
- /janito/{plugins/web/webtools/tools → tools/adapters/local}/open_url.py +0 -0
- /janito/{plugins/dev/visualization/tools → tools/adapters/local}/read_chart.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/read_files.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/remove_directory.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/__init__.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/core.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/match_lines.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/pattern_utils.py +0 -0
- /janito/{plugins/core/codeanalyzer/tools → tools/adapters/local}/search_text/traverse_directory.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/core.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/jinja2_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +0 -0
- /janito/{plugins/core/filemanager/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +0 -0
- {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/WHEEL +0 -0
- {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/entry_points.txt +0 -0
- {janito-3.6.1.dist-info → janito-3.8.0.dist-info}/licenses/LICENSE +0 -0
- {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 .
|
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
|
-
@
|
17
|
-
class
|
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
|
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
|
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(
|
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
|
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
|
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 .
|
8
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
9
9
|
from janito.i18n import tr
|
10
10
|
|
11
11
|
|
12
|
-
@
|
13
|
-
class
|
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
|
-
|
124
|
-
|
125
|
-
|
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 .
|
8
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
9
9
|
from janito.i18n import tr
|
10
10
|
|
11
11
|
|
12
|
-
@
|
13
|
-
class
|
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
|
-
|
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 .
|
8
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
9
9
|
from janito.i18n import tr
|
10
10
|
|
11
11
|
|
12
|
-
@
|
13
|
-
class
|
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
|
-
|
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 .
|
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
|
-
@
|
11
|
-
class
|
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 .
|
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
|
-
@
|
13
|
-
class
|
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 .
|
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
|
-
@
|
13
|
-
class
|
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).
|