janito 1.14.3__py3-none-any.whl → 2.0.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 +6 -1
- janito/__main__.py +1 -1
- janito/agent/setup_agent.py +139 -0
- janito/agent/templates/profiles/{system_prompt_template_base.txt.j2 → system_prompt_template_main.txt.j2} +1 -1
- janito/cli/__init__.py +9 -0
- janito/cli/chat_mode/bindings.py +37 -0
- janito/cli/chat_mode/chat_entry.py +23 -0
- janito/cli/chat_mode/prompt_style.py +19 -0
- janito/cli/chat_mode/session.py +272 -0
- janito/{shell/prompt/completer.py → cli/chat_mode/shell/autocomplete.py} +7 -6
- janito/cli/chat_mode/shell/commands/__init__.py +55 -0
- janito/cli/chat_mode/shell/commands/base.py +9 -0
- janito/cli/chat_mode/shell/commands/clear.py +12 -0
- janito/{shell → cli/chat_mode/shell}/commands/conversation_restart.py +34 -30
- janito/cli/chat_mode/shell/commands/edit.py +25 -0
- janito/cli/chat_mode/shell/commands/help.py +16 -0
- janito/cli/chat_mode/shell/commands/history_view.py +93 -0
- janito/cli/chat_mode/shell/commands/lang.py +25 -0
- janito/cli/chat_mode/shell/commands/last.py +137 -0
- janito/cli/chat_mode/shell/commands/livelogs.py +49 -0
- janito/cli/chat_mode/shell/commands/multi.py +51 -0
- janito/cli/chat_mode/shell/commands/prompt.py +64 -0
- janito/cli/chat_mode/shell/commands/role.py +36 -0
- janito/cli/chat_mode/shell/commands/session.py +40 -0
- janito/{shell → cli/chat_mode/shell}/commands/session_control.py +2 -2
- janito/cli/chat_mode/shell/commands/termweb_log.py +92 -0
- janito/cli/chat_mode/shell/commands/tools.py +32 -0
- janito/{shell → cli/chat_mode/shell}/commands/utility.py +4 -7
- janito/{shell → cli/chat_mode/shell}/commands/verbose.py +5 -5
- janito/cli/chat_mode/shell/session/__init__.py +1 -0
- janito/{shell → cli/chat_mode/shell}/session/manager.py +9 -1
- janito/cli/chat_mode/toolbar.py +90 -0
- janito/cli/cli_commands/list_models.py +35 -0
- janito/cli/cli_commands/list_providers.py +9 -0
- janito/cli/cli_commands/list_tools.py +53 -0
- janito/cli/cli_commands/model_selection.py +50 -0
- janito/cli/cli_commands/model_utils.py +84 -0
- janito/cli/cli_commands/set_api_key.py +19 -0
- janito/cli/cli_commands/show_config.py +51 -0
- janito/cli/cli_commands/show_system_prompt.py +62 -0
- janito/cli/config.py +28 -0
- janito/cli/console.py +3 -0
- janito/cli/core/__init__.py +4 -0
- janito/cli/core/event_logger.py +59 -0
- janito/cli/core/getters.py +31 -0
- janito/cli/core/runner.py +141 -0
- janito/cli/core/setters.py +174 -0
- janito/cli/core/unsetters.py +54 -0
- janito/cli/main.py +8 -196
- janito/cli/main_cli.py +312 -0
- janito/cli/prompt_core.py +230 -0
- janito/cli/prompt_handler.py +6 -0
- janito/cli/rich_terminal_reporter.py +101 -0
- janito/cli/single_shot_mode/__init__.py +6 -0
- janito/cli/single_shot_mode/handler.py +137 -0
- janito/cli/termweb_starter.py +73 -24
- janito/cli/utils.py +25 -0
- janito/cli/verbose_output.py +196 -0
- janito/config.py +5 -0
- janito/config_manager.py +110 -0
- janito/conversation_history.py +30 -0
- janito/{agent/tools_utils/dir_walk_utils.py → dir_walk_utils.py} +3 -2
- janito/driver_events.py +98 -0
- janito/drivers/anthropic/driver.py +113 -0
- janito/drivers/azure_openai/driver.py +36 -0
- janito/drivers/driver_registry.py +33 -0
- janito/drivers/google_genai/driver.py +54 -0
- janito/drivers/google_genai/schema_generator.py +67 -0
- janito/drivers/mistralai/driver.py +41 -0
- janito/drivers/openai/driver.py +334 -0
- janito/event_bus/__init__.py +2 -0
- janito/event_bus/bus.py +68 -0
- janito/event_bus/event.py +15 -0
- janito/event_bus/handler.py +31 -0
- janito/event_bus/queue_bus.py +57 -0
- janito/exceptions.py +23 -0
- janito/formatting_token.py +54 -0
- janito/i18n/pt.py +1 -0
- janito/llm/__init__.py +5 -0
- janito/llm/agent.py +443 -0
- janito/llm/auth.py +62 -0
- janito/llm/driver.py +239 -0
- janito/llm/driver_config.py +34 -0
- janito/llm/driver_config_builder.py +34 -0
- janito/llm/driver_input.py +12 -0
- janito/llm/message_parts.py +60 -0
- janito/llm/model.py +38 -0
- janito/llm/provider.py +187 -0
- janito/perf_singleton.py +3 -0
- janito/performance_collector.py +167 -0
- janito/provider_config.py +98 -0
- janito/provider_registry.py +152 -0
- janito/providers/__init__.py +7 -0
- janito/providers/anthropic/model_info.py +22 -0
- janito/providers/anthropic/provider.py +65 -0
- janito/providers/azure_openai/model_info.py +15 -0
- janito/providers/azure_openai/provider.py +72 -0
- janito/providers/deepseek/__init__.py +1 -0
- janito/providers/deepseek/model_info.py +16 -0
- janito/providers/deepseek/provider.py +91 -0
- janito/providers/google/__init__.py +1 -0
- janito/providers/google/model_info.py +40 -0
- janito/providers/google/provider.py +69 -0
- janito/providers/mistralai/model_info.py +37 -0
- janito/providers/mistralai/provider.py +69 -0
- janito/providers/openai/__init__.py +1 -0
- janito/providers/openai/model_info.py +137 -0
- janito/providers/openai/provider.py +107 -0
- janito/providers/openai/schema_generator.py +63 -0
- janito/providers/provider_static_info.py +21 -0
- janito/providers/registry.py +26 -0
- janito/report_events.py +38 -0
- janito/termweb/app.py +1 -1
- janito/tools/__init__.py +16 -0
- janito/tools/adapters/__init__.py +1 -0
- janito/tools/adapters/local/__init__.py +54 -0
- janito/tools/adapters/local/adapter.py +92 -0
- janito/{agent/tools → tools/adapters/local}/ask_user.py +30 -13
- janito/tools/adapters/local/copy_file.py +84 -0
- janito/{agent/tools → tools/adapters/local}/create_directory.py +11 -10
- janito/tools/adapters/local/create_file.py +82 -0
- janito/tools/adapters/local/delete_text_in_file.py +136 -0
- janito/{agent/tools → tools/adapters/local}/fetch_url.py +18 -19
- janito/tools/adapters/local/find_files.py +140 -0
- janito/tools/adapters/local/get_file_outline/core.py +151 -0
- janito/{agent/tools → tools/adapters/local}/get_file_outline/python_outline.py +125 -0
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -0
- janito/{agent/tools → tools/adapters/local}/get_file_outline/search_outline.py +12 -7
- janito/{agent/tools → tools/adapters/local}/move_file.py +13 -9
- janito/{agent/tools → tools/adapters/local}/open_url.py +7 -5
- janito/tools/adapters/local/python_code_run.py +165 -0
- janito/tools/adapters/local/python_command_run.py +163 -0
- janito/tools/adapters/local/python_file_run.py +162 -0
- janito/{agent/tools → tools/adapters/local}/remove_directory.py +15 -9
- janito/{agent/tools → tools/adapters/local}/remove_file.py +17 -14
- janito/{agent/tools → tools/adapters/local}/replace_text_in_file.py +27 -22
- janito/tools/adapters/local/run_bash_command.py +176 -0
- janito/tools/adapters/local/run_powershell_command.py +219 -0
- janito/{agent/tools → tools/adapters/local}/search_text/core.py +32 -12
- janito/{agent/tools → tools/adapters/local}/search_text/match_lines.py +13 -4
- janito/{agent/tools → tools/adapters/local}/search_text/pattern_utils.py +12 -4
- janito/{agent/tools → tools/adapters/local}/search_text/traverse_directory.py +15 -2
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/core.py +12 -11
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/css_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/html_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/js_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/json_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/markdown_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/ps1_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/python_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/xml_validator.py +1 -1
- janito/{agent/tools → tools/adapters/local}/validate_file_syntax/yaml_validator.py +1 -1
- janito/{agent/tools/get_lines.py → tools/adapters/local/view_file.py} +45 -27
- janito/tools/inspect_registry.py +17 -0
- janito/tools/tool_base.py +105 -0
- janito/tools/tool_events.py +58 -0
- janito/tools/tool_run_exception.py +12 -0
- janito/{agent → tools}/tool_use_tracker.py +2 -4
- janito/{agent/tools_utils/utils.py → tools/tool_utils.py} +18 -9
- janito/tools/tools_adapter.py +207 -0
- janito/tools/tools_schema.py +104 -0
- janito/utils.py +11 -0
- janito/version.py +4 -0
- janito-2.0.0.dist-info/METADATA +232 -0
- janito-2.0.0.dist-info/RECORD +180 -0
- janito/agent/__init__.py +0 -0
- janito/agent/api_exceptions.py +0 -4
- janito/agent/config.py +0 -147
- janito/agent/config_defaults.py +0 -12
- janito/agent/config_utils.py +0 -0
- janito/agent/content_handler.py +0 -0
- janito/agent/conversation.py +0 -238
- janito/agent/conversation_api.py +0 -306
- janito/agent/conversation_exceptions.py +0 -18
- janito/agent/conversation_tool_calls.py +0 -39
- janito/agent/conversation_ui.py +0 -17
- janito/agent/event.py +0 -24
- janito/agent/event_dispatcher.py +0 -24
- janito/agent/event_handler_protocol.py +0 -5
- janito/agent/event_system.py +0 -15
- janito/agent/llm_conversation_history.py +0 -82
- janito/agent/message_handler.py +0 -20
- janito/agent/message_handler_protocol.py +0 -5
- janito/agent/openai_client.py +0 -149
- janito/agent/openai_schema_generator.py +0 -187
- janito/agent/profile_manager.py +0 -96
- janito/agent/queued_message_handler.py +0 -50
- janito/agent/rich_live.py +0 -32
- janito/agent/rich_message_handler.py +0 -115
- janito/agent/runtime_config.py +0 -36
- janito/agent/test_handler_protocols.py +0 -47
- janito/agent/test_openai_schema_generator.py +0 -93
- janito/agent/tests/__init__.py +0 -1
- janito/agent/tool_base.py +0 -63
- janito/agent/tool_executor.py +0 -122
- janito/agent/tool_registry.py +0 -49
- janito/agent/tools/__init__.py +0 -47
- janito/agent/tools/create_file.py +0 -59
- janito/agent/tools/delete_text_in_file.py +0 -97
- janito/agent/tools/find_files.py +0 -106
- janito/agent/tools/get_file_outline/core.py +0 -81
- janito/agent/tools/present_choices.py +0 -64
- janito/agent/tools/python_command_runner.py +0 -201
- janito/agent/tools/python_file_runner.py +0 -199
- janito/agent/tools/python_stdin_runner.py +0 -208
- janito/agent/tools/replace_file.py +0 -72
- janito/agent/tools/run_bash_command.py +0 -218
- janito/agent/tools/run_powershell_command.py +0 -251
- janito/agent/tools_utils/__init__.py +0 -1
- janito/agent/tools_utils/action_type.py +0 -7
- janito/agent/tools_utils/test_gitignore_utils.py +0 -46
- janito/cli/_livereload_log_utils.py +0 -13
- janito/cli/_print_config.py +0 -96
- janito/cli/_termweb_log_utils.py +0 -17
- janito/cli/_utils.py +0 -9
- janito/cli/arg_parser.py +0 -272
- janito/cli/cli_main.py +0 -281
- janito/cli/config_commands.py +0 -211
- janito/cli/config_runner.py +0 -35
- janito/cli/formatting_runner.py +0 -12
- janito/cli/livereload_starter.py +0 -60
- janito/cli/logging_setup.py +0 -38
- janito/cli/one_shot.py +0 -80
- janito/livereload/app.py +0 -25
- janito/rich_utils.py +0 -59
- janito/shell/__init__.py +0 -0
- janito/shell/commands/__init__.py +0 -61
- janito/shell/commands/config.py +0 -22
- janito/shell/commands/edit.py +0 -24
- janito/shell/commands/history_view.py +0 -18
- janito/shell/commands/lang.py +0 -19
- janito/shell/commands/livelogs.py +0 -42
- janito/shell/commands/prompt.py +0 -62
- janito/shell/commands/termweb_log.py +0 -94
- janito/shell/commands/tools.py +0 -26
- janito/shell/commands/track.py +0 -36
- janito/shell/main.py +0 -326
- janito/shell/prompt/load_prompt.py +0 -57
- janito/shell/prompt/session_setup.py +0 -57
- janito/shell/session/config.py +0 -109
- janito/shell/session/history.py +0 -0
- janito/shell/ui/interactive.py +0 -226
- janito/termweb/static/editor.css +0 -158
- janito/termweb/static/editor.css.bak +0 -145
- janito/termweb/static/editor.html +0 -46
- janito/termweb/static/editor.html.bak +0 -46
- janito/termweb/static/editor.js +0 -265
- janito/termweb/static/editor.js.bak +0 -259
- janito/termweb/static/explorer.html.bak +0 -59
- janito/termweb/static/favicon.ico +0 -0
- janito/termweb/static/favicon.ico.bak +0 -0
- janito/termweb/static/index.html +0 -53
- janito/termweb/static/index.html.bak +0 -54
- janito/termweb/static/index.html.bak.bak +0 -175
- janito/termweb/static/landing.html.bak +0 -36
- janito/termweb/static/termicon.svg +0 -1
- janito/termweb/static/termweb.css +0 -214
- janito/termweb/static/termweb.css.bak +0 -237
- janito/termweb/static/termweb.js +0 -162
- janito/termweb/static/termweb.js.bak +0 -168
- janito/termweb/static/termweb.js.bak.bak +0 -157
- janito/termweb/static/termweb_quickopen.js +0 -135
- janito/termweb/static/termweb_quickopen.js.bak +0 -125
- janito/tests/test_rich_utils.py +0 -44
- janito/web/__init__.py +0 -0
- janito/web/__main__.py +0 -25
- janito/web/app.py +0 -145
- janito-1.14.3.dist-info/METADATA +0 -313
- janito-1.14.3.dist-info/RECORD +0 -162
- janito-1.14.3.dist-info/licenses/LICENSE +0 -21
- /janito/{shell → cli/chat_mode/shell}/input_history.py +0 -0
- /janito/{shell/commands/session.py → cli/chat_mode/shell/session/history.py} +0 -0
- /janito/{agent/tools_utils/formatting.py → formatting.py} +0 -0
- /janito/{agent/tools_utils/gitignore_utils.py → gitignore_utils.py} +0 -0
- /janito/{agent/platform_discovery.py → platform_discovery.py} +0 -0
- /janito/{agent/tools → tools/adapters/local}/get_file_outline/__init__.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/get_file_outline/markdown_outline.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/search_text/__init__.py +0 -0
- /janito/{agent/tools → tools/adapters/local}/validate_file_syntax/__init__.py +0 -0
- {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/WHEEL +0 -0
- {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/entry_points.txt +0 -0
- {janito-1.14.3.dist-info → janito-2.0.0.dist-info}/top_level.txt +0 -0
janito/report_events.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
import attr
|
2
|
+
from typing import Any, ClassVar, Optional, Dict
|
3
|
+
from enum import Enum
|
4
|
+
from janito.event_bus.event import Event
|
5
|
+
|
6
|
+
|
7
|
+
class ReportSubtype(Enum):
|
8
|
+
ACTION_INFO = "action_info"
|
9
|
+
ERROR = "error"
|
10
|
+
SUCCESS = "success"
|
11
|
+
WARNING = "warning"
|
12
|
+
STDOUT = "stdout"
|
13
|
+
STDERR = "stderr"
|
14
|
+
PROGRESS = "progress"
|
15
|
+
|
16
|
+
|
17
|
+
class ReportAction(Enum):
|
18
|
+
READ = "READ"
|
19
|
+
CREATE = "CREATE"
|
20
|
+
DELETE = "DELETE"
|
21
|
+
UPDATE = "UPDATE"
|
22
|
+
EXECUTE = "EXECUTE"
|
23
|
+
# Add more as needed
|
24
|
+
|
25
|
+
|
26
|
+
@attr.s(auto_attribs=True, kw_only=True)
|
27
|
+
class ReportEvent(Event):
|
28
|
+
"""
|
29
|
+
Event for reporting status, errors, warnings, and output.
|
30
|
+
Uses enums for subtype and action for type safety and clarity.
|
31
|
+
"""
|
32
|
+
|
33
|
+
category: ClassVar[str] = "report"
|
34
|
+
subtype: ReportSubtype
|
35
|
+
message: str
|
36
|
+
action: Optional[ReportAction] = None
|
37
|
+
tool: Optional[str] = None
|
38
|
+
context: Optional[Dict[str, Any]] = None
|
janito/termweb/app.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from quart import Quart, send_from_directory, request, jsonify, websocket
|
2
2
|
import os
|
3
|
-
from janito.agent.tools_utils.dir_walk_utils import walk_dir_with_gitignore
|
4
3
|
|
5
4
|
app = Quart(__name__)
|
6
5
|
|
@@ -84,6 +83,7 @@ if __name__ == "__main__":
|
|
84
83
|
import sys
|
85
84
|
|
86
85
|
port = 8088
|
86
|
+
# Allow override from CLI for direct execution/testing
|
87
87
|
if "--port" in sys.argv:
|
88
88
|
idx = sys.argv.index("--port")
|
89
89
|
if idx + 1 < len(sys.argv):
|
janito/tools/__init__.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
from janito.tools.adapters.local import (
|
2
|
+
local_tools_adapter as _internal_local_tools_adapter,
|
3
|
+
LocalToolsAdapter,
|
4
|
+
)
|
5
|
+
|
6
|
+
|
7
|
+
def get_local_tools_adapter():
|
8
|
+
|
9
|
+
# Use set_verbose_tools on the returned adapter to set verbosity as needed
|
10
|
+
return _internal_local_tools_adapter
|
11
|
+
|
12
|
+
|
13
|
+
__all__ = [
|
14
|
+
"LocalToolsAdapter",
|
15
|
+
"get_local_tools_adapter",
|
16
|
+
]
|
@@ -0,0 +1 @@
|
|
1
|
+
# Tools providers package: for plug-and-play tool collections, integrations, and adapters.
|
@@ -0,0 +1,54 @@
|
|
1
|
+
from .adapter import LocalToolsAdapter
|
2
|
+
|
3
|
+
from .ask_user import AskUserTool
|
4
|
+
from .copy_file import CopyFileTool
|
5
|
+
from .create_directory import CreateDirectoryTool
|
6
|
+
from .create_file import CreateFileTool
|
7
|
+
from .fetch_url import FetchUrlTool
|
8
|
+
from .find_files import FindFilesTool
|
9
|
+
from .view_file import ViewFileTool
|
10
|
+
from .move_file import MoveFileTool
|
11
|
+
from .open_url import OpenUrlTool
|
12
|
+
from .python_code_run import PythonCodeRunTool
|
13
|
+
from .python_command_run import PythonCommandRunTool
|
14
|
+
from .python_file_run import PythonFileRunTool
|
15
|
+
from .remove_directory import RemoveDirectoryTool
|
16
|
+
from .remove_file import RemoveFileTool
|
17
|
+
from .replace_text_in_file import ReplaceTextInFileTool
|
18
|
+
from .run_bash_command import RunBashCommandTool
|
19
|
+
from .run_powershell_command import RunPowershellCommandTool
|
20
|
+
from .get_file_outline.core import GetFileOutlineTool
|
21
|
+
from .get_file_outline.search_outline import SearchOutlineTool
|
22
|
+
from .search_text.core import SearchTextTool
|
23
|
+
from .validate_file_syntax.core import ValidateFileSyntaxTool
|
24
|
+
|
25
|
+
# Singleton tools adapter with all standard tools registered
|
26
|
+
local_tools_adapter = LocalToolsAdapter()
|
27
|
+
|
28
|
+
# Register tools
|
29
|
+
for tool_class in [
|
30
|
+
AskUserTool,
|
31
|
+
CopyFileTool,
|
32
|
+
CreateDirectoryTool,
|
33
|
+
CreateFileTool,
|
34
|
+
FetchUrlTool,
|
35
|
+
FindFilesTool,
|
36
|
+
ViewFileTool,
|
37
|
+
MoveFileTool,
|
38
|
+
OpenUrlTool,
|
39
|
+
PythonCodeRunTool,
|
40
|
+
PythonCommandRunTool,
|
41
|
+
PythonFileRunTool,
|
42
|
+
RemoveDirectoryTool,
|
43
|
+
RemoveFileTool,
|
44
|
+
ReplaceTextInFileTool,
|
45
|
+
RunBashCommandTool,
|
46
|
+
RunPowershellCommandTool,
|
47
|
+
GetFileOutlineTool,
|
48
|
+
SearchOutlineTool,
|
49
|
+
SearchTextTool,
|
50
|
+
ValidateFileSyntaxTool,
|
51
|
+
]:
|
52
|
+
local_tools_adapter.register_tool(tool_class)
|
53
|
+
|
54
|
+
# DEBUG: Print registered tools at startup
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from typing import Type, Dict, Any
|
2
|
+
from janito.tools.tools_adapter import ToolsAdapterBase as ToolsAdapter
|
3
|
+
|
4
|
+
|
5
|
+
class LocalToolsAdapter(ToolsAdapter):
|
6
|
+
def disable_execution_tools(self):
|
7
|
+
"""Unregister all tools with provides_execution = True."""
|
8
|
+
to_remove = [name for name, entry in self._tools.items()
|
9
|
+
if getattr(entry["instance"], "provides_execution", False)]
|
10
|
+
for name in to_remove:
|
11
|
+
self.unregister_tool(name)
|
12
|
+
|
13
|
+
"""
|
14
|
+
Adapter for local, statically registered tools in the agent/tools system.
|
15
|
+
Handles registration, lookup, enabling/disabling, listing, and now, tool execution (merged from executor).
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self, tools=None, event_bus=None, allowed_tools=None):
|
19
|
+
super().__init__(tools=tools, event_bus=event_bus, allowed_tools=allowed_tools)
|
20
|
+
self._tools: Dict[str, Dict[str, Any]] = {}
|
21
|
+
if tools:
|
22
|
+
for tool in tools:
|
23
|
+
self.register_tool(tool)
|
24
|
+
|
25
|
+
def register_tool(self, tool_class: Type):
|
26
|
+
instance = tool_class()
|
27
|
+
if not hasattr(instance, "run") or not callable(instance.run):
|
28
|
+
raise TypeError(
|
29
|
+
f"Tool '{tool_class.__name__}' must implement a callable 'run' method."
|
30
|
+
)
|
31
|
+
tool_name = getattr(instance, "tool_name", None)
|
32
|
+
if not tool_name or not isinstance(tool_name, str):
|
33
|
+
raise ValueError(
|
34
|
+
f"Tool '{tool_class.__name__}' must provide a class attribute 'tool_name' (str) for its registration name."
|
35
|
+
)
|
36
|
+
if tool_name in self._tools:
|
37
|
+
raise ValueError(f"Tool '{tool_name}' is already registered.")
|
38
|
+
self._tools[tool_name] = {
|
39
|
+
"function": instance.run,
|
40
|
+
"class": tool_class,
|
41
|
+
"instance": instance,
|
42
|
+
}
|
43
|
+
|
44
|
+
def unregister_tool(self, name: str):
|
45
|
+
if name in self._tools:
|
46
|
+
del self._tools[name]
|
47
|
+
|
48
|
+
def disable_tool(self, name: str):
|
49
|
+
self.unregister_tool(name)
|
50
|
+
|
51
|
+
def get_tool(self, name: str):
|
52
|
+
return self._tools[name]["instance"] if name in self._tools else None
|
53
|
+
|
54
|
+
def list_tools(self):
|
55
|
+
return list(self._tools.keys())
|
56
|
+
|
57
|
+
def get_tool_classes(self):
|
58
|
+
return [entry["class"] for entry in self._tools.values()]
|
59
|
+
|
60
|
+
def get_tools(self):
|
61
|
+
return [entry["instance"] for entry in self._tools.values()]
|
62
|
+
|
63
|
+
|
64
|
+
def add_tool(self, tool):
|
65
|
+
# Register by instance (useful for hand-built objects)
|
66
|
+
if not hasattr(tool, "run") or not callable(tool.run):
|
67
|
+
raise TypeError(f"Tool '{tool}' must implement a callable 'run' method.")
|
68
|
+
tool_name = getattr(tool, "tool_name", None)
|
69
|
+
if not tool_name or not isinstance(tool_name, str):
|
70
|
+
raise ValueError(
|
71
|
+
f"Tool '{tool}' must provide a 'tool_name' (str) attribute."
|
72
|
+
)
|
73
|
+
if tool_name in self._tools:
|
74
|
+
raise ValueError(f"Tool '{tool_name}' is already registered.")
|
75
|
+
self._tools[tool_name] = {
|
76
|
+
"function": tool.run,
|
77
|
+
"class": tool.__class__,
|
78
|
+
"instance": tool,
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
# Optional: a local-tool decorator
|
83
|
+
|
84
|
+
|
85
|
+
def register_local_tool(tool=None):
|
86
|
+
def decorator(cls):
|
87
|
+
LocalToolsAdapter().register_tool(cls)
|
88
|
+
return cls
|
89
|
+
|
90
|
+
if tool is None:
|
91
|
+
return decorator
|
92
|
+
return decorator(tool)
|
@@ -1,5 +1,5 @@
|
|
1
|
-
from janito.
|
2
|
-
from janito.
|
1
|
+
from janito.tools.tool_base import ToolBase
|
2
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
3
3
|
|
4
4
|
from rich import print as rich_print
|
5
5
|
from janito.i18n import tr
|
@@ -8,16 +8,20 @@ from prompt_toolkit import PromptSession
|
|
8
8
|
from prompt_toolkit.key_binding import KeyBindings
|
9
9
|
from prompt_toolkit.enums import EditingMode
|
10
10
|
from prompt_toolkit.formatted_text import HTML
|
11
|
+
from janito.cli.chat_mode.prompt_style import chat_shell_style
|
11
12
|
from prompt_toolkit.styles import Style
|
12
13
|
|
14
|
+
toolbar_style = Style.from_dict({"bottom-toolbar": "fg:yellow bg:darkred"})
|
13
15
|
|
14
|
-
|
16
|
+
|
17
|
+
@register_local_tool
|
15
18
|
class AskUserTool(ToolBase):
|
16
19
|
"""
|
17
|
-
|
20
|
+
Prompts the user for clarification or input with a question.
|
18
21
|
|
19
22
|
Args:
|
20
23
|
question (str): The question to ask the user.
|
24
|
+
|
21
25
|
Returns:
|
22
26
|
str: The user's response as a string. Example:
|
23
27
|
- "Yes"
|
@@ -25,8 +29,11 @@ class AskUserTool(ToolBase):
|
|
25
29
|
- "Some detailed answer..."
|
26
30
|
"""
|
27
31
|
|
32
|
+
tool_name = "ask_user"
|
33
|
+
|
28
34
|
def run(self, question: str) -> str:
|
29
35
|
|
36
|
+
print() # Print an empty line before the question panel
|
30
37
|
rich_print(Panel.fit(question, title=tr("Question"), style="cyan"))
|
31
38
|
|
32
39
|
bindings = KeyBindings()
|
@@ -55,13 +62,13 @@ class AskUserTool(ToolBase):
|
|
55
62
|
buf.validate_and_handle()
|
56
63
|
_f12_index["value"] = (idx + 1) % len(_f12_instructions)
|
57
64
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
# Use shared CLI styles
|
66
|
+
|
67
|
+
# prompt_style contains the prompt area and input background
|
68
|
+
# toolbar_style contains the bottom-toolbar styling
|
69
|
+
|
70
|
+
# Use the shared chat_shell_style for input styling only
|
71
|
+
style = chat_shell_style
|
65
72
|
|
66
73
|
def get_toolbar():
|
67
74
|
f12_hint = ""
|
@@ -82,7 +89,7 @@ class AskUserTool(ToolBase):
|
|
82
89
|
style=style,
|
83
90
|
)
|
84
91
|
|
85
|
-
prompt_icon = HTML("<
|
92
|
+
prompt_icon = HTML("<inputline>💬 </inputline>")
|
86
93
|
|
87
94
|
while True:
|
88
95
|
response = session.prompt(prompt_icon)
|
@@ -95,4 +102,14 @@ class AskUserTool(ToolBase):
|
|
95
102
|
session.multiline = False
|
96
103
|
continue
|
97
104
|
else:
|
98
|
-
|
105
|
+
sanitized = response.strip()
|
106
|
+
try:
|
107
|
+
sanitized.encode("utf-8")
|
108
|
+
except UnicodeEncodeError:
|
109
|
+
sanitized = sanitized.encode("utf-8", errors="replace").decode(
|
110
|
+
"utf-8"
|
111
|
+
)
|
112
|
+
rich_print(
|
113
|
+
"[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
|
114
|
+
)
|
115
|
+
return sanitized
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
from typing import List, Union
|
4
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
5
|
+
from janito.tools.tool_base import ToolBase
|
6
|
+
from janito.tools.tool_utils import display_path
|
7
|
+
from janito.report_events import ReportAction
|
8
|
+
from janito.i18n import tr
|
9
|
+
|
10
|
+
|
11
|
+
@register_local_tool
|
12
|
+
class CopyFileTool(ToolBase):
|
13
|
+
"""
|
14
|
+
Copy one or more files to a target directory, or copy a single file to a new file.
|
15
|
+
Args:
|
16
|
+
sources (str): Space-separated path(s) to the file(s) to copy.
|
17
|
+
For multiple sources, provide a single string with paths separated by spaces.
|
18
|
+
target (str): Destination path. If copying multiple sources, this must be an existing directory.
|
19
|
+
overwrite (bool, optional): Overwrite existing files. Default: False.
|
20
|
+
Recommended only after reading the file to be overwritten.
|
21
|
+
Returns:
|
22
|
+
str: Status string for each copy operation.
|
23
|
+
"""
|
24
|
+
|
25
|
+
tool_name = "copy_file"
|
26
|
+
|
27
|
+
def run(self, sources: str, target: str, overwrite: bool = False) -> str:
|
28
|
+
source_list = [src for src in sources.split() if src]
|
29
|
+
messages = []
|
30
|
+
if len(source_list) > 1:
|
31
|
+
if not os.path.isdir(target):
|
32
|
+
return tr(
|
33
|
+
"❗ Target must be an existing directory when copying multiple files: '{target}'",
|
34
|
+
target=display_path(target),
|
35
|
+
)
|
36
|
+
for src in source_list:
|
37
|
+
if not os.path.isfile(src):
|
38
|
+
messages.append(
|
39
|
+
tr(
|
40
|
+
"❗ Source file does not exist: '{src}'",
|
41
|
+
src=display_path(src),
|
42
|
+
)
|
43
|
+
)
|
44
|
+
continue
|
45
|
+
dst = os.path.join(target, os.path.basename(src))
|
46
|
+
messages.append(self._copy_one(src, dst, overwrite=overwrite))
|
47
|
+
else:
|
48
|
+
src = source_list[0]
|
49
|
+
if os.path.isdir(target):
|
50
|
+
dst = os.path.join(target, os.path.basename(src))
|
51
|
+
else:
|
52
|
+
dst = target
|
53
|
+
messages.append(self._copy_one(src, dst, overwrite=overwrite))
|
54
|
+
return "\n".join(messages)
|
55
|
+
|
56
|
+
def _copy_one(self, src, dst, overwrite=False) -> str:
|
57
|
+
disp_src = display_path(src)
|
58
|
+
disp_dst = display_path(dst)
|
59
|
+
if not os.path.isfile(src):
|
60
|
+
return tr("❗ Source file does not exist: '{src}'", src=disp_src)
|
61
|
+
if os.path.exists(dst) and not overwrite:
|
62
|
+
return tr(
|
63
|
+
"❗ Target already exists: '{dst}'. Set overwrite=True to replace.",
|
64
|
+
dst=disp_dst,
|
65
|
+
)
|
66
|
+
try:
|
67
|
+
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
68
|
+
shutil.copy2(src, dst)
|
69
|
+
note = (
|
70
|
+
"\n⚠️ Overwrote existing file. (recommended only after reading the file to be overwritten)"
|
71
|
+
if (os.path.exists(dst) and overwrite)
|
72
|
+
else ""
|
73
|
+
)
|
74
|
+
self.report_success(
|
75
|
+
tr("✅ Copied '{src}' to '{dst}'", src=disp_src, dst=disp_dst)
|
76
|
+
)
|
77
|
+
return tr("✅ Copied '{src}' to '{dst}'", src=disp_src, dst=disp_dst) + note
|
78
|
+
except Exception as e:
|
79
|
+
return tr(
|
80
|
+
"❗ Copy failed from '{src}' to '{dst}': {err}",
|
81
|
+
src=disp_src,
|
82
|
+
dst=disp_dst,
|
83
|
+
err=str(e),
|
84
|
+
)
|
@@ -1,14 +1,13 @@
|
|
1
|
-
from janito.
|
1
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
2
2
|
|
3
|
-
|
4
|
-
from janito.
|
5
|
-
from janito.
|
6
|
-
from janito.agent.tools_utils.action_type import ActionType
|
3
|
+
from janito.tools.tool_utils import display_path
|
4
|
+
from janito.tools.tool_base import ToolBase
|
5
|
+
from janito.report_events import ReportAction
|
7
6
|
from janito.i18n import tr
|
8
7
|
import os
|
9
8
|
|
10
9
|
|
11
|
-
@
|
10
|
+
@register_local_tool
|
12
11
|
class CreateDirectoryTool(ToolBase):
|
13
12
|
"""
|
14
13
|
Create a new directory at the specified file_path.
|
@@ -16,17 +15,19 @@ class CreateDirectoryTool(ToolBase):
|
|
16
15
|
file_path (str): Path for the new directory.
|
17
16
|
Returns:
|
18
17
|
str: Status message indicating the result. Example:
|
19
|
-
- "
|
20
|
-
- "
|
18
|
+
- "5c5 Successfully created the directory at ..."
|
19
|
+
- "5d7 Cannot create directory: ..."
|
21
20
|
"""
|
22
21
|
|
22
|
+
tool_name = "create_directory"
|
23
|
+
|
23
24
|
def run(self, file_path: str) -> str:
|
24
25
|
# file_path = expand_path(file_path)
|
25
26
|
# Using file_path as is
|
26
27
|
disp_path = display_path(file_path)
|
27
|
-
self.
|
28
|
-
ActionType.WRITE,
|
28
|
+
self.report_action(
|
29
29
|
tr("📁 Create directory '{disp_path}' ...", disp_path=disp_path),
|
30
|
+
ReportAction.CREATE,
|
30
31
|
)
|
31
32
|
try:
|
32
33
|
if os.path.exists(file_path):
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import os
|
2
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
3
|
+
|
4
|
+
from janito.tools.tool_utils import display_path
|
5
|
+
from janito.tools.tool_base import ToolBase
|
6
|
+
from janito.report_events import ReportAction
|
7
|
+
from janito.i18n import tr
|
8
|
+
|
9
|
+
|
10
|
+
from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
|
11
|
+
|
12
|
+
|
13
|
+
@register_local_tool
|
14
|
+
class CreateFileTool(ToolBase):
|
15
|
+
"""
|
16
|
+
Create a new file with the given content.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
file_path (str): Path to the file to create.
|
20
|
+
content (str): Content to write to the file.
|
21
|
+
overwrite (bool, optional): Overwrite existing file if True. Default: False. Recommended only after reading the file to be overwritten.
|
22
|
+
Returns:
|
23
|
+
str: Status message indicating the result. Example:
|
24
|
+
- "✅ Successfully created the file at ..."
|
25
|
+
|
26
|
+
Note: Syntax validation is automatically performed after this operation.
|
27
|
+
"""
|
28
|
+
|
29
|
+
tool_name = "create_file"
|
30
|
+
|
31
|
+
def run(self, file_path: str, content: str, overwrite: bool = False) -> str:
|
32
|
+
expanded_file_path = file_path # Using file_path as is
|
33
|
+
disp_path = display_path(expanded_file_path)
|
34
|
+
file_path = expanded_file_path
|
35
|
+
if os.path.exists(file_path) and not overwrite:
|
36
|
+
try:
|
37
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
38
|
+
existing_content = f.read()
|
39
|
+
except Exception as e:
|
40
|
+
existing_content = f"[Error reading file: {e}]"
|
41
|
+
return tr(
|
42
|
+
"❗ Cannot create file: file already exists at '{disp_path}'.\n--- Current file content ---\n{existing_content}",
|
43
|
+
disp_path=disp_path,
|
44
|
+
existing_content=existing_content,
|
45
|
+
)
|
46
|
+
# Determine if we are overwriting an existing file
|
47
|
+
is_overwrite = os.path.exists(file_path) and overwrite
|
48
|
+
if is_overwrite:
|
49
|
+
# Overwrite branch: log only overwrite warning (no create message)
|
50
|
+
self.report_action(
|
51
|
+
tr("⚠️ Overwriting file '{disp_path}'", disp_path=disp_path),
|
52
|
+
ReportAction.CREATE,
|
53
|
+
)
|
54
|
+
dir_name = os.path.dirname(file_path)
|
55
|
+
if dir_name:
|
56
|
+
os.makedirs(dir_name, exist_ok=True)
|
57
|
+
if not is_overwrite:
|
58
|
+
# Create branch: log file creation message
|
59
|
+
self.report_action(
|
60
|
+
tr("📝 Create file '{disp_path}' ...", disp_path=disp_path),
|
61
|
+
ReportAction.CREATE,
|
62
|
+
)
|
63
|
+
with open(file_path, "w", encoding="utf-8", errors="replace") as f:
|
64
|
+
f.write(content)
|
65
|
+
new_lines = content.count("\n") + 1 if content else 0
|
66
|
+
self.report_success(
|
67
|
+
tr("✅ {new_lines} lines", new_lines=new_lines), ReportAction.CREATE
|
68
|
+
)
|
69
|
+
# Perform syntax validation and append result
|
70
|
+
validation_result = validate_file_syntax(file_path)
|
71
|
+
if is_overwrite:
|
72
|
+
# Overwrite branch: return minimal overwrite info to user
|
73
|
+
return (
|
74
|
+
tr("✅ {new_lines} lines", new_lines=new_lines)
|
75
|
+
+ f"\n{validation_result}"
|
76
|
+
)
|
77
|
+
else:
|
78
|
+
# Create branch: return detailed create success to user
|
79
|
+
return (
|
80
|
+
tr("✅ Created file {new_lines} lines.", new_lines=new_lines)
|
81
|
+
+ f"\n{validation_result}"
|
82
|
+
)
|
@@ -0,0 +1,136 @@
|
|
1
|
+
from janito.tools.tool_base import ToolBase
|
2
|
+
from janito.report_events import ReportAction
|
3
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
4
|
+
from janito.i18n import tr
|
5
|
+
import shutil
|
6
|
+
from janito.tools.adapters.local.validate_file_syntax.core import validate_file_syntax
|
7
|
+
|
8
|
+
|
9
|
+
@register_local_tool
|
10
|
+
class DeleteTextInFileTool(ToolBase):
|
11
|
+
"""
|
12
|
+
Delete all occurrences of text between start_marker and end_marker (inclusive) in a file, using exact string markers.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
file_path (str): Path to the file to modify.
|
16
|
+
start_marker (str): The starting delimiter string.
|
17
|
+
end_marker (str): The ending delimiter string.
|
18
|
+
backup (bool, optional): If True, create a backup (.bak) before deleting. Defaults to False.
|
19
|
+
Returns:
|
20
|
+
str: Status message indicating the result.
|
21
|
+
"""
|
22
|
+
|
23
|
+
tool_name = "delete_text_in_file"
|
24
|
+
|
25
|
+
def run(
|
26
|
+
self,
|
27
|
+
file_path: str,
|
28
|
+
start_marker: str,
|
29
|
+
end_marker: str,
|
30
|
+
backup: bool = False,
|
31
|
+
) -> str:
|
32
|
+
from janito.tools.tool_utils import display_path
|
33
|
+
|
34
|
+
disp_path = display_path(file_path)
|
35
|
+
info_msg = tr(
|
36
|
+
"📝 Delete text in {disp_path} between markers: '{start_marker}' ... '{end_marker}'",
|
37
|
+
disp_path=disp_path,
|
38
|
+
start_marker=start_marker,
|
39
|
+
end_marker=end_marker,
|
40
|
+
)
|
41
|
+
self.report_action(info_msg, ReportAction.CREATE)
|
42
|
+
try:
|
43
|
+
content = self._read_file_content(file_path)
|
44
|
+
occurrences, match_lines = self._find_marker_blocks(
|
45
|
+
content, start_marker, end_marker
|
46
|
+
)
|
47
|
+
if occurrences == 0:
|
48
|
+
self.report_warning(
|
49
|
+
tr(" ℹ️ No blocks found between markers."), ReportAction.CREATE
|
50
|
+
)
|
51
|
+
return tr(
|
52
|
+
"No blocks found between markers in {file_path}.",
|
53
|
+
file_path=file_path,
|
54
|
+
)
|
55
|
+
backup_path = file_path + ".bak"
|
56
|
+
if backup:
|
57
|
+
self._backup_file(file_path, backup_path)
|
58
|
+
new_content, deleted_blocks = self._delete_blocks(
|
59
|
+
content, start_marker, end_marker
|
60
|
+
)
|
61
|
+
self._write_file_content(file_path, new_content)
|
62
|
+
validation_result = validate_file_syntax(file_path)
|
63
|
+
self._report_success(match_lines)
|
64
|
+
return tr(
|
65
|
+
"Deleted {count} block(s) between markers in {file_path}. (backup at {backup_path})",
|
66
|
+
count=deleted_blocks,
|
67
|
+
file_path=file_path,
|
68
|
+
backup_path=backup_path if backup else "N/A",
|
69
|
+
) + (f"\n{validation_result}" if validation_result else "")
|
70
|
+
except Exception as e:
|
71
|
+
self.report_error(tr(" ❌ Error: {error}", error=e), ReportAction.REPLACE)
|
72
|
+
return tr("Error deleting text: {error}", error=e)
|
73
|
+
|
74
|
+
def _read_file_content(self, file_path):
|
75
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
76
|
+
return f.read()
|
77
|
+
|
78
|
+
def _find_marker_blocks(self, content, start_marker, end_marker):
|
79
|
+
"""Find all blocks between start_marker and end_marker, return count and starting line numbers."""
|
80
|
+
lines = content.splitlines(keepends=True)
|
81
|
+
joined = "".join(lines)
|
82
|
+
match_lines = []
|
83
|
+
idx = 0
|
84
|
+
occurrences = 0
|
85
|
+
while True:
|
86
|
+
start_idx = joined.find(start_marker, idx)
|
87
|
+
if start_idx == -1:
|
88
|
+
break
|
89
|
+
end_idx = joined.find(end_marker, start_idx + len(start_marker))
|
90
|
+
if end_idx == -1:
|
91
|
+
break
|
92
|
+
upto = joined[:start_idx]
|
93
|
+
line_no = upto.count("\n") + 1
|
94
|
+
match_lines.append(line_no)
|
95
|
+
idx = end_idx + len(end_marker)
|
96
|
+
occurrences += 1
|
97
|
+
return occurrences, match_lines
|
98
|
+
|
99
|
+
def _delete_blocks(self, content, start_marker, end_marker):
|
100
|
+
"""Delete all blocks between start_marker and end_marker (inclusive)."""
|
101
|
+
count = 0
|
102
|
+
new_content = content
|
103
|
+
while True:
|
104
|
+
start_idx = new_content.find(start_marker)
|
105
|
+
if start_idx == -1:
|
106
|
+
break
|
107
|
+
end_idx = new_content.find(end_marker, start_idx + len(start_marker))
|
108
|
+
if end_idx == -1:
|
109
|
+
break
|
110
|
+
new_content = (
|
111
|
+
new_content[:start_idx] + new_content[end_idx + len(end_marker) :]
|
112
|
+
)
|
113
|
+
count += 1
|
114
|
+
return new_content, count
|
115
|
+
|
116
|
+
def _backup_file(self, file_path, backup_path):
|
117
|
+
shutil.copy2(file_path, backup_path)
|
118
|
+
|
119
|
+
def _write_file_content(self, file_path, content):
|
120
|
+
with open(file_path, "w", encoding="utf-8", errors="replace") as f:
|
121
|
+
f.write(content)
|
122
|
+
|
123
|
+
def _report_success(self, match_lines):
|
124
|
+
if match_lines:
|
125
|
+
lines_str = ", ".join(str(line_no) for line_no in match_lines)
|
126
|
+
self.report_success(
|
127
|
+
tr(
|
128
|
+
" ✅ deleted block(s) starting at line(s): {lines_str}",
|
129
|
+
lines_str=lines_str,
|
130
|
+
),
|
131
|
+
ReportAction.CREATE,
|
132
|
+
)
|
133
|
+
else:
|
134
|
+
self.report_success(
|
135
|
+
tr(" ✅ deleted block(s) (lines unknown)"), ReportAction.CREATE
|
136
|
+
)
|