janito 1.7.0__py3-none-any.whl β 1.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/__init__.py +1 -1
- janito/agent/config.py +1 -1
- janito/agent/config_defaults.py +2 -2
- janito/agent/conversation.py +70 -27
- janito/agent/conversation_api.py +104 -4
- janito/agent/conversation_exceptions.py +6 -0
- janito/agent/conversation_tool_calls.py +17 -3
- janito/agent/event.py +24 -0
- janito/agent/event_dispatcher.py +24 -0
- janito/agent/event_handler_protocol.py +5 -0
- janito/agent/event_system.py +15 -0
- janito/agent/message_handler.py +4 -1
- janito/agent/message_handler_protocol.py +5 -0
- janito/agent/openai_client.py +5 -8
- janito/agent/openai_schema_generator.py +23 -4
- janito/agent/profile_manager.py +15 -83
- janito/agent/queued_message_handler.py +22 -3
- janito/agent/rich_message_handler.py +66 -72
- janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +14 -0
- janito/agent/templates/profiles/system_prompt_template_base_pt.txt.j2 +13 -0
- janito/agent/test_handler_protocols.py +47 -0
- janito/agent/tests/__init__.py +1 -0
- janito/agent/tool_base.py +1 -1
- janito/agent/tool_executor.py +109 -0
- janito/agent/tool_registry.py +3 -75
- janito/agent/tool_use_tracker.py +46 -0
- janito/agent/tools/__init__.py +8 -9
- janito/agent/tools/ask_user.py +19 -11
- janito/agent/tools/create_directory.py +43 -28
- janito/agent/tools/create_file.py +60 -29
- janito/agent/tools/dir_walk_utils.py +16 -0
- janito/agent/tools/fetch_url.py +10 -11
- janito/agent/tools/find_files.py +49 -32
- janito/agent/tools/get_lines.py +54 -18
- janito/agent/tools/memory.py +32 -52
- janito/agent/tools/move_file.py +72 -23
- janito/agent/tools/outline_file/__init__.py +85 -0
- janito/agent/tools/outline_file/formatting.py +20 -0
- janito/agent/tools/outline_file/markdown_outline.py +14 -0
- janito/agent/tools/outline_file/python_outline.py +71 -0
- janito/agent/tools/present_choices.py +62 -0
- janito/agent/tools/present_choices_test.py +18 -0
- janito/agent/tools/remove_directory.py +31 -26
- janito/agent/tools/remove_file.py +31 -13
- janito/agent/tools/replace_text_in_file.py +135 -36
- janito/agent/tools/run_bash_command.py +47 -50
- janito/agent/tools/run_powershell_command.py +52 -36
- janito/agent/tools/run_python_command.py +49 -29
- janito/agent/tools/search_outline.py +17 -0
- janito/agent/tools/search_text.py +208 -0
- janito/agent/tools/tools_utils.py +47 -4
- janito/agent/tools/utils.py +14 -15
- janito/agent/tools/validate_file_syntax.py +163 -0
- janito/cli/arg_parser.py +36 -4
- janito/cli/logging_setup.py +7 -2
- janito/cli/main.py +96 -2
- janito/cli/runner/_termweb_log_utils.py +17 -0
- janito/cli/runner/cli_main.py +119 -77
- janito/cli/runner/config.py +2 -2
- janito/cli/termweb_starter.py +73 -0
- janito/cli_chat_shell/chat_loop.py +42 -7
- janito/cli_chat_shell/chat_state.py +1 -1
- janito/cli_chat_shell/chat_ui.py +0 -1
- janito/cli_chat_shell/commands/__init__.py +15 -6
- janito/cli_chat_shell/commands/{history_reset.py β history_start.py} +13 -5
- janito/cli_chat_shell/commands/lang.py +16 -0
- janito/cli_chat_shell/commands/prompt.py +42 -0
- janito/cli_chat_shell/commands/session_control.py +36 -1
- janito/cli_chat_shell/commands/termweb_log.py +86 -0
- janito/cli_chat_shell/commands/utility.py +5 -2
- janito/cli_chat_shell/commands/verbose.py +29 -0
- janito/cli_chat_shell/session_manager.py +9 -1
- janito/cli_chat_shell/shell_command_completer.py +20 -0
- janito/cli_chat_shell/ui.py +110 -99
- janito/i18n/__init__.py +35 -0
- janito/i18n/messages.py +23 -0
- janito/i18n/pt.py +46 -0
- janito/rich_utils.py +43 -43
- janito/termweb/app.py +95 -0
- janito/termweb/static/editor.html +238 -0
- janito/termweb/static/editor.html.bak +238 -0
- janito/termweb/static/explorer.html.bak +59 -0
- janito/termweb/static/favicon.ico +0 -0
- janito/termweb/static/favicon.ico.bak +0 -0
- janito/termweb/static/index.html +55 -0
- janito/termweb/static/index.html.bak +55 -0
- janito/termweb/static/index.html.bak.bak +175 -0
- janito/termweb/static/landing.html.bak +36 -0
- janito/termweb/static/termicon.svg +1 -0
- janito/termweb/static/termweb.css +235 -0
- janito/termweb/static/termweb.css.bak +286 -0
- janito/termweb/static/termweb.js +187 -0
- janito/termweb/static/termweb.js.bak +187 -0
- janito/termweb/static/termweb.js.bak.bak +157 -0
- janito/termweb/static/termweb_quickopen.js +135 -0
- janito/termweb/static/termweb_quickopen.js.bak +125 -0
- janito/web/app.py +4 -4
- {janito-1.7.0.dist-info β janito-1.8.0.dist-info}/METADATA +58 -25
- janito-1.8.0.dist-info/RECORD +127 -0
- {janito-1.7.0.dist-info β janito-1.8.0.dist-info}/WHEEL +1 -1
- janito/agent/templates/profiles/system_prompt_template_base.toml +0 -76
- janito/agent/templates/profiles/system_prompt_template_default.toml +0 -3
- janito/agent/templates/profiles/system_prompt_template_technical.toml +0 -13
- janito/agent/tests/test_prompt_toml.py +0 -61
- janito/agent/tool_registry_core.py +0 -2
- janito/agent/tools/get_file_outline.py +0 -146
- janito/agent/tools/py_compile_file.py +0 -40
- janito/agent/tools/replace_file.py +0 -51
- janito/agent/tools/search_files.py +0 -65
- janito/cli/runner/scan.py +0 -57
- janito/cli_chat_shell/commands/system.py +0 -73
- janito-1.7.0.dist-info/RECORD +0 -89
- {janito-1.7.0.dist-info β janito-1.8.0.dist-info}/entry_points.txt +0 -0
- {janito-1.7.0.dist-info β janito-1.8.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.7.0.dist-info β janito-1.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
import threading
|
2
|
+
from typing import Any, Dict, List
|
3
|
+
|
4
|
+
|
5
|
+
class ToolUseTracker:
|
6
|
+
_instance = None
|
7
|
+
_lock = threading.Lock()
|
8
|
+
|
9
|
+
def __new__(cls):
|
10
|
+
if not cls._instance:
|
11
|
+
with cls._lock:
|
12
|
+
if not cls._instance:
|
13
|
+
cls._instance = super().__new__(cls)
|
14
|
+
cls._instance._history = []
|
15
|
+
return cls._instance
|
16
|
+
|
17
|
+
def record(self, tool_name: str, params: Dict[str, Any]):
|
18
|
+
self._history.append({"tool": tool_name, "params": params})
|
19
|
+
|
20
|
+
def get_history(self) -> List[Dict[str, Any]]:
|
21
|
+
return list(self._history)
|
22
|
+
|
23
|
+
def get_operations_on_file(self, file_path: str) -> List[Dict[str, Any]]:
|
24
|
+
ops = []
|
25
|
+
for entry in self._history:
|
26
|
+
params = entry["params"]
|
27
|
+
if any(isinstance(v, str) and file_path in v for v in params.values()):
|
28
|
+
ops.append(entry)
|
29
|
+
return ops
|
30
|
+
|
31
|
+
def file_fully_read(self, file_path: str) -> bool:
|
32
|
+
for entry in self._history:
|
33
|
+
if entry["tool"] == "get_lines":
|
34
|
+
params = entry["params"]
|
35
|
+
if params.get("file_path") == file_path:
|
36
|
+
# If both from_line and to_line are None, full file was read
|
37
|
+
if (
|
38
|
+
params.get("from_line") is None
|
39
|
+
and params.get("to_line") is None
|
40
|
+
):
|
41
|
+
return True
|
42
|
+
return False
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def instance(cls):
|
46
|
+
return cls()
|
janito/agent/tools/__init__.py
CHANGED
@@ -3,11 +3,11 @@ from . import create_directory
|
|
3
3
|
from . import create_file
|
4
4
|
from . import fetch_url
|
5
5
|
from . import find_files
|
6
|
-
from . import get_file_outline
|
7
6
|
from . import get_lines
|
7
|
+
from . import outline_file
|
8
8
|
from . import gitignore_utils
|
9
9
|
from . import move_file
|
10
|
-
from . import
|
10
|
+
from . import validate_file_syntax
|
11
11
|
from . import remove_directory
|
12
12
|
from . import remove_file
|
13
13
|
from . import replace_text_in_file
|
@@ -15,10 +15,8 @@ from . import rich_live
|
|
15
15
|
from . import run_bash_command
|
16
16
|
from . import run_powershell_command
|
17
17
|
from . import run_python_command
|
18
|
-
from . import
|
19
|
-
from . import
|
20
|
-
from . import replace_file
|
21
|
-
from . import memory
|
18
|
+
from . import present_choices
|
19
|
+
from . import search_text
|
22
20
|
|
23
21
|
__all__ = [
|
24
22
|
"ask_user",
|
@@ -26,11 +24,11 @@ __all__ = [
|
|
26
24
|
"create_file",
|
27
25
|
"fetch_url",
|
28
26
|
"find_files",
|
29
|
-
"
|
27
|
+
"outline_file",
|
30
28
|
"get_lines",
|
31
29
|
"gitignore_utils",
|
32
30
|
"move_file",
|
33
|
-
"
|
31
|
+
"validate_file_syntax",
|
34
32
|
"remove_directory",
|
35
33
|
"remove_file",
|
36
34
|
"replace_text_in_file",
|
@@ -40,6 +38,7 @@ __all__ = [
|
|
40
38
|
"run_python_command",
|
41
39
|
"search_files",
|
42
40
|
"tools_utils",
|
43
|
-
"replace_file",
|
44
41
|
"memory",
|
42
|
+
"present_choices",
|
43
|
+
"search_text",
|
45
44
|
]
|
janito/agent/tools/ask_user.py
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
from janito.agent.tool_base import ToolBase
|
2
2
|
from janito.agent.tool_registry import register_tool
|
3
3
|
|
4
|
+
from rich import print as rich_print
|
5
|
+
from janito.i18n import tr
|
6
|
+
from rich.panel import Panel
|
7
|
+
from prompt_toolkit import PromptSession
|
8
|
+
from prompt_toolkit.key_binding import KeyBindings
|
9
|
+
from prompt_toolkit.enums import EditingMode
|
10
|
+
from prompt_toolkit.formatted_text import HTML
|
11
|
+
from prompt_toolkit.styles import Style
|
12
|
+
|
4
13
|
|
5
14
|
@register_tool(name="ask_user")
|
6
15
|
class AskUserTool(ToolBase):
|
@@ -16,16 +25,9 @@ class AskUserTool(ToolBase):
|
|
16
25
|
- "Some detailed answer..."
|
17
26
|
"""
|
18
27
|
|
19
|
-
def
|
20
|
-
from rich import print as rich_print
|
21
|
-
from rich.panel import Panel
|
22
|
-
from prompt_toolkit import PromptSession
|
23
|
-
from prompt_toolkit.key_binding import KeyBindings
|
24
|
-
from prompt_toolkit.enums import EditingMode
|
25
|
-
from prompt_toolkit.formatted_text import HTML
|
26
|
-
from prompt_toolkit.styles import Style
|
28
|
+
def run(self, question: str) -> str:
|
27
29
|
|
28
|
-
rich_print(Panel.fit(question, title="Question", style="cyan"))
|
30
|
+
rich_print(Panel.fit(question, title=tr("Question"), style="cyan"))
|
29
31
|
|
30
32
|
bindings = KeyBindings()
|
31
33
|
mode = {"multiline": False}
|
@@ -35,7 +37,13 @@ class AskUserTool(ToolBase):
|
|
35
37
|
pass
|
36
38
|
|
37
39
|
# F12 instruction rotation
|
38
|
-
_f12_instructions = [
|
40
|
+
_f12_instructions = [
|
41
|
+
tr("proceed"),
|
42
|
+
tr("go ahead"),
|
43
|
+
tr("continue"),
|
44
|
+
tr("next"),
|
45
|
+
tr("okay"),
|
46
|
+
]
|
39
47
|
_f12_index = {"value": 0}
|
40
48
|
|
41
49
|
@bindings.add("f12")
|
@@ -56,7 +64,7 @@ class AskUserTool(ToolBase):
|
|
56
64
|
)
|
57
65
|
|
58
66
|
def get_toolbar():
|
59
|
-
f12_hint = "
|
67
|
+
f12_hint = ""
|
60
68
|
if mode["multiline"]:
|
61
69
|
return HTML(
|
62
70
|
f"<b>Multiline mode (Esc+Enter to submit). Type /single to switch.</b>{f12_hint}"
|
@@ -1,50 +1,65 @@
|
|
1
1
|
from janito.agent.tool_registry import register_tool
|
2
2
|
from janito.agent.tools.utils import expand_path, display_path
|
3
3
|
from janito.agent.tool_base import ToolBase
|
4
|
+
from janito.i18n import tr
|
4
5
|
import os
|
5
|
-
import shutil
|
6
6
|
|
7
7
|
|
8
8
|
@register_tool(name="create_directory")
|
9
9
|
class CreateDirectoryTool(ToolBase):
|
10
10
|
"""
|
11
|
-
Create a new directory at the specified
|
12
|
-
|
11
|
+
Create a new directory at the specified file_path.
|
13
12
|
Args:
|
14
|
-
|
15
|
-
overwrite (bool, optional): Whether to overwrite if the directory exists. Defaults to False.
|
13
|
+
file_path (str): Path for the new directory.
|
16
14
|
Returns:
|
17
15
|
str: Status message indicating the result. Example:
|
18
|
-
- "
|
19
|
-
- "
|
16
|
+
- "β
Successfully created the directory at ..."
|
17
|
+
- "β Cannot create directory: ..."
|
20
18
|
"""
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
disp_path = display_path(original_path, path)
|
20
|
+
def run(self, file_path: str) -> str:
|
21
|
+
file_path = expand_path(file_path)
|
22
|
+
disp_path = display_path(file_path)
|
26
23
|
self.report_info(
|
27
|
-
|
24
|
+
tr("π Creating directory: '{disp_path}' ...", disp_path=disp_path)
|
28
25
|
)
|
29
26
|
try:
|
30
|
-
if os.path.exists(
|
31
|
-
if not os.path.isdir(
|
27
|
+
if os.path.exists(file_path):
|
28
|
+
if not os.path.isdir(file_path):
|
32
29
|
self.report_error(
|
33
|
-
|
30
|
+
tr(
|
31
|
+
"β Path '{disp_path}' exists and is not a directory.",
|
32
|
+
disp_path=disp_path,
|
33
|
+
)
|
34
34
|
)
|
35
|
-
return
|
36
|
-
|
37
|
-
|
38
|
-
f"\u2757 Directory '{disp_path}' already exists (overwrite=False)"
|
35
|
+
return tr(
|
36
|
+
"β Path '{disp_path}' exists and is not a directory.",
|
37
|
+
disp_path=disp_path,
|
39
38
|
)
|
40
|
-
|
41
|
-
|
39
|
+
self.report_error(
|
40
|
+
tr(
|
41
|
+
"β Directory '{disp_path}' already exists.",
|
42
|
+
disp_path=disp_path,
|
42
43
|
)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
)
|
45
|
+
return tr(
|
46
|
+
"β Cannot create directory: '{disp_path}' already exists.",
|
47
|
+
disp_path=disp_path,
|
48
|
+
)
|
49
|
+
os.makedirs(file_path, exist_ok=True)
|
50
|
+
self.report_success(
|
51
|
+
tr("β
Directory created at '{disp_path}'", disp_path=disp_path)
|
52
|
+
)
|
53
|
+
return tr(
|
54
|
+
"β
Successfully created the directory at '{disp_path}'.",
|
55
|
+
disp_path=disp_path,
|
56
|
+
)
|
48
57
|
except Exception as e:
|
49
|
-
self.report_error(
|
50
|
-
|
58
|
+
self.report_error(
|
59
|
+
tr(
|
60
|
+
"β Error creating directory '{disp_path}': {error}",
|
61
|
+
disp_path=disp_path,
|
62
|
+
error=e,
|
63
|
+
)
|
64
|
+
)
|
65
|
+
return tr("β Cannot create directory: {error}", error=e)
|
@@ -3,45 +3,76 @@ import shutil
|
|
3
3
|
from janito.agent.tool_registry import register_tool
|
4
4
|
from janito.agent.tools.utils import expand_path, display_path
|
5
5
|
from janito.agent.tool_base import ToolBase
|
6
|
-
from janito.
|
6
|
+
from janito.i18n import tr
|
7
7
|
|
8
8
|
|
9
9
|
@register_tool(name="create_file")
|
10
10
|
class CreateFileTool(ToolBase):
|
11
11
|
"""
|
12
|
-
Create a new file with the given content
|
13
|
-
|
14
|
-
This tool will NOT overwrite existing files. If the file already exists, the operation fails and no changes are made to the file itself.
|
15
|
-
|
12
|
+
Create a new file with the given content, or overwrite if specified.
|
16
13
|
Args:
|
17
|
-
|
14
|
+
file_path (str): Path to the file to create or overwrite.
|
18
15
|
content (str): Content to write to the file.
|
19
|
-
|
16
|
+
overwrite (bool, optional): If True, overwrite the file if it exists. Defaults to False.
|
17
|
+
CRITICAL: If you use overwrite=True, you MUST provide the full content for the file. Using placeholders or partial content will result in file corruption. Before overwriting, read the full original file.
|
20
18
|
Returns:
|
21
19
|
str: Status message indicating the result. Example:
|
22
|
-
- "
|
23
|
-
- "\u2757 Cannot create file: ..."
|
20
|
+
- "β
Successfully created the file at ..."
|
24
21
|
"""
|
25
22
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
return
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
23
|
+
def run(self, file_path: str, content: str, overwrite: bool = False) -> str:
|
24
|
+
expanded_file_path = expand_path(file_path)
|
25
|
+
disp_path = display_path(expanded_file_path)
|
26
|
+
file_path = expanded_file_path
|
27
|
+
backup_path = None
|
28
|
+
if os.path.exists(file_path):
|
29
|
+
if not overwrite:
|
30
|
+
return tr(
|
31
|
+
"β οΈ File already exists at '{disp_path}'. Use overwrite=True to overwrite.",
|
32
|
+
disp_path=disp_path,
|
33
|
+
)
|
34
|
+
# Check ToolUseTracker for full read before overwrite
|
35
|
+
try:
|
36
|
+
from janito.agent.tool_use_tracker import ToolUseTracker
|
37
|
+
|
38
|
+
tracker = ToolUseTracker()
|
39
|
+
if not tracker.file_fully_read(file_path):
|
40
|
+
self.report_error(
|
41
|
+
"β Refusing to overwrite file: full file has not been read with get_lines."
|
42
|
+
)
|
43
|
+
return tr(
|
44
|
+
"β Refusing to overwrite file: full file has not been read with get_lines."
|
45
|
+
)
|
46
|
+
except Exception as e:
|
47
|
+
self.report_error(f"[ToolUseTracker] Error: {e}")
|
48
|
+
return tr("[ToolUseTracker] Error: {e}")
|
49
|
+
backup_path = file_path + ".bak"
|
50
|
+
shutil.copy2(file_path, backup_path)
|
51
|
+
self.report_info(
|
52
|
+
tr("π Updating file: '{disp_path}' ...", disp_path=disp_path)
|
53
|
+
)
|
54
|
+
mode = "w"
|
55
|
+
updated = True
|
56
|
+
else:
|
57
|
+
dir_name = os.path.dirname(file_path)
|
58
|
+
if dir_name:
|
59
|
+
os.makedirs(dir_name, exist_ok=True)
|
60
|
+
self.report_info(
|
61
|
+
tr("π Creating file: '{disp_path}' ...", disp_path=disp_path)
|
62
|
+
)
|
63
|
+
mode = "w"
|
64
|
+
updated = False
|
65
|
+
with open(file_path, mode, encoding="utf-8", errors="replace") as f:
|
44
66
|
f.write(content)
|
45
67
|
new_lines = content.count("\n") + 1 if content else 0
|
46
|
-
|
47
|
-
|
68
|
+
if updated:
|
69
|
+
self.report_success(tr("β
({new_lines} lines).", new_lines=new_lines))
|
70
|
+
msg = tr(
|
71
|
+
"β
Updated file ({new_lines} lines, backup at {backup_path}).",
|
72
|
+
new_lines=new_lines,
|
73
|
+
backup_path=backup_path,
|
74
|
+
)
|
75
|
+
return msg
|
76
|
+
else:
|
77
|
+
self.report_success(tr("β
({new_lines} lines).", new_lines=new_lines))
|
78
|
+
return tr("β
Created file ({new_lines} lines).", new_lines=new_lines)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import os
|
2
|
+
from janito.agent.tools.gitignore_utils import filter_ignored
|
3
|
+
|
4
|
+
|
5
|
+
def walk_dir_with_gitignore(root_dir, max_depth=0):
|
6
|
+
"""
|
7
|
+
Walks the directory tree starting at root_dir, yielding (root, dirs, files) tuples,
|
8
|
+
with .gitignore rules applied. If max_depth > 0, limits recursion to that depth.
|
9
|
+
"""
|
10
|
+
for root, dirs, files in os.walk(root_dir):
|
11
|
+
rel_path = os.path.relpath(root, root_dir)
|
12
|
+
depth = 0 if rel_path == "." else rel_path.count(os.sep) + 1
|
13
|
+
if max_depth > 0 and depth > max_depth:
|
14
|
+
continue
|
15
|
+
dirs, files = filter_ignored(root, dirs, files)
|
16
|
+
yield root, dirs, files
|
janito/agent/tools/fetch_url.py
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
import requests
|
2
2
|
from bs4 import BeautifulSoup
|
3
3
|
from janito.agent.tool_registry import register_tool
|
4
|
-
|
5
4
|
from janito.agent.tool_base import ToolBase
|
5
|
+
from janito.i18n import tr
|
6
6
|
|
7
7
|
|
8
8
|
@register_tool(name="fetch_url")
|
9
9
|
class FetchUrlTool(ToolBase):
|
10
10
|
"""
|
11
11
|
Fetch the content of a web page and extract its text.
|
12
|
-
|
13
12
|
Args:
|
14
13
|
url (str): The URL of the web page to fetch.
|
15
14
|
search_strings (list[str], optional): Strings to search for in the page content.
|
@@ -20,22 +19,23 @@ class FetchUrlTool(ToolBase):
|
|
20
19
|
- "Warning: Empty URL provided. Operation skipped."
|
21
20
|
"""
|
22
21
|
|
23
|
-
def
|
22
|
+
def run(self, url: str, search_strings: list[str] = None) -> str:
|
24
23
|
if not url.strip():
|
25
|
-
self.report_warning("β οΈ Warning: Empty URL provided. Operation skipped.")
|
26
|
-
return "Warning: Empty URL provided. Operation skipped."
|
27
|
-
self.report_info(
|
24
|
+
self.report_warning(tr("β οΈ Warning: Empty URL provided. Operation skipped."))
|
25
|
+
return tr("Warning: Empty URL provided. Operation skipped.")
|
26
|
+
self.report_info(tr("π Fetching URL: {url} ...", url=url))
|
28
27
|
response = requests.get(url, timeout=10)
|
29
28
|
response.raise_for_status()
|
30
29
|
self.update_progress(
|
31
30
|
{
|
32
31
|
"event": "progress",
|
33
|
-
"message":
|
32
|
+
"message": tr(
|
33
|
+
"Fetched URL with status {status}", status=response.status_code
|
34
|
+
),
|
34
35
|
}
|
35
36
|
)
|
36
37
|
soup = BeautifulSoup(response.text, "html.parser")
|
37
38
|
text = soup.get_text(separator="\n")
|
38
|
-
|
39
39
|
if search_strings:
|
40
40
|
filtered = []
|
41
41
|
for s in search_strings:
|
@@ -48,7 +48,6 @@ class FetchUrlTool(ToolBase):
|
|
48
48
|
if filtered:
|
49
49
|
text = "\n...\n".join(filtered)
|
50
50
|
else:
|
51
|
-
text = "No lines found for the provided search strings."
|
52
|
-
|
53
|
-
self.report_success("β
Result")
|
51
|
+
text = tr("No lines found for the provided search strings.")
|
52
|
+
self.report_success(tr("β
Result"))
|
54
53
|
return text
|
janito/agent/tools/find_files.py
CHANGED
@@ -1,52 +1,69 @@
|
|
1
1
|
from janito.agent.tool_base import ToolBase
|
2
2
|
from janito.agent.tool_registry import register_tool
|
3
|
-
from janito.agent.tools.tools_utils import pluralize
|
4
|
-
|
3
|
+
from janito.agent.tools.tools_utils import pluralize, display_path
|
4
|
+
from janito.agent.tools.dir_walk_utils import walk_dir_with_gitignore
|
5
|
+
from janito.i18n import tr
|
5
6
|
import fnmatch
|
6
|
-
|
7
|
+
import os
|
7
8
|
|
8
9
|
|
9
10
|
@register_tool(name="find_files")
|
10
11
|
class FindFilesTool(ToolBase):
|
11
12
|
"""
|
12
13
|
Find files in one or more directories matching a pattern. Respects .gitignore.
|
13
|
-
|
14
14
|
Args:
|
15
|
-
|
16
|
-
pattern (str): File pattern to match. Uses Unix shell-style wildcards (fnmatch), e.g. '*.py', 'data_??.csv', '[a-z]*.txt'.
|
17
|
-
|
15
|
+
paths (str): String of one or more paths (space-separated) to search in. Each path can be a directory.
|
16
|
+
pattern (str): File pattern(s) to match. Multiple patterns can be separated by spaces. Uses Unix shell-style wildcards (fnmatch), e.g. '*.py', 'data_??.csv', '[a-z]*.txt'.
|
17
|
+
max_depth (int, optional): Maximum directory depth to search. If 0 (default), search is recursive with no depth limit. If >0, limits recursion to that depth. Setting max_depth=1 disables recursion (only top-level directory).
|
18
|
+
max_results (int, optional): Maximum number of results to return. 0 means no limit (default).
|
18
19
|
Returns:
|
19
20
|
str: Newline-separated list of matching file paths. Example:
|
20
21
|
"/path/to/file1.py\n/path/to/file2.py"
|
21
22
|
"Warning: Empty file pattern provided. Operation skipped."
|
23
|
+
If max_results is reached, appends a note to the output.
|
22
24
|
"""
|
23
25
|
|
24
|
-
def
|
25
|
-
self,
|
26
|
-
directories: list[str],
|
27
|
-
pattern: str,
|
28
|
-
recursive: bool = True,
|
29
|
-
) -> str:
|
30
|
-
import os
|
31
|
-
|
26
|
+
def run(self, paths: str, pattern: str, max_depth: int = 0) -> str:
|
32
27
|
if not pattern:
|
33
28
|
self.report_warning(
|
34
|
-
"β οΈ
|
29
|
+
tr("β οΈ Warning: Empty file pattern provided. Operation skipped.")
|
35
30
|
)
|
36
|
-
return "Warning: Empty file pattern provided. Operation skipped."
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
for directory in directories:
|
31
|
+
return tr("Warning: Empty file pattern provided. Operation skipped.")
|
32
|
+
output = set()
|
33
|
+
patterns = pattern.split()
|
34
|
+
for directory in paths.split():
|
41
35
|
disp_path = display_path(directory)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
36
|
+
depth_msg = (
|
37
|
+
tr(" (max depth: {max_depth})", max_depth=max_depth)
|
38
|
+
if max_depth > 0
|
39
|
+
else ""
|
40
|
+
)
|
41
|
+
self.report_info(
|
42
|
+
tr(
|
43
|
+
"π Searching for files '{pattern}' in '{disp_path}'{depth_msg} ...",
|
44
|
+
pattern=pattern,
|
45
|
+
disp_path=disp_path,
|
46
|
+
depth_msg=depth_msg,
|
47
|
+
)
|
48
|
+
)
|
49
|
+
for root, dirs, files in walk_dir_with_gitignore(
|
50
|
+
directory, max_depth=max_depth
|
51
|
+
):
|
52
|
+
for pat in patterns:
|
53
|
+
for filename in fnmatch.filter(files, pat):
|
54
|
+
output.add(os.path.join(root, filename))
|
55
|
+
self.report_success(
|
56
|
+
tr(
|
57
|
+
" β
{count} {file_word} found",
|
58
|
+
count=len(output),
|
59
|
+
file_word=pluralize("file", len(output)),
|
60
|
+
)
|
61
|
+
)
|
62
|
+
# If searching in '.', strip leading './' from results
|
63
|
+
if paths.strip() == ".":
|
64
|
+
output = {
|
65
|
+
p[2:] if (p.startswith("./") or p.startswith(".\\")) else p
|
66
|
+
for p in output
|
67
|
+
}
|
68
|
+
result = "\n".join(sorted(output))
|
69
|
+
return result
|
janito/agent/tools/get_lines.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
from janito.agent.tool_base import ToolBase
|
2
2
|
from janito.agent.tool_registry import register_tool
|
3
3
|
from janito.agent.tools.tools_utils import pluralize
|
4
|
+
from janito.i18n import tr
|
4
5
|
|
5
6
|
|
6
7
|
@register_tool(name="get_lines")
|
7
8
|
class GetLinesTool(ToolBase):
|
8
9
|
"""
|
9
10
|
Read lines from a file. Returns specific lines if a range is provided, or the entire file if no range is given. If both from_line and to_line are None, the entire file is returned in one callβno need to chunk or split requests when reading the full file.
|
10
|
-
|
11
11
|
Args:
|
12
12
|
file_path (str): Path to the file to read lines from.
|
13
13
|
from_line (int, optional): Starting line number (1-based). If None, starts from the first line.
|
@@ -20,15 +20,21 @@ class GetLinesTool(ToolBase):
|
|
20
20
|
- "β not found"
|
21
21
|
"""
|
22
22
|
|
23
|
-
def
|
23
|
+
def run(self, file_path: str, from_line: int = None, to_line: int = None) -> str:
|
24
24
|
from janito.agent.tools.tools_utils import display_path
|
25
25
|
|
26
26
|
disp_path = display_path(file_path)
|
27
27
|
if from_line and to_line:
|
28
|
-
self.report_info(
|
28
|
+
self.report_info(
|
29
|
+
tr(
|
30
|
+
"π Reading {disp_path} {from_line}-{to_line}",
|
31
|
+
disp_path=disp_path,
|
32
|
+
from_line=from_line,
|
33
|
+
to_line=to_line,
|
34
|
+
)
|
35
|
+
)
|
29
36
|
else:
|
30
|
-
self.report_info(
|
31
|
-
|
37
|
+
self.report_info(tr("π Reading {disp_path} all", disp_path=disp_path))
|
32
38
|
try:
|
33
39
|
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
34
40
|
lines = f.readlines()
|
@@ -44,32 +50,62 @@ class GetLinesTool(ToolBase):
|
|
44
50
|
at_end = True
|
45
51
|
if at_end:
|
46
52
|
self.report_success(
|
47
|
-
|
53
|
+
tr(
|
54
|
+
" β
{selected_len} {line_word} (end)",
|
55
|
+
selected_len=selected_len,
|
56
|
+
line_word=pluralize("line", selected_len),
|
57
|
+
)
|
48
58
|
)
|
49
59
|
elif to_line < total_lines:
|
50
60
|
self.report_success(
|
51
|
-
|
61
|
+
tr(
|
62
|
+
" β
{selected_len} {line_word} ({remaining} to eof)",
|
63
|
+
selected_len=selected_len,
|
64
|
+
line_word=pluralize("line", selected_len),
|
65
|
+
remaining=total_lines - to_line,
|
66
|
+
)
|
52
67
|
)
|
53
68
|
else:
|
54
69
|
self.report_success(
|
55
|
-
|
70
|
+
tr(
|
71
|
+
" β
{selected_len} {line_word}",
|
72
|
+
selected_len=selected_len,
|
73
|
+
line_word=pluralize("line", selected_len),
|
74
|
+
)
|
56
75
|
)
|
57
|
-
# Prepare header
|
58
76
|
if from_line and to_line:
|
59
77
|
if to_line >= total_lines or selected_len < (to_line - from_line + 1):
|
60
|
-
header =
|
78
|
+
header = tr(
|
79
|
+
"---\n{disp_path} {from_line}-{to_line} (end)\n---\n",
|
80
|
+
disp_path=disp_path,
|
81
|
+
from_line=from_line,
|
82
|
+
to_line=to_line,
|
83
|
+
)
|
61
84
|
else:
|
62
|
-
header =
|
85
|
+
header = tr(
|
86
|
+
"---\n{disp_path} {from_line}-{to_line} (of {total_lines})\n---\n",
|
87
|
+
disp_path=disp_path,
|
88
|
+
from_line=from_line,
|
89
|
+
to_line=to_line,
|
90
|
+
total_lines=total_lines,
|
91
|
+
)
|
63
92
|
elif from_line:
|
64
|
-
header =
|
93
|
+
header = tr(
|
94
|
+
"---\n{disp_path} {from_line}-END (of {total_lines})\n---\n",
|
95
|
+
disp_path=disp_path,
|
96
|
+
from_line=from_line,
|
97
|
+
total_lines=total_lines,
|
98
|
+
)
|
65
99
|
else:
|
66
|
-
header = (
|
67
|
-
|
100
|
+
header = tr(
|
101
|
+
"---\n{disp_path} All lines (total: {total_lines})\n---\n",
|
102
|
+
disp_path=disp_path,
|
103
|
+
total_lines=total_lines,
|
68
104
|
)
|
69
105
|
return header + "".join(selected)
|
70
106
|
except Exception as e:
|
71
107
|
if isinstance(e, FileNotFoundError):
|
72
|
-
self.report_error("β not found")
|
73
|
-
return "β not found"
|
74
|
-
self.report_error(
|
75
|
-
return
|
108
|
+
self.report_error(tr("β not found"))
|
109
|
+
return tr("β not found")
|
110
|
+
self.report_error(tr(" β Error: {error}", error=e))
|
111
|
+
return tr("Error reading file: {error}", error=e)
|