* Improved symbol retrieval tool responses
1. FindSymbol (LS and JB) now has max_matches. Both tools now perform grouping like other symbol retrieval tools
2. When a result exceeds max_answer_chars, the caller may pass a shortened message. This is used in references and overview tools
* Progressive shortening for tool results that exceed max_answer_chars
Change _limit_length to accept an optional list of closures instead of
a single shortened_result string. Each closure produces a progressively
shorter version of the result, tried in order until one fits. The last
closure is assumed to always fit and is used unconditionally.
* Extended grouper
More flexible grouping logic was needed to achieve the above results. Grouper also no longer
mutates the input (using deepcopy)
---------
Co-authored-by: Dominik Jain <dominik.jain@oraios-ai.de>
* Use deterministic workspace dir for EclipseJDTLS to enable cache reuse
Replace uuid.uuid4().hex with a hash of the project path for the JDTLS
workspace directory. This allows the workspace cache (project index,
shared index, configuration) to be reused across server restarts instead
of rebuilding from scratch each time.
The random UUID was inherited from microsoft/multilspy (a research/testing
library where ephemeral workspaces made sense), but for Serena's use case
— long-lived projects with repeated restarts — deterministic workspace
paths significantly reduce startup time, especially for large Java projects.
* Fix import sort order for ruff lint
Black dependency created a dependabot warning and couldn't be bumped since it collided with pathspec dependencies. Since ruff can also format, black is not needed. But we now have these small format changes, mostly in test files
* fix: check PATH for rust-analyzer before common locations, with functionality validation
Fixes#800 - rust-analyzer fails if rustup is not installed even when
rust-analyzer exists in PATH (e.g. NixOS, distro packages).
Changes:
- Move PATH check (shutil.which) before common location fallbacks
- Add _is_rust_analyzer_functional() to validate PATH-found binaries
by running --version, catching broken rustup proxy symlinks
- Add tests for Nix PATH detection and broken proxy skip scenarios
* fix: make test_skip_broken_rustup_proxy_in_path work on Windows
The test used hardcoded Unix paths (/usr/bin/rust-analyzer) and
os.path.expanduser("~/.cargo/bin/rust-analyzer") which doesn't
include the .exe extension. On Windows, the production code builds
common paths using pathlib with "rust-analyzer.exe", so the mock
paths never matched, causing RuntimeError instead of finding the
cargo fallback.
Use platform-appropriate paths: on Windows, use .exe extension and
pathlib.Path.home() to match how the production code constructs paths.
---------
Co-authored-by: VoidChecksum <Admin@CyberNord>
quote_arg() only wrapped arguments containing spaces in double quotes.
Shell metacharacters ($, backtick, ;, |, &, etc.) were left unescaped.
On POSIX, use shlex.quote() which single-quotes to prevent all shell
interpretation. Retain original Windows behavior since cmd.exe does not
interpret single-quoted strings.
Three related bugs preventing proper cleanup of language server processes:
1. SerenaAgent.shutdown() checked `hasattr(self, "_is_initialized")` but that
attribute was never set, making shutdown always return early (no-op).
Replace with `hasattr(self, "_active_project")` which is set early in
__init__ and correctly guards against __del__ on partial construction.
2. _activate_project() replaced self._active_project without shutting down
the previous project first, orphaning its language server processes.
Now calls old_project.shutdown() before switching.
3. MCP server_lifespan() logged "shutting down" but never called
agent.shutdown(), so language servers were orphaned on MCP server exit.
Now calls agent.shutdown() in a try/finally block.
1. agent.py: f-string had literal 'f' before variable, producing
"Find the current log file here: f/Users/..." instead of the path.
2. ls.py: delete_text_between_positions logged "insert_text_at_position
called before Language Server started" — wrong method name.
When a registered project path points to a stale or unreachable
filesystem (e.g. a disconnected SSHFS mount), `path.exists()` raises
`OSError: [Errno 5] Input/output error` instead of returning False.
This uncaught exception crashes the entire MCP server on startup,
making all Serena tools unavailable.
Wrap the `path.exists()` call in a try/except OSError so that
inaccessible paths are gracefully skipped with a warning, matching
the existing behavior for non-existent paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, only project.yml resave due to incomplete config accounted for this,
but dynamic language changes triggered via the dashboard (which triggered a save
to update the language list) did not.
Since ProjectConfig can always contain local overrides,
* we handle configuration updates by explicitly updating on disk only the
keys known to have been changed
* we explicitly represent the presence of local overrides, such that
inadmissible save operations are detectable.
References and definition not working yet. Since ansible resolves same files as yaml LS, we will need to add disambiguation later.
Work in progress, improvements on the language server itself are being worked on, ansible support in Serena will be extended accordingly in the future.