mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 08:15:59 +00:00
Merge remote-tracking branch 'origin/main' into agent-config-update-events
Conflicts: CHANGELOG.md src/serena/agent.py
This commit is contained in:
commit
4aeef5ab85
21 files changed
+366
-145
No files matched your search
+26
-1
@@ -2,11 +2,36 @@
|
||||
|
||||
Status of the `main` branch. Changes prior to the next official version change will appear here.
|
||||
|
||||
* Language Servers:
|
||||
- `typescript_vts`: Add `initialization_options` setting in `ls_specific_settings.typescript_vts`.
|
||||
The dict is forwarded to vtsls via `initializationOptions`, `workspace/didChangeConfiguration`,
|
||||
and `workspace/configuration` pulls. Enables Yarn PnP setups with `typescript.tsdk` pointing
|
||||
at the Yarn-generated SDK.
|
||||
|
||||
* Dashboard:
|
||||
- Make list of trusted hosts configurable, fixing host validation introduced in v1.5.2 allowing only
|
||||
default local hostnames, effectively preventing remote connections
|
||||
- Decouple configuration computation from the agent's task queue by introducing events for agent config/status updates.
|
||||
This allows the dashboard to display the configuration while the project provided at startup is still initialising. #1064
|
||||
- Fix empty executions queue displaying "Loading..."
|
||||
|
||||
|
||||
# v1.5.3 (2026-05-26)
|
||||
|
||||
Add meta-data for the GitHub MCP registry
|
||||
|
||||
# v1.5.2 (2026-05-26)
|
||||
|
||||
* General:
|
||||
- Not existing paths return `False` on is ignored checks (instead of raising an error)
|
||||
- Add `serena-agent` CLI command so that `uvx serena-agent` can be used as entrypoint.
|
||||
- Fortls and pyright are now installed on the fly instead of being bundled in the serena-agent package.
|
||||
|
||||
* Dashboard:
|
||||
- Add host validation
|
||||
|
||||
* Hooks:
|
||||
- Extend list of extensions that are considered code files (affects the reminder hook counter).
|
||||
|
||||
# v1.5.1 (2026-05-18)
|
||||
|
||||
* General:
|
||||
|
||||
@@ -226,7 +226,7 @@ To achieve this, Serena offers multiple levels of (composable) configuration:
|
||||
**Install Serena**. Serena is installed via uv as follows:
|
||||
|
||||
```bash
|
||||
uv tool install -p 3.13 serena-agent@latest --prerelease=allow
|
||||
uv tool install -p 3.13 serena-agent
|
||||
```
|
||||
|
||||
After successful installation, the command `serena` should be available in your shell.
|
||||
@@ -261,3 +261,5 @@ Please refer to the [user guide](https://oraios.github.io/serena/02-usage/000_in
|
||||
A significant part of Serena, especially support for various languages, was contributed by the open source community.
|
||||
We are very grateful for the many contributors who made this possible and who played an important role in making Serena
|
||||
what it is today.
|
||||
|
||||
<!-- mcp-name: io.github.oraios/serena -->
|
||||
@@ -20,7 +20,7 @@ to be provided manually, this is mentioned in the notes below the respective lan
|
||||
|
||||
With `uv` installed and on your PATH, install Serena with this command:
|
||||
|
||||
uv tool install -p 3.13 serena-agent@latest --prerelease=allow
|
||||
uv tool install -p 3.13 serena-agent
|
||||
|
||||
Upon completion, the command `serena` should be available in your terminal.
|
||||
|
||||
@@ -37,7 +37,7 @@ Note that you can switch backends at any time via Serena's [configuration](050_c
|
||||
|
||||
To update Serena to the latest version, run:
|
||||
|
||||
uv tool upgrade serena-agent --prerelease=allow
|
||||
uv tool upgrade serena-agent
|
||||
|
||||
:::{tip}
|
||||
To keep informed about updates, make sure you regularly open [Serena's Dashboard](060_dashboard),
|
||||
|
||||
@@ -1022,6 +1022,30 @@ Supported settings:
|
||||
|---|---|---|
|
||||
| `vtsls_version` | `0.2.9` | Override the `@vtsls/language-server` npm package version Serena installs. |
|
||||
| `npm_registry` | `null` | Override the npm registry Serena uses for the managed install. |
|
||||
| `initialization_options` | `null` | Dict forwarded to vtsls on three LSP channels: the `initializationOptions` field of the `initialize` request, a `workspace/didChangeConfiguration` notification sent right after initialize, and as the response to `workspace/configuration` pull requests (section-scoped). Typical use is Yarn PnP: point `typescript.tsdk` at the Yarn-generated SDK and enable `vtsls.autoUseWorkspaceTsdk`. |
|
||||
|
||||
Example (Yarn PnP project with TypeScript in a subdirectory; run `yarn dlx @yarnpkg/sdks vscode` in the project once to generate the SDK):
|
||||
|
||||
```yaml
|
||||
ls_specific_settings:
|
||||
typescript_vts:
|
||||
initialization_options:
|
||||
typescript:
|
||||
tsdk: "project/.yarn/sdks/typescript/lib"
|
||||
vtsls:
|
||||
autoUseWorkspaceTsdk: true
|
||||
```
|
||||
|
||||
vtsls reads `typescript.tsdk` through the `workspace/configuration` pull, not through `initializationOptions`, so Serena answers those pulls from the same dict (and also pushes it on `workspace/didChangeConfiguration` for compatibility with servers that expect the notification). Without `autoUseWorkspaceTsdk: true`, vtsls falls back to its bundled TypeScript and ignores `tsdk` (there is no UI prompt to confirm the switch in a headless LSP).
|
||||
|
||||
The dict is forwarded to vtsls verbatim — Serena does not validate its structure. For the list of supported keys and their expected types, refer to the vtsls [configuration schema](https://github.com/yioneko/vtsls/blob/main/packages/service/configuration.schema.json) and the underlying [VS Code TypeScript settings](https://code.visualstudio.com/docs/languages/typescript). `null` (the default) and `{}` are both treated as "unset": no `initializationOptions` are sent and no `workspace/didChangeConfiguration` notification is pushed. A non-dict value (e.g. a string or list) raises an error at server start.
|
||||
|
||||
**Troubleshooting:**
|
||||
|
||||
- *vtsls keeps using its bundled TypeScript and ignores `tsdk`* — ensure `vtsls.autoUseWorkspaceTsdk: true` is set alongside `typescript.tsdk`. Without it vtsls does not auto-switch to the workspace TS in a headless LSP.
|
||||
- *tsserver fails to start after pointing at a custom `tsdk`* — verify the path resolves to a directory containing `tsserver.js` (e.g. `.yarn/sdks/typescript/lib`, not `.yarn/sdks/typescript`). Relative paths are interpreted relative to the project root.
|
||||
- *Setting appears in `solidlsp` logs but vtsls does not react* — cross-check the key against the vtsls configuration schema linked above. The dict is forwarded as-is, so an unknown or wrong-typed key is silently ignored by vtsls.
|
||||
- *Need to inspect what Serena is actually forwarding* — the dict is logged at INFO level via the `Forwarding user-provided initializationOptions to vtsls: …` line at language server startup.
|
||||
|
||||
#### Vue
|
||||
|
||||
|
||||
+3
-4
@@ -4,7 +4,7 @@ requires = ["hatchling"]
|
||||
|
||||
[project]
|
||||
name = "serena-agent"
|
||||
version = "1.5.2.dev0"
|
||||
version = "1.5.4.dev0"
|
||||
description = "A powerful MCP toolkit for coding, providing semantic retrieval and editing capabilities - the IDE for your agent"
|
||||
authors = [{ name = "Oraios AI", email = "info@oraios-ai.de" }]
|
||||
readme = "README.md"
|
||||
@@ -18,8 +18,6 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"requests==2.33.0",
|
||||
"pyright==1.1.403",
|
||||
"fortls==3.2.2",
|
||||
"overrides==7.7.0",
|
||||
"python-dotenv==1.2.2",
|
||||
"mcp==1.27.0",
|
||||
@@ -52,7 +50,7 @@ dependencies = [
|
||||
"filelock==3.25.2",
|
||||
"cryptography==46.0.7",
|
||||
"regex==2026.2.28",
|
||||
"pythonnet==3.1.0-rc0 ; sys_platform == 'win32'",
|
||||
"pythonnet==3.1.0 ; sys_platform == 'win32'",
|
||||
]
|
||||
|
||||
|
||||
@@ -64,6 +62,7 @@ explicit = true
|
||||
|
||||
[project.scripts]
|
||||
serena = "serena.cli:top_level"
|
||||
serena-agent = "serena.cli:top_level"
|
||||
serena-hooks = "serena.hooks:hook_commands"
|
||||
|
||||
[project.license]
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
||||
"name": "io.github.oraios/serena",
|
||||
"title": "Serena MCP: the IDE for your agent",
|
||||
"description": "A powerful toolkit for coding, providing semantic retrieval and editing capabilities.",
|
||||
"version": "1.5.3",
|
||||
"repository": {
|
||||
"source": "github",
|
||||
"url": "https://github.com/oraios/serena"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"registryType": "pypi",
|
||||
"identifier": "serena-agent",
|
||||
"version": "1.5.3",
|
||||
"transport": {
|
||||
"type": "stdio"
|
||||
},
|
||||
"runtimeHint": "uvx",
|
||||
"runtimeArguments": [
|
||||
{
|
||||
"description": "Select the python version",
|
||||
"name": "-p",
|
||||
"value": "3.13",
|
||||
"type": "named"
|
||||
},
|
||||
{
|
||||
"type": "positional",
|
||||
"value": "start-mcp-server"
|
||||
},
|
||||
{
|
||||
"type": "positional",
|
||||
"value": "--project-from-cwd",
|
||||
"isRequired": false
|
||||
},
|
||||
{
|
||||
"type": "named",
|
||||
"name": "--context",
|
||||
"isRequired": false,
|
||||
"default": "desktop-app",
|
||||
"description": "The name of a markdown file representing Serena's execution context, typically related to the executing client. Allowed values are: agent, antigravity, chatgpt, claude-code, codex, context.template, copilot-cli, desktop-app, ide, jb-ai-assistant, jb-copilot-plugin, junie, oaicompat-agent, vscode "
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = "1.5.2.dev0"
|
||||
__version__ = "1.5.4.dev0"
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
+10
-3
@@ -663,19 +663,26 @@ class SerenaAgent:
|
||||
# create the dashboard backend (if enabled), which will register callback.
|
||||
dashboard_api: SerenaDashboardAPI | None = None
|
||||
if self.serena_config.web_dashboard:
|
||||
dashboard_api = SerenaDashboardAPI(get_memory_log_handler(), tool_names, agent=self, tool_usage_stats=self._tool_usage_stats)
|
||||
dashboard_api = SerenaDashboardAPI(
|
||||
get_memory_log_handler(),
|
||||
tool_names,
|
||||
agent=self,
|
||||
tool_usage_stats=self._tool_usage_stats,
|
||||
host=self.serena_config.web_dashboard_listen_address,
|
||||
trusted_hosts=self.serena_config.web_dashboard_trusted_hosts,
|
||||
)
|
||||
|
||||
# propagate the initial config/state to listeners, particularly to the dashboard API.
|
||||
# Note: The only ongoing task can be the LS initialisation, which does not change the config
|
||||
# This is executed before starting the dashboard thread to ensure that the dashboard
|
||||
# has the correct initial state before can requests come in.
|
||||
# has the correct initial state before requests can come in.
|
||||
self._on_config_changed()
|
||||
|
||||
# start the dashboard backend thread and the dashboard frontend manager (if enabled).
|
||||
# This should be the last thing to happen in the initialization since the dashboard
|
||||
# may access various parts of the agent
|
||||
if dashboard_api:
|
||||
dashboard_thread, port = dashboard_api.run_in_thread(host=self.serena_config.web_dashboard_listen_address)
|
||||
dashboard_thread, port = dashboard_api.run_in_thread()
|
||||
self._dashboard_manager = DashboardManager(
|
||||
port,
|
||||
self.serena_config.web_dashboard_listen_address,
|
||||
|
||||
@@ -720,6 +720,7 @@ class SerenaConfig(SharedConfig, ModeSelectionDefinitionWithBaseModes):
|
||||
web_dashboard_open_on_launch: bool = True
|
||||
web_dashboard_interface: str | None = None
|
||||
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"
|
||||
tool_timeout: float = DEFAULT_TOOL_TIMEOUT
|
||||
|
||||
|
||||
+11
-7
@@ -197,11 +197,16 @@ class SerenaDashboardAPI:
|
||||
tool_names: list[str],
|
||||
agent: "SerenaAgent",
|
||||
tool_usage_stats: ToolUsageStats | None = None,
|
||||
host: str = "127.0.0.1",
|
||||
trusted_hosts: list[str] | None = None,
|
||||
) -> None:
|
||||
self._memory_log_handler = memory_log_handler
|
||||
self._tool_names = tool_names
|
||||
self._agent = agent
|
||||
self._host = host
|
||||
self._app = Flask(__name__)
|
||||
if trusted_hosts:
|
||||
self._app.config["TRUSTED_HOSTS"] = trusted_hosts
|
||||
self._tool_usage_stats = tool_usage_stats
|
||||
self._loaded_news: dict[str, str] = {}
|
||||
self._news_ready = threading.Event()
|
||||
@@ -777,7 +782,7 @@ class SerenaDashboardAPI:
|
||||
|
||||
raise RuntimeError(f"No free ports found starting from {start_port}")
|
||||
|
||||
def run(self, host: str, port: int) -> int:
|
||||
def run(self, port: int) -> int:
|
||||
"""
|
||||
Runs the dashboard on the given host and port and returns the port number.
|
||||
"""
|
||||
@@ -785,14 +790,13 @@ class SerenaDashboardAPI:
|
||||
from flask import cli
|
||||
|
||||
cli.show_server_banner = lambda *args, **kwargs: None
|
||||
|
||||
self._app.run(host=host, port=port, debug=False, use_reloader=False, threaded=True)
|
||||
self._app.run(host=self._host, port=port, debug=False, use_reloader=False, threaded=True)
|
||||
return port
|
||||
|
||||
def run_in_thread(self, host: str) -> tuple[threading.Thread, int]:
|
||||
port = self._find_first_free_port(self.BASE_PORT, host)
|
||||
log.info("Starting dashboard (listen_address=%s, port=%d)", host, port)
|
||||
thread = threading.Thread(target=lambda: self.run(host=host, port=port), daemon=True)
|
||||
def run_in_thread(self) -> tuple[threading.Thread, int]:
|
||||
port = self._find_first_free_port(self.BASE_PORT, self._host)
|
||||
log.info("Starting dashboard (listen_address=%s, port=%d)", self._host, port)
|
||||
thread = threading.Thread(target=lambda: self.run(port=port), daemon=True)
|
||||
thread.start()
|
||||
return thread, port
|
||||
|
||||
|
||||
@@ -272,6 +272,8 @@ class PreToolUseRemindAboutSymbolicToolsHook(PreToolUseHook):
|
||||
|
||||
#: file suffixes for source-like files where symbolic tools are usually more
|
||||
#: appropriate than repeated raw reads. Lowercase and extension-only.
|
||||
#: Note: ``search_for_pattern`` is always available regardless of extension and
|
||||
#: is a better alternative to repeated raw reads for any structured file type.
|
||||
_CODE_FILE_EXTENSIONS: frozenset[str] = frozenset(
|
||||
(
|
||||
".al",
|
||||
@@ -289,14 +291,19 @@ class PreToolUseRemindAboutSymbolicToolsHook(PreToolUseHook):
|
||||
".fs",
|
||||
".fsx",
|
||||
".go",
|
||||
".graphql",
|
||||
".gql",
|
||||
".groovy",
|
||||
".h",
|
||||
".hcl",
|
||||
".hpp",
|
||||
".hs",
|
||||
".html",
|
||||
".java",
|
||||
".jl",
|
||||
".js",
|
||||
".json",
|
||||
".jsonc",
|
||||
".jsx",
|
||||
".kt",
|
||||
".kts",
|
||||
@@ -305,6 +312,7 @@ class PreToolUseRemindAboutSymbolicToolsHook(PreToolUseHook):
|
||||
".m",
|
||||
".matlab",
|
||||
".php",
|
||||
".proto",
|
||||
".ps1",
|
||||
".py",
|
||||
".r",
|
||||
@@ -313,11 +321,17 @@ class PreToolUseRemindAboutSymbolicToolsHook(PreToolUseHook):
|
||||
".scala",
|
||||
".sh",
|
||||
".sol",
|
||||
".sql",
|
||||
".svelte",
|
||||
".swift",
|
||||
".tf",
|
||||
".tfvars",
|
||||
".toml",
|
||||
".ts",
|
||||
".tsx",
|
||||
".vue",
|
||||
".yaml",
|
||||
".yml",
|
||||
".zig",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -191,8 +191,8 @@ class Project(ToStringMixin):
|
||||
|
||||
def _is_ignored_relative_path(self, relative_path: str | Path, ignore_non_source_files: bool = True) -> bool:
|
||||
"""
|
||||
Determine whether an existing path should be ignored based on file type and ignore patterns.
|
||||
Raises `FileNotFoundError` if the path does not exist.
|
||||
Determine whether a path should be ignored based on file type and ignore patterns.
|
||||
Returns False for non-existent paths since they cannot be matched by ignore patterns.
|
||||
|
||||
:param relative_path: Relative path to check
|
||||
:param ignore_non_source_files: whether files that are not source files (according to the file masks
|
||||
@@ -208,7 +208,8 @@ class Project(ToStringMixin):
|
||||
|
||||
abs_path = os.path.join(self.project_root, relative_path)
|
||||
if not os.path.exists(abs_path):
|
||||
raise FileNotFoundError(f"File {abs_path} not found, the ignore check cannot be performed")
|
||||
log.debug(f"Path {abs_path} does not exist, skipping ignore check")
|
||||
return False
|
||||
|
||||
# Check file extension if it's a file
|
||||
is_file = os.path.isfile(abs_path)
|
||||
@@ -284,10 +285,11 @@ class Project(ToStringMixin):
|
||||
|
||||
def validate_relative_path(self, relative_path: str, require_not_ignored: bool = False) -> None:
|
||||
"""
|
||||
Validates that the given relative path to an existing file/dir is safe to read or edit,
|
||||
meaning it's inside the project directory.
|
||||
Validates that the given relative path is safe to read or edit,
|
||||
meaning it's inside the project directory and not ignored.
|
||||
|
||||
Passing a path to a non-existing file will lead to a `FileNotFoundError`.
|
||||
Non-existent paths are allowed (not considered ignored) to support
|
||||
editing tools that create new files.
|
||||
|
||||
:param relative_path: the path to validate, relative to the project root
|
||||
:param require_not_ignored: if True, the path must not be ignored according to the project's ignore settings
|
||||
|
||||
@@ -67,6 +67,14 @@ web_dashboard_interface:
|
||||
# the address the web dashboard will listen on (bind address).
|
||||
web_dashboard_listen_address: 127.0.0.1
|
||||
|
||||
# trusted hosts used to access the web dashboard.
|
||||
# By default, only allow access via local addresses for security reasons.
|
||||
# If you want to allow access from remote machines, add the hostname used to access the dashboard to this list.
|
||||
# If the list is empty/undefined, all hosts are trusted.
|
||||
web_dashboard_trusted_hosts:
|
||||
- 127.0.0.1
|
||||
- localhost
|
||||
|
||||
# address where JetBrains plugin servers are running (only relevant when using the JetBrains language backend)
|
||||
jetbrains_plugin_server_address: 127.0.0.1
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
from collections.abc import Iterable, Mapping, Sequence
|
||||
from dataclasses import dataclass, replace
|
||||
@@ -154,6 +155,42 @@ class RuntimeDependencyCollection:
|
||||
)
|
||||
|
||||
|
||||
DEFAULT_UVX_PYTHON_VERSION = "3.13"
|
||||
|
||||
|
||||
def build_uvx_launch_command(
|
||||
package: str,
|
||||
version: str,
|
||||
entrypoint: str,
|
||||
extra_args: Sequence[str] = (),
|
||||
python_version: str = DEFAULT_UVX_PYTHON_VERSION,
|
||||
) -> list[str]:
|
||||
"""Build a command that runs a pinned PyPI package's console script on demand via ``uvx`` / ``uv x``.
|
||||
|
||||
Resolution order:
|
||||
1. Prefer ``uvx`` (env var ``UVX`` or PATH lookup).
|
||||
2. Fall back to ``uv x`` if only ``uv`` is on PATH.
|
||||
3. Raise ``RuntimeError`` if neither is available.
|
||||
|
||||
:param package: PyPI package name (e.g. ``"pyright"``).
|
||||
:param version: Pinned package version.
|
||||
:param entrypoint: Console script provided by the package (e.g. ``"pyright-langserver"``).
|
||||
:param extra_args: Arguments appended after the entrypoint (e.g. ``("--stdio",)``).
|
||||
:param python_version: Python interpreter version passed via ``-p`` (uv will fetch it if missing).
|
||||
"""
|
||||
base_args = ["-p", python_version, "--from", f"{package}=={version}", entrypoint, *extra_args]
|
||||
|
||||
uvx_path = os.environ.get("UVX") or shutil.which("uvx")
|
||||
if uvx_path is not None:
|
||||
return [uvx_path, *base_args]
|
||||
|
||||
uv_path = shutil.which("uv")
|
||||
if uv_path is not None:
|
||||
return [uv_path, "x", *base_args]
|
||||
|
||||
raise RuntimeError("Could not find 'uvx' or 'uv' in PATH. Install uv (https://docs.astral.sh/uv/).")
|
||||
|
||||
|
||||
def build_npm_install_command(package_name: str, version: str, registry: str | None = None) -> list[str]:
|
||||
"""Build a pinned npm install command for a package in a Serena-managed install directory."""
|
||||
command = ["npm", "install", "--prefix", "./"]
|
||||
|
||||
@@ -6,19 +6,26 @@ import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from overrides import override
|
||||
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.ls import DocumentSymbols, LSPConstants, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls import (
|
||||
DocumentSymbols,
|
||||
LanguageServerDependencyProvider,
|
||||
LanguageServerDependencyProviderUvx,
|
||||
LSPConstants,
|
||||
LSPFileBuffer,
|
||||
SolidLanguageServer,
|
||||
)
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
FORTLS_VERSION = "3.2.2"
|
||||
|
||||
|
||||
class FortranLanguageServer(SolidLanguageServer):
|
||||
"""Fortran Language Server implementation using fortls."""
|
||||
@@ -170,24 +177,17 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
|
||||
return DocumentSymbols(fixed_root_symbols)
|
||||
|
||||
@staticmethod
|
||||
def _check_fortls_installation() -> str:
|
||||
"""Check if fortls is available."""
|
||||
fortls_path = shutil.which("fortls")
|
||||
if fortls_path is None:
|
||||
raise RuntimeError("fortls is not installed or not in PATH.\nInstall it with: pip install fortls")
|
||||
return fortls_path
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
# Check fortls installation
|
||||
fortls_path = self._check_fortls_installation()
|
||||
super().__init__(config, repository_root_path, None, "fortran", solidlsp_settings)
|
||||
|
||||
# Command to start fortls language server
|
||||
# fortls uses stdio for LSP communication by default
|
||||
fortls_cmd = f"{fortls_path}"
|
||||
|
||||
super().__init__(
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=fortls_cmd, cwd=repository_root_path), "fortran", solidlsp_settings
|
||||
def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
|
||||
return LanguageServerDependencyProviderUvx(
|
||||
self._custom_settings,
|
||||
self._ls_resources_dir,
|
||||
package="fortls",
|
||||
entrypoint="fortls",
|
||||
default_version=FORTLS_VERSION,
|
||||
version_setting_key="fortls_version",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -6,19 +6,20 @@ import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
from typing import cast
|
||||
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer
|
||||
from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderUvx, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PYRIGHT_VERSION = "1.1.403"
|
||||
|
||||
|
||||
class PyrightServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -44,14 +45,15 @@ class PyrightServer(SolidLanguageServer):
|
||||
self.found_source_files = False
|
||||
|
||||
def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
|
||||
return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)
|
||||
|
||||
class DependencyProvider(LanguageServerDependencyProviderSinglePath):
|
||||
def _get_or_install_core_dependency(self) -> str:
|
||||
return sys.executable
|
||||
|
||||
def _create_launch_command(self, core_path: str) -> list[str]:
|
||||
return [core_path, "-m", "pyright.langserver", "--stdio"]
|
||||
return LanguageServerDependencyProviderUvx(
|
||||
self._custom_settings,
|
||||
self._ls_resources_dir,
|
||||
package="pyright",
|
||||
entrypoint="pyright-langserver",
|
||||
default_version=PYRIGHT_VERSION,
|
||||
version_setting_key="pyright_version",
|
||||
extra_args=("--stdio",),
|
||||
)
|
||||
|
||||
@override
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
|
||||
@@ -10,12 +10,11 @@ You can pass the following entries in ``ls_specific_settings["python_ty"]``:
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
from typing import cast
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
from solidlsp.ls import LanguageServerDependencyProvider, SolidLanguageServer
|
||||
from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderUvx, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
@@ -44,28 +43,15 @@ class TyLanguageServer(SolidLanguageServer):
|
||||
)
|
||||
|
||||
def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
|
||||
return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)
|
||||
|
||||
class DependencyProvider(LanguageServerDependencyProvider):
|
||||
def create_launch_command(self) -> list[str]:
|
||||
# respecting an explicit override
|
||||
ls_path = self._custom_settings.get("ls_path")
|
||||
if ls_path is not None:
|
||||
return [ls_path, "server"]
|
||||
|
||||
ty_version = self._custom_settings.get("ty_version", TY_VERSION)
|
||||
|
||||
# preferring uvx for on-demand execution
|
||||
uvx_path = os.environ.get("UVX") or shutil.which("uvx")
|
||||
if uvx_path is not None:
|
||||
return [uvx_path, "--from", f"ty=={ty_version}", "ty", "server"]
|
||||
|
||||
# falling back to uv's uvx-compatible subcommand when only `uv` is available
|
||||
uv_path = shutil.which("uv")
|
||||
if uv_path is not None:
|
||||
return [uv_path, "x", "--from", f"ty=={ty_version}", "ty", "server"]
|
||||
|
||||
raise RuntimeError("Could not find 'uvx' or 'uv' in PATH.\nInstall uv or provide ls_specific_settings.python_ty.ls_path.")
|
||||
return LanguageServerDependencyProviderUvx(
|
||||
self._custom_settings,
|
||||
self._ls_resources_dir,
|
||||
package="ty",
|
||||
entrypoint="ty",
|
||||
default_version=TY_VERSION,
|
||||
version_setting_key="ty_version",
|
||||
extra_args=("server",),
|
||||
)
|
||||
|
||||
@override
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
|
||||
@@ -34,7 +34,20 @@ DEFAULT_VTSLS_VERSION = "0.2.9"
|
||||
class VtsLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Provides TypeScript specific instantiation of the LanguageServer class using vtsls.
|
||||
Contains various configurations and settings specific to TypeScript via vtsls wrapper.
|
||||
|
||||
Supported entries in ``ls_specific_settings["typescript_vts"]``:
|
||||
- ``vtsls_version``: version of ``@vtsls/language-server`` to install (default: ``"0.2.9"``).
|
||||
- ``npm_registry``: custom npm registry for the managed install.
|
||||
- ``initialization_options``: optional dict forwarded verbatim as LSP
|
||||
``initializationOptions``. Useful for Yarn PnP projects, e.g.::
|
||||
|
||||
initialization_options:
|
||||
typescript:
|
||||
tsdk: "project/.yarn/sdks/typescript/lib"
|
||||
vtsls:
|
||||
autoUseWorkspaceTsdk: true
|
||||
|
||||
See https://github.com/yioneko/vtsls/issues/169 for the PnP recipe.
|
||||
"""
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
@@ -113,13 +126,30 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
assert os.path.exists(vts_executable_path), "vtsls executable not found. Please install @vtsls/language-server and try again."
|
||||
return f"{vts_executable_path} --stdio"
|
||||
|
||||
@staticmethod
|
||||
def _get_initialize_params(repository_absolute_path: str) -> InitializeParams:
|
||||
@property
|
||||
def _initialization_options(self) -> dict:
|
||||
"""
|
||||
Validated user-provided ``initializationOptions``.
|
||||
|
||||
:raises ValueError: if ``ls_specific_settings.typescript_vts.initialization_options``
|
||||
is set to a value that is not a dict.
|
||||
"""
|
||||
opts = self._custom_settings.get("initialization_options")
|
||||
if opts is None:
|
||||
return {}
|
||||
if not isinstance(opts, dict):
|
||||
raise ValueError(f"ls_specific_settings.typescript_vts.initialization_options must be a dict, got {type(opts).__name__}")
|
||||
return opts
|
||||
|
||||
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
|
||||
"""
|
||||
Returns the initialize params for the VTS Language Server.
|
||||
|
||||
If ``initialization_options`` is set in ``ls_specific_settings["typescript_vts"]``,
|
||||
it is forwarded verbatim as LSP ``initializationOptions``.
|
||||
"""
|
||||
root_uri = pathlib.Path(repository_absolute_path).as_uri()
|
||||
initialize_params = {
|
||||
initialize_params: dict = {
|
||||
"locale": "en",
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
@@ -152,6 +182,11 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
if self._initialization_options:
|
||||
log.info("Forwarding user-provided initializationOptions to vtsls: %s", self._initialization_options)
|
||||
initialize_params["initializationOptions"] = self._initialization_options
|
||||
|
||||
return cast(InitializeParams, initialize_params)
|
||||
|
||||
def _start_server(self) -> None:
|
||||
@@ -179,12 +214,12 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
def execute_client_command_handler(params: dict) -> list:
|
||||
return []
|
||||
|
||||
def workspace_configuration_handler(params: dict) -> list[dict] | dict:
|
||||
# VTS may request workspace configuration
|
||||
# Return empty configuration for each requested item
|
||||
if "items" in params:
|
||||
return [{}] * len(params["items"])
|
||||
return {}
|
||||
init_options = self._initialization_options
|
||||
|
||||
def workspace_configuration_handler(params: dict) -> list[object]:
|
||||
# vtsls pulls settings for sections like "typescript", "vtsls", "javascript".
|
||||
# Return the matching sub-dicts from the user-provided initialization_options.
|
||||
return [init_options.get(item.get("section", ""), {}) for item in params["items"]]
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -227,6 +262,14 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
log.debug(f"completionProvider: {init_response['capabilities']['completionProvider']}")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
|
||||
# vtsls also reads settings via workspace/didChangeConfiguration (in addition
|
||||
# to initializationOptions and workspace/configuration pulls). Push the same
|
||||
# user-provided settings on all three channels for maximum compatibility,
|
||||
# e.g. so that `typescript.tsdk` is honoured for Yarn PnP projects.
|
||||
if init_options:
|
||||
self.server.notify.workspace_did_change_configuration({"settings": init_options})
|
||||
|
||||
if self.server_ready.wait(timeout=1.0):
|
||||
log.info("VTS server is ready")
|
||||
else:
|
||||
|
||||
+44
-1
@@ -8,7 +8,7 @@ import shutil
|
||||
import threading
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, Hashable, Iterator
|
||||
from collections.abc import Callable, Hashable, Iterator, Sequence
|
||||
from contextlib import contextmanager
|
||||
from copy import copy
|
||||
from pathlib import Path, PurePath
|
||||
@@ -22,6 +22,7 @@ from sensai.util.string import ToStringMixin
|
||||
from serena.util.file_system import match_path
|
||||
from serena.util.text_utils import MatchedConsecutiveLines
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.language_servers.common import build_uvx_launch_command
|
||||
from solidlsp.ls_config import FilenameMatcher, Language, LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.ls_process import LanguageServerInterface, StdioLanguageServer
|
||||
@@ -333,6 +334,48 @@ class LanguageServerDependencyProviderSinglePath(LanguageServerDependencyProvide
|
||||
"""
|
||||
|
||||
|
||||
class LanguageServerDependencyProviderUvx(LanguageServerDependencyProvider):
|
||||
"""
|
||||
Dependency provider for language servers distributed as a PyPI package, run on demand via ``uvx`` / ``uv x``.
|
||||
|
||||
The pinned package version can be overridden by the user via the LS-specific setting given by
|
||||
``version_setting_key``. Alternatively, the LS-specific setting "ls_path" can be set to the path of an
|
||||
already-installed language server executable, in which case it is launched directly, bypassing uv entirely.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
custom_settings: "SolidLSPSettings.CustomLSSettings",
|
||||
ls_resources_dir: str,
|
||||
*,
|
||||
package: str,
|
||||
entrypoint: str,
|
||||
default_version: str,
|
||||
version_setting_key: str,
|
||||
extra_args: Sequence[str] = (),
|
||||
):
|
||||
"""
|
||||
:param package: the PyPI package name (e.g. ``"pyright"``)
|
||||
:param entrypoint: the console script provided by the package (e.g. ``"pyright-langserver"``)
|
||||
:param default_version: the package version to pin unless overridden
|
||||
:param version_setting_key: the LS-specific setting key through which the user can override the version
|
||||
:param extra_args: arguments appended after the entrypoint (e.g. ``("--stdio",)``)
|
||||
"""
|
||||
super().__init__(custom_settings, ls_resources_dir)
|
||||
self._package = package
|
||||
self._entrypoint = entrypoint
|
||||
self._default_version = default_version
|
||||
self._version_setting_key = version_setting_key
|
||||
self._extra_args = tuple(extra_args)
|
||||
|
||||
def create_launch_command(self) -> list[str]:
|
||||
ls_path = self._custom_settings.get("ls_path")
|
||||
if ls_path is not None:
|
||||
return [ls_path, *self._extra_args]
|
||||
version = self._custom_settings.get(self._version_setting_key, self._default_version)
|
||||
return build_uvx_launch_command(self._package, version, self._entrypoint, self._extra_args)
|
||||
|
||||
|
||||
class SolidLanguageServer(ABC):
|
||||
"""
|
||||
High-level abstraction for language server interaction, which wraps the underlying low-level LSP interface
|
||||
|
||||
@@ -102,6 +102,31 @@ class TestGlobalIgnoredPaths:
|
||||
assert project.is_ignored_path(str(self.project_path / "debug.log"))
|
||||
assert not project.is_ignored_path(str(self.project_path / "main.py"))
|
||||
|
||||
def test_nonexistent_path_not_ignored(self) -> None:
|
||||
"""Non-existent paths should return False (not ignored), not raise FileNotFoundError."""
|
||||
project = _create_test_project(self.project_path)
|
||||
nonexistent = str(self.project_path / "src" / "new_file.py")
|
||||
assert not os.path.exists(nonexistent)
|
||||
assert not project.is_ignored_path(nonexistent)
|
||||
|
||||
def test_nonexistent_path_with_ignore_patterns(self) -> None:
|
||||
"""Non-existent paths matching ignore patterns still return False (cannot check)."""
|
||||
project = _create_test_project(
|
||||
self.project_path,
|
||||
global_ignored_paths=["*.log"],
|
||||
)
|
||||
nonexistent = str(self.project_path / "nonexistent.log")
|
||||
assert not os.path.exists(nonexistent)
|
||||
# Should not raise, should not match the *.log pattern
|
||||
assert not project.is_ignored_path(nonexistent)
|
||||
|
||||
def test_validate_relative_path_allows_nonexistent(self) -> None:
|
||||
"""validate_relative_path with require_not_ignored should not raise for non-existent paths."""
|
||||
project = _create_test_project(self.project_path)
|
||||
nonexistent = "src/new_file.py"
|
||||
# Should not raise
|
||||
project.validate_relative_path(nonexistent, require_not_ignored=True)
|
||||
|
||||
|
||||
class TestRegisteredProjectGlobalIgnoredPaths:
|
||||
"""RegisteredProject.get_project_instance() correctly passes global patterns to Project."""
|
||||
|
||||
@@ -421,14 +421,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "clr-loader"
|
||||
version = "0.2.10"
|
||||
version = "0.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/46/7eea92b6aa2d68af78e049cbecec5f757f1aad44ecdecdc16bbad7eead51/clr_loader-0.3.1.tar.gz", hash = "sha256:2e073e9aaf49d1ae2f56ecba27987ad5fb68be4bcd9dd34a5bed8f0e4e128366", size = 86805, upload-time = "2026-04-18T17:49:44.287Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/61/cf819f8e8bb4d4c74661acf2498ba8d4a296714be3478d21eaabf64f5b9b/clr_loader-0.2.10-py3-none-any.whl", hash = "sha256:ebbbf9d511a7fe95fa28a95a4e04cd195b097881dfe66158dc2c281d3536f282", size = 56483, upload-time = "2026-01-03T23:13:05.439Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/da/ec1a6e36624000b6df0dd61183c42342ee5814c073315e802cadaad04d2f/clr_loader-0.3.1-py3-none-any.whl", hash = "sha256:cbad189de20d202a7d621956b0fc38049e13c9bf7ca2923441eff725cd121aa1", size = 55730, upload-time = "2026-04-18T17:49:42.99Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -671,19 +671,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fortls"
|
||||
version = "3.2.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "json5" },
|
||||
{ name = "packaging" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c1/2b/db1e5cd07fc9e74a2e4fb8f65946f8fd79ef72211001af00982a04d977d5/fortls-3.2.2.tar.gz", hash = "sha256:b43b2b8cbd447ae848c63b8f008c2df96fd48c3a967b33f6ed64b3421496883b", size = 7085222, upload-time = "2025-03-16T18:03:11.377Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/7e/41a55a49e764243fd72b1ed202a95d9a9193586d8665bdc94e4df92050e4/fortls-3.2.2-py3-none-any.whl", hash = "sha256:b7ae8378ce9741064b8340d3f1fbbbf7ab697285e174a0208fed98485c4edfe3", size = 278359, upload-time = "2025-03-16T18:03:09.969Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
version = "4.0.12"
|
||||
@@ -1135,15 +1122,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json5"
|
||||
version = "0.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441, upload-time = "2026-01-01T19:42:14.99Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163, upload-time = "2026-01-01T19:42:13.962Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.26.0"
|
||||
@@ -1616,15 +1594,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overrides"
|
||||
version = "7.7.0"
|
||||
@@ -2235,19 +2204,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/86/637cda4983dc0936b73a385f3906256953ac434537b812814cb0b6d231a2/pyobjc_framework_webkit-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1aaa3bf12c7b68e1a36c0b294d2728e06f2cc220775e6dc4541d5046290e4dc8", size = 50680, upload-time = "2025-11-14T10:07:23.331Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.403"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "nodeenv" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pystray"
|
||||
version = "0.19.5"
|
||||
@@ -2347,14 +2303,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pythonnet"
|
||||
version = "3.1.0rc0"
|
||||
version = "3.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "clr-loader" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/59/4295652a9a93a2bd6755ecce2dc32e3d3c704e7b8ef301946ee65c818a59/pythonnet-3.1.0rc0.tar.gz", hash = "sha256:5f0f505e100233d70864b00dc5f5dd760603cfd94ac64da5c8b0e15ed95af939", size = 241025, upload-time = "2026-02-25T10:46:53.632Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/57/da1992e44663b71365c6e842c8d7fa453d4ec45fb99a68cfee5b7e944d3c/pythonnet-3.1.0.tar.gz", hash = "sha256:7b34c382905d10a371509ffafd64cae0416305c28817738a9cd138336f4e9991", size = 250599, upload-time = "2026-05-23T20:30:21.578Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/3d/a378aa74085fc6f72923d5e39fc30efbc97541cfd49b3fe0a35f20d09ff7/pythonnet-3.1.0rc0-py3-none-any.whl", hash = "sha256:286ccf23d08e1cc2b5200bb91d0ee649436d2e8386fec36a85193c66d0ff8b5b", size = 293150, upload-time = "2026-02-25T10:46:48.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/4b/52414f442624d2589f5374a48c08d5ae94f24bea67fc13a20a752884e5b7/pythonnet-3.1.0-cp310.cp311.cp312.cp313.cp314-none-any.whl", hash = "sha256:698dd88edc198819ad63b624a6ebe76208c7b46e4fe13626f65e484f0358d6ba", size = 217578, upload-time = "2026-05-23T20:30:19.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/67/031124fdcb937c266a3265118525bbf6dc13b8c79786d6a7290aecb6e7bb/pythonnet-3.1.0-cp310.cp311.cp312.cp313.cp314-none-win32.win_amd64.whl", hash = "sha256:7bdd4de03df3547a48122a3989265c8b31d5be0d19dadffa009eec7df8085e0b", size = 1644898, upload-time = "2026-05-23T20:30:16.213Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2856,7 +2813,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "serena-agent"
|
||||
version = "1.5.2.dev0"
|
||||
version = "1.5.4.dev0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "anthropic" },
|
||||
@@ -2866,7 +2823,6 @@ dependencies = [
|
||||
{ name = "dotenv" },
|
||||
{ name = "filelock" },
|
||||
{ name = "flask" },
|
||||
{ name = "fortls" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "joblib" },
|
||||
{ name = "lsprotocol" },
|
||||
@@ -2876,7 +2832,6 @@ dependencies = [
|
||||
{ name = "psutil" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pygls" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pystray" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "python-multipart" },
|
||||
@@ -2937,7 +2892,6 @@ requires-dist = [
|
||||
{ name = "dotenv", specifier = "==0.9.9" },
|
||||
{ name = "filelock", specifier = "==3.25.2" },
|
||||
{ name = "flask", specifier = "==3.1.3" },
|
||||
{ name = "fortls", specifier = "==3.2.2" },
|
||||
{ name = "google-genai", marker = "extra == 'google'", specifier = "==1.27.0" },
|
||||
{ name = "jinja2", specifier = "==3.1.6" },
|
||||
{ name = "jinja2", marker = "extra == 'dev'", specifier = "==3.1.6" },
|
||||
@@ -2955,14 +2909,13 @@ requires-dist = [
|
||||
{ name = "pygls", specifier = "==2.1.1" },
|
||||
{ name = "pyinstrument", marker = "extra == 'dev'", specifier = "==5.1.1" },
|
||||
{ name = "pyjwt", marker = "extra == 'dev'", specifier = "==2.12.0" },
|
||||
{ name = "pyright", specifier = "==1.1.403" },
|
||||
{ name = "pystray", specifier = "==0.19.5" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = "==8.4.1" },
|
||||
{ name = "pytest-timeout", marker = "extra == 'dev'", specifier = "==2.4.0" },
|
||||
{ name = "pytest-xdist", marker = "extra == 'dev'", specifier = "==3.8.0" },
|
||||
{ name = "python-dotenv", specifier = "==1.2.2" },
|
||||
{ name = "python-multipart", specifier = "==0.0.27" },
|
||||
{ name = "pythonnet", marker = "sys_platform == 'win32'", specifier = "==3.1.0rc0" },
|
||||
{ name = "pythonnet", marker = "sys_platform == 'win32'", specifier = "==3.1.0" },
|
||||
{ name = "pywebview", specifier = "==6.2" },
|
||||
{ name = "pyyaml", specifier = "==6.0.2" },
|
||||
{ name = "regex", specifier = "==2026.2.28" },
|
||||
|
||||
Reference in new issue
Block a user