siada-cli 0.0.1__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.
- agent_config.yaml +34 -0
- siada/__init__.py +13 -0
- siada/agent_hub/__init__.py +10 -0
- siada/agent_hub/coder/bug_fix_agent.py +398 -0
- siada/agent_hub/coder/code_gen_agent.py +235 -0
- siada/agent_hub/coder/fe_gen_agent.py +37 -0
- siada/agent_hub/coder/issue_review_agent.py +68 -0
- siada/agent_hub/coder/prompt/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/capabilities.py +18 -0
- siada/agent_hub/coder/prompt/base/non_interactive.py +14 -0
- siada/agent_hub/coder/prompt/base/prompt_builder.py +33 -0
- siada/agent_hub/coder/prompt/base/rules.py +40 -0
- siada/agent_hub/coder/prompt/base/tool_use.py +14 -0
- siada/agent_hub/coder/prompt/bug_prompt/bug_fix_prompt.py +51 -0
- siada/agent_hub/coder/prompt/bug_prompt/rules.py +53 -0
- siada/agent_hub/coder/prompt/code_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/fe_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/issue_review_prompt.py +121 -0
- siada/agent_hub/coder/tracing/__init__.py +22 -0
- siada/agent_hub/coder/tracing/logger_tracing_processor.py +458 -0
- siada/agent_hub/hooks/__init__.py +0 -0
- siada/agent_hub/hooks/io_hooks.py +30 -0
- siada/agent_hub/siada_agent.py +214 -0
- siada/config/__init__.py +0 -0
- siada/config/config_loader.py +67 -0
- siada/entrypoint/__init__.py +3 -0
- siada/entrypoint/args_parser/__init__.py +0 -0
- siada/entrypoint/args_parser/args.py +195 -0
- siada/entrypoint/interaction/__init__.py +0 -0
- siada/entrypoint/interaction/base_turn.py +107 -0
- siada/entrypoint/interaction/config.py +30 -0
- siada/entrypoint/interaction/controller.py +122 -0
- siada/entrypoint/interaction/nointeractive_controller.py +27 -0
- siada/entrypoint/interaction/turn/interface.py +116 -0
- siada/entrypoint/interaction/turn/models.py +34 -0
- siada/entrypoint/interaction/turn/run_turn.py +641 -0
- siada/entrypoint/interaction/turn_factory.py +35 -0
- siada/entrypoint/interaction/turn_models.py +28 -0
- siada/entrypoint/siadahub.py +413 -0
- siada/foundation/__init__.py +0 -0
- siada/foundation/code_agent_context.py +48 -0
- siada/foundation/config.py +59 -0
- siada/foundation/constants.py +2 -0
- siada/foundation/context.py +76 -0
- siada/foundation/logging.py +341 -0
- siada/foundation/tools/__init__.py +0 -0
- siada/foundation/tools/get_git_diff.py +105 -0
- siada/io/__init__.py +0 -0
- siada/io/banner.py +148 -0
- siada/io/color_settings.py +207 -0
- siada/io/color_utils.py +16 -0
- siada/io/components/__init__.py +0 -0
- siada/io/components/mdstream.py +204 -0
- siada/io/console_printer.py +68 -0
- siada/io/io.py +661 -0
- siada/io/key_bindings.py +129 -0
- siada/io/notification_command.py +40 -0
- siada/io/test_banner.py +64 -0
- siada/models/__init__.py +0 -0
- siada/models/agent.py +47 -0
- siada/models/coder/__init__.py +0 -0
- siada/models/converter.py +27 -0
- siada/models/model_base_config.py +121 -0
- siada/models/model_run_config.py +153 -0
- siada/provider/__init__.py +0 -0
- siada/provider/client_factory.py +112 -0
- siada/provider/llm_client.py +11 -0
- siada/provider/openrouter/__init__.py +0 -0
- siada/provider/openrouter/coverter.py +17 -0
- siada/provider/openrouter/openroute_provider.py +33 -0
- siada/provider/provider_factory.py +53 -0
- siada/queries/tree-sitter-language-pack/README.md +7 -0
- siada/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- siada/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- siada/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- siada/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- siada/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- siada/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- siada/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- siada/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- siada/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- siada/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- siada/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- siada/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- siada/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- siada/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- siada/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- siada/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- siada/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- siada/queries/tree-sitter-languages/README.md +23 -0
- siada/queries/tree-sitter-languages/c-tags.scm +9 -0
- siada/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- siada/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-languages/dart-tags.scm +91 -0
- siada/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- siada/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-languages/elm-tags.scm +19 -0
- siada/queries/tree-sitter-languages/go-tags.scm +30 -0
- siada/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- siada/queries/tree-sitter-languages/java-tags.scm +20 -0
- siada/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- siada/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-languages/php-tags.scm +26 -0
- siada/queries/tree-sitter-languages/python-tags.scm +12 -0
- siada/queries/tree-sitter-languages/ql-tags.scm +26 -0
- siada/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-languages/rust-tags.scm +60 -0
- siada/queries/tree-sitter-languages/scala-tags.scm +65 -0
- siada/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- siada/services/__init__.py +30 -0
- siada/services/at_file_recommendation_design_document.md +1213 -0
- siada/services/code_context_manager.py +136 -0
- siada/services/enhanced_fix_result_check.py +546 -0
- siada/services/execution_trace_collector.py +207 -0
- siada/services/file_recommendation/__init__.py +136 -0
- siada/services/file_recommendation/core/__init__.py +17 -0
- siada/services/file_recommendation/core/completion_engine.py +351 -0
- siada/services/file_recommendation/core/config.py +62 -0
- siada/services/file_recommendation/core/file_discovery.py +353 -0
- siada/services/file_recommendation/core/suggestion.py +110 -0
- siada/services/file_recommendation/utils/__init__.py +22 -0
- siada/services/file_recommendation/utils/path_utils.py +138 -0
- siada/services/file_recommendation/utils/text_utils.py +253 -0
- siada/services/fix_result_check.py +377 -0
- siada/services/handle_at_command/README.md +247 -0
- siada/services/handle_at_command/__init__.py +115 -0
- siada/services/handle_at_command/exceptions.py +46 -0
- siada/services/handle_at_command/models.py +92 -0
- siada/services/handle_at_command/parser.py +189 -0
- siada/services/handle_at_command/processor.py +321 -0
- siada/services/handle_at_command/resolver.py +318 -0
- siada/services/handle_at_command/utils.py +345 -0
- siada/services/model_info_service.py +103 -0
- siada/services/siada_runner.py +170 -0
- siada/session/__init__.py +29 -0
- siada/session/session_manager.py +66 -0
- siada/session/session_models.py +43 -0
- siada/support/__init__.py +0 -0
- siada/support/completer.py +204 -0
- siada/support/editor.py +146 -0
- siada/support/envprocessor.py +54 -0
- siada/support/repo.py +51 -0
- siada/support/slash_commands.py +410 -0
- siada/support/spinner.py +320 -0
- siada/tools/__init__.py +0 -0
- siada/tools/ast/README.md +345 -0
- siada/tools/ast/ast_tool.py +341 -0
- siada/tools/ast/models.py +11 -0
- siada/tools/coder/__init__.py +0 -0
- siada/tools/coder/ask_followup_question.py +70 -0
- siada/tools/coder/cmd_runner.py +132 -0
- siada/tools/coder/file_operator.py +284 -0
- siada/tools/coder/file_search/README.md +223 -0
- siada/tools/coder/file_search/__init__.py +10 -0
- siada/tools/coder/file_search/bin/rg-linux-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-linux-x64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-x64 +0 -0
- siada/tools/coder/file_search/bin/rg.exe +0 -0
- siada/tools/coder/file_search/search.py +437 -0
- siada/tools/coder/file_search/setup_package.py +150 -0
- siada/tools/coder/file_search/test_search.py +142 -0
- siada/tools/coder/files.py +147 -0
- siada/tools/coder/fix_attempt_completion.py +58 -0
- siada/tools/coder/issue_review_completion.py +29 -0
- siada/tools/coder/observation/__init__.py +0 -0
- siada/tools/coder/observation/error.py +22 -0
- siada/tools/coder/observation/file_observation.py +216 -0
- siada/tools/coder/observation/observation.py +71 -0
- siada/tools/coder/repo_map/__init__.py +157 -0
- siada/tools/coder/repo_map/dump.py +29 -0
- siada/tools/coder/repo_map/io.py +226 -0
- siada/tools/coder/repo_map/repo_map.py +881 -0
- siada/tools/coder/repo_map/special.py +203 -0
- siada/tools/coder/repo_map/token_counter.py +154 -0
- siada/tools/coder/repo_map/waiting.py +221 -0
- siada/tools/coder/reproduce_completion.py +39 -0
- siada/tools/coder/run_cmd.py +60 -0
- siada/tools/coder/test_completion.py +25 -0
- siada/tools/coder/tool_docs.py +44 -0
- siada/tools/compression_tool.py +200 -0
- siada/tools/read_many_files/README.md +220 -0
- siada/tools/read_many_files/__init__.py +25 -0
- siada/tools/read_many_files/file_processor.py +486 -0
- siada/tools/read_many_files/filters.py +326 -0
- siada/tools/read_many_files/formatters.py +204 -0
- siada/tools/read_many_files/models.py +189 -0
- siada/tools/read_many_files_tool.py +233 -0
- siada/tools/tool_call_format/__init__.py +55 -0
- siada/tools/tool_call_format/file_to_language.py +231 -0
- siada/tools/tool_call_format/formatter_factory.py +80 -0
- siada/tools/tool_call_format/formatters.py +282 -0
- siada/tools/tool_call_format/tool_call_formatter.py +54 -0
- siada/tools/web/__init__.py +0 -0
- siada/tools/web/web_crawl.py +208 -0
- siada/utils.py +194 -0
- siada_cli-0.0.1.dist-info/LICENSE +201 -0
- siada_cli-0.0.1.dist-info/METADATA +457 -0
- siada_cli-0.0.1.dist-info/RECORD +215 -0
- siada_cli-0.0.1.dist-info/WHEEL +4 -0
- siada_cli-0.0.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import mimetypes
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from agents import RunContextWrapper, function_tool
|
|
6
|
+
from openhands_aci.editor import OHEditor, ToolResult, ToolError
|
|
7
|
+
from openhands_aci.utils.diff import get_diff
|
|
8
|
+
|
|
9
|
+
from siada.foundation.logging import logger
|
|
10
|
+
|
|
11
|
+
from binaryornot.check import is_binary
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from siada.tools.coder.files import read_lines
|
|
15
|
+
from siada.tools.coder.observation.file_observation import FileReadObservation, FileEditObservation
|
|
16
|
+
from siada.tools.coder.observation.observation import FunctionCallResult, FileEditSource
|
|
17
|
+
from siada.tools.coder.observation.error import ErrorObservation
|
|
18
|
+
from siada.tools.coder.observation.observation import FileReadSource
|
|
19
|
+
from siada.tools.coder.tool_docs import EDIT_DOCS
|
|
20
|
+
from siada.foundation.code_agent_context import CodeAgentContext
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@function_tool(
|
|
24
|
+
name_override="read_file", description_override="Read the file."
|
|
25
|
+
)
|
|
26
|
+
async def read(
|
|
27
|
+
context: RunContextWrapper[CodeAgentContext],
|
|
28
|
+
path: str,
|
|
29
|
+
start: int = 0,
|
|
30
|
+
end: int = -1,
|
|
31
|
+
impl_source: FileReadSource = FileReadSource.DEFAULT,
|
|
32
|
+
view_range: list[int] | None = None
|
|
33
|
+
) -> FunctionCallResult:
|
|
34
|
+
"""
|
|
35
|
+
Read file content with support for various file types including text, images, PDFs, and videos.
|
|
36
|
+
|
|
37
|
+
This function handles different file types and returns appropriate observations:
|
|
38
|
+
- Text files: Returns content as string with optional line range selection
|
|
39
|
+
- Image files: Returns base64-encoded data URI
|
|
40
|
+
- PDF files: Returns base64-encoded data URI
|
|
41
|
+
- Video files: Returns base64-encoded data URI
|
|
42
|
+
- Binary files: Returns error observation
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
context: The run context wrapper containing agent context and root directory
|
|
46
|
+
path: File path to read (relative to working directory or absolute)
|
|
47
|
+
start: Starting line number for text files (0-based, default: 0)
|
|
48
|
+
end: Ending line number for text files (-1 means end of file, default: -1)
|
|
49
|
+
impl_source: Implementation source for reading (DEFAULT or OH_ACI)
|
|
50
|
+
view_range: Optional line range for OH_ACI implementation [start, end]
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Observation: FileReadObservation with file content or ErrorObservation on failure
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
No exceptions are raised directly - all errors are captured and returned as ErrorObservation
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
# Cannot read binary files
|
|
60
|
+
if is_binary(path):
|
|
61
|
+
return ErrorObservation('ERROR_BINARY_FILE')
|
|
62
|
+
|
|
63
|
+
# Get the working directory from context and initialize file editor
|
|
64
|
+
working_dir = context.context.root_dir
|
|
65
|
+
file_editor = OHEditor(workspace_root=working_dir)
|
|
66
|
+
|
|
67
|
+
# Use OH_ACI implementation if specified
|
|
68
|
+
if impl_source == FileReadSource.OH_ACI:
|
|
69
|
+
result_str, _ = _execute_file_editor(
|
|
70
|
+
file_editor,
|
|
71
|
+
command='view',
|
|
72
|
+
path=path,
|
|
73
|
+
view_range=view_range,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return FileReadObservation(
|
|
77
|
+
content=result_str,
|
|
78
|
+
path=path,
|
|
79
|
+
impl_source=FileReadSource.OH_ACI,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# NOTE: the client code is running inside the sandbox,
|
|
83
|
+
# so there's no need to check permission
|
|
84
|
+
# Resolve the file path (convert relative to absolute if needed)
|
|
85
|
+
filepath = _resolve_path(path, working_dir)
|
|
86
|
+
try:
|
|
87
|
+
# Handle image files - return as base64-encoded data URI
|
|
88
|
+
if filepath.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
|
|
89
|
+
with open(filepath, 'rb') as file: # noqa: ASYNC101
|
|
90
|
+
image_data = file.read()
|
|
91
|
+
encoded_image = base64.b64encode(image_data).decode('utf-8')
|
|
92
|
+
mime_type, _ = mimetypes.guess_type(filepath)
|
|
93
|
+
if mime_type is None:
|
|
94
|
+
mime_type = 'image/png' # default to PNG if mime type cannot be determined
|
|
95
|
+
encoded_image = f'data:{mime_type};base64,{encoded_image}'
|
|
96
|
+
|
|
97
|
+
return FileReadObservation(path=filepath, content=encoded_image)
|
|
98
|
+
# Handle PDF files - return as base64-encoded data URI
|
|
99
|
+
elif filepath.lower().endswith('.pdf'):
|
|
100
|
+
with open(filepath, 'rb') as file: # noqa: ASYNC101
|
|
101
|
+
pdf_data = file.read()
|
|
102
|
+
encoded_pdf = base64.b64encode(pdf_data).decode('utf-8')
|
|
103
|
+
encoded_pdf = f'data:application/pdf;base64,{encoded_pdf}'
|
|
104
|
+
return FileReadObservation(path=filepath, content=encoded_pdf)
|
|
105
|
+
# Handle video files - return as base64-encoded data URI
|
|
106
|
+
elif filepath.lower().endswith(('.mp4', '.webm', '.ogg')):
|
|
107
|
+
with open(filepath, 'rb') as file: # noqa: ASYNC101
|
|
108
|
+
video_data = file.read()
|
|
109
|
+
encoded_video = base64.b64encode(video_data).decode('utf-8')
|
|
110
|
+
mime_type, _ = mimetypes.guess_type(filepath)
|
|
111
|
+
if mime_type is None:
|
|
112
|
+
mime_type = 'video/mp4' # default to MP4 if MIME type cannot be determined
|
|
113
|
+
encoded_video = f'data:{mime_type};base64,{encoded_video}'
|
|
114
|
+
|
|
115
|
+
return FileReadObservation(path=filepath, content=encoded_video)
|
|
116
|
+
|
|
117
|
+
# Handle text files - read with UTF-8 encoding and apply line range if specified
|
|
118
|
+
with open(filepath, 'r', encoding='utf-8') as file: # noqa: ASYNC101
|
|
119
|
+
lines = read_lines(file.readlines(), start, end)
|
|
120
|
+
except FileNotFoundError:
|
|
121
|
+
# File does not exist at the specified path
|
|
122
|
+
return ErrorObservation(
|
|
123
|
+
f'File not found: {filepath}. Your current working directory is {working_dir}.'
|
|
124
|
+
)
|
|
125
|
+
except UnicodeDecodeError:
|
|
126
|
+
# File contains non-UTF-8 content that cannot be decoded
|
|
127
|
+
return ErrorObservation(f'File could not be decoded as utf-8: {filepath}.')
|
|
128
|
+
except IsADirectoryError:
|
|
129
|
+
# Path points to a directory instead of a file
|
|
130
|
+
return ErrorObservation(
|
|
131
|
+
f'Path is a directory: {filepath}. You can only read files'
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Join the lines back into a single string and return the observation
|
|
135
|
+
code_view = ''.join(lines)
|
|
136
|
+
return FileReadObservation(path=filepath, content=code_view)
|
|
137
|
+
|
|
138
|
+
@function_tool(
|
|
139
|
+
name_override="edit_file", description_override=EDIT_DOCS
|
|
140
|
+
)
|
|
141
|
+
async def edit(
|
|
142
|
+
context: RunContextWrapper[CodeAgentContext],
|
|
143
|
+
command: str,
|
|
144
|
+
path: str,
|
|
145
|
+
file_text: str | None = None,
|
|
146
|
+
old_str: str | None = None,
|
|
147
|
+
new_str: str | None = None,
|
|
148
|
+
insert_line: int | None = None,
|
|
149
|
+
view_range: list[int] | None = None
|
|
150
|
+
) -> FunctionCallResult:
|
|
151
|
+
return _edit_file(
|
|
152
|
+
context=context,
|
|
153
|
+
command=command,
|
|
154
|
+
path=path,
|
|
155
|
+
file_text=file_text,
|
|
156
|
+
old_str=old_str,
|
|
157
|
+
new_str=new_str,
|
|
158
|
+
insert_line=insert_line,
|
|
159
|
+
view_range=view_range
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _edit_file(
|
|
163
|
+
context: RunContextWrapper[CodeAgentContext],
|
|
164
|
+
command: str,
|
|
165
|
+
path: str,
|
|
166
|
+
file_text: str | None = None,
|
|
167
|
+
old_str: str | None = None,
|
|
168
|
+
new_str: str | None = None,
|
|
169
|
+
insert_line: int | None = None,
|
|
170
|
+
view_range: list[int] | None = None
|
|
171
|
+
) -> FunctionCallResult:
|
|
172
|
+
file_editor = OHEditor(workspace_root=context.context.root_dir)
|
|
173
|
+
result_str, (old_content, new_content) = _execute_file_editor(
|
|
174
|
+
file_editor,
|
|
175
|
+
command=command,
|
|
176
|
+
path=path,
|
|
177
|
+
file_text=file_text,
|
|
178
|
+
old_str=old_str,
|
|
179
|
+
new_str=new_str,
|
|
180
|
+
insert_line=insert_line,
|
|
181
|
+
view_range=view_range,
|
|
182
|
+
enable_linting=False,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return FileEditObservation(
|
|
186
|
+
content=result_str,
|
|
187
|
+
path=path,
|
|
188
|
+
old_content=old_str,
|
|
189
|
+
new_content=new_str,
|
|
190
|
+
impl_source=FileEditSource.OH_ACI,
|
|
191
|
+
diff=get_diff(
|
|
192
|
+
old_contents=old_content or '',
|
|
193
|
+
new_contents=new_content or '',
|
|
194
|
+
filepath=path,
|
|
195
|
+
),
|
|
196
|
+
command=command,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _execute_file_editor(
|
|
201
|
+
editor: OHEditor,
|
|
202
|
+
command: str,
|
|
203
|
+
path: str,
|
|
204
|
+
file_text: str | None = None,
|
|
205
|
+
view_range: list[int] | None = None,
|
|
206
|
+
old_str: str | None = None,
|
|
207
|
+
new_str: str | None = None,
|
|
208
|
+
insert_line: int | str | None = None,
|
|
209
|
+
enable_linting: bool = False,
|
|
210
|
+
) -> tuple[str, tuple[str | None, str | None]]:
|
|
211
|
+
"""Execute file editor command and handle exceptions.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
editor: The OHEditor instance
|
|
215
|
+
command: Editor command to execute
|
|
216
|
+
path: File path
|
|
217
|
+
file_text: Optional file text content
|
|
218
|
+
view_range: Optional view range tuple (start, end)
|
|
219
|
+
old_str: Optional string to replace
|
|
220
|
+
new_str: Optional replacement string
|
|
221
|
+
insert_line: Optional line number for insertion (can be int or str)
|
|
222
|
+
enable_linting: Whether to enable linting
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
tuple: A tuple containing the output string and a tuple of old and new file content
|
|
226
|
+
"""
|
|
227
|
+
result: ToolResult | None = None
|
|
228
|
+
|
|
229
|
+
# Convert insert_line from string to int if needed
|
|
230
|
+
if insert_line is not None and isinstance(insert_line, str):
|
|
231
|
+
try:
|
|
232
|
+
insert_line = int(insert_line)
|
|
233
|
+
except ValueError:
|
|
234
|
+
return (
|
|
235
|
+
f"ERROR:\nInvalid insert_line value: '{insert_line}'. Expected an integer.",
|
|
236
|
+
(None, None),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
result = editor(
|
|
241
|
+
command=command,
|
|
242
|
+
path=path,
|
|
243
|
+
file_text=file_text,
|
|
244
|
+
view_range=view_range,
|
|
245
|
+
old_str=old_str,
|
|
246
|
+
new_str=new_str,
|
|
247
|
+
insert_line=insert_line,
|
|
248
|
+
enable_linting=enable_linting,
|
|
249
|
+
)
|
|
250
|
+
except ToolError as e:
|
|
251
|
+
result = ToolResult(error=e.message)
|
|
252
|
+
except TypeError as e:
|
|
253
|
+
# Handle unexpected arguments or type errors
|
|
254
|
+
return f'ERROR:\n{str(e)}', (None, None)
|
|
255
|
+
|
|
256
|
+
if result.error:
|
|
257
|
+
return f'ERROR:\n{result.error}', (None, None)
|
|
258
|
+
|
|
259
|
+
if not result.output:
|
|
260
|
+
logger.warning(f'No output from file_editor for {path}')
|
|
261
|
+
return '', (None, None)
|
|
262
|
+
|
|
263
|
+
return result.output, (result.old_content, result.new_content)
|
|
264
|
+
|
|
265
|
+
def _resolve_path(path: str, working_dir: str) -> str:
|
|
266
|
+
"""
|
|
267
|
+
Resolve a file path to an absolute path.
|
|
268
|
+
|
|
269
|
+
If the provided path is relative, it will be resolved relative to the working directory.
|
|
270
|
+
If the path is already absolute, it will be returned as-is.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
path: The file path to resolve (can be relative or absolute)
|
|
274
|
+
working_dir: The working directory to use as base for relative paths
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
str: The absolute file path
|
|
278
|
+
"""
|
|
279
|
+
filepath = Path(path)
|
|
280
|
+
if not filepath.is_absolute():
|
|
281
|
+
# Convert relative path to absolute by joining with working directory
|
|
282
|
+
return str(Path(working_dir) / filepath)
|
|
283
|
+
# Return absolute path as-is
|
|
284
|
+
return str(filepath)
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# File Search - High-Performance File Search Tool
|
|
2
|
+
|
|
3
|
+
A Python file search module based on ripgrep, providing fast and accurate code search functionality.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **High-Performance Search**: Uses ripgrep tool, several times faster than traditional regex search
|
|
8
|
+
- **Context Information**: Provides context around matching lines
|
|
9
|
+
- **File Filtering**: Supports glob pattern filtering for specific file types
|
|
10
|
+
- **Cross-Platform Support**: Supports Windows, macOS, Linux
|
|
11
|
+
- **Package-Friendly**: Supports both development environment and packaged distribution
|
|
12
|
+
- **Auto Fallback**: Prioritizes built-in binary files, can fallback to system installation
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
### Basic Usage
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from file_search import regex_search_files
|
|
20
|
+
|
|
21
|
+
# Search for TODO comments
|
|
22
|
+
results = regex_search_files(
|
|
23
|
+
cwd="/path/to/project",
|
|
24
|
+
directory_path="/path/to/search",
|
|
25
|
+
regex=r"TODO:",
|
|
26
|
+
file_pattern="*.py"
|
|
27
|
+
)
|
|
28
|
+
print(results)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Class Interface
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from file_search import RipgrepSearcher
|
|
35
|
+
|
|
36
|
+
searcher = RipgrepSearcher()
|
|
37
|
+
results = searcher.search_in_files(
|
|
38
|
+
directory_path="./siada",
|
|
39
|
+
regex=r"def\s+\w+\(",
|
|
40
|
+
file_pattern="*.py",
|
|
41
|
+
cwd="/path/to/project"
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Installation and Configuration
|
|
46
|
+
|
|
47
|
+
### Automatic Configuration (Recommended)
|
|
48
|
+
|
|
49
|
+
The tool automatically searches for ripgrep binary files in the following priority order:
|
|
50
|
+
|
|
51
|
+
1. `RIPGREP_BINARY_PATH` environment variable
|
|
52
|
+
2. Built-in binary files (development/packaging environment)
|
|
53
|
+
3. ripgrep in system PATH
|
|
54
|
+
|
|
55
|
+
### Manual Configuration
|
|
56
|
+
|
|
57
|
+
To specify a specific ripgrep path:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export RIPGREP_BINARY_PATH="/path/to/rg"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Binary Files
|
|
64
|
+
|
|
65
|
+
Built-in support for binary files on the following platforms:
|
|
66
|
+
- Windows: `rg.exe`
|
|
67
|
+
- macOS Intel: `rg-macos-x64`
|
|
68
|
+
- macOS Apple Silicon: `rg-macos-arm64`
|
|
69
|
+
- Linux x64: `rg-linux-x64`
|
|
70
|
+
- Linux ARM64: `rg-linux-arm64`
|
|
71
|
+
|
|
72
|
+
## Usage Examples
|
|
73
|
+
|
|
74
|
+
### Search Code Comments
|
|
75
|
+
```python
|
|
76
|
+
# Search for TODO/FIXME comments
|
|
77
|
+
regex_search_files(".", ".", r"TODO:|FIXME:|HACK:", "*.py")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Search Functions and Classes
|
|
81
|
+
```python
|
|
82
|
+
# Search for function definitions
|
|
83
|
+
regex_search_files(".", ".", r"def\s+\w+\(", "*.py")
|
|
84
|
+
|
|
85
|
+
# Search for class definitions
|
|
86
|
+
regex_search_files(".", ".", r"class\s+\w+", "*.py")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Search Import Statements
|
|
90
|
+
```python
|
|
91
|
+
regex_search_files(".", ".", r"^import\s+|^from\s+\w+\s+import", "*.py")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Multi-File Type Search
|
|
95
|
+
```python
|
|
96
|
+
regex_search_files(".", ".", r"console\.log", "*.{js,ts}")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Output Format
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Found 2 results.
|
|
103
|
+
|
|
104
|
+
src/main.py
|
|
105
|
+
│----
|
|
106
|
+
│def process_data(data):
|
|
107
|
+
│ # TODO: Add error handling
|
|
108
|
+
│ return data
|
|
109
|
+
│----
|
|
110
|
+
|
|
111
|
+
src/utils.py
|
|
112
|
+
│----
|
|
113
|
+
│class Helper:
|
|
114
|
+
│ def __init__(self):
|
|
115
|
+
│ # TODO: Initialize properly
|
|
116
|
+
│ pass
|
|
117
|
+
│----
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Parameter Description
|
|
121
|
+
|
|
122
|
+
- **cwd**: Current working directory, used to calculate relative paths
|
|
123
|
+
- **directory_path**: Directory path to search
|
|
124
|
+
- **regex**: Regular expression pattern (Rust syntax)
|
|
125
|
+
- **file_pattern**: File pattern filter (e.g., "*.py", "*.js", "*")
|
|
126
|
+
|
|
127
|
+
## Regular Expression Syntax
|
|
128
|
+
|
|
129
|
+
Uses Rust regular expression syntax with main features:
|
|
130
|
+
- Unicode support
|
|
131
|
+
- Multi-line mode by default
|
|
132
|
+
- Supports lookahead and lookbehind assertions
|
|
133
|
+
- Detailed syntax: https://docs.rs/regex/latest/regex/#syntax
|
|
134
|
+
|
|
135
|
+
## Performance Characteristics
|
|
136
|
+
|
|
137
|
+
- **Result Limit**: Returns maximum 300 results
|
|
138
|
+
- **Output Limit**: Prevents excessive memory usage
|
|
139
|
+
- **Context Control**: Provides 1 line of context before and after each match
|
|
140
|
+
|
|
141
|
+
## Package Distribution
|
|
142
|
+
|
|
143
|
+
### Package Configuration
|
|
144
|
+
|
|
145
|
+
Add to `pyproject.toml`:
|
|
146
|
+
|
|
147
|
+
```toml
|
|
148
|
+
[tool.setuptools.package-data]
|
|
149
|
+
"src.tools.coder.file_search" = ["bin/*", "README.md"]
|
|
150
|
+
|
|
151
|
+
[tool.setuptools.packages.find]
|
|
152
|
+
where = ["src"]
|
|
153
|
+
include = ["src.tools.coder.file_search*"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Verify Packaging
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Run packaging check
|
|
160
|
+
python siada/tools/coder/file_search/setup_package.py
|
|
161
|
+
|
|
162
|
+
# Test functionality
|
|
163
|
+
python siada/tools/coder/file_search/test_search.py
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Troubleshooting
|
|
167
|
+
|
|
168
|
+
### Common Issues
|
|
169
|
+
|
|
170
|
+
**Cannot find ripgrep binary file**
|
|
171
|
+
- Set environment variable: `export RIPGREP_BINARY_PATH="/path/to/rg"`
|
|
172
|
+
- Install system ripgrep: `brew install ripgrep` (macOS) or `apt install ripgrep` (Ubuntu)
|
|
173
|
+
|
|
174
|
+
**Permission errors**
|
|
175
|
+
- Unix systems: `chmod +x file_search/bin/rg-*`
|
|
176
|
+
|
|
177
|
+
**No search results**
|
|
178
|
+
- Check search path and regular expression syntax
|
|
179
|
+
- Confirm file pattern matches target files
|
|
180
|
+
|
|
181
|
+
### Debugging
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from file_search.search import RipgrepSearcher
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
searcher = RipgrepSearcher()
|
|
188
|
+
print(f"Ripgrep binary: {searcher.rg_path}")
|
|
189
|
+
except RuntimeError as e:
|
|
190
|
+
print(f"Error: {e}")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Docker Environment
|
|
194
|
+
|
|
195
|
+
```dockerfile
|
|
196
|
+
# Install system ripgrep
|
|
197
|
+
RUN apt-get update && apt-get install -y ripgrep
|
|
198
|
+
|
|
199
|
+
# Or set environment variable
|
|
200
|
+
ENV RIPGREP_BINARY_PATH=/usr/bin/rg
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Testing
|
|
204
|
+
|
|
205
|
+
Run test cases:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
python siada/tools/coder/file_search/test_search.py
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Tests include:
|
|
212
|
+
- Binary file detection
|
|
213
|
+
- Basic search functionality
|
|
214
|
+
- File type filtering
|
|
215
|
+
- Error handling mechanisms
|
|
216
|
+
|
|
217
|
+
## Technical Details
|
|
218
|
+
|
|
219
|
+
- Based on ripgrep high-performance search engine
|
|
220
|
+
- Supports automatic adaptation for development and packaging environments
|
|
221
|
+
- Automatic permission repair mechanism
|
|
222
|
+
- Multi-path search strategy
|
|
223
|
+
- Graceful error handling and fallback mechanisms
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|