mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-11 03:13:51 +00:00
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:
1 parent
8a1bb1f80a
commit
1a3943b917
6 files changed
+58
-11
No files matched your search
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user