Add configuration option jetbrains_launch_command, allowing Serena to spawn IDE instances automatically

upon project activation

Serena searches for the plugin instance upon project activation, and if the instance is not found
and the setting is configured, launches a new IDE instance based on the command
This commit is contained in:
Dominik Jain committed 2026-06-02 17:08:17 +02:00
1 parent 8a1bb1f80a
commit 1a3943b917
6 files changed
+58 -11

No files matched your search

+4
View File
@@ -11,6 +11,10 @@ Status of the `main` branch. Changes prior to the next official version change w
processed by the Svelte LS instead of the TypeScript LS.
- Improve quoting of arguments in shell executions
* JetBrains:
- Add configuration option `jetbrains_launch_command`, allowing Serena to spawn IDE instances automatically
upon project activation
* Dashboard:
- Make list of trusted hosts configurable, fixing host validation introduced in v1.5.2 allowing only
default local hostnames, effectively preventing remote connections
+6 -2
View File
@@ -84,7 +84,7 @@ language_backend: JetBrains
Note that the file might not exist yet if you never executed Serena before.
**Per-Server Instance Configuration**.
**Per-Instance Configuration**.
The configuration setting in the global config file can be overridden on a
per-instance basis by providing the arguments `--language-backend JetBrains` when
launching the Serena MCP server.
@@ -121,8 +121,12 @@ You will also notice that your client will use the JetBrains-specific tools like
Having installed the plugin in your IDE and having configured Serena to use the JetBrains backend,
the general workflow is simple:
1. Open the project you want to work on in your JetBrains IDE.
Note that the project must be appropriately set up in your IDE, i.e. symbol lookups for all relevant programming languages and frameworks should work in the IDE.
Note that the project must be appropriately set up in your IDE, i.e. symbol lookups for all relevant programming languages and frameworks should work in the IDE.
You can optionally make Serena open an IDE instance for your project root folder automatically upon project activation, allowing you to skip this step for a project that was previously set up correctly.
To enable this, configure `jetbrains_launch_command` in [Serena's global configuration file](global-config) appropriately.
2. Activate the project's root folder as a project in Serena (see [Project Creation](project-creation-indexing) and [Project Activation](project-activation)).
3. Start using Serena's tools as usual.
+22
View File
@@ -7,6 +7,7 @@ import multiprocessing
import os
import platform
import signal
import subprocess
import threading
from collections import defaultdict
from collections.abc import Callable, Iterator, Sequence
@@ -40,6 +41,7 @@ from serena.config.serena_config import (
ToolInclusionDefinition,
)
from serena.dashboard import SerenaDashboardAPI, SerenaDashboardTrayManager, SerenaDashboardViewer, open_url_in_browser
from serena.jetbrains import jetbrains_plugin_client
from serena.ls_manager import LanguageServerManager
from serena.memories.memory_manager import MemoryManager
from serena.project import Project
@@ -60,6 +62,7 @@ from serena.util.gui import system_has_usable_display
from serena.util.inspection import iter_subclasses
from serena.util.logging import MemoryLogHandler
from solidlsp.ls_config import Language
from solidlsp.util import subprocess_util
if TYPE_CHECKING:
from serena.gui_log_viewer import GuiLogViewer
@@ -1165,6 +1168,25 @@ class SerenaAgent:
if self.get_language_backend().is_lsp():
self.issue_task(init_language_server_manager)
def init_jetbrains_ide() -> None:
assert self.serena_config.jetbrains_launch_command is not None
try:
client = jetbrains_plugin_client.JetBrainsPluginClient.from_project(project, log_warning=False)
log.info("Found Serena JetBrains Plugin server: %s", client)
except jetbrains_plugin_client.ServerNotFoundError:
log.info("Serena JetBrains Plugin server not found for project %s", project.project_name)
if self.serena_config.jetbrains_launch_command:
cmd = subprocess_util.convert_shell_cmd([self.serena_config.jetbrains_launch_command, project.project_root])
log.info("Launching IDE with command: %s", cmd)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
stdout, stderr = p.communicate()
if p.returncode != 0:
log.error(f"Failed to launch JetBrains IDE: {stderr.decode('utf-8')}")
# for JetBrains mode, search for plugin server and spawn IDE if not found and enabled
if self.get_language_backend().is_jetbrains():
self.issue_task(init_jetbrains_ide)
if self._project_activation_callback is not None:
self._project_activation_callback()
+4
View File
@@ -722,6 +722,10 @@ class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes):
web_dashboard_listen_address: str = "127.0.0.1"
web_dashboard_trusted_hosts: list[str] = field(default_factory=lambda: ["127.0.0.1", "localhost"])
jetbrains_plugin_server_address: str = "127.0.0.1"
jetbrains_launch_command: str | None = None
"""
JetBrains IDE launch command, which can be used to auto-start an IDE instance on demand.
"""
tool_timeout: float = DEFAULT_TOOL_TIMEOUT
token_count_estimator: str = RegisteredTokenCountEstimator.CHAR_COUNT.name
@@ -105,7 +105,12 @@ class JetBrainsPluginClientManager:
futures.append(future)
return futures
def find_client(self, project_root: Path) -> "JetBrainsPluginClient":
def find_client(self, project_root: Path, log_warning: bool = True) -> "JetBrainsPluginClient":
"""
:param project_root: the project root path to find a plugin instance for
:param log_warning: whether to log a warning if no matching plugin instance is found
:return: the instance
"""
plugin_paths_found = []
for future in self._submit_scan():
client = future.result()
@@ -114,12 +119,13 @@ class JetBrainsPluginClientManager:
elif client.project_root is not None:
plugin_paths_found.append(client.project_root)
log.warning(
"Searched for Serena JetBrains plugin service for project at %s but found no matching service. "
"Found plugin instances for the following project paths: %s",
project_root,
plugin_paths_found,
)
if log_warning:
log.warning(
"Searched for Serena JetBrains plugin service for project at %s but found no matching service. "
"Found plugin instances for the following project paths: %s",
project_root,
plugin_paths_found,
)
raise ServerNotFoundError(
f"Found no Serena service in a JetBrains IDE instance for the project at {project_root}. "
"STOP. Do not attempt any other tools or workarounds. Ask the user to open this folder as a project in a JetBrains IDE "
@@ -219,7 +225,7 @@ class JetBrainsPluginClient(ToStringMixin):
return ["_port", "project_root", "_plugin_version"]
@classmethod
def from_project(cls, project: Project) -> Self:
def from_project(cls, project: Project, log_warning: bool = True) -> Self:
resolved_path = Path(project.project_root).resolve()
if cls._last_port is not None:
@@ -227,7 +233,7 @@ class JetBrainsPluginClient(ToStringMixin):
if client.matches(resolved_path):
return client
client = JetBrainsPluginClientManager().find_client(resolved_path)
client = JetBrainsPluginClientManager().find_client(resolved_path, log_warning=log_warning)
cls._last_port = client._port
return client
@@ -80,6 +80,13 @@ web_dashboard_trusted_hosts:
# address where JetBrains plugin servers are running (only relevant when using the JetBrains language backend)
jetbrains_plugin_server_address: 127.0.0.1
# command used to launch a JetBrains IDE on demand (only relevant when using the JetBrains language backend)
# If this is non-empty and, at project activation, the Serena JetBrains Plugin server instance corresponding
# to the project is not found, use this command to spawn a new instance.
# Specifically, if this is <cmd>, then `<cmd> <projectDir>` is launched.
# Provide the full path to the executable/script (e.g., "/usr/bin/idea" or "C:/Users/bob/AppData/Local/JetBrains/Toolbox/scripts/idea.cmd").
jetbrains_launch_command:
# the minimum log level for the GUI log window and the dashboard (10 = debug, 20 = info, 30 = warning, 40 = error)
log_level: 20