From 34db0f1446f760a6b820fe4f68f868f11e8d0d96 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 9 Jul 2025 20:00:03 +0200 Subject: [PATCH] Make ReadFileTool independent of language server, adding a read function in Project --- src/serena/project.py | 12 ++++++++++++ src/serena/tools/file_tools.py | 2 +- src/serena/tools/tools_base.py | 10 +++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/serena/project.py b/src/serena/project.py index 321a6223..4a37b7a9 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -30,3 +30,15 @@ class Project: def path_to_project_yml(self) -> str: return os.path.join(self.project_root, self.project_config.rel_path_to_project_yml()) + + def read_file(self, relative_path: str) -> str: + """ + Reads a file relative to the project root. + + :param relative_path: the path to the file relative to the project root + :return: the content of the file + """ + abs_path = Path(self.project_root) / relative_path + if not abs_path.exists(): + raise FileNotFoundError(f"File not found: {abs_path}") + return abs_path.read_text(encoding=self.project_config.encoding) diff --git a/src/serena/tools/file_tools.py b/src/serena/tools/file_tools.py index 0bd091ee..526561fb 100644 --- a/src/serena/tools/file_tools.py +++ b/src/serena/tools/file_tools.py @@ -41,7 +41,7 @@ class ReadFileTool(Tool): """ self.agent.validate_relative_path(relative_path) - result = self.language_server.retrieve_full_file_content(relative_path) + result = self.project.read_file(relative_path) result_lines = result.splitlines() if end_line is None: result_lines = result_lines[start_line:] diff --git a/src/serena/tools/tools_base.py b/src/serena/tools/tools_base.py index 67f43f97..a3eca53e 100644 --- a/src/serena/tools/tools_base.py +++ b/src/serena/tools/tools_base.py @@ -11,6 +11,7 @@ from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metada from sensai.util import logging from sensai.util.string import dict_string +from serena.project import Project from serena.prompt_factory import PromptFactory from serena.symbol import SymbolManager from serena.util.class_decorators import singleton @@ -55,11 +56,18 @@ class Component(ABC): assert self.agent.symbol_manager is not None return self.agent.symbol_manager + @property + def project(self) -> Project: + project = self.agent.get_active_project() + if project is None: + raise ValueError("No active project") + return project + def create_code_editor(self) -> "CodeEditor": from ..code_editor import JetBrainsCodeEditor, LanguageServerCodeEditor if self.agent.serena_config.jetbrains: - return JetBrainsCodeEditor(project=self.agent.get_active_project(), agent=self.agent) + return JetBrainsCodeEditor(project=self.project, agent=self.agent) else: return LanguageServerCodeEditor(self.symbol_manager, agent=self.agent)