Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 0 additions & 130 deletions .serena/project.yml

This file was deleted.

3 changes: 3 additions & 0 deletions src/serena/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ def __init__(
context: SerenaAgentContext | None = None,
modes: ModeSelectionDefinition | None = None,
memory_log_handler: MemoryLogHandler | None = None,
memory_paths: list[str] | None = None,
):
"""
:param project: the project to load immediately or None to not load any project; may be a path to the project or a name of
Expand All @@ -245,6 +246,7 @@ def __init__(

# project-specific instances, which will be initialized upon project activation
self._active_project: Project | None = None
self._memory_paths = memory_paths

# determine registered project to be activated (if any)
registered_project_to_activate: RegisteredProject | None = (
Expand Down Expand Up @@ -644,6 +646,7 @@ def is_using_language_server(self) -> bool:

def _activate_project(self, project: Project, update_modes_and_tools: bool = True) -> None:
log.info(f"Activating {project.project_name} at {project.project_root}")
project.set_memory_paths(self._memory_paths)

# Check if the project requires a different language backend than the one initialized at startup
project_backend = project.project_config.language_backend
Expand Down
13 changes: 13 additions & 0 deletions src/serena/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ def __init__(self) -> None:
)
@click.option("--trace-lsp-communication", type=bool, is_flag=False, default=None, help="Whether to trace LSP communication.")
@click.option("--tool-timeout", type=float, default=None, help="Override tool execution timeout in config.")
@click.option(
"--memory-path",
type=str,
default=None,
help="Comma-separated list of memory directory paths (absolute or relative to project root). "
"The first path is the primary write location; subsequent paths are additional sources. "
"Append ':ro' to any extra path to make it read-only (e.g. '/shared/memories:ro'). "
"Writes to existing memories update them in-place; new memories go to the primary. "
"Defaults to '<project_root>/.serena/memories'.",
)
@click.option(
"--project-from-cwd",
is_flag=True,
Expand All @@ -247,6 +257,7 @@ def start_mcp_server(
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | None,
trace_lsp_communication: bool | None,
tool_timeout: float | None,
memory_path: str | None,
) -> None:
# initialize logging, using INFO level initially (will later be adjusted by SerenaAgent according to the config)
# * memory log handler (for use by GUI/Dashboard)
Expand Down Expand Up @@ -279,6 +290,7 @@ def start_mcp_server(
log.warning("No project root found from %s; not activating any project", os.getcwd())

project_file = project_file_arg or project
memory_paths_list = [p.strip() for p in memory_path.split(",") if p.strip()] if memory_path else None
factory = SerenaMCPFactory(context=context, project=project_file, memory_log_handler=memory_log_handler)
server = factory.create_mcp_server(
host=host,
Expand All @@ -291,6 +303,7 @@ def start_mcp_server(
log_level=log_level,
trace_lsp_communication=trace_lsp_communication,
tool_timeout=tool_timeout,
memory_paths=memory_paths_list,
)
if project_file_arg:
log.warning(
Expand Down
17 changes: 14 additions & 3 deletions src/serena/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,19 @@ def _set_mcp_tools(self, mcp: FastMCP, openai_tool_compatible: bool = False) ->
mcp._tool_manager._tools[tool.get_name()] = mcp_tool
log.info(f"Starting MCP server with {len(mcp._tool_manager._tools)} tools: {list(mcp._tool_manager._tools.keys())}")

def _create_serena_agent(self, serena_config: SerenaConfig, modes: ModeSelectionDefinition | None = None) -> SerenaAgent:
def _create_serena_agent(
self,
serena_config: SerenaConfig,
modes: ModeSelectionDefinition | None = None,
memory_paths: list[str] | None = None,
) -> SerenaAgent:
return SerenaAgent(
project=self.project, serena_config=serena_config, context=self.context, modes=modes, memory_log_handler=self.memory_log_handler
project=self.project,
serena_config=serena_config,
context=self.context,
modes=modes,
memory_log_handler=self.memory_log_handler,
memory_paths=memory_paths,
)

def _create_default_serena_config(self) -> SerenaConfig:
Expand All @@ -280,6 +290,7 @@ def create_mcp_server(
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | None = None,
trace_lsp_communication: bool | None = None,
tool_timeout: float | None = None,
memory_paths: list[str] | None = None,
) -> FastMCP:
"""
Create an MCP server with process-isolated SerenaAgent to prevent asyncio contamination.
Expand Down Expand Up @@ -321,7 +332,7 @@ def create_mcp_server(
mode_selection_def: ModeSelectionDefinition | None = None
if modes:
mode_selection_def = ModeSelectionDefinition(default_modes=modes)
self.agent = self._create_serena_agent(config, mode_selection_def)
self.agent = self._create_serena_agent(config, mode_selection_def, memory_paths)

except Exception as e:
show_fatal_exception_safe(e)
Expand Down
Loading
Loading