diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index 81ce63539..378e9db2c 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -74,7 +74,7 @@ class RoleZero(Role): tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: Optional[ToolRecommender] = None tool_execution_map: Annotated[dict[str, Callable], Field(exclude=True)] = {} - special_tool_commands: list[str] = ["Plan.finish_current_task", "end"] + special_tool_commands: list[str] = ["Plan.finish_current_task", "end", "Bash.run"] # Equipped with three basic tools by default for optional use editor: Editor = Editor() browser: Browser = Browser() @@ -144,11 +144,12 @@ class RoleZero(Role): "goto_line", "insert_content_at_line", "open_file", - "scroll_down", + "read" "scroll_down", "scroll_up", "search_dir", "search_file", "set_workdir", + "write", ] } ) diff --git a/metagpt/roles/di/swe_agent.py b/metagpt/roles/di/swe_agent.py index cd15fc5af..f147e80f3 100644 --- a/metagpt/roles/di/swe_agent.py +++ b/metagpt/roles/di/swe_agent.py @@ -11,7 +11,7 @@ from metagpt.prompts.di.swe_agent import ( from metagpt.roles.di.role_zero import RoleZero from metagpt.schema import Message from metagpt.tools.libs.git import git_create_pull -from metagpt.tools.libs.terminal import Bash +from metagpt.tools.libs.terminal import Terminal class SWEAgent(RoleZero): @@ -19,14 +19,8 @@ class SWEAgent(RoleZero): profile: str = "Issue Solver" goal: str = "Resolve GitHub issue or bug in any existing codebase" _instruction: str = NEXT_STEP_TEMPLATE - tools: list[str] = [ - # "Bash", - "Browser:goto,scroll", - "RoleZero", - "git_create_pull", - "Editor", - ] - terminal: Bash = Field(default_factory=Bash, exclude=True) + tools: list[str] = ["Browser:goto,scroll", "RoleZero", "git_create_pull", "Editor", "Terminal"] + terminal: Terminal = Field(default_factory=Terminal, exclude=True) output_diff: str = "" max_react_loop: int = 40 run_eval: bool = False @@ -39,7 +33,6 @@ class SWEAgent(RoleZero): def _update_tool_execution(self): self.tool_execution_map.update( { - "Bash.run": self.terminal.run, "git_create_pull": git_create_pull, } ) diff --git a/metagpt/tools/libs/editor.py b/metagpt/tools/libs/editor.py index 81f2bd4a7..8013b99c9 100644 --- a/metagpt/tools/libs/editor.py +++ b/metagpt/tools/libs/editor.py @@ -42,9 +42,8 @@ class LineNumberError(Exception): @register_tool() class Editor(BaseModel): """ - A state-of-state tool for open/reading, understanding, and editing/writing files. - Args: - working_dir: The working directory to use for the editor. + A tool for reading, understanding, writing, and editing files. + Support local file including text-based files (txt, md, json, py, html, js, css, etc.), pdf, docx, excluding images, csv, excel, or online links """ model_config = ConfigDict(arbitrary_types_allowed=True) @@ -281,7 +280,7 @@ class Editor(BaseModel): def set_workdir(self, path: str) -> None: """ Sets the working directory to the given path. eg: repo directory. - You need to set it up before operating the file. + You MUST to set it up before open the file. Args: path: str: The path to set as the working directory. diff --git a/requirements.txt b/requirements.txt index e669da46d..ed8965b46 100644 --- a/requirements.txt +++ b/requirements.txt @@ -74,5 +74,5 @@ pylint~=3.0.3 pygithub~=2.3 htmlmin fsspec -grep-ast~=0.3.3 -tree-sitter~=0.21.3 \ No newline at end of file +grep-ast~=0.3.3 # linter +tree-sitter~=0.21.3 # linter \ No newline at end of file diff --git a/tests/data/tools/test_script_for_editor.py b/tests/data/tools/test_script_for_editor.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/metagpt/tools/libs/test_editor.py b/tests/metagpt/tools/libs/test_editor.py index e2774ddc5..bcef2b74e 100644 --- a/tests/metagpt/tools/libs/test_editor.py +++ b/tests/metagpt/tools/libs/test_editor.py @@ -518,6 +518,41 @@ def test_edit_file_by_replace(temp_py_file): assert new_content.strip() == EXPECTED_CONTENT_AFTER_REPLACE_TEXT.strip() +def test_append_file(temp_file_path): + editor = Editor() + # 写入初始内容 + initial_content = "Line 1\nLine 2\nLine 3\n" + temp_file_path.write_text(initial_content) + + # 追加内容到文件 + append_content = "Line 4\nLine 5\n" + + result = editor.append_file(str(temp_file_path), append_content) + + # 预期内容 + expected_content = initial_content + append_content + + # 读取文件并断言内容与预期一致 + with open(temp_file_path, "r") as f: + new_content = f.read() + assert new_content == expected_content + + # 输出的预期结果 + expected_output = ( + f"[File: {temp_file_path.resolve()} (5 lines total after edit)]\n" + "(this is the beginning of the file)\n" + "1|Line 1\n" + "2|Line 2\n" + "3|Line 3\n" + "4|Line 4\n" + "5|Line 5\n" + "(this is the end of the file)\n" + "[File updated (edited at line 3). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]" + ) + + assert result.split("\n") == expected_output.split("\n") + + def test_search_dir(tmp_path): editor = Editor() dir_path = tmp_path / "test_dir"