Merge remote-tracking branch 'origin/main' into features/diagnostics_and_ls_extensions

Conflicts:
	CHANGELOG.md
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-04-30 18:25:22 +02:00
commit 90d6f2cde8
18 files changed
+283 -192

No files matched your search

+25 -49
View File
@@ -3,15 +3,18 @@ project_name: "serena"
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp
# csharp_omnisharp dart elixir elm erlang
# fortran fsharp go groovy haskell
# java julia kotlin lua markdown
# matlab nix pascal perl php
# php_phpactor powershell python python_jedi r
# rego ruby ruby_solargraph rust scala
# swift terraform toml typescript typescript_vts
# vue yaml zig
# al ansible bash clojure cpp
# cpp_ccls crystal csharp csharp_omnisharp dart
# elixir elm erlang fortran fsharp
# go groovy haskell haxe hlsl
# java json julia kotlin lean4
# lua luau markdown matlab msl
# nix ocaml pascal perl php
# php_phpactor powershell python python_jedi python_ty
# r rego ruby ruby_solargraph rust
# scala solidity swift systemverilog terraform
# toml typescript typescript_vts vue yaml
# zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
@@ -45,49 +48,12 @@ read_only: false
# list of tool names to exclude.
# This extends the existing exclusions (e.g. from the global configuration)
#
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
# * `activate_project`: Activates a project based on the project name or path.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_memory`: Delete a memory file. Should only happen if a user asks for it explicitly,
# for example by saying that the information retrieved from a memory file is no longer correct
# or no longer relevant for the project.
# * `edit_memory`: Replaces content matching a regular expression in a memory.
# * `execute_shell_command`: Executes a shell command.
# * `find_file`: Finds files in the given relative paths
# * `find_referencing_symbols`: Finds symbols that reference the given symbol using the language server backend
# * `find_symbol`: Performs a global (or local) search using the language server backend.
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Provides instructions Serena usage (i.e. the 'Serena Instructions Manual')
# for clients that do not read the initial instructions when the MCP server is connected.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: List available memories. Any memory can be read using the `read_memory` tool.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Read the content of a memory file. This tool should only be used if the information
# is relevant to the current task. You can infer whether the information
# is relevant from the memory file name.
# You should not read the same memory file multiple times in the same conversation.
# * `rename_memory`: Renames or moves a memory. Moving between project and global scope is supported
# (e.g., renaming "global/foo" to "bar" moves it from global to project scope).
# * `rename_symbol`: Renames a symbol throughout the codebase using language server refactoring capabilities.
# For JB, we use a separate tool.
# * `replace_content`: Replaces content in a file (optionally using regular expressions).
# * `replace_symbol_body`: Replaces the full definition of a symbol using the language server backend.
# * `safe_delete_symbol`:
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `write_memory`: Write some information (utf-8-encoded) about this project that can be useful for future tasks to a memory in md format.
# The memory name should be meaningful.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
included_optional_tools: []
# initial prompt for the project. It will always be given to the LLM upon activating the project
@@ -122,14 +88,17 @@ encoding: utf-8
base_modes:
# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# This setting can, in turn, be overridden by CLI parameters (--mode).
# Set this to [] to not use the default modes defined in the global config for this project.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
fixed_tools: []
# time budget (seconds) per tool call for the retrieval of additional symbol information
@@ -167,3 +136,10 @@ ignored_memory_patterns: []
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
ls_specific_settings: {}
# list of mode names to be activated additionally for this project.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# Otherwise, this setting overrides the global configuration.
# Set this to a list of mode names to always include the respective modes for this project.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
added_modes:
+6
View File
@@ -2,6 +2,12 @@
Status of the `main` branch. Changes prior to the next official version change will appear here.
* General:
- Breaking change in mode definitions: Projects (project.yml) can no longer override `base_modes`.
Instead, they can define `added_modes` to add modes on top of base and default modes.
See updated [documentation on modes](https://oraios.github.io/serena/02-usage/050_configuration.html#modes).
- Serena's default configuration now uses `interactive` and `editing` as `base_modes` instead of as `default_modes`.
* JetBrains:
- Add new tools:
- `jet_brains_list_inspections`: Lists available IDE inspections (akin to diagnostics), optionally filtered by language or group
+1 -1
View File
@@ -4,7 +4,7 @@
</p>
<h3 align="center">
Serena is the IDE for your coding agent.
The IDE for Your Coding Agent
</h3>
<div align="center">
+15 -21
View File
@@ -118,32 +118,26 @@ Examples of built-in modes include:
Find the concrete definitions of these modes [here](https://github.com/oraios/serena/tree/main/src/serena/resources/config/modes).
Active modes are configured in (from lowest to highest precedence):
The modes to be activated are configured in:
* the global configuration file (`serena_config.yml`)
- defines `base_modes`, which are always included
- defines `default_modes`, which can be overridden by projects or command line parameters
* the project configuration file (`project.yml`)
- defines `default_modes` (overriding the default modes in the global configuration)
- defines `added_modes`, which are added on top
* at startup via command-line parameters
- can override default modes with `--mode`
- can define modes to be added on top with `--add-mode`
The two former sources define both **base modes** and **default modes**.
Ultimately, the active modes are the union of base modes and default modes (after applying all overrides).
Command-line parameters override default modes but not base modes.
Base modes should thus be used to define modes that you always want to be active, regardless of command-line parameters.
Ultimately, the active modes are given by the union of
* `base_modes` defined in the global configuration (always active)
* `default_modes` (defined in the global configuration, optionally overridden by the project/CLI)
* `added_modes` (defined in the project configuration/via CLI parameters)
Command-line parameters for overriding default modes:
When launching the MCP sever, specify modes using `--mode <mode-name>`; multiple modes can be specified, e.g. `--mode planning --mode no-onboarding`.
:::{important}
By default, Serena activates the two modes `interactive` and `editing` (as defined in the global configuration).
As soon as you start to specify modes via the command line, only the modes you explicitly specify will be active, however.
Therefore, if you want to keep the default modes, you must specify them as well.
For example, to add mode `no-memories` to the default behaviour, specify
```shell
--mode interactive --mode editing --mode no-memories
```
If you want to keep certain modes as always active, regardless of command-line parameters,
define them as *base modes* in the global or project configuration.
:::
So you should
* define modes you definitely always want to use in `base_modes`,
* define modes that you typically want to use but sometimes want to override in `default_modes`,
* use `added_modes` to add modes that you need only for specific projects/sessions.
:::{note}
**Mode Compatibility**: While you can combine modes, some may be semantically incompatible (e.g., `interactive` and `one-shot`).
+35 -14
View File
@@ -31,6 +31,8 @@ from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode
from serena.config.serena_config import (
LanguageBackend,
ModeSelectionDefinition,
ModeSelectionDefinitionWithAddedModes,
ModeSelectionDefinitionWithBaseModes,
NamedToolInclusionDefinition,
RegisteredProject,
SerenaConfig,
@@ -206,18 +208,36 @@ class ActiveModes:
def __init__(self) -> None:
self._configured_base_modes: Sequence[str] | None = None
self._configured_default_modes: Sequence[str] | None = None
self._added_modes: set[str] = set()
self._dynamically_activated_mode_names: set[str] = set()
"""
the subset of active mode names that are dynamically activated (not necessarily enabled after project change)
"""
self._active_mode_names: Sequence[str] = []
"""
the full list of active mode names
"""
def apply(self, mode_selection: ModeSelectionDefinition) -> None:
log.debug("Applying mode selection definition %s", mode_selection)
# apply overrides
log.debug("Applying mode selection: default_modes=%s, base_modes=%s", mode_selection.default_modes, mode_selection.base_modes)
if mode_selection.base_modes is not None:
self._configured_base_modes = mode_selection.base_modes
if isinstance(mode_selection, ModeSelectionDefinitionWithBaseModes):
if mode_selection.base_modes is not None:
self._configured_base_modes = mode_selection.base_modes
if mode_selection.default_modes is not None:
self._configured_default_modes = mode_selection.default_modes
log.debug("Current mode selection: base_modes=%s, default_modes=%s", self._configured_base_modes, self._configured_default_modes)
self._active_mode_names = sorted(set(self._configured_base_modes or []) | set(self._configured_default_modes or []))
# apply added modes (if any)
if isinstance(mode_selection, ModeSelectionDefinitionWithAddedModes):
if mode_selection.added_modes:
log.debug("Adding modes: %s", mode_selection.added_modes)
self._added_modes.update(mode_selection.added_modes)
log.debug("Current added modes: %s", self._added_modes)
self._dynamically_activated_mode_names = set(self._configured_default_modes or []) | self._added_modes
self._active_mode_names = sorted(set(self._configured_base_modes or []) | self._dynamically_activated_mode_names)
def get_mode_names(self) -> Sequence[str]:
return self._active_mode_names
@@ -231,8 +251,8 @@ class ActiveModes:
def get_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._active_mode_names]
def get_default_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._configured_default_modes or []]
def get_dynamically_activated_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._dynamically_activated_mode_names]
def get_base_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._configured_base_modes or []]
@@ -511,8 +531,7 @@ class SerenaAgent:
:param serena_config: the Serena configuration or None to read the configuration from the default location.
:param context: the context in which the agent is operating, None for default context.
The context may adjust prompts, tool availability, and tool descriptions.
:param modes: list of modes in which the agent is operating (they will be combined), None for default modes.
The modes may adjust prompts, tool availability, and tool descriptions.
:param modes: mode selection definition to apply for this session
:param memory_log_handler: a MemoryLogHandler instance from which to read log messages; if None, a new one will be created
if necessary.
"""
@@ -521,7 +540,7 @@ class SerenaAgent:
self._gui_log_viewer: Optional["GuiLogViewer"] = None
self._dashboard_manager: DashboardManager | None = None
self._project_prompt_status = ProjectPromptProvisionStatus()
self._mode_overrides = modes
self._session_mode_selection_definition = modes
self.version = serena_version()
# obtain serena configuration using the decoupled factory function
@@ -712,9 +731,11 @@ class SerenaAgent:
# * base modes: These cannot be changed, so they are fully applied
for base_mode in modes.get_base_modes():
tool_inclusion_definitions.append(base_mode)
# * default modes: When not in a single-project context, these modes are dynamic (can later be turned off),
# so we consider only their inclusions (but not their exclusions, because these must not be hard)
for mode in modes.get_default_modes():
# * dynamically activated modes:
# - When not in a single-project context, these modes can later be turned off,
# so we consider only their inclusions (but not their exclusions, because these must not be hard).
# - In a single-project context, we can consider them fully.
for mode in modes.get_dynamically_activated_modes():
if is_single_project:
tool_inclusion_definitions.append(mode)
else:
@@ -986,8 +1007,8 @@ class SerenaAgent:
self._active_modes.apply(self.serena_config)
if self._active_project:
self._active_modes.apply(self._active_project.project_config)
if self._mode_overrides:
self._active_modes.apply(self._mode_overrides)
if self._session_mode_selection_definition:
self._active_modes.apply(self._session_mode_selection_definition)
if log_message:
active_mode_names = self._active_modes.get_mode_names()
log.info(f"Active modes ({len(active_mode_names)}): {', '.join(active_mode_names)}")
+26 -10
View File
@@ -23,6 +23,7 @@ from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode
from serena.config.serena_config import (
LanguageBackend,
ModeSelectionDefinition,
ModeSelectionDefinitionWithAddedModes,
ProjectConfig,
RegisteredProject,
SerenaConfig,
@@ -37,7 +38,6 @@ from serena.constants import (
)
from serena.prompt_factory import SerenaPromptFactory
from serena.util.cli_util import AutoRegisteringGroup
from serena.util.dataclass import get_dataclass_default
from serena.util.logging import MemoryLogHandler
from solidlsp.ls_config import Language
from solidlsp.ls_types import SymbolKind
@@ -46,15 +46,17 @@ from solidlsp.util.subprocess_util import subprocess_kwargs
log = logging.getLogger(__name__)
_MAX_CONTENT_WIDTH = 200
_MODES_EXPLANATION = f"""\b\nBuilt-in mode names or paths to custom mode YAMLs with which to
override the default modes defined in the global Serena configuration or
_MODES_EXPLANATION = """\b\nBuilt-in mode names or paths to custom mode YAMLs with which to
override the default_modes defined in the global Serena configuration or
the active project.
For details on mode configuration, see
https://oraios.github.io/serena/02-usage/050_configuration.html#modes.
If no configuration changes were made, the base defaults are:
{get_dataclass_default(SerenaConfig, "default_modes")}.
Overriding them means that they no longer apply, so you will need to
re-specify them in addition to further modes if you want to keep them."""
"""
_ADD_MODES_EXPLANATION = """\b\nMode names or paths to custom mode YAMLs which shall
be added on top of the other modes specified by the global/project configuration.
For details on mode configuration, see
https://oraios.github.io/serena/02-usage/050_configuration.html#modes.
"""
def find_project_root(root: str | Path | None = None) -> str | None:
@@ -228,13 +230,22 @@ class TopLevelCommands(AutoRegisteringGroup):
)
@click.option(
"--mode",
"modes",
"default_modes",
type=str,
multiple=True,
default=(),
show_default=False,
help=_MODES_EXPLANATION,
)
@click.option(
"--add-mode",
"added_modes",
type=str,
multiple=True,
default=(),
show_default=False,
help=_ADD_MODES_EXPLANATION,
)
@click.option(
"--language-backend",
type=click.Choice([lb.value for lb in LanguageBackend]),
@@ -300,7 +311,8 @@ class TopLevelCommands(AutoRegisteringGroup):
project_file_arg: str | None,
project_from_cwd: bool | None,
context: str,
modes: Sequence[str],
default_modes: Sequence[str],
added_modes: Sequence[str],
language_backend: str | None,
transport: Literal["stdio", "sse", "streamable-http"],
host: str,
@@ -346,11 +358,15 @@ class TopLevelCommands(AutoRegisteringGroup):
project_file = project_file_arg or project
mode_selection_def: ModeSelectionDefinition | None = None
if default_modes or added_modes:
mode_selection_def = ModeSelectionDefinitionWithAddedModes(default_modes=default_modes or None, added_modes=added_modes or None)
factory = SerenaMCPFactory(context=context, project=project_file, memory_log_handler=memory_log_handler)
server = factory.create_mcp_server(
host=host,
port=port,
modes=modes,
mode_selection_def=mode_selection_def,
language_backend=LanguageBackend.from_str(language_backend) if language_backend else None,
enable_web_dashboard=enable_web_dashboard,
open_web_dashboard=open_web_dashboard,
+20 -6
View File
@@ -168,10 +168,22 @@ class NamedToolInclusionDefinition(ToolInclusionDefinition):
@dataclass
class ModeSelectionDefinition:
base_modes: Sequence[str] | None = None
default_modes: Sequence[str] | None = None
@dataclass
class ModeSelectionDefinitionWithBaseModes(ModeSelectionDefinition):
base_modes: Sequence[str] | None = ("interactive", "editing")
"""
the base modes to use, which are always guaranteed to be included
"""
@dataclass
class ModeSelectionDefinitionWithAddedModes(ModeSelectionDefinition):
added_modes: Sequence[str] | None = None
class LanguageBackend(Enum):
LSP = "LSP"
"""
@@ -228,7 +240,7 @@ class LineEnding(Enum):
@dataclass
class SharedConfig(ModeSelectionDefinition, ToolInclusionDefinition, ToStringMixin):
class SharedConfig(ToolInclusionDefinition, ToStringMixin):
"""Shared between SerenaConfig and ProjectConfig, the latter used to override values in the form
(same as in ModeSelectionDefinition).
The defaults here shall be none and should be set to the global default values in SerenaConfig.
@@ -255,7 +267,7 @@ Uses $projectDir and $projectFolderName as placeholders.
@dataclass(kw_only=True)
class ProjectConfig(SharedConfig):
class ProjectConfig(SharedConfig, ModeSelectionDefinitionWithAddedModes):
project_name: str
languages: list[Language]
ignored_paths: list[str] = field(default_factory=list)
@@ -471,6 +483,9 @@ class ProjectConfig(SharedConfig):
excluded_tools = data["excluded_tools"] or []
included_optional_tools = data["included_optional_tools"] or []
if "base_modes" in data and data["base_modes"] is not None:
log.warning("The base_modes setting in project.yml is deprecated and will be ignored.")
return cls(
project_name=data["project_name"],
languages=languages,
@@ -486,7 +501,7 @@ class ProjectConfig(SharedConfig):
encoding=data["encoding"],
line_ending=line_ending,
language_backend=language_backend,
base_modes=data["base_modes"],
added_modes=data["added_modes"],
default_modes=data["default_modes"],
symbol_info_budget=symbol_info_budget,
ls_specific_settings=data.get("ls_specific_settings", {}),
@@ -677,7 +692,7 @@ class RegisteredProject(ToStringMixin):
@dataclass(kw_only=True)
class SerenaConfig(SharedConfig):
class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes):
"""
Holds the Serena agent configuration, which is typically loaded from a YAML configuration file
(when instantiated via :method:`from_config_file`), which is updated when projects are added or removed.
@@ -731,7 +746,6 @@ class SerenaConfig(SharedConfig):
"""
the language backend to use for code understanding features
"""
default_modes: Sequence[str] | None = ("interactive", "editing")
line_ending: LineEnding = LineEnding.NATIVE
symbol_info_budget: float = 10.0
"""
+3 -6
View File
@@ -3,7 +3,7 @@ The Serena Model Context Protocol (MCP) Server
"""
import sys
from collections.abc import AsyncIterator, Iterator, Sequence
from collections.abc import AsyncIterator, Iterator
from contextlib import asynccontextmanager
from copy import deepcopy
from dataclasses import dataclass
@@ -272,7 +272,7 @@ class SerenaMCPFactory:
self,
host: str = "127.0.0.1",
port: int = 8000,
modes: Sequence[str] = (),
mode_selection_def: ModeSelectionDefinition | None = None,
language_backend: LanguageBackend | None = None,
enable_web_dashboard: bool | None = None,
enable_gui_log_window: bool | None = None,
@@ -286,7 +286,7 @@ class SerenaMCPFactory:
:param host: The host to bind to
:param port: The port to bind to
:param modes: List of mode names or paths to mode files
:param mode_selection_def: the mode selection definition to apply
:param language_backend: the language backend to use, overriding the configuration setting.
:param enable_web_dashboard: Whether to enable the web dashboard. If not specified, will take the value from the serena configuration.
:param enable_gui_log_window: Whether to enable the GUI log window. It currently does not work on macOS, and setting this to True will be ignored then.
@@ -318,9 +318,6 @@ class SerenaMCPFactory:
if language_backend is not None:
config.language_backend = language_backend
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)
except Exception as e:
@@ -1,10 +1,11 @@
description: JetBrains tools replace language server-based tools
prompt: |
You have access to the very powerful JetBrains tools for symbolic operations.
These party replace regular tools you were informed about:
These partly replace regular tools you were informed about:
* `jet_brains_find_symbol` replaces `find_symbol`
* `jet_brains_find_referencing_symbols` replaces `find_referencing_symbols`
* `jet_brains_get_symbols_overview` replaces `get_symbols_overview`
* `jet_brains_rename` replaces `rename_symbol`
excluded_tools:
- find_symbol
- find_referencing_symbols
+26 -61
View File
@@ -3,16 +3,18 @@ project_name: "project_name"
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp
# csharp_omnisharp dart elixir elm erlang
# fortran fsharp go groovy haskell
# haxe java julia kotlin lua
# markdown
# matlab nix pascal perl php
# php_phpactor powershell python python_jedi r
# rego ruby ruby_solargraph rust scala
# swift terraform toml typescript typescript_vts
# vue yaml zig
# al ansible bash clojure cpp
# cpp_ccls crystal csharp csharp_omnisharp dart
# elixir elm erlang fortran fsharp
# go groovy haskell haxe hlsl
# java json julia kotlin lean4
# lua luau markdown matlab msl
# nix ocaml pascal perl php
# php_phpactor powershell python python_jedi python_ty
# r rego ruby ruby_solargraph rust
# scala solidity swift systemverilog terraform
# toml typescript typescript_vts vue yaml
# zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
@@ -65,71 +67,34 @@ read_only: false
# list of tool names to exclude.
# This extends the existing exclusions (e.g. from the global configuration)
#
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project based on the project name or path.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_memory`: Delete a memory file. Should only happen if a user asks for it explicitly,
# for example by saying that the information retrieved from a memory file is no longer correct
# or no longer relevant for the project.
# * `edit_memory`: Replaces content matching a regular expression in a memory.
# * `execute_shell_command`: Executes a shell command.
# * `find_file`: Finds files in the given relative paths
# * `find_referencing_symbols`: Finds symbols that reference the given symbol using the language server backend
# * `find_symbol`: Performs a global (or local) search using the language server backend.
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Provides instructions Serena usage (i.e. the 'Serena Instructions Manual')
# for clients that do not read the initial instructions when the MCP server is connected.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: List available memories. Any memory can be read using the `read_memory` tool.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Read the content of a memory file. This tool should only be used if the information
# is relevant to the current task. You can infer whether the information
# is relevant from the memory file name.
# You should not read the same memory file multiple times in the same conversation.
# * `rename_memory`: Renames or moves a memory. Moving between project and global scope is supported
# (e.g., renaming "global/foo" to "bar" moves it from global to project scope).
# * `rename_symbol`: Renames a symbol throughout the codebase using language server refactoring capabilities.
# For JB, we use a separate tool.
# * `replace_content`: Replaces content in a file (optionally using regular expressions).
# * `replace_symbol_body`: Replaces the full definition of a symbol using the language server backend.
# * `safe_delete_symbol`:
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `write_memory`: Write some information (utf-8-encoded) about this project that can be useful for future tasks to a memory in md format.
# The memory name should be meaningful.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
included_optional_tools: []
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
fixed_tools: []
# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:
# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# list of mode names that are to be activated by default, overriding the setting in the global configuration.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply
# for this project.
# This setting can, in turn, be overridden by CLI parameters (--mode).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:
# list of mode names to be activated additionally for this project, e.g. ["query-projects"]
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
added_modes:
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
+23 -16
View File
@@ -37,13 +37,15 @@ gui_log_window: False
# Further information: https://oraios.github.io/serena/02-usage/060_dashboard.html
web_dashboard: True
# whether to open the Dashboard window/browser tab when Serena starts (provided that web_dashboard is enabled).
# If set to false, you can still open the dashboard manually by clicking on the Serena icon in your system
# tray on Windows and macOS. On Linux, there is no system tray support, so you can only open the dashboard by
# a) telling the LLM to "open the dashboard" (provided that the open_dashboard tool is enabled) or by
# b) manually navigating to http://localhost:24282/dashboard/ in your web browser (actual port
# may be higher if you have multiple instances running; try ports 24283, 24284, etc.)
# See also: https://oraios.github.io/serena/02-usage/060_dashboard.html
# whether to open the Dashboard window/browser tab when Serena starts (provided that `web_dashboard` is enabled).
# If set to false, you can still open the dashboard manually:
# * When using an interface that supports a tray icon (see setting `web_dashboard_interface`),
# you can conveniently open the dashboard from the system tray.
# * When using the `browser` interface (no tray icon), so you can only open the dashboard by
# a) telling the LLM to "open the dashboard" (provided that the open_dashboard tool is enabled) or by
# b) manually navigating to http://localhost:24282/dashboard/ in your web browser (actual port
# may be higher if you have multiple instances running; try ports 24283, 24284, etc.)
# Further information: https://oraios.github.io/serena/02-usage/060_dashboard.html
web_dashboard_open_on_launch: True
# defines the interface (application mode) used for the web dashboard (if enabled).
@@ -59,6 +61,7 @@ web_dashboard_open_on_launch: True
# opening the dashboard in browser tabs when selected from the tray menu.
# This is EXPERIMENTAL. It is tested on Windows only. We will establish macOS support, but it is yet untested.
# On Linux, this cannot be universally supported, but it may work in some desktop environments.
# See https://oraios.github.io/serena/02-usage/060_dashboard.html
web_dashboard_interface:
# the address the web dashboard will listen on (bind address).
@@ -112,19 +115,23 @@ included_optional_tools: []
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
fixed_tools: []
# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If this is undefined, no base modes are included.
# The project configuration (project.yml) may override this setting.
# list of mode names to that are always to be included in the set of active modes.
# The full set of modes to be activated is base_modes + default_modes + added_modes,
# where added_modes can be defined by projects/CLI parameters.
# If this is undefined/empty, no base modes are included.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
base_modes:
# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# These modes can be overridden by the project configuration (project.yml) or through the CLI (--mode).
default_modes:
- interactive
- editing
# list of mode names that are to be activated by default (but can be overridden by projects/CLI params).
# The full set of modes to be activated is base_modes + default_modes + added_modes,
# where added_modes are defined by projects/CLI parameters.
# If this is undefined/empty, no default modes are defined.
# These modes can be overridden by the project configuration (project.yml) or through the CLI (--mode).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:
# Used as default for tools where the apply method has a default maximal answer length.
# Even though the value of the max_answer_chars can be changed when calling the tool, it may make sense to adjust this default
# through the global configuration.
+1 -1
View File
@@ -23,7 +23,7 @@ from solidlsp.ls_types import SymbolKind
class RestartLanguageServerTool(Tool, ToolMarkerOptional):
"""Restarts the language server, may be necessary when edits not through Serena happen."""
"""Restarts the language server(s)."""
def apply(self) -> str:
"""Use this tool only on explicit user request or after confirmation.
+3
View File
@@ -173,6 +173,9 @@ class GitignoreParser:
except PermissionError as ex:
log.debug(f"Skipping entry due to permission error: {entry.path}", exc_info=ex)
continue
except FileNotFoundError as ex:
log.debug(f"Skipping entry due to file not found error (possibly a broken link): {entry.path}", exc_info=ex)
continue
while queue:
next_abs_path = queue.pop(0)
@@ -224,6 +224,15 @@ class TypeScriptLanguageServer(SolidLanguageServer):
def _create_launch_command(self, core_path: str) -> list[str]:
return [core_path, "--stdio"]
def _get_language_id_for_file(self, relative_file_path: str) -> str:
# JSX is parsed as TS without this, which silently truncates symbol
# ranges at the first multi-line JSX expression.
if relative_file_path.endswith(".tsx"):
return "typescriptreact"
if relative_file_path.endswith(".jsx"):
return "javascriptreact"
return self.language_id
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
"""
Returns the initialize params for the TypeScript Language Server.
+10 -4
View File
@@ -487,10 +487,14 @@ class SolidLanguageServer(ABC):
:param config: the global SolidLSP configuration.
:param repository_root_path: the root path of the repository.
:param process_launch_info: (DEPRECATED - implement _create_dependency_provider instead)
:param process_launch_info: (DEPRECATED: pass None and implement _create_dependency_provider instead)
the command used to start the actual language server.
The command must pass appropriate flags to the binary, so that it runs in the stdio mode,
as opposed to HTTP, TCP modes supported by some language servers.
:param language_id: The language identifier which will be passed to the language server in the `textDocument/didOpen`
notification by default.
If the language server uses multiple language identifiers, it must override the method `get_language_id_for_file`
to provide the appropriate identifier for each type of file.
:param cache_version_raw_document_symbols: the version, for caching, of the raw document symbols coming
from this specific language server. This should be incremented by subclasses calling this constructor
whenever the format of the raw document symbols changes (typically because the language server
@@ -1116,10 +1120,12 @@ class SolidLanguageServer(ABC):
pass
def _get_language_id_for_file(self, relative_file_path: str) -> str:
"""Return the language ID for a file.
"""
Determines the language identifier to pass to the language server for the given file,
particularly `textDocument/didOpen` requests.
Override in subclasses to return file-specific language IDs.
Default implementation returns self.language_id.
Override this method in subclasses to return file-specific language identifiers.
The default implementation returns the main identifier passed at construction (self.language_id).
"""
return self.language_id
@@ -0,0 +1,44 @@
// Regression fixture: mirrors the real-world pattern that caused
// languageId=typescript (instead of typescriptreact) to truncate symbol
// ranges at the first multi-line JSX expression.
import * as React from "react"
type SectionProps = { title: string; emphasised?: boolean }
function Section({ title, emphasised }: SectionProps) {
return emphasised ? (
<h1 style={{ color: "red" }}>{title}</h1>
) : (
<h2 style={{ color: "gray" }}>{title}</h2>
)
}
export function JsxComponent({ heading, items }: { heading: string; items: string[] }) {
const renderHeader = (title: string) =>
title.length > 10 ? (
<Section title={title} emphasised />
) : (
<Section title={title} />
)
// The truncation bug used to cut JsxComponent's range right around here,
// hiding everything below from find_symbol. Keep this comment so the
// regression test can assert the function body extends past it.
const renderItem = (item: string, idx: number) => (
<li key={idx} style={{ marginBottom: 4 }}>
{item}
</li>
)
return (
<div>
{renderHeader(heading)}
<ul>{items.map(renderItem)}</ul>
</div>
)
}
export function trailingHelper(): string {
return "this symbol is invisible to find_symbol when JsxComponent's range is truncated"
}
@@ -5,7 +5,8 @@
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
"skipLibCheck": true,
"jsx": "react"
},
"include": ["**/*.ts"]
"include": ["**/*.ts", "**/*.tsx"]
}
@@ -61,6 +61,37 @@ class TestTypescriptLanguageServer:
for symbol in implementing_symbols
), f"Expected ConsoleGreeter.formatGreeting symbol, got: {implementing_symbols}"
@pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True)
def test_tsx_symbol_range_not_truncated_by_jsx(self, language_server: SolidLanguageServer) -> None:
# Regression: when the language id is sent as "typescript" instead of
# "typescriptreact" for .tsx files, tsserver parses JSX as syntax
# errors and recovers by truncating the enclosing symbol's range at
# the first multi-line JSX expression. find_symbol then returns a
# body that ends mid-component and hides everything below.
file_path = "jsx_component.tsx"
roots = language_server.request_document_symbols(file_path).root_symbols
jsx_component = next((s for s in roots if s.get("name") == "JsxComponent"), None)
assert jsx_component is not None, "JsxComponent not found at root level of jsx_component.tsx"
end_line = jsx_component["location"]["range"]["end"]["line"]
# JsxComponent's body extends to line 38 (0-based 37) in the fixture;
# the truncation bug cut it at the first multi-line JSX (~line 21).
# Use a generous lower bound so the test survives small fixture edits
# that don't affect the regression behaviour we care about.
assert end_line >= 30, (
f"JsxComponent symbol range truncated at line {end_line + 1} (1-based); "
f"expected end at or past line 31 (1-based). "
f"This indicates the .tsx file was opened with the wrong languageId."
)
# The trailing helper must be visible as a top-level symbol — it lives
# past the truncation point and disappears entirely when the bug is
# active because tsserver stops emitting symbols after the parse error.
assert any(s.get("name") == "trailingHelper" for s in roots), (
"trailingHelper missing from jsx_component.tsx root symbols; tsserver likely stopped parsing at the first JSX expression."
)
@pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True)
def test_bare_symbol_names(self, language_server) -> None:
all_symbols = request_all_symbols(language_server)