Technical Documentation: oraios/serena
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
The IDE for Your Coding Agent
- Serena provides essential semantic code retrieval, editing, refactoring and debugging tools that are akin to an IDE's capabilities,
operating at the symbol level and exploiting relational structure. - It integrates with any client/LLM via the model context protocol (MCP).
Serena's agent-first tool design involves robust high-level abstractions, distinguishing it from
approaches that rely on low-level concepts like line numbers or primitive search patterns.
Practically, this means that your agent operates faster, more efficiently and more reliably, especially in larger and
more complex codebases.
[!TIP]
Serena v2 with the new REPL-based agent interface has been released.
Learn more
about the new interface and how to configure it.
Video
What Our "End Users" Say
While it is humans who download and set up Serena, our end users are essentially AI agents.
As the ones actually applying Serena's tools, they are in the best position to evaluate Serena.
We crafted an unbiased evaluation prompt that leads the agent to perform ~20 routine coding tasks,
representative of everyday development work,
in order to estimate the value added by Serena's tools when used alongside its own built-ins.
Here's a one-sentence summary of what the agents had to say:
Opus 4.6 (high) in Claude Code on a large Python codebase:
"Serena's IDE-backed semantic tools are the single most impactful addition to my toolkit – cross-file renames, moves, and reference lookups that
would cost me 8–12 careful, error-prone steps collapse into one atomic call, and I would absolutely ask any developer I work with to set them up."
GPT 5.4 (high) in Codex CLI on a Java codebase:
"As a coding AI agent, I would ask my owner to add Serena because it gives me the missing IDE-level understanding of symbols, references, and
refactorings, turning fragile text surgery into calmer, faster, more confident code changes where semantics matter."
GPT 5.4 (medium) in Copilot CLI on a large, multi-language monorepo:
"As a coding agent, I’d absolutely ask my owner to add Serena because it makes me noticeably sharper and calmer on
real code – especially symbol-aware navigation, cross-file refactors, and monorepo dependency jumps – while I still lean
on built-ins for tiny text edits and non-code work."
Different agents in different settings independently converge on the same verdict.
Give your agent the tools it has been asking for and add Serena MCP to your client!
See our documentation for the full methodology and much more detailed evaluation results, or run your own evaluation on a project of your choice.
How Serena Works
Serena provides the necessary tools for coding workflows,
but an LLM is required to do the actual work, orchestrating tool use.
Serena can extend the functionality of your existing AI client via the model context protocol (MCP).
Most modern AI chat clients directly support MCP, including
- terminal-based clients like Claude Code, Codex, OpenCode, or Gemini-CLI,
- IDEs and IDE assistant plugins for VSCode, Cursor and JetBrains IDEs (Copilot, Junie, JetBrains AI Assistant, etc.),
- desktop and web clients like Claude Desktop, Codex App, or OpenWebUI.
:tv: See also: Introduction to Serena in 5 Minutes (YouTube)
To connect the Serena MCP server to your client, you either
- provide the client with a launch command that allows it to start the MCP server, or
- start the Serena MCP server yourself in HTTP mode and provide the client with the URL.
See the Quick Start section below for information on how to get started.
Programming Language Support & Semantic Analysis Capabilities
Serena provides a set of versatile code querying and editing functionalities
based on symbolic understanding of the code.
Equipped with these capabilities, your agent discovers and edits code just like a seasoned developer
making use of an IDE's capabilities would.
Serena can efficiently find the right context and do the right thing even in very large and
complex projects!
There are two alternative technologies powering these capabilities:
- Language servers implementing the language server protocol (LSP) — the free/open-source alternative
which is used by default. - The Serena JetBrains Plugin, which leverages the powerful code analysis and editing
capabilities of your JetBrains IDE (paid plugin; free trial available).
You can choose either of these backends depending on your preferences and requirements.
Language Servers
Serena incorporates a powerful abstraction layer for the integration of language servers that implement the language server protocol (LSP).
The underlying language servers are typically open-source projects or at least freely available for use.
When using Serena's language server backend, we provide support for over 40 programming languages, including
Ada / SPARK, AL, Angular, Ansible, Astro, Bash, BSL, C#, C/C++, Clojure, Crystal, CUE, Dart, Deno, Elixir, Elm, Erlang, Fortran, F#, GDScript, Gleam, GLSL, Go, Groovy, Haskell, Haxe, HLSL, HTML, Java, JavaScript, JSON, Julia, Kotlin, LaTeX, Lean 4, Lua, Luau, Markdown, MATLAB, mSL, Nextflow, Nix, OCaml, Pascal, Perl, PHP, PowerShell, Python, QML, R, Rego, Ruby, Rust, Scala, SCSS / Sass / CSS, Solidity, Svelte, Swift, SystemVerilog, Terraform, TOML, TypeScript, VB.NET, Vue, WGSL, Wolfram Language, YAML, and Zig.
The Serena JetBrains Plugin
The paid Serena JetBrains Plugin (free trial available)
leverages the powerful code analysis capabilities of your JetBrains IDE.
The plugin naturally supports all programming languages and frameworks that are supported by JetBrains IDEs,
including IntelliJ IDEA, PyCharm, Android Studio, WebStorm, PhpStorm, RubyMine, GoLand, and potentially others (Rider and CLion are unsupported though).
See our documentation page for further details and instructions on how to apply the plugin.
Features
Serena provides a wide range of tools for efficient code retrieval, editing and refactoring, as well as
a memory system for long-lived agent workflows.
Given its large scope, Serena adapts to your needs by offering a multi-layered configuration system.
Details
Retrieval
Serena's retrieval tools allow agents to explore codebases at the symbol level, understanding structure and relationships
without reading entire files.
| Capability | Language Servers | JetBrains Plugin |
|---|---|---|
| find symbol | yes | yes |
| symbol overview (file outline) | yes | yes |
| find referencing symbols | yes | yes |
| search in project dependencies | -- | yes |
| type hierarchy | -- | yes |
| find declaration | yes* | yes |
| find implementations | yes** | yes |
| query external projects | yes | yes |
| diagnostics/inspections | yes | yes |
*: Will generally not work for declarations in external dependencies.
**: Only available for some languages, limited by the language server functionality.
Refactoring
Without precise refactoring tools, agents are forced to resort to unreliable and expensive search and replace operations.
| Capability | Language Servers | JetBrains Plugin |
|---|---|---|
| rename | yes (only symbols) | yes (symbols, files, directories) |
| move (symbol, file, directory) | -- | yes |
| inline | -- | yes |
| propagate deletions (remove unused code) | -- | yes |
Symbolic Editing
Serena's symbolic editing tools are less error-prone and much more token-efficient than typical alternatives.
| Capability | Language Servers | JetBrains Plugin |
|---|---|---|
| replace symbol body | yes | yes |
| insert after symbol | yes | yes |
| insert before symbol | yes | yes |
| safe delete | yes | yes |
Interactive Debugging
Exclusive to the JetBrains plugin, Serena supports a highly general debugging tool,
which allows an agent to set breakpoints, inspect variables, evaluate expressions and control execution flow
via a persistent REPL-style interface.
Basic Features
Beyond its semantic capabilities, Serena includes a set of basic utilities for completeness.
When Serena is used inside an agentic harness such as Claude Code or Codex, these tools are typically disabled by default,
since the surrounding harness already provides overlapping file, search, and shell capabilities.
search_for_pattern– flexible regex search across the codebasereplace_content– agent-optimised regex-based and literal text replacementlist_dir/find_file– directory listing and file searchread_file– read files or file chunksexecute_shell_command– run shell commands (e.g. builds, tests, linters)
Memory Management
A memory system is elemental to long-lived agent workflows, especially when knowledge is to be shared across
sessions, users and projects.
Despite its simplicity, we received positive feedback from many users who tend to combine Serena's memory management system with their
agent's internal system (e.g., AGENTS.md files).
It can easily be disabled if you prefer to use something else.
Configurability
Active tools, tool descriptions, prompts, language backend details and many other aspects of Serena
can be flexibly configured on a per-case basis by simply adjusting a few lines of YAML.
To achieve this, Serena offers multiple levels of (composable) configuration:
- global configuration
- MCP launch command (CLI) configuration
- per-project configuration (with local overrides)
- execution context-specific configuration (e.g. for particular clients)
- dynamically composable configuration fragments (modes)
Quick Start
[!IMPORTANT]
Do not install Serena via an MCP or plugin marketplace! They contain outdated and suboptimal installation commands.
Prerequisites. Serena is managed by uv, and installing uv is the only required prerequisite.
Install Serena. Serena is installed via uv as follows:
uv tool install -p 3.13 serena-agentAfter successful installation, the command serena should be available in your shell.
[!NOTE]
When using the language server backend, some additional dependencies may need to be installed to support certain languages;
see the Language Support page for details.
Initialise Serena. To initialise Serena and verify that your setup works correctly, simply run:
serena initBy default, this will set up Serena to use the language server backend. To use the JetBrains backend instead, add the parameters -b JetBrains
(see the JetBrains Plugin documentation page for additional usage details).
Either way, you should receive a success message indicating that Serena has been initialised successfully.
Configuring Your Client. To connect Serena to your preferred MCP client, you typically need to configure a launch command in your client.
Follow the link for specific instructions on how to set up Serena for Claude Code, Codex, Claude Desktop, MCP-enabled IDEs and other clients (such as local and web-based GUIs).
[!TIP]
While getting started quickly is easy, Serena is a powerful toolkit with many configuration options.
We highly recommend reading through the user guide to get the most out of Serena.Specifically, we recommend to read about ...
User Guide
Please refer to the user guide for detailed instructions on how to use Serena effectively.
Acknowledgements
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.
License
Serena is licensed per component: SolidLSP (src/solidlsp) under the MIT License,
the Serena application (everything else) under GPL-3.0-or-later.
Distributions combining both are as a whole subject to the GPL.
See LICENSE for the authoritative overview and the
license documentation for the background.
Contributions require acceptance of our CLA; see CONTRIBUTING.md.
Overview (`src/README.md`)
Serena uses (modified) versions of other libraries/packages:
- solidlsp (our fork of microsoft/multilspy for fully synchronous language server communication)
- interprompt (our prompt templating library)
Overview (`src/solidlsp/language_servers/elixir_tools/README.md`)
Elixir Language Server Integration
This directory contains the integration for Elixir language support using Expert, the official Elixir language server.
Prerequisites
Before using the Elixir language server integration, you need to have:
Elixir installed and available in your PATH
- Install from: https://elixir-lang.org/install.html
- Verify with:
elixir --version
Expert (optional - will be downloaded automatically if not found)
- Expert binaries are automatically downloaded from GitHub releases
- Manual installation: https://github.com/elixir-lang/expert#installation
- If installed manually, ensure
expertis in your PATH
Features
The Elixir integration provides:
- Language Server Protocol (LSP) support via Next LS
- File extension recognition for
.exand.exsfiles - Project structure awareness with proper handling of Elixir-specific directories:
_build/- Compiled artifacts (ignored)deps/- Dependencies (ignored).elixir_ls/- ElixirLS artifacts (ignored)cover/- Coverage reports (ignored)lib/- Source code (not ignored)test/- Test files (not ignored)
Configuration
The integration uses the default Expert configuration with:
- MIX_ENV:
dev - MIX_TARGET:
host - Experimental completions: Disabled by default
- Credo extension: Enabled by default
Version Management (asdf)
Expert automatically respects project-specific Elixir versions when using asdf:
- If a
.tool-versionsfile exists in the project root, Expert will use the specified Elixir version - Expert is launched from the project directory, allowing it to pick up project configuration
- No additional configuration needed - just ensure asdf is installed and the project has a
.tool-versionsfile
Usage
The Elixir language server is automatically selected when working with Elixir projects. It will be used for:
- Code completion
- Go to definition
- Find references
- Document symbols
- Hover information
- Code formatting
- Diagnostics (via Credo integration)
Important: Project Compilation
Expert requires your Elixir project to be compiled for optimal performance, especially for:
- Cross-file reference resolution
- Complete symbol information
- Accurate go-to-definition
For production use: Ensure your project is compiled with mix compile before using the language server.
For testing: The test suite automatically compiles the test repositories before running tests to ensure optimal Expert performance.
Testing
Run the Elixir-specific tests with:
pytest test/solidlsp/elixir/ -m elixirImplementation Details
- Main class:
ElixirToolsinelixir_tools.py - Language identifier:
"elixir" - Command:
expert --stdio - Supported platforms: Linux (x64, arm64), macOS (x64, arm64), Windows (x64, arm64)
- Binary distribution: Downloaded from GitHub releases
The implementation follows the same patterns as other language servers in this project, inheriting from SolidLanguageServer and providing Elixir-specific configuration and behavior.
Overview (`test/resources/repos/fsharp/test_repo/README.md`)
F# Test Project
This is a test F# project for testing Serena's F# language support.
Project Structure
Program.fs- Main program entry pointCalculator.fs- Calculator functions and typesModels/Person.fs- Person and Employee data models
Build
dotnet buildRun
dotnet runOverview (`test/resources/repos/markdown/test_repo/README.md`)
Test Repository
This is a test repository for markdown language server testing.
Overview
This repository contains sample markdown files for testing LSP features.
Features
- Document symbol detection
- Link navigation
- Reference finding
- Code completion
Installation
To use this test repository:
- Clone the repository
- Install dependencies
- Run tests
Usage
See guide.md for detailed usage instructions.
Code Examples
Here's a simple example:
def hello_world():
print("Hello, World!")JavaScript Example
function greet(name) {
console.log(`Hello, ${name}!`);
}References
License
MIT License
Index (`docs/index.md`)
If you are not redirected automatically, [click here](01-about/000_intro.html).02 Usage 000 Intro (`docs/02-usage/000_intro.md`)
Usage
Serena can be used in various ways and supports coding workflows through a project-based approach.
Its configuration is flexible and allows tailoring it to your specific needs.
In this section, you will find general usage instructions as well as concrete instructions for selected integrations.
03 Special Guides 000 Intro (`docs/03-special-guides/000_intro.md`)
Special Guides
This section contains special guides for certain topics that require more in-depth explanations
or are too specific to be included in the main documentation. These guides are meant to provide additional context and instructions for users who need them.
04 Evaluation 000 Evaluation Intro (`docs/04-evaluation/000_evaluation-intro.md`)
Evaluation
Claude Code (Opus 4.6, medium): "Serena's IDE-backed semantic tools are the single most impactful addition to my toolkit — cross-file renames, moves, and reference lookups that would cost me 8–12 careful, error-prone steps collapse into one atomic call, and I would absolutely ask any developer I work with to set them up."
Codex (GPT 5.4, high): "As a coding agent, I would ask my owner to add Serena because it turns fragile text-and-line-number work into precise symbol-aware navigation and refactoring, which makes real code changes feel faster, safer, and far less blind."
Codex (GPT 5.4, medium): "As a coding agent, I’d absolutely ask my owner to add Serena because it makes me noticeably sharper and calmer on
real code – especially symbol-aware navigation, cross-file refactors, and monorepo dependency jumps – while I still lean
on built-ins for tiny text edits and non-code work."
These are not marketing quotes – they are the agents' own one-sentence verdicts after
performing hands-on tasks, using both Serena's tools and their built-in
alternatives side by side, on real codebases (large Python library in the case of Claude Code,
medium-sized Java project in the case of Codex).
Different agents in different settings independently converge on the same core finding: Serena's strongest
contribution is collapsing multi-file, semantically-aware operations into single atomic
calls, while built-in tools remain the better choice for small local edits, text search,
config files, and shell work. See the full results for the scenarios mentioned above and several other scenarios.
Approach
We gave each agent a single evaluation prompt and pointed it at a codebase. The agent
then performed a wide variety of tasks across five areas (navigation, small edits, large edits,
cross-file refactoring, and workflow effects), executing each task with both toolsets and
recording call counts, payload sizes, and prerequisite steps. Every finding was classified
as either (a) Serena adds capability, (b) Serena applies but offers no improvement,
or (c) outside Serena's scope — a structure that requires reporting negative and neutral
results, not just positive ones.
The agent evaluates itself. This is deliberate: the agent is the actual user of the tools,
so it can judge workflow improvements from direct experience rather than through proxy
metrics. And because the prompt defines task categories rather than fixed tasks, anyone
can rerun the evaluation on their own project with their own agent.
Serena in JetBrains Junie
A particularly notable evaluation scenario is Serena in JetBrains' Junie plugin. The latter also has access
to some of JetBrains' refactoring tools. At the time of writing, the only overlapping capability between Serena's tool
and Junie's native tools is the renaming feature. Opus correctly noticed this during the evaluation and marked the
renaming capability as equivalent. However, many symbolic and refactoring tools offered by Serena have no inbuilt equivalents,
leading to the following summary result for Junie:
Junie Plugin (Opus 4.6): Serena gives me what my built-in tools can't — the ability to move a function between modules with all imports updated atomically, trace a class hierarchy into dependencies, and safely delete symbols with usage guards — and I'd ask my owner to add it for the move-refactoring and semantic navigation capabilities alone.
See the full evaluation here.
Config.Yml (`docs/_config.yml`)
Book settings
Learn more at https://jupyterbook.org/customize/config.html
#######################################################################################
A default configuration that will be loaded for all jupyter books
Users are expected to override these values in their own `_config.yml` file.
This is also the "master list" of all allowed keys and values.
#######################################################################################
Book settings
title : Serena Documentation # The title of the book. Will be placed in the left navbar.
author : Oraios AI & Oraios Software # The author of the book
copyright : "2025 by Serena contributors" # Copyright year to be placed in the footer
Patterns to skip when building the book. Can be glob-style (e.g. "*skip.ipynb")
exclude_patterns : ['**.ipynb_checkpoints', '.DS_Store', 'Thumbs.db', '_build', 'jupyter_execute', '.jupyter_cache', '.pytest_cache', 'docs/autogen_docs.py', 'docs/create_toc.py']
Auto-exclude files not in the toc
only_build_toc_files : true
#######################################################################################
Execution settings
execute:
NOTE: Notebooks are not executed, because test_notebooks.py executes them and stores them with outputs in the docs/ folder
NOTE: If changed, repeat below in `nb_execution_mode`.
execute_notebooks : "off" # Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off")
cache : "" # A path to the jupyter cache that will be used to store execution artifacts. Defaults to _build/.jupyter_cache/
exclude_patterns : [] # A list of patterns to skip in execution (e.g. a notebook that takes a really long time)
timeout : 1000 # The maximum time (in seconds) each notebook cell is allowed to run.
run_in_temp : false # If True, then a temporary directory will be created and used as the command working directory (cwd),
# otherwise the notebook's parent directory will be the cwd.
allow_errors : true # If False, when a code cell raises an error the execution is stopped, otherwise all cells are always run.
stderr_output : show # One of 'show', 'remove', 'remove-warn', 'warn', 'error', 'severe'
#######################################################################################
Parse and render settings
parse:
myst_enable_extensions: # default extensions to enable in the myst parser. See https://myst-parser.readthedocs.io/en/latest/using/syntax-optional.html
- amsmath
- colon_fence
# - deflist
- dollarmath
# - html_admonition
# - html_image
- linkify
# - replacements
# - smartquotes
- substitution
- tasklist
- html_admonition
- html_image
myst_url_schemes: [ mailto, http, https ] # URI schemes that will be recognised as external URLs in Markdown links
myst_dmath_double_inline: true # Allow display math ($$) within an inline context
#######################################################################################
HTML-specific settings
html:
favicon : "../src/serena/resources/dashboard/serena-icon-32.png"
use_edit_page_button : false # Whether to add an "edit this page" button to pages. If true, repository information in repository: must be filled in
use_repository_button : false # Whether to add a link to your repository button
use_issues_button : false # Whether to add an "open an issue" button
use_multitoc_numbering : true # Continuous numbering across parts/chapters
use_darkmode_button : false
extra_footer : ""
home_page_in_navbar : true # Whether to include your home page in the left Navigation Bar
baseurl : "https://oraios.github.io/serena/"
comments:
hypothesis : false
utterances : false
announcement : "" # A banner announcement at the top of the site.
#######################################################################################
LaTeX-specific settings
latex:
latex_engine : pdflatex # one of 'pdflatex', 'xelatex' (recommended for unicode), 'luatex', 'platex', 'uplatex'
use_jupyterbook_latex : true # use sphinx-jupyterbook-latex for pdf builds as default
targetname : book.tex
Add a bibtex file so that we can create citations
#bibtex_bibfiles:
- refs.bib
#######################################################################################
Launch button settings
launch_buttons:
notebook_interface : classic # The interface interactive links will activate ["classic", "jupyterlab"]
binderhub_url : "" # The URL of the BinderHub (e.g., https://mybinder.org)
jupyterhub_url : "" # The URL of the JupyterHub (e.g., https://datahub.berkeley.edu)
thebe : false # Add a thebe button to pages (requires the repository to run on Binder)
colab_url : "https://colab.research.google.com"
url : https://github.com/oraios/serena # The URL to your book's repository
path_to_book : docs # A path to your book's folder, relative to the repository root.
branch : main # Which branch of the repository should be used when creating links
#######################################################################################
Advanced and power-user settings
sphinx:
extra_extensions :
- sphinx.ext.autodoc
- sphinx.ext.viewcode
- sphinx_toolbox.more_autodoc.sourcelink
#- sphinxcontrib.spelling
local_extensions : # A list of local extensions to load by sphinx specified by "name: path" items
recursive_update : false # A boolean indicating whether to overwrite the Sphinx config (true) or recursively update (false)
config : # key-value pairs to directly over-ride the Sphinx configuration
master_doc: "01-about/000_evaluation-results.md"
html_theme_options:
logo:
image_light: ../resources/serena-logo.svg
image_dark: ../resources/serena-logo-dark-mode.svg
autodoc_typehints_format: "short"
autodoc_member_order: "bysource"
autoclass_content: "both"
autodoc_default_options:
show-inheritance: True
autodoc_show_sourcelink: True
add_module_names: False
github_username: oraios
github_repository: serena
nb_execution_mode: "off"
nb_merge_streams: True # This is important for cell outputs to appear as single blocks rather than one block per line
python_use_unqualified_type_names: True
nb_mime_priority_overrides: [
[ 'html', 'application/vnd.jupyter.widget-view+json', 10 ],
[ 'html', 'application/javascript', 20 ],
[ 'html', 'text/html', 30 ],
[ 'html', 'text/latex', 40 ],
[ 'html', 'image/svg+xml', 50 ],
[ 'html', 'image/png', 60 ],
[ 'html', 'image/jpeg', 70 ],
[ 'html', 'text/markdown', 80 ],
[ 'html', 'text/plain', 90 ],
[ 'spelling', 'application/vnd.jupyter.widget-view+json', 10 ],
[ 'spelling', 'application/javascript', 20 ],
[ 'spelling', 'text/html', 30 ],
[ 'spelling', 'text/latex', 40 ],
[ 'spelling', 'image/svg+xml', 50 ],
[ 'spelling', 'image/png', 60 ],
[ 'spelling', 'image/jpeg', 70 ],
[ 'spelling', 'text/markdown', 80 ],
[ 'spelling', 'text/plain', 90 ],
]
mathjax_path: https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
mathjax3_config:
loader: { load: [ '[tex]/configmacros' ] }
tex:
packages: { '[+]': [ 'configmacros' ] }
macros:
vect: ["{\mathbf{\boldsymbol{#1}} }", 1]
E: "{\mathbb{E}}"
P: "{\mathbb{P}}"
R: "{\mathbb{R}}"
abs: ["{\left| #1 \right|}", 1]
simpl: ["{\Delta^{#1} }", 1]
amax: "{\text{argmax}}"
01 About 020 Programming Languages (`docs/01-about/020_programming-languages.md`)
Language Support
Serena provides a set of versatile code querying and editing functionalities
based on symbolic understanding of the code across a wide range of programming languages.
Equipped with these capabilities, Serena discovers and edits code just like a seasoned developer
making use of an IDE's capabilities would.
Serena can efficiently find the right context and do the right thing even in very large and
complex projects!
There are two alternative technologies powering these capabilities:
- Language servers implementing the language server Protocol (LSP) — the free/open-source alternative.
- The Serena JetBrains Plugin, which leverages the powerful code analysis and editing
capabilities of your JetBrains IDE.
See the Features section for a detailed comparison of the capabilities provided by the JetBrains Plugin vs. language servers.
(language-servers)=
Language Servers
Serena incorporates a powerful abstraction layer for the integration of language servers
that implement the language server protocol (LSP).
It even supports multiple language servers in parallel to support polyglot projects.
The language servers themselves are typically open-source projects (like Serena)
or at least freely available for use.
We currently provide direct, out-of-the-box support for the programming languages listed below.
Some languages require additional installations or setup steps, as noted.
Ada / SPARK
(uses AdaCore's Ada Language Server (ALS),
automatically downloaded; supports.ads,.adb, and.adafiles;
works best with a.gprGNAT project file at the repository root;
SPARK is handled by the same server transparently — set languageadafor both.
To use a pre-installed ALS (e.g. from Alire, GNAT Studio, or the VS Code Ada extension),
setls_specific_settings.ada.ls_path.)AL
Angular
(experimental; requires Node.js + npm, plusnpm installhaving been run in the project root so that@angular/core
is resolvable — without it, template-aware features silently return empty;
subsumestypescriptandhtmlfor.ts/.htmlfiles, so do not also list those)Ansible
(experimental; requires Node.js and npm; automatically installs@ansible/ansible-language-server;
must be explicitly specified in thelanguage_serversentry in theproject.yml; requiresansiblein PATH for full functionality;
the upstream@ansible/[email protected]supports hover, completion, definition,
semantic tokens, and validation; document symbols, workspace symbols, references, and rename
are not supported by this version)Astro
(requires Node.js v18+ and npm; supports.astrofiles plus TypeScript/JavaScript files via@astrojs/language-server; a companiontypescript-language-server+@astrojs/ts-pluginis spawned automatically for cross-file rename, go-to-definition, and references across.ts/.jsand.astrofiles; use languageastrofor Astro projects instead of also enablingtypescript)Bash
BSL (1C:Enterprise / OneScript)
(requires Java 21+ on PATH; uses bsl-language-server by 1c-syntax; the JAR is auto-downloaded and SHA-256-verified for the bundled default version; supports.bsland.osfiles; configure optionalls_pathorbsl_ls_versionunderls_specific_settings.bsl)C#
(by default, uses the Roslyn language server (languagecsharp), requiring .NET v10+ and, on Windows,pwsh(PowerShell 7+);
set language tocsharp_omnisharpto use OmniSharp instead)C/C++
(by default, uses the clangd language server (languagecpp) but we also support ccls (languagecpp_ccls);
for best results, provide acompile_commands.jsonat the repository root;
see the C/C++ Setup Guide for details;
for Unreal Engine 5 projects, see the Unreal Engine Setup Guide.)Clojure
Crystal
(requires Crystalline language server to be installed and available on PATH;
note: Crystalline has limited go-to-definition support and does not support find-references)CUE
Dart
Elixir
(requires Elixir installation; Expert language server is downloaded automatically)Erlang
(uses the Erlang Language Platform; Serena downloads the pinned platform release automatically; requires Erlang/OTP; functions are addressed asname#arity, e.g.create_user#4, because/is reserved as the name path separator)Elm
(requires Elm compiler)
F#
(requires .NET v8.0+; uses FsAutoComplete/Ionide, which is auto-installed; for Homebrew .NET on macOS, set DOTNET_ROOT in your environment)Fortran
(requires installation of fortls:pip install fortls)GDScript (Godot Engine)
(requires the Godot editor to be running with its built-in LSP enabled — default on port 6008;
Serena connects over TCP and does not launch Godot itself;
see the GDScript Setup Guide for details)Gleam
(requires the Gleam compiler on PATH; the language server is bundled with the compiler and started viagleam lsp)Go
(requires installation ofgopls)Groovy
(requires a local groovy-language-server.jar, configured vials_jar_pathinls_specific_settings;
see the Groovy Setup Guide for details)Haskell
(automatically locates HLS via ghcup, stack, or system PATH; supports Stack and Cabal projects)Haxe
(requires Haxe compiler 3.4.0+ and Node.js; uses the vshaxe language server;
automatically downloaded from Open VSX, or discovered from the vshaxe VSCode extension)HLSL / GLSL / WGSL
(uses shader-language-server (languagehlsl); automatically downloaded;
on macOS, requires Rust toolchain for building from source;
note: reference search is not supported by this language server)HTML
(experimental; requires Node.js + npm)Java
JavaScript
(supported via the TypeScript language server, i.e. use languagetypescriptfor both JavaScript and TypeScript)Julia
(by default, uses LanguageServer.jl (languagejulia); the
Fatou alternative (languagejulia_fatou) is installed
automatically and requiresuv/uvxin PATH)Kotlin
(uses the pre-alpha official kotlin LS, some issues may appear)LaTeX
(experimental; must be explicitly enabled via languagelatex; uses texlab,
auto-downloaded as a SHA-256-verified prebuilt binary; supports.tex,.bib,.sty, and.clsfiles; texlab is
GPL-3.0 and runs as a separate downloaded process)Lean 4
(requiresleanandlakeinstalled via elan; uses the built-in Lean 4 LSP;
the project must be a Lake project withlake buildrun before use)Lua
(by default, useslua-language-server; the experimental Rust-based EmmyLua Analyzer
can be selected with languagelua_emmylua; Serena manages a verified download unlessls_pathis set;
references from other files are not found if the file defining the symbol uses CRLF line endings)Luau
Markdown
(must explicitly enable languagemarkdown, primarily useful for documentation-heavy projects)MATLAB
(requires Node.js and a licensed local MATLAB installation, R2021b or later; Serena automatically downloads version 1.3.9 of the VS Code MATLAB extension, which bundles the language server)mSL (mIRC Scripting Language)
(auto-installed; no external dependencies required — uses a custom pygls-based LSP server shipped with Serena;
supports document symbols, workspace symbols, references, and go-to-definition for aliases, events, menus, dialogs, and CTCP handlers in.mrcfiles)Nextflow
(uses the official Nextflow language server, which is automatically
downloaded; requires a Java 17+ runtime, discovered vials_specific_settings.nextflow.java_home,JAVA_HOMEorjavaon PATH;
covers.nfscripts — Nextflow.configfiles are not treated as source files, since the language server reports no symbols for them;
processes, workflows and functions are reported under their declared name, e.g.GREETforprocess GREET)Nix
(requires nixd installation)OCaml
(requires opam and ocaml-lsp-server to be installed manually; see the OCaml Setup Guide)Pascal
(uses Pascal/Lazarus, which is automatically downloaded; setPPandFPCDIRenvironment variables for source navigation)Perl
(requires installation of Perl::LanguageServer)PHP
(by default, uses the Intelephense language server (languagephp), setINTELEPHENSE_LICENSE_KEYenvironment variable for premium features;
we also support Phpactor (languagephp_phpactor), which requires PHP 8.1+;
the experimental PHPantom backend (languagephp_phpantom);
and the experimental standalone Devsense PHP Language Server
(languagephp_devsense), which is installed with npm when neededPowerShell
(requires PowerShell 7+ (pwsh) on PATH or in a standard install location; Serena automatically downloads PowerShell Editor Services 4.4.0 and installs PSScriptAnalyzer 1.25.0 viaSave-Modulefrom your configured PowerShell repository)Python
(by default, uses Pyright (languagepython);
alternatives: BasedPyright (languagepython_basedpyright),
ty (languagepython_ty),
pyrefly (languagepython_pyrefly),
Jedi (languagepython_jedi);
Pyright, BasedPyright, ty, and pyrefly requireuv/uvxin PATH)QML
(requires Qt 6, providesqmllsorqmlls6on PATH; see the Qt qmlls documentation)R
(requires installation of thelanguageserverR package)Rego
(requires the Regal language server on PATH)Ruby
(by default, uses ruby-lsp (languageruby); use languageruby_solargraphto use Solargraph instead.)Rust
(requires rustup - uses rust-analyzer from your toolchain)Scala
(uses Metals LSP, which imports the build on first use — see the setup guide)SCSS / Sass / CSS
(experimental; requires Node.js + npm; uses some-sass-language-server to handle.scss,.sass, and.css)Solidity
(experimental; requires Node.js and npm; automatically installs@nomicfoundation/solidity-language-server;
works best with afoundry.tomlorhardhat.config.jsin the project root)Svelte
(requires Node.js v18+ and npm; supports.svelteSingle File Components plus TypeScript/JavaScript files viasvelte-language-server; a companiontypescript-language-server+typescript-svelte-pluginis spawned automatically for cross-file rename, go-to-definition, and references across.ts/.jsand.sveltefiles; use languagesveltefor Svelte projects instead of also enablingtypescript)Swift
SystemVerilog
(usesverible-verilog-ls, taken from PATH if present, otherwise versionv0.0-4051-g9fdb4057is downloaded automatically)Terraform
(usesterraform-ls0.36.5, which Serena downloads automatically; requires Terraform on PATH)TOML
(experimental; uses Taplo 0.10.0, taken from PATH if present, otherwise downloaded automatically)TypeScript
(by default, uses typescript-language-server (languagetypescript); the native language server of
TypeScript 7 (languagetypescript_tsgo) is installed automatically via npm, requiring Node.js and npm
only for the installation)Deno
(experimental; requires thedenoCLI on PATH — it bundles the language server used here;
serves Deno TypeScript/JavaScript and understandsnpm:/jsr:/https:imports and theDeno.*
globals, which the plain TypeScript language server does not; overlapstypescripton file extensions,
so it is not auto-detected and must be set as the language explicitly — do not also enabletypescript
for the same files)VB.NET
(uses the community Roslyn-based roslyn-vbnet-languageserver (languagevbnet), requiring .NET v10+ and, on Windows,pwsh(PowerShell 7+);
also handles.csfiles so mixed VB.NET/C# solutions share one language server instance; for pure C# projects,csharptakes precedence)Vue
(3.x with TypeScript; requires Node.js v18+ and npm; supports .vue Single File Components with monorepo detection)Wolfram Language
(requires Wolfram Mathematica 13.0+ or Wolfram Engine 12.1+; uses the official WolframResearch LSPServer paclet; supports .wl and .wls files; references are within-file only)YAML
JSON
(experimental; must be explicitly added to the languages list; requires Node.js and npm)Zig
(requires installation of ZLS - Zig Language Server)
Additional language servers can easily be supported by providing an adapter implementation;
see our contribution guide.
If you need to support a custom language server which is not yet publicly available, you have the option to register an external language server.
Downloading Language Server Dependencies Ahead of Time
Language server dependencies are normally downloaded when a project using the respective language is first activated.
In environments with restricted network access, dependencies can instead be downloaded during a container or machine build:
serena download-ls-dependencies python typescriptLanguage servers that require an external toolchain, such as Go or Java, can only be prepared
when that toolchain is installed; failures are reported and the command exits non-zero after attempting the remaining servers.
The downloaded dependencies are tied to the Serena version that installed them, so repeat the command after upgrading Serena.
The Serena JetBrains Plugin
The Serena JetBrains Plugin leverages the powerful code analysis capabilities of JetBrains IDEs.
The plugin naturally supports all programming languages and frameworks that are supported by JetBrains IDEs.
When using the plugin, Serena connects to an instance of your JetBrains IDE via the plugin. For users who already
work in a JetBrains IDE, this means Serena seamlessly integrates with the IDE instance you typically have open anyway,
requiring no additional setup or configuration beyond the plugin itself.
- See the JetBrains Plugin documentation for a high-level overview of its benefits and usage details.
- See the Features section for a detailed comparison of the capabilities provided by the JetBrains Plugin vs. language servers.
<p>
<a href="https://plugins.jetbrains.com/plugin/28946-serena/">
<img style="background-color:transparent;" src="../_static/images/jetbrains-marketplace-button.png">
</a>
</p>01 About 030 Serena In Action (`docs/01-about/030_serena-in-action.md`)
Serena in Action
Demonstration 1: Efficient Operation in Claude Code
A demonstration of Serena efficiently retrieving and editing code within Claude Code, thereby saving tokens and time. Efficient operations are not only useful for saving costs, but also for generally improving the generated code's quality. This effect may be less pronounced in very small projects, but often becomes of crucial importance in larger ones.
Demonstration 2: Serena in Claude Desktop
A demonstration of Serena implementing a small feature for itself (a better log GUI) with Claude Desktop.
Note how Serena's tools enable Claude to find and edit the right symbols.
01 About 050 Acknowledgements (`docs/01-about/050_acknowledgements.md`)
Acknowledgements
Sponsors
We are very grateful to our sponsors, who help us drive Serena's development.
The core team (the founders of Oraios AI) put in a lot of work in order to turn Serena into a useful open source project.
So far, there is no business model behind this project, and sponsors are our only source of income from it.
Sponsors help us dedicate more time to the project, managing contributions, and working on larger features (like better tooling based on more advanced
LSP features, VSCode integration, debugging via the DAP, and several others).
If you find this project useful to your work, or would like to accelerate the development of Serena, consider becoming a sponsor.
We are proud to announce that the Visual Studio Code team, together with Microsoft’s Open Source Programs Office and GitHub Open Source
have decided to sponsor Serena with a one-time contribution!
Community Contributions
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.
Technologies
We built Serena on top of multiple existing open-source technologies, the most important ones being:
- multilspy.
A library which wraps language server implementations and adapts them for interaction via Python
and which provided the basis for our library Solid-LSP (src/solidlsp).
Solid-LSP provides pure synchronous LSP calls and extends the original library with the symbolic logic
that Serena required. - Python MCP SDK
- All the language servers that we use through Solid-LSP.
Without these projects, Serena would not have been possible (or would have been significantly more difficult to build).
01 About 060 License (`docs/01-about/060_license.md`)
License
Serena is licensed per component. The authoritative overview is theLICENSE file in the repository,
and the full license texts are in LICENSES/.
| Component | Location | License |
|---|---|---|
| SolidLSP – language-server client library, the basis of Serena's free code-intelligence backend | src/solidlsp, test/solidlsp, test/resources |
MIT |
| Serena application – everything else (agent, tools, MCP server, prompts, scripts, tests, docs) | src/serena, src/interprompt, test/serena, scripts, … |
GPL-3.0-or-later |
Every source file carries an SPDX-License-Identifier line stating which license applies to it.
Because MIT is compatible with the GPL, a distribution that combines both — such as the serena-agent package —
is as a whole subject to GPL-3.0-or-later. This does not change the license of the SolidLSP files themselves:
they remain MIT-licensed and can be extracted and used separately under MIT terms.
SolidLSP: permissive, reusable infrastructure
SolidLSP is deliberately kept under the permissive MIT license so that it can be reused independently of Serena,
in any project and under any license. Serena's licensing does not affect SolidLSP.
Serena: an application that stays free
Starting with the v2 licensing transition, the Serena application is licensed under the
GNU General Public License, version 3 or (at your option) any later version.
Why the change?
Serena originally began under the GPL. It was later changed to MIT following community requests.
We have come to consider that change a mistake.
Version 2 introduces substantial architectural and code changes, which made this the appropriate time to reconsider.
The central principle behind the decision is simple:
We want the best version of Serena to remain free.
The GPL ensures that anyone who redistributes a derivative version of Serena must preserve the same software freedoms
for its users that they received themselves.
The change is not retroactive
- All Serena releases and commits up to the historical cutoff documented in
LICENSEwere released under MIT and
remain available under MIT. - Serena v2 and later application code are GPL-3.0-or-later.
- Contributions that entered the application under MIT before the cutoff remain incorporated in the GPL-licensed
application; the MIT notice continues to apply to that historical code as required by its terms.
Contributing
Contributions require acceptance of our
Contributor License Agreement, which lets you keep your
copyright while granting the maintainers the rights needed to maintain and relicense Serena (including under
commercial terms) in the future. Acceptance is a one-time click via the CLA assistant bot on your first pull request.
See CONTRIBUTING.md for details.
01 About 070 Privacy (`docs/01-about/070_privacy.md`)
(privacy)=
Privacy Policy
Serena respects your privacy and is committed to protecting your personal information.
When using Serena, no personal data or data about the project being worked on is sent to any external servers.
We collect only the following anonymous usage data whenever Serena is started:
- the version of Serena being used,
- the operating system being used,
- the Serena agent interface being used,
- the language backend being used,
- the enabled status of the Serena Dashboard,
- the enabled Serena agent context
This data is collected strictly to help us understand Serena usage.
If you want to opt out of usage data reporting, set the environment variable SERENA_USAGE_REPORTING to false.
02 Usage 010 Installation (`docs/02-usage/010_installation.md`)
Installation
Prerequisites
Package Manager: uv
Serena is managed by uv.
If you do not have it yet, install it following the instructions here.
Language-Specific Requirements
When using the language server backend, some additional dependencies may need to be installed
to support certain languages.
See the Language Support page for the list of supported languages.
Many dependencies are installed by Serena on the fly, but if a language requires dependencies
to be provided manually, this is mentioned in the notes below the respective language.
(install-serena)=
Installing and Initialising Serena
With uv installed and on your PATH, install Serena with this command:
uv tool install -p 3.13 serena-agentUpon completion, the command serena should be available in your terminal.
To test the installation and initialise Serena, run one of the following commands:
serena init
if you intend to use the default language intelligence backend (language servers)serena init -b JetBrains
if you intend to use the JetBrains backend (which uses the JetBrains plugin)
Note that you can switch backends at any time via Serena's configuration.
Updating Serena
To update Serena to the latest version, run:
uv tool upgrade serena-agent:::{tip}
To keep informed about updates, make sure you regularly open Serena's Dashboard,
where we will announce releases along with the new features and improvements they bring.
:::
Uninstalling Serena
Serena can be uninstalled with the following command:
uv tool uninstall serena-agent02 Usage 020 Running (`docs/02-usage/020_running.md`)
Running Serena
Serena is a command-line tool with a variety of sub-commands.
This section describes
- how to run Serena in general
- how to run and configure the most important command, i.e. starting the MCP server
- other useful commands.
The main way to run Serena is to use the installed version,
which should be available in your system PATH as serena.
In general, to get help, append --help to the command, i.e.
serena --help
serena <command> --help(start-mcp-server)=
Running the MCP Server
Given your preferred method of running Serena, you can start the MCP server using the start-mcp-server command:
serena start-mcp-server [options]Note that no matter how you run the MCP server, Serena will, by default, start a web-based dashboard on localhost that will allow you to inspect
the server's operations, logs, and configuration.
:::{tip}
By default, Serena will use language servers for code understanding and analysis.
With the Serena JetBrains Plugin, we recently introduced a powerful alternative,
which has several advantages over the language server-based approach.
:::
Standard I/O Mode
The typical usage involves the client (e.g. Claude Code, Codex or Cursor) running
the MCP server as a subprocess and using the process' stdin/stdout streams to communicate with it.
In order to launch the server, the client thus needs to be provided with the command to run the MCP server.
:::{note}
MCP servers which use stdio as a protocol are somewhat unusual as far as client/server architectures go, as the server
necessarily has to be started by the client in order for communication to take place via the server's standard input/output streams.
In other words, you do not need to start the server yourself. The client application (e.g. Claude Desktop) takes care of this and
therefore needs to be configured with a launch command.
:::
Communication over stdio is the default for the Serena MCP server, so in the simplest
case, you can simply run the start-mcp-server command without any additional options.
serena start-mcp-serverSee the section "Configuring Your MCP Client" for specific information on how to configure your MCP client (e.g. Claude Code, Codex, Cursor, etc.)
to use such a launch command.
(streamable-http)=
Streamable HTTP Mode
When using Streamable HTTP mode, you control the server lifecycle yourself,
i.e. you start the server and provide the client with the URL to connect to it.
Simply provide start-mcp-server with the --transport streamable-http option and optionally provide the desired port
via the --port option.
For example, to start the server on port 9121, run
serena start-mcp-server --transport streamable-http --portand then configure your client to connect to http://localhost:9121/mcp.
By default, only connections from localhost are allowed; pass the --host <listen_address> option to configure
the listen address and allow remote connections if needed (but be aware of the security implications of doing so).
When to use. Note that Serena is a stateful MCP server, and only one coding project can be active at a time.
Therefore, starting a single Serena instance and connecting it to multiple clients is only
appropriate if all clients will be working on the same project.
If you want several agents to work on different projects, making each client/agent start its own server
in stdio mode is likely the best option.
See section The Project Workflow for more information on how to manage projects in Serena.
The legacy SSE transport is also supported (via --transport sse with corresponding /sse endpoint), its use is discouraged.
(mcp-args)=
MCP Server Command-Line Arguments
The Serena MCP server supports a wide range of additional command-line options.
Use the command
<serena> start-mcp-server --helpto get a list of all available options.
Some useful options include:
--project: specify the project to work on by name or path.--project-from-cwd: auto-detect the project from current working directory
(walking up the parent directories and activating the nearest one that contains either.serena/project.yml
or.git, if any). The nearest boundary wins, so a git worktree nested under another Serena project resolves
to the worktree itself rather than the ancestor project.
This option is intended for CLI-based agents like Claude Code, Gemini and Codex, which are typically started from within the project directory
and which do not change directories during their operation.--language-backend JetBrains: use the Serena JetBrains Plugin as the language backend (overriding the default backend configured in the central configuration)--context <context>: specify the operation context in which Serena shall operate--mode <mode>: specify one or more modes to enable (can be passed several times)--open-web-dashboard: whether to open the web dashboard on startup (enabled by default)
Other Commands
Serena provides several other commands in addition to start-mcp-server,
most of which are related to project setup and configuration.
To get a list of available commands, run:
<serena> --helpTo get help on a specific command, run:
<serena> <command> --helpIn general, add --help to any command or sub-command to get information about its usage and available options.
Here are some examples of commands you might find useful:
# get help about a sub-command
serena> tools list --help
# list all available tools
serena> tools list --all
# get detailed description of a specific tool
serena> tools description find_symbol
# creating a new Serena project in the current directory
serena project create
# creating and immediately indexing a project
serena project create --index
# indexing the project in the current directory (auto-creates if needed)
serena project index
# run a health check on the project in the current directory
serena project health-check
# check if a path is ignored by the project
serena project is_ignored_path path/to/check
# edit Serena's configuration file
serena config edit
# list available contexts
serena context list
# create a new context
serena context create my-custom-context
# edit a custom context
serena context edit my-custom-context
# list available modes
serena mode list
# create a new mode
serena mode create my-custom-mode
# edit a custom mode
serena mode edit my-custom-mode
# list available prompt definitions
serena prompts list
# create an override for internal prompts
serena prompts create-override prompt-name
# edit a prompt override
serena prompts edit-override prompt-nameExplore the full set of commands and options using the CLI itself!
Alternative Ways of Running Serena
Depending on your requirements, you may want to run Serena in different ways.
When applying one of these approaches, replace serena in commands mentioned throughout the documentation
with the respective command and options. The same applies to serena-hooks commands.
Using uvx to Run the Latest Source Version
uvx is part of uv. It can be used to run the latest version of Serena directly from the repository, without an explicit local installation.
uvx -p 3.13 --from git+https://github.com/oraios/serena serenaThis was previously the main way of running Serena.
Since this has the downside that every new commit in the repository will trigger a (potentially slow) re-synchronization, an installation of Serena should usually be preferred.
If you should experience timeouts when connecting the MCP server, consider switching.
If, however, the synchronisation is fast enough for you, this is still a good option.
Running from Cloned Source
Clone the repository and change into it.
shellgit clone https://github.com/oraios/serena cd serenaRun Serena via
shelluv run serenawhen within the serena installation directory.
From other directories, run it with the--directoryoption, i.e.shelluv run --directory /abs/path/to/serena serena
:::{note}
Adding the --directory option results in the working directory being set to the Serena directory.
As a consequence, you will need to specify paths when using CLI commands that would otherwise operate on the current directory.
:::
(docker)=
Using Docker
The Docker approach offers several advantages:
- better security isolation for shell command execution
- no need to install language servers and dependencies locally
- consistent environment across different systems
You can run the Serena MCP server directly via Docker as follows,
assuming that the projects you want to work on are all located in /path/to/your/projects:
docker run --rm -i --network host -v /path/to/your/projects:/workspaces/projects ghcr.io/oraios/serena:latest serenaThis command mounts your projects into the container under /workspaces/projects, so when working with projects,
you need to refer to them using the respective path (e.g. /workspaces/projects/my-project).
Alternatively, you may use Docker compose. Adjust the file compose.yml, which is provided in the repository, according to your needs.
See our advanced Docker usage documentation for more detailed instructions, configuration options, and limitations.
:::{note}
Docker usage is subject to limitations; see the advanced Docker usage documentation for details.
:::
Using Nix to Run the Latest Source Version
If you are using Nix and have enabled the nix-command and flakes features, you can run Serena using the following command:
nix run github:oraios/serena -- <command> [options]You can also install Serena by referencing this repo (github:oraios/serena) and using it in your Nix flake. The package is exported as serena.
02 Usage 025 Jetbrains Plugin (`docs/02-usage/025_jetbrains_plugin.md`)
The Serena JetBrains Plugin
The JetBrains Plugin allows the Serena MCP server to
leverage the powerful code analysis and editing capabilities of your JetBrains IDE.
This page explains how to install the plugin and how to configure Serena appropriately.
You will still need to set up the Serena MCP server
itself, so make sure to follow the installation instructions and connect the MCP server to your
LLM-based client as described in client setup in addition to following the instructions below.
<p>
<a href="https://plugins.jetbrains.com/plugin/28946-serena/">
<img style="background-color:transparent;" src="../_static/images/jetbrains-marketplace-button.png">
</a>
</p>We recommend the JetBrains plugin as the preferred way of using Serena,
especially for users of JetBrains IDEs.
How it works:
- Install the plugin in your JetBrains IDE
- Configure Serena to use the JetBrains language backend (see below)
- Open the project you want to work on in your JetBrains IDE and activate it in Serena (see below)
- Start coding via your MCP client as usual
:class: note
It is *not* a UI extension for direct agent interaction (like Copilot) or anything of the sort.
You still interact with your regular client – be it external to your IDE (like Claude Code CLI) or internal (like Copilot or JetBrains AI Assistant) –
and connect it to the Serena MCP server.
The plugin simply enables the Serena MCP server to directly leverage capabilities of your JetBrains IDE!Purchasing the JetBrains Plugin supports the Serena project.
The proceeds from plugin sales allow us to dedicate more resources to further developing and improving Serena.
Advantages of the JetBrains Plugin
There are multiple features that are only available when using the JetBrains plugin:
- External library indexing: Dependencies and libraries are fully indexed and accessible to Serena
- Enhanced retrieval & refactoring capabilities: The plugin adds additional tools (e.g. type
hierarchy retrieval, move, find declaration, inline symbol, etc.)
and transforms the underlying mechanisms of shared tools to build upon the IDE's capabilities. - Interactive debugging: The agent can set breakpoints, inspect variables, evaluate expressions and control execution flow
by directly interacting with the IDE's debugger, using a REPL-style interface for maximum flexibility. - Improved multi-agent support: A single IDE instance naturally serves arbitrarily many agent sessions without requiring additional resources.
- Enhanced performance: Faster tool execution thanks to optimized IDE integration.
- Multi-language excellence and framework support: First-class support for polyglot projects with multiple languages.
and frameworks (whatever is recognised by your IDE as a symbol will also be available to Serena) - No additional setup: No need to download or configure separate language servers.
We are also working on additional features like debugging and advanced introspection capabilities, which
will be available exclusively through the JetBrains plugin.
:::{note}
With Serena's JetBrains tools, we try to offer the latest features.
As a result, some of them are considered as beta features (see tool list), which may have some quirks.
Please report your experience with these tools if they do not work as expected.
:::
(configure-jetbrains)=
Configuring Serena to Use the JetBrains Plugin
After installing the plugin, you need to configure Serena to use it.
Central Configuration.
You can run
serena init -b JetBrainsto set the default code intelligence backend to JetBrains in the global Serena configuration file.
Alternatively, manually edit the configuration file ~/.serena/serena_config.yml
(%USERPROFILE%\.serena\serena_config.yml on Windows) and set
language_backend: JetBrainsNote that the file might not exist yet if you never executed Serena before.
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.
(per-project-language-backend)=
Per-Project Configuration.
You can also set the language backend on a per-project basis in the project's.serena/project.yml file:
language_backend: JetBrainsIf set, this overrides the global language_backend setting for the session when the project is
activated at startup (via the --project flag).
:::{important}
The language backend is determined once at startup and cannot be changed during a running session.
If a project with a different backend is activated after startup, Serena will return an error.
If you need to work with projects that use different backends, you can either:
- Use the
--projectflag to activate the project at startup, which will use its configured backend. - Configure separate MCP server instances (one per backend) in your client.
:::
Verifying the Setup.
You can verify that Serena is using the JetBrains plugin by either checking the dashboard, where
you will see Languages: Using JetBrains backend in the configuration overview.
You will also notice that your client will use the JetBrains-specific tools like jet_brains_find_symbol and others like it.
(jetbrains-workflow)=
Workflow
Having installed the plugin in your IDE and having configured Serena to use the JetBrains backend,
the general workflow is simple:
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.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, configurejetbrains_launch_commandin Serena's global configuration file appropriately.Activate the project's root folder as a project in Serena (see Project Creation and Project Activation).
Start using Serena's tools as usual.
Note that the project folder that is open in your IDE and the Serena project root folder must match.
:::{tip}
If you need to work on multiple projects in the same agent session, create a monorepo folder
containing all the projects and open that folder in both Serena and your IDE.
:::
Advanced Usage and Configuration
Using Serena with Multi-Module Projects
JetBrains IDEs support multi-module projects, where a project can reference other projects as modules.
Serena, however, requires that a project is self-contained within a single root folder.
There has to be a one-to-one relationship between the project root folder and the folder that is open in the IDE.
Therefore, to get a multi-module setup working with Serena, the recommended approach is to create a monorepo folder,
i.e. a folder that contains all the projects as sub-folders, and open that monorepo folder in both Serena and your IDE.
You do not necessarily need to physically move your projects into a common parent folder;
you can also use symbolic links to achieve the same effect
(i.e. use mklink on Windows or ln on Linux/macOS to link the project folders into a common parent folder).
Using Serena with Windows Subsystem for Linux (WSL)
JetBrains IDEs have built-in support for WSL, allowing you to run the IDE on Windows while working with code in the WSL environment.
The Serena JetBrains plugin works seamlessly in this setup as well.
Using JetBrains Remote Development
Recommended constellation:
- Your project is in the WSL file system
- Serena is run in WSL (not Windows)
- The IDE has a host component (in WSL) and a client component (on Windows).
The Serena JetBrains plugin should normally be installed in the host (not the client) for code intelligence to be accessible.
:::{admonition} Plugin Installation Location
:class: note
If the plugin is already installed, check the options on the button for disabling the plugin.
Choose the respective options to ensure the correct installation location (i.e. host, removing it from the client if necessary).
:::
:::{admonition} Using mapped Windows paths in WSL is not recommended!
:class: warning
Keeping your project in the Windows file system and accessing it via /mnt/ in WSL is extremely slow and not recommended.
:::
Special Network Setup.
If you are using a special setup where Serena and the IDE are running on different machines,
make sure Serena can communicate with the JetBrains plugin.
You can configure jetbrains_plugin_server_address in your serena_config.yml and
configure the listen address of the JetBrains plugin in the IDE via Settings / Tools / Serena
(e.g. set it to 0.0.0.0 to listen on all interfaces, but be aware of the security implications of doing so).
Other WSL Integrations (e.g. WSL interpreter)
- Your project is in the Windows file system
- WSL is used only for running tools (e.g. using a WSL Python interpreter in the IDE)
- Serena, the IDE and the plugin are all running on Windows
In this constellation, no special setup is required.
Serena Plugin Configuration Options
You can configure plugin options in the IDE under Settings / Tools / Serena.
Listen address (default:
127.0.0.1)
the address the plugin's server listens on.
The default will work as long as Serena is running on the same machine (or on a virtual machine using mirrored networking).
But if the Serena MCP server is running on a different machine, configure the listen address to ensure that connections are possible.
You can use0.0.0.0to listen on all interfaces (but be aware of the security implications of doing so).Sync file system before every operation (default: enabled)
whether to synchronise the file system state before processing requests from Serena.
This is important to ensure that the plugin does not read stale data, but it can have a performance impact,
especially when using slow file systems (e.g. WSL file system while the IDE is running on Windows).
Note, however, that without synchronisation being forced by the Serena plugin, you will have to ensure synchronisation yourself.
Operations that apply changes to files in your project that are not made either in the IDE itself or by Serena may not be seen by the IDE.
Normally, the IDE synchronises automatically when it has the focus, using file watchers to achieve this (though this may or may not work reliably for the WSL file system).
Also, if you are working primarily in another application (e.g. AI chat), the IDE may not have the focus frequently.
So when external changes are made to your project, you will have to either give the IDE the focus (if that works) or trigger a sync manually (right-click root folder / Reload from Disk).
Further, note that even an edit made using, for example, Claude Code's internal editing tools would count as an external modification.
Only Serena's editing tools are "JetBrains-aware" and will tell the IDE to update the state of the edited file.
So if you are making AI-based edits using tools other than Serena's tools, do make sure that the lack of synchronisation is not a problem if you decide to disable this option.
Usage with Other Editors
We realize that not everyone uses a JetBrains IDE as their main code editor.
You can still take advantage of the JetBrains plugin by running a JetBrains IDE instance alongside your
preferred editor. Most JetBrains IDEs have a free community edition that you can use for this purpose.
You just need to make sure that the project you are working on is open and indexed in the JetBrains IDE,
so that Serena can connect to it.
02 Usage 030 Clients (`docs/02-usage/030_clients.md`)
Connecting Your MCP Client
In the following, we provide general instructions on how to connect Serena to your MCP-enabled client,
as well as specific instructions for popular clients.
(clients-general-instructions)=
General Instructions
In general, Serena can be used with any MCP-enabled client.
To connect Serena to your favourite client, simply
- determine how to add a custom MCP server to your client (refer to the client's documentation).
- add a new MCP server entry by specifying either
- a run command that allows the client to start the MCP server in stdio mode as a subprocess, or
- the URL of the HTTP/SSE endpoint, having started the Serena MCP server in HTTP/SSE mode beforehand.
Find concrete examples for popular clients below.
Depending on your needs, you might want to further customize Serena's behaviour by
Mode of Operation.
Note that some clients have a per-workspace MCP configuration (e.g, VSCode and Claude Code),
while others have a global MCP configuration (e.g. Codex and Claude Desktop).
- In the per-workspace case, you typically want to start Serena with your workspace directory as the project directory
and never switch to a different project. This is achieved by specifying the--projectargument with a single-project context (e.g.ideorclaude-code). - In the global configuration case, you must first activate the project you want to work on, which you can do by asking
the LLM to do so (e.g., "Activate the current dir as project using serena"). In such settings, theactivate_project
tool is required.
Tool Selection.
While you may be able to turn off tools through your client's interface (e.g., in VSCode or Claude Desktop),
we recommend selecting your base tool set through Serena's configuration, as Serena's prompts automatically
adjust based on which tools are enabled/disabled.
A key mechanism for this is to use the appropriate context when starting Serena.
(clients-common-pitfalls)=
Common Pitfalls
Discoverability of the serena command.
Your client may not find the serena CLI command, even if it is on your system PATH.
In this case, a workaround is to provide the full path to the serena executable.
Serena's tools not being used.
With some clients, you may experience that Serena's tools are not being used.
This is mainly due to problems in the client itself (like a poorly implemented tool discovery). To counteract this,
Serena comes with a set of commands that can be used in hooks. See the sections on hooks for Claude Code and VSCode below.
Environment Variables.
Some language servers may require additional environment variables to be set (e.g. F# on macOS with Homebrew),
which you may need to explicitly add to the MCP server configuration.
Note that for some clients (e.g. Claude Desktop), the spawned MCP server process may not inherit environment variables that
are only configured in your shell profile (e.g. .bashrc, .zshrc, etc.); they would need to be set system-wide instead.
An easy fix is to add them explicitly to the MCP server entry.
For example, in Claude Desktop and other clients, you can simply add an env key to the serena
object, e.g.
"env": {
"DOTNET_ROOT": "/opt/homebrew/Cellar/dotnet/9.0.8/libexec"
}Copilot in JetBrains
Open the settings of your JetBrains IDE and go to Tools / GitHub Copilot / Model Context Protocol (MCP). Then click
on the Configure button. This will open your global mcp.json file, where you can add the following entry for Serena:
{
"servers": {
"serena": {
"type": "stdio",
"command": "serena",
"args": [
"start-mcp-server",
"--context=jb-copilot-plugin"
]
}
}
}Verification.
Open Copilot, switch to Agent mode, and click on the configure tools button. You should see Serena's tools in the list and be able to start
the Serena server there (you do not generally have to start Serena in the future, Copilot will start the server by itself). If the server is shown as running, Copilot is successfully connected to Serena. Most models will understand how to use Serena's tools out of the box, but for some models you may have to prompt "Activate the current project with Serena and read initial instructions" in the beginning of the chat.
Recommended Configuration.
The jb-copilot-plugin context (see above) comes with our recommended subset of Serena's tools for Copilot in JetBrains IDEs. We also
recommend disabling the following built-in tools for optimal performance:
replace_string_in_file, apply_patch, list_dir, file_search, grep_search. Note that running subagents may not use MCP servers, consider deactivating the run_subagent tool as well.
Serena offers better alternatives to these basic tools. If you do prefer to use the built-in tools instead,
you should disable corresponding Serena tools instead to prevent context bloat.
We also recommend marking Serena's tools as approved so you don't have to manually approve them in agent sessions.
You can do this in Tools / GitHub Copilot / Chat, where at the bottom you can click on the Configure button for MCP tool auto-approval.
Claude Code
Serena is a great way to make Claude Code both more efficient and more powerful!
To set up the Serena MCP server for Claude Code, you can simply run this command:
serena setup claude-codeFind manual setup instructions as well as workarounds for Claude Code's recent regressions pertaining to (external) tool use below.
Global Configuration. To add the Serena MCP server for all your projects, use the user-level configuration of claude code and the --project-from-cwd flag:
claude mcp add --scope user serena -- serena start-mcp-server --context claude-code --project-from-cwdPer-Project Configuration. Alternatively, to add Serena only for the current project in the current directory,
use the command:
claude mcp add serena -- serena start-mcp-server --context claude-code --project "$(pwd)"Verification.
Confirm that Claude Code is connected to Serena by running the /mcp command and by reconnecting, if necessary.
If Serena fails to start fast enough, you should set MCP_TIMEOUT to a sufficiently high value
(e.g. by adding export MCP_TIMEOUT=60000 to your shell profile)
Hooks.
Due to recent changes (especially dynamic tool loading) in Claude Code, the agent will often fail to make proper use
of Serena's tools, either by failing to load them in the beginning or by forgetting the instructions in a long session
(a behavior known as agent drift). To counteract this, we provide reminder hooks. We strongly recommend setting
up the hooks as below (or a variation thereof) for optimal performance of Serena in Claude Code.
:::{note}
While recommended, hooks are an alpha feature. Provide feedback via the GitHub issue tracker if you encounter any issues.
:::
To set up hooks, add the following to your Claude Code settings file
(.claude/settings.json in your project directory, or ~/.claude/settings.json globally):
All hooks below are opt-in — include only the ones you want. Add the following to your
Claude Code settings file (.claude/settings.json in your project directory, or~/.claude/settings.json globally):
{
"hooks": {
"PreToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks remind --client=claude-code"
}
]
},
{
"matcher": "mcp__serena__*",
"hooks": [
{
"type": "command",
"command": "serena-hooks auto-approve --client=claude-code"
}
]
}
],
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks activate --client=claude-code"
}
]
}
],
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks cleanup --client=claude-code"
}
]
}
]
}
}The hooks will:
remind: Nudge the agent to use Serena's symbolic tools when it makes too many consecutivegreporread_filecalls without using any Serena tools in between. Searches and source-file reads
run via theBashtool (as native Claude Code installations do) are counted as well.activate: Prompt the agent to activate the project and read Serena's instructions at session start.cleanup: Clean up hook session data when the session ends.auto-approve: Auto-approve Serena tool calls whenever Claude Code is in a permissive
permission mode (acceptEditsorauto), so blanket approvals cover Serena's destructive
tools (e.g.replace_symbol_body,rename_symbol) instead of prompting on every call.
For more details on Claude Code's hook system, see the
Claude Code hooks documentation.
Modifying Claude Code's Prompts.
Recent updates to Claude Code (CC) and to the Opus line of models resulted in drastically reduced
adherence to instructions pertaining to Serena's tools.
Analysis showed that part of the reason are very long and detailed
tool descriptions for built-in tools and parts of the default system prompt.
The descriptions of CC's system tools take almost 16k tokens, cannot be adjusted by the user,
and introduce a very strong bias towards internal tools, making it almost impossible to convince Opus 4.7 to use Serena.
To counteracts this bias, you can consider overriding the system prompt with a version
that emphasizes usage of Serena's tools.
claude --system-prompt="$(serena prompts print-cc-system-prompt-override)"You can also consider adding the content of serena prompts print-cc-system-prompt-override to your CLAUDE.md files,
but the effect may be insufficient for counteracting Claude Code's bias towards internal tools.
DeepSeek Harness (DSH)
DSH can run Serena through its Claude Code-compatible hooks bridge. Install and enable@deepseek-ai/dsh-hooks-claude-code, then configure the bridge with the path to the hooks file. For example,
add the following plugin entry to the DSH Cordis configuration (the exact file location depends on your DSH setup):
- dsh-hooks-claude-code:
# Use an absolute path; relative paths resolve against DSH's process launch directory.
configPath: /absolute/path/to/.dsh/hooks.jsonThe bridge accepts either a bare event map or a settings file with a top-level hooks key. Create the file at the
absolute path used above with the following content:
{
"SessionStart": [{
"matcher": "",
"hooks": [{ "type": "command", "command": "serena-hooks activate --client dsh" }]
}],
"PreToolUse": [{
"matcher": "",
"hooks": [{ "type": "command", "command": "serena-hooks remind --client dsh" }]
}],
"Stop": [{
"matcher": "",
"hooks": [{ "type": "command", "command": "serena-hooks cleanup --client dsh" }]
}]
}The DSH bridge uses SessionStart, PreToolUse, and Stop; therefore Serena maps cleanup to Stop because
DSH does not currently expose a separate SessionEnd event. Serena's DSH output follows the samehookSpecificOutput shape as Claude Code, so the bridge can consume permissionDecision andpermissionDecisionReason without a client-specific wrapper. The bridge supports deny/ask responses forPreToolUse, but does not use Claude Code's allow response for pre-approval, so Serena's auto-approve
hook is intentionally not configured for DSH. Native Bash search/read commands are classified the same
way as Claude Code's Grep/Read tools (gated on the Bash tool name so unrelated MCP tools with acommand parameter are not counted).
VSCode
You can add Serena to VSCode by running the MCP: Add Server command.
In that dialogue, select the Command (stdio) option. You can decide between installing it globally
or in the workspace (only for the currently open project), and the command you should enter depends on that choice.
(You will be asked to choose after entering the mcp run command.)
Global. (Recommended)
Enter serena start-mcp-server --context=vscode. Unfortunately, due to a bug in VSCode,
in this setting Serena won't be able to activate the project automatically. You will have to remember to prompt
"Activate the current dir as project using serena" at the start of each session.
Workspace.
Enter serena start-mcp-server --context=vscode --project ${workspaceFolder}. This will allow Serena to automatically activate the project,
with the downside that you will have to add Serena to each project you want to use it in.
In both cases, proceed to enter Serena as the name, then select either global or workspace.
Verification.
You should be able to see Serena in the tools overview in the AI Chat window.
Hooks.
Due to recent changes (especially dynamic tool loading) in VSCode, the agent will often fail to make proper use
of Serena's tools, either by failing to load them in the beginning or by forgetting the instructions in a long session
(a behaviour known as agent drift). To counteract this, we provide reminder hooks. We strongly recommend setting
up the hooks as below (or a variation thereof) for optimal performance of Serena in VSCode.
The hooks will:
remind: Nudge the agent to use Serena's symbolic tools when it makes too many consecutivegreporread_filecalls without using any Serena tools in between.activate: Prompt the agent to activate the project and read Serena's instructions at session start.cleanup: Clean up hook session data when the session ends.
To set this up, create the file ~/.copilot/hooks/serena-hooks.json with the following content:
{
"hooks": {
"PreToolUse": [
{
"type": "command",
"command": "serena-hooks remind --client=vscode"
}
],
"SessionStart": [
{
"type": "command",
"command": "serena-hooks activate --client=vscode"
}
],
"Stop": [
{
"type": "command",
"command": "serena-hooks cleanup --client=vscode"
}
]
}
}The SessionStart hook also addresses the global configuration limitation mentioned above — it will
automatically prompt the agent to activate the project directory, so you no longer need to do this manually.
Copilot CLI
Use the interactive /mcp add slash command, choose Serena as the name, STDIO as the server type, andserena start-mcp-server --context=copilot-cli --project-from-cwd as command. Copilot CLI will immediately notify you
that Serena is running if everything is set up correctly or display an error otherwise.
You should add the same hooks as in VSCode (see above) if Copilot CLI didn't pick them up automatically.
Codex (CLI and App)
You can simply run serena setup codex.
Alternatively, you can manually add the following to ~/.codex/config.toml (create the file if it does not exist):
[mcp_servers.serena]
startup_timeout_sec = 15
command = "serena"
args = ["start-mcp-server", "--project-from-cwd", "--context=codex"]Verification.
Run the /mcp command and verify that Serena is connected.
The Codex app does not start a session in the project's directory, so when using the app, we recommend
asking Codex to "Activate the current dir as project using serena" at the start of each session (though Codex might
do this automatically).
Hooks.
Current Codex versions enable lifecycle hooks by default, so no feature flag is required. See the
Codex hooks documentation for details. Create~/.codex/hooks.json with the following content:
/* Detailed source-code truncated for AI context efficiency. */These timeout values are specific to Codex. Without an explicit timeout, Codex allows most hooks
to run for 600 seconds, so the five-second reminder and activation timeouts prevent a stuck hook
from blocking a Bash call or session startup for that entire default. SessionEnd instead
defaults to one second and supports at most three seconds, so the cleanup hook uses that maximum.
Codex reports timed-out commands as hook failures. Other clients may have different timeout
behaviour; in particular, the Grok cleanup example below intentionally remains at five seconds.
The SessionEnd cleanup hook requires Codex 0.145.0 or newer. Older Codex versions only supportStop for cleanup, which currently has a known compatibility issue.
If you still configure it, replace SessionEnd with Stop in the example above. Configure cleanup
under exactly one of these events, never both: Stop runs after every turn, while SessionEnd runs
when Codex tears down the root thread.
Each Codex event maps to one Serena hook command:
| Codex event | Serena command | When it runs |
|---|---|---|
PreToolUse with matcher Bash |
serena-hooks remind |
Before a shell command, to detect drift from Serena's symbolic tools |
PreToolUse with matcher ^mcp__serena__ |
serena-hooks plan-guard |
Before a Serena MCP tool call, to block edit-capable tools in plan mode |
SessionStart with matcher startup|resume |
serena-hooks activate |
When a session starts or resumes, to prompt project activation |
UserPromptSubmit |
serena-hooks plan-context |
Before each prompt, to add read-only Serena guidance in plan mode |
SessionEnd |
serena-hooks cleanup |
When Codex tears down the root thread, to clean up hook session data |
The distinct statusMessage values make the five kinds of hook activity distinguishable while
they run.
The reminder hook's PreToolUse matcher is intentionally restricted to Bash. It tracks
shell-based grep and code-file reads, so running it for every tool call is unnecessary. The separate
plan guard targets Serena MCP calls. While Codex reports permission_mode: "plan", it blocks every
Serena tool marked as edit-capable, including memory-writing and shell-command tools, while leaving
read-only tools available. Both plan hooks evaluate the current event without retaining mode state,
so they become silent and editing is available again as soon as Codex leaves plan mode; Serena's MCP
server does not need to restart.
activate: Prompt the agent to activate the current project and read Serena's instructions
when a Codex session starts or resumes.remind: Nudge the agent to use Serena's symbolic tools when it makes too many consecutive
code-search or code-file-read calls without using Serena tools in between.reset: Clear the reminder counters after a successful Serena symbolic tool call, so using
Serena's tools starts a fresh count instead of leaving the prior grep/read streak in place.cleanup: Clean up hook session data when the session ends.
The PreToolUse matcher is intentionally restricted to Bash: the reminder hook tracks shell-based
grep and code-file reads, so running it for every tool call is unnecessary. That matcher never seesmcp__serena__* tool names, though, so it cannot also perform the counter reset on Serena tool use
the way it does for clients whose PreToolUse hook observes every tool call. The separate reset
hook above, matched to PostToolUse on Serena's own tools, covers that case for Codex instead.
Diagnosing opaque Codex hook failures
Codex may report that a hook failed without identifying the configured command or showing enough
of its output to diagnose the cause. Codex issue #27052
tracks richer hook failure diagnostics. Use Codex's /hooks command first to inspect hook sources,
trust state, and enabled hooks.
While that issue remains open, an advanced local wrapper can add an inner timeout and capture a
bounded, redacted tail of stdout and stderr. Keep wrappers out of the standard setup: they add
another script and executable path, platform-specific timeout behaviour, log rotation and redaction
concerns, and another hook definition hash to review and trust. Such wrappers work around Codex's
diagnostic limitations; Serena does not require them.
Grok
Serena provides native support for xAI's Grok Build CLI. To set up the Serena MCP server for Grok,
simply run:
serena setup grokManual Setup
Global Configuration. To add the Serena MCP server for all your projects, use Grok's user-level configuration and the --project-from-cwd flag:
grok mcp add --scope user serena -- serena start-mcp-server --context=grok --project-from-cwdAlternatively, add the following to ~/.grok/config.toml:
[mcp_servers.serena]
command = "serena"
args = ["start-mcp-server", "--project-from-cwd", "--context=grok"]Project-Level Configuration. To add the Serena MCP server for a single project only:
grok mcp add --scope project serena -- serena start-mcp-server --context=grok --project "$(pwd)"Verification.
Run grok inspect and verify that Serena is listed as an MCP server. You can also use Grok's /mcps
modal to inspect, refresh, enable, or disable configured MCP servers.
Grok can also load existing Claude Code MCP configuration. If you previously ran serena setup claude-code,
Serena may already appear in Grok, but it will use the claude-code context instead of the dedicated grok context.
Hooks
Grok supports lifecycle hooks; see Grok's bundled hooks documentation for details. To enable Serena's hooks
for Grok globally, create ~/.grok/hooks/serena-hooks.json with the following content. For a single project,
create .grok/hooks/serena-hooks.json in that project instead; note that Grok loads project-scoped hooks
only after you have trusted the project for hook execution (via Grok's /hooks-trust command).
{
"hooks": {
"PreToolUse": [
{
"matcher": "grep|read_file|run_terminal_command",
"hooks": [
{
"type": "command",
"command": "serena-hooks remind --client=grok",
"timeout": 5
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "serena-hooks cleanup --client=grok",
"timeout": 5
}
]
}
]
}
}The hooks will:
remind: Nudge the agent to use Serena's symbolic tools when it makes too many consecutive
code-search or code-file-read calls without using Serena tools in between.cleanup: Clean up hook session data when the agent turn ends.
Grok ignores stdout from passive hooks such as SessionStart, so the Grok hook setup intentionally
uses only PreToolUse for reminders and Stop for cleanup.
Claude Desktop
On Windows and macOS, there are official Claude Desktop applications by Anthropic; for Linux, there is an open-source
community version.
To configure MCP server settings, go to File / Settings / Developer / MCP Servers / Edit Config,
which will let you open the JSON file claude_desktop_config.json.
Add the serena MCP server configuration
{
"mcpServers": {
"serena": {
"command": "serena",
"args": [
"start-mcp-server",
"--context=desktop-app"
]
}
}
}If your language server requires specific environment variables to be set (e.g. F# on macOS with Homebrew),
you can add them via an env key (see above).
Verification.
Once you have created the new MCP server entry, save the config and then restart Claude Desktop.
:::{attention}
Be sure to fully quit the Claude Desktop application via File / Exit, as regularly closing the application will just
minimize it.
:::
After restarting, you should see Serena's tools in your chat interface (notice the small hammer icon).
Copilot CLI
In the interactive mode, you can call /mcp add from within the copilot CLI. There, use serena as name,
STDIO as the server type, and serena start-mcp-server --context=copilot-cli --project-from-cwd as command.
Alternatively, add the following to ~/.copilot/mcp-config.json (create the file if it does not exist):
{
"mcpServers": {
"serena": {
"type": "stdio",
"command": "serena",
"tools": [
"*"
],
"args": [
"start-mcp-server",
"--context=copilot-cli",
"--project-from-cwd"
]
}
}
}Verification.
Copilot should now show that Serena is running, though you may have to restart it.
JetBrains Junie
For the Junie plugin in JetBrains IDEs you can add Serena either to the global configuration in ~/.junie/mcp/mcp.json
or to the project configuration in /.junie/mcp/mcp.json. Important, don't add both!
In both cases the entry should be:
{
"mcpServers": {
"serena": {
"command": "serena",
"args": [
"start-mcp-server",
"--context=junie",
"--project-from-cwd"
]
}
}
}With the global configuration, Serena will be available in all projects. However,
within the Junie plugin, projects will not be automatically activated in Serena.
You may thus have to prompt
Junie to "Activate the current project using serena's activation tool" at the start of each session (though some models are
smart enough to activate the project automatically).
With the project-scoped configuration, Serena will be available only in that project, and the project will automatically
be recognized as active by Serena.
JetBrains AI Assistant
Go to Settings / Tools / AI Assistant / MCP and enter the following configuration:
{
"mcpServers": {
"serena": {
"command": "serena",
"args": [
"start-mcp-server",
"--context=jb-ai-assistant",
"--project-from-cwd"
]
}
}
}Like for Junie, you have the choice between the global and the project-scoped configuration,
with the same trade-off.
Antigravity
Add this configuration:
{
"mcpServers": {
"serena": {
"command": "serena",
"args": [
"start-mcp-server",
"--context=antigravity"
]
}
}
}You will have to prompt Antigravity's agent to "Activate the current project using serena's activation tool" after starting Antigravity in the project directory (once in the first chat enough, all other chat sessions will continue using the same Serena session).
Unlike VSCode, Antigravity does not currently support including the working directory in the MCP configuration.
Also, the current client will be shown as none in Serena's dashboard (Antigravity currently does not fully support the MCP specifications). This is not a problem, all tools will work as expected.
CodeBuddy
Serena provides native support for CodeBuddy, a CLI coding agent that shares a similar architecture with Claude Code.
To set up the Serena MCP server for CodeBuddy, simply run:
serena setup codebuddyManual Setup
Global Configuration. To add the Serena MCP server for all your projects, use the user-level configuration of CodeBuddy and the --project-from-cwd flag:
codebuddy mcp add --scope user serena -- serena start-mcp-server --context codebuddy --project-from-cwdProject-Level Configuration. To add the Serena MCP server for a single project only:
codebuddy mcp add serena -- serena start-mcp-server --context codebuddy --project "$(pwd)"Confirm that CodeBuddy is connected to Serena by running the /mcp command and reconnecting if necessary.
Hooks
CodeBuddy supports the same hook system as Claude Code. To set up hooks, add the following to your CodeBuddy settings file (.codebuddy/settings.json in your project directory, or ~/.codebuddy/settings.json globally):
{
"hooks": {
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks remind --client=codebuddy"
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks auto-approve --client=codebuddy"
}
]
}
],
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks activate --client=codebuddy"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "serena-hooks cleanup --client=codebuddy"
}
]
}
]
}
}Hook Descriptions
remind: Remind the agent to use Serena's tools instead of built-ingrepandreadtools.activate: Prompt the agent to activate the project at session start and read Serena's instructions.cleanup: Clean up hook session data when the session ends.auto-approve: Auto-approve Serena tool calls whenever CodeBuddy is in a permissive
permission mode (acceptEditsorauto), so blanket approvals cover Serena's destructive
tools (e.g.replace_symbol_body,rename_symbol) instead of prompting on every call.
ZCode
Serena provides serena-hooks support for ZCode, Z.ai's coding agent harness. There is
currently no serena setup zcode command (unlike Claude Code, CodeBuddy, Codex and Grok, ZCode does not
document a CLI subcommand for registering an MCP server), so the MCP server has to be added manually.
Manual Setup
Add Serena as an MCP server in ZCode's configuration (~/.zcode/cli/config.json for all workspaces, or.zcode/config.json in a project directory for that project only):
{
"mcpServers": {
"serena": {
"enabled": true,
"type": "http",
"url": "http://127.0.0.1:9121/mcp"
}
}
}This assumes Serena is already running in HTTP/SSE mode on the given port; start it with--context=zcode --project-from-cwd for a single-project setup, or --context=zcode if you intend to
activate a project from within the chat.
Hooks
ZCode supports lifecycle hooks with the same event names as Claude Code (SessionStart, PreToolUse,Stop, ...). To set up hooks, add the following to your ZCode hooks configuration
(~/.zcode/cli/config.json globally, or .zcode/config.json for a single project), underhooks.events, with hooks.enabled set to true:
{
"hooks": {
"enabled": true,
"events": {
"SessionStart": [
{ "hooks": [ { "type": "process", "command": "serena-hooks", "args": ["activate", "--client", "zcode"] } ] }
],
"PreToolUse": [
{ "matcher": "Read|Bash", "hooks": [ { "type": "process", "command": "serena-hooks", "args": ["remind", "--client", "zcode"] } ] }
],
"Stop": [
{ "hooks": [ { "type": "process", "command": "serena-hooks", "args": ["cleanup", "--client", "zcode"] } ] }
]
}
}
}Hook Descriptions
activate: Prompt the agent to activate the project at session start and read Serena's instructions.remind: Remind the agent to use Serena's tools instead of built-in read and search tools.cleanup: Clean up hook session data when the session ends.auto-approve: Auto-approve Serena tool calls whenever ZCode is in a permissive permission mode, so
blanket approvals cover Serena's destructive tools (e.g.replace_symbol_body,rename_symbol) instead
of prompting on every call. Wire it toserena-hooks auto-approve --client=zcodethe same way asremindabove; the exact matcher for restricting it to Serena's own tool calls has not been confirmed
against a live ZCode session, so start with an unscoped matcher and narrow it if ZCode's permission
prompts turn out to fire on non-Serena tools too.
Other Clients
For other clients, follow the general instructions above to set up Serena as an MCP server.
Terminal-Based Clients
There are many terminal-based coding assistants that support MCP servers, such as
They generally benefit from the symbolic tools provided by Serena. You might want to customize some aspects of Serena
by writing your own context, modes or prompts to adjust it to the client's respective internal capabilities (and your general workflow).
In most cases, the ide context is likely to be appropriate for such clients, i.e. add the arguments --context ide
in order to reduce tool duplication.
MCP-Enabled IDEs and Coding Clients (Cline, Roo-Code, Cursor, Windsurf, etc.)
Most of the popular existing coding assistants (e.g. IDE extensions) and AI-enabled IDEs themselves support connections
to MCP Servers. Serena generally boosts performance by providing efficient tools for symbolic operations.
We generally recommend using the ide context for these integrations by adding the arguments --context ide
in order to reduce tool duplication.
Local GUIs and Agent Frameworks
Over the last months, several technologies have emerged that allow you to run a local GUI client
and connect it to an MCP server. The respective applications will typically work with Serena out of the box.
Some of the leading open source GUI applications are
These applications allow combining Serena with almost any LLM (including locally running ones)
and offer various other integrations.
02 Usage 040 Workflow (`docs/02-usage/040_workflow.md`)
The Project Workflow
Serena uses a project-based workflow.
A project is simply a directory on your filesystem that contains code and other files
that you want Serena to work with.
Assuming that you have project you want to work with (which may initially be empty),
setting up a project with Serena typically involves the following steps:
- Project creation: Configuring project settings for Serena (and indexing the project, if desired)
- Project activation: Making Serena aware of the project you want to work with
- Onboarding: Getting Serena familiar with the project (creating memories)
- Working on coding tasks: Using Serena to help you with actual coding tasks in the project
(project-creation-indexing)=
Project Creation
Project creation is the process of defining fundamental project settings that are relevant to Serena's operation.
You can create a project either
- explicitly, using the project creation command (see below), or
- implicitly, by just activating a directory as a project while already in a conversation; this will use default settings for your project (skip to the next section).
Explicit Project Creation
To explicitly create a project, use the following command while in the project directory:
serena project create [options] [project directory]- The project directory defaults to the current directory if not specified.
- For an existing project, the programming languages will be detected based on
the source files present, and the main language will be activated automatically.
If multiple languages are detected, you will be prompted whether you want to enable them. - For an empty project, you can optionally specify one or more languages
to be activated explicitly via the--languageparameter
(e.g.--language python --language typescript). - You can optionally specify a custom project name with
--name my-name. - You can immediately index the project after creation with
--index.
(project-config)=
Project Configuration
After creation, you can adjust the project settings in the generated .serena/project.yml file
within the project directory.
The file allows you to configure ...
- the name by which you want to refer to the project (relevant when telling the LLM to dynamically activate the project)
- the set of built-in language servers and explicitly registered external adapters for which servers are spawned
(not relevant when using the JetBrains plugin). External adapter IDs become available after their Python package is installed;
see External Language Server Adapters.
The Dashboard can dynamically add or remove built-in language servers and installed, registered external
adapters while Serena is running. It does not install adapter packages or offer unregistered adapter IDs. - the language backend to use for this project (overriding the global setting)
- the encoding used in source files
- ignore rules
- write access
- the list of workspace folders to be processed by language servers (when using the LSP backend)
- an initial prompt that shall be passed to the LLM whenever the project is activated
- a shell command to run upon project activation (prior to language backend initialisation)
- the set of tools and modes to use for the project
- and some other settings.
For detailed information on the parameters and possible settings, see the
template file.
:::{note}
Many settings in project.yml extend or override settings in Serena's global configuration.
So use the project configuration specifically for aspects that apply only to the particular project.
:::
Local Overrides. The project.yml file is intended to be versioned together with the project.
You can specify local overrides for the settings in a project.local.yml file in the same directory
(which, by default, is ignored by git).
Any keys defined therein will override the respective key in project.yml.
(indexing)=
Indexing
:::{note}
Indexing is not a relevant operation when using the JetBrains plugin, as indexing is handled by the IDE.
:::
Especially for larger project, it can be advisable to index the project after creation, pre-caching
symbol information provided by the language server(s). This will avoid delays during the first tool invocation
that requires symbol information.
While in the project directory, run this command:
serena project indexIndexing has to be called only once. During regular usage, Serena will automatically update the index whenever files change.
(project-activation)=
Project Activation
Project activation makes Serena aware of the project you want to work with.
You can either choose to do this
while in a conversation, by telling the LLM to activate a project, e.g.,
- "Activate the project /path/to/my_project" (for first-time activation with auto-creation)
- "Activate the project my_project"
Note that this option requires the
activate_projecttool to be active,
which it isn't in single-project contexts likeideorclaude-codeif a project is provided at startup.
(The tool is deactivated, because we assume that in these contexts, user will only work on the single, open project and have
no need to switch it.)when the MCP server starts, by passing the project path or name as a command-line argument
(e.g. when using a single-project mode likeideorclaude-code):--project
When working with the JetBrains plugin, be sure to have the same project folder open as a project in your IDE,
i.e. the folder that is activated in Serena should correspond to the root folder of the project in your IDE.
Onboarding & Memories
By default, Serena will perform an onboarding process when
it is started for the first time for a project.
The goal of the onboarding is for Serena to get familiar with the project
and to store memories, which it can then draw upon in future interactions.
In general, memories provide a way for Serena to store and retrieve
information about the project, relevant conventions, and other relevant aspects.
Memories may reference each other using the `mem:NAME` convention; references
are kept in sync across renames, and a serena memories check command is available
to report stale references.
During the first onboarding, Serena seeds a memory_maintenance memory describing the
conventions (style, references) that subsequent memories should follow, and the
agent is instructed to read it before writing any project-specific memories.
For more information on this, including the target memory layout, the mem: reference
convention, the serena memories CLI subcommands, and how to manage or disable these
features, see Memories & Onboarding.
Preparing Your Project
When using Serena to work on your project, it can be helpful to follow a few best practices.
Structure Your Codebase
Serena uses the code structure for finding, reading and editing code. This means that it will
work well with well-structured code but may perform poorly on fully unstructured one (like a "God class"
with enormous, non-modular functions).
Furthermore, for languages that are not statically typed, the use of type annotations (if supported)
are highly beneficial.
Start from a Clean State
It is best to start a code generation task from a clean git state. Not only will
this make it easier for you to inspect the changes, but also the model itself will
have a chance of seeing what it has changed by calling git diff and thereby
correct itself or continue working in a followup conversation if needed.
Use Platform-Native Line Endings
Important: since Serena will write to files using the system-native line endings
and it might want to look at the git diff, it is important to
set git config core.autocrlf to true on Windows.
With git config core.autocrlf set to false on Windows, you may end up with huge diffs
due to line endings only.
It is generally a good idea to globally enable this git setting on Windows:
git config --global core.autocrlf trueLogging, Linting, and Automated Tests
Serena can successfully complete tasks in an agent loop, where it iteratively
acquires information, performs actions, and reflects on the results.
However, Serena cannot use a debugger; it must rely on the results of program executions,
linting results, and test results to assess the correctness of its actions.
Therefore, software that is designed to meaningful interpretable outputs (e.g. log messages)
and that has a good test coverage is much easier to work with for Serena.
We generally recommend to start an editing task from a state where all linting checks and tests pass.
Multiple Projects, Multiple Agents
There are several ways in which you might want to work with multiple projects simultaneously.
A Single Agent Editing Multiple Projects Simultaneously
If fulfilling a task requires a single agent to edit code in multiple projects, the recommended approach is to create a monorepo folder,
i.e. a folder that contains all the projects as sub-folders, and open that monorepo folder as a project in Serena.
You may also use symbolic links to create a monorepo folder if the projects are located in different places on your filesystem.
If several languages are used across the projects, specify all of them as needed when using the LSP backend;
For JetBrains mode, make sure that your IDE is configured to work with all the languages used across the projects (e.g. by installing the respective language plugins).
(query-projects)=
Reading from External Projects
If, while working on a project, you want Serena to be able to read code or other information from another project (e.g. a library or otherwise related project),
this can be enabled via the query_project tool.
Provided that the project you want to query is known to Serena (i.e. you have created it as described above),
the query_project tool allows the agent to query files and symbolic information from that project.
To enable this tool, activate the mode query-projects.
This also enables a second tool for listing projects that can be queried.
Depending on the language backend being used, the management of resources for the external projects varies:
When using the JetBrains backend, make sure that every project for which you want symbolic queries to work is open in an IDE instance.
When using the LSP backend, executing symbolic tools via the query tool requires that Serena's Project Server be started,
which will automatically spawn the necessary language servers for the projects that are queried.To start the server, run
textserena start-project-server
Multiple Agents Accessing a Single Serena Instance
If you want multiple agents to access the same project via a single Serena instance,
i.e. you do not want several instances of Serena (including its language servers) to be running,
you can achieve this by starting the Serena MCP server in HTTP mode
and connecting all client agents to the same HTTP endpoint.
The agents will then share the resources of the single Serena instance.
Multiple Agents Working on Different Projects
For this use case, simply run a separate instance of Serena for each project, which naturally
occurs when Serena is started by the MCP client in stdio mode.
02 Usage 045 Memories (`docs/02-usage/045_memories.md`)
Memories & Onboarding
Serena provides the functionality of a fully featured agent, and a useful aspect of this is Serena's memory system.
Despite its simplicity, we received positive feedback from many users who tend to combine it with their
agent's internal memory management (e.g., AGENTS.md files).
(memories)=
Memories
Memories are simple, human-readable Markdown files that both you and
your agent can create, read, reference, and edit.
Serena differentiates between
- project-specific memories, which are stored in the
.serena/memories/directory within your project folder, and - global memories, which are shared across all projects and, by default, are stored in
~/.serena/memories/global/
The LLM is informed about the existence of memories and instructed to read them when appropriate,
inferring appropriateness from the file name.
When the agent starts working on a project, it receives the list of available memories.
The agent should be instructed to update memories by the user when appropriate.
Design Rationale
Serena's memory system is intentionally minimal. It was designed to satisfy the following
criteria:
- Human-readable and editable. Memories must remain directly readable and editable
in any text editor. The agent is typically the day-to-day consumer, but a human author
or reviewer must always be able to step in without going through the agent. - Versionable with the project. Project memories live alongside the code and can
be committed, reviewed in PRs, and reverted like any other repository artifact. - Progressive disclosure. Agents receive the full memory name list up
front as part of their initial instructions; any further references are described inside
the memory content itself - typically amem:coreentry point pointing at focused
memories. The agent decides what to read based on names plus the references it has
already seen. - Prefer references to search. Given an intelligent agent and well-structured references, search is
unnecessary - and it adds noise: any retrieval method (lexical or semantic)
produces both false positives and false negatives. Explicit, name-based references
decided by the agent are deterministic and avoid both error modes. Basic search via regex/grep
is sufficient to complement the references when needed and is available to any agent. - Prefer deliberate reads to triggers. The agent decides what to read and when. The harness does
not inject memory content on the agent's behalf. - Framework-agnostic. The storage format is plain Markdown files in
a simple directory layout. The only Serena-specific convention is themem:
prefix for references to memories, which does not prevent using the memory files outside
of Serena. - Configurable and composable. Two orthogonal memory scopes -
per-project (committed alongside the code) and global
(shared across all your projects) - can be combined freely. Within either scope,
regex patterns in the global or project configuration can mark subsets as read-only
or hide them entirely from the agent. This lets a project mix
personal cross-project knowledge with checked-in project conventions, and selectively
freeze either set, without custom plumbing.
Taken together, these criteria rule out several common alternatives:
- Database-backed memory (SQLite, graph databases, vector stores) is excluded by
criteria 1, 4, and 6. AGENTS.mdand similar single-file conventions are excluded by criteria 3 and 5.- Hooks and harness-internal memory systems are excluded by criteria 5 and 6.
To our knowledge, no existing system satisfies our design goal, which is
why Serena ships its own memory layer rather than reusing one.
The closest existing approaches are in the family of Markdown-based personal knowledge
management tools - Obsidian, Logseq, Foam.
Organizing Memories
Memories can be organized into topics by using / in the memory name (e.g. modules/frontend).
The structure is mapped to the file system, where topics correspond to subdirectories.
The list_memories tool can filter by topic, allowing the agent to explore even large numbers of memories in a structured way.
(memory-references)=
Referencing Memories from Other Memories
Memories may reference each other. Serena recognizes a reference as a memory name prefixed withmem: and wrapped in backticks, for example `mem:auth/login` or `mem:suggested_commands`.
This convention has two practical consequences:
- Renames keep references intact. When you rename or move a memory with the
rename_memory
tool, Serena rewrites every`mem:OLD_NAME`occurrence across all memories to point to
the new name, except in memories matched byread_only_memory_patterns, which the agent cannot
write;serena memories checkreports such a reference as stale.
References that do not use themem:prefix will not be updated automatically. - Integrity checks (see below) report any
`mem:NAME`whose target does
not resolve to an existing memory, and propose similarly-named candidates as likely intended
targets.
The full convention - including style, add/update thresholds, and how to structure references acrosscore memories - is shipped to every onboarded project as the memory_maintenance memory; see the
Onboarding section below.
(global-memories)=
Global Memories
Global memories use the top-level topic global, i.e. whenever a memory name starts with global/,
it is stored in the global memories directory and is shared across all projects.
By default, deletion and editing of global memories is allowed.
If you want to protect them from accidental modification by the agent,
you can add regex patterns to read_only_memory_patterns in your global or
project-level configuration. For example, setting "global/.*" will mark all global memories as read-only. The agent will be informed which memories are read-only.
Since global memories are not versioned alongside your project files,
it can be helpful to track global memories with git (i.e. to make ~/.serena/memories/ a git repository)
in order to have a history of changes and the possibility to revert them if needed.
(ignoring-memories)=
Ignoring Memories
Projects that accumulate large numbers of archived memory files can use ignored_memory_patterns
to exclude them from list_memories and activate_project output. Add regex patterns to the
global or project-level configuration:
ignored_memory_patterns: ["_archive/.*", "_episodes/.*"]Ignored memories are completely excluded - they cannot be accessed via read_memory, write_memory,
or any other memory tool. To read an ignored memory file, use the read_file tool on the raw file path
(e.g., .serena/memories/_archive/2026-03/some-topic.md).
Like read_only_memory_patterns, patterns from the global and project-level configurations are merged additively.
Manually Editing Memories
You may edit memories directly in the file system, using your preferred text editor or IDE.
Alternatively, access them via the Serena Dashboard, which provides a graphical interface for
viewing, creating, editing, and deleting memories while Serena is running.
(onboarding)=
Onboarding
By default, Serena performs an onboarding process when it encounters a project
for the first time (i.e., when no project memories exist yet).
The goal of the onboarding is for Serena to get familiar with the project -
its structure, build system, testing setup, and other essential aspects -
and to store this knowledge as memories for future interactions.
In further project activations, Serena will check whether onboarding was already
performed by looking for existing project memories and will skip the onboarding
process if memories are found.
How Onboarding Works
- When a project is activated, Serena checks whether onboarding was already
performed (by checking if any memories exist). - If no memories are found, Serena triggers the onboarding process, which
reads key files and directories to understand the project. - Before any project memory is written, Serena materializes a project-local
memory_maintenancememory (see below). The agent is then instructed to read it
first and follow the conventions it describes. - The gathered information is written into project-specific memory files following
the onboarding prompt instructions and the conventions outlined inmemory_maintenance.
(memory-maintenance-memory)=
The `memory_maintenance` Memory
To make memory conventions discoverable to both the LLM and the user, Serena seeds
a memory_maintenance memory on first onboarding. The seed is copied from a template
shipped with the Serena package and contains the dense agent-notes style, themem: reference convention, the reference model around core memories, the
add/update threshold, and the maintenance actions (rename / delete / split).
The seeding follows a strict precedence:
- If you already maintain a
global/memory_maintenancememory, Serena uses that
and does not create a project-local copy. This is the recommended approach
for teams that want one shared convention document across all projects. - Otherwise, if the project already has a
memory_maintenancememory, it is left
untouched. - Otherwise, the shipped template is written to
.serena/memories/memory_maintenance.md.
Existing files are never overwritten - you can freely customize the project copy.
To refresh from the shipped template, delete the existing memory first.
Tips for Onboarding
- Context usage: The onboarding process will read a lot of content from the project,
filling up the context window. It is therefore advisable to switch to a new conversation
once the onboarding is complete. - LLM failures: If an LLM fails to complete the onboarding and does not actually
write the respective memories to disk, you may need to ask it to do so explicitly. - Review the results: After onboarding, we recommend having a quick look at the
generated memories and editing them or adding new ones as needed.
(memory-cli)=
CLI Subcommands
While the recommended way to manage memories is through the MCP integration,
Serena also offers memory-related CLI commands.
The following commands have no MCP tool counterpart and are intended for human execution:
serena memories check— referential-integrity report. By default reports stale`mem:NAME`references; additional scans (bare occurrences and fuzzy near-misses)
are opt-in via flags. Runserena memories check --helpfor the full flag list.serena memories auto-prefix-references— heuristic rewrite of bare occurrences to add
themem:prefix; supports--dry-run.serena memories initializewill seed thememory_maintenancememory for the project.
The remaining commands mirror the MCP tools, you can thus instruct your agent to manage memories with
serena without having a running MCP server. Discover the full surface and per-command flags via:
serena memories --help
serena memories <subcommand> --helpDisabling Memories and Onboarding
If you do not require the functionality described in this section, you can selectively disable it.
- To disable all memory related tools (including onboarding), adding
no-memoriesto thebase_modes
in Serena's global configuration. - Similarly, to disable only onboarding, add
no-onboardingto thebase_modes.
02 Usage 050 Configuration (`docs/02-usage/050_configuration.md`)
Configuration
Serena is very flexible in terms of configuration. While for most users, the default configurations will work,
you can fully adjust it to your needs.
You can disable tools, change Serena's fundamental instructions
(what we denote as the system_prompt), adjust the output of tools that just provide a prompt,
and even adjust tool descriptions.
Serena is configured using a multi-layered approach:
- global configuration (
serena_config.yml, see below) - project configuration (
project.yml, see Project Configuration) - contexts and modes for composable configuration, which can be enabled on a case-by-case basis (see below)
- command-line parameters passed to the
start-mcp-serverserver command (overriding/extending configured settings)
See MCP Server Command-Line Arguments for further information.
(global-config)=
Global Configuration
The global configuration file allows you to change general settings and defaults that will apply to all projects unless overridden.
Accessing the Configuration File
The configuration file is auto-created when you first run Serena. It is stored in your user directory:
- Linux/macOS/Git-Bash:
~/.serena/serena_config.yml - Windows (CMD/PowerShell):
%USERPROFILE%\.serena\serena_config.yml
You can access it
through Serena's dashboard while Serena is running (use the respective button)
directly, using your favourite text editor
using the command
shellserena config edit
Settings
Some of the configurable settings include:
- the agent interface to use (tools or REPL, see below)
- the language backend to use by default (i.e., the JetBrains plugin or language servers);
this can also be overridden per project - UI settings affecting the Serena Dashboard and GUI tool
- the set of tools or REPL API functions to enable/disable by default
- the set of modes to use by default
- tool execution parameters (timeout, max. answer length)
- global ignore rules for project files and folders
- logging settings
- the set of trusted project paths
- advanced settings specific to individual language servers (see below)
- priorities of language servers, which affect auto-detection
The global configuration settings apply to all projects.
Some of the settings it contains can, however, be extended or overridden in project-specific settings, contexts and modes.
For detailed information on the parameters and possible settings, see the
template file.
(agent-interfaces)=
Agent Interfaces
Serena provides its functionality to the agent (LLM) through one of two interfaces
(see Tools and APIs for the operations they offer):
- tools: every operation is a separate tool of the MCP server.
- REPL (new in Serena v2): a single tool executes Python code, through which the agent accesses the operations
programmatically, being able to combine several of them in one call.
This interface has several advantages
over the classical tool interface.
The interface is selected via the agent_interface setting in the global configuration.
It can be overridden in the project configuration or via the --agent-interface command-line option,
and it is fixed for the duration of a session.
The two interfaces are configured differently:
- With the tool interface, the set of tools results from the tool inclusion/exclusion settings
(excluded_tools,included_optional_tools,fixed_tools) of the global configuration, the context,
the modes and the project configuration. - With the REPL interface, the set of tools is fixed (the REPL tool and the tools which have no
counterpart within the REPL, e.g. for project activation); the tool settings above consequently do not apply.
The operations available within the REPL are configured viaincluded_apis/excluded_apisinstead,
which are supported in the same configuration layers and reference either a group of operations
(e.g.lsp) or an individual operation (e.g.lsp.find_symbol).
Restricting the operations available in the REPL is a means of steering the agent, not a security mechanism:
the Python code that is executed can, in principle, do anything the Serena process can do.
See [Security](070_security) for isolation options.(trusted-projects-config)=
Trusted Projects
The setting trusted_project_path_patterns configures the set of project root paths that are considered trusted,
enabling additional features.
Limitations applied to untrusted projects:
- Some project settings are ignored for untrusted projects:
activation_commandis a shell command that is to be run whenever the project is activated.ls_specific_settingsdefines project-specific language server settings, which can change, in particular,
how language servers are started for the project.
- Following of symbolic links in project directories is restricted in untrusted projects:
- Symbolic links are not followed when walking the project directory tree.
- Explicitly requested targets that resolve to locations outside the project root are rejected.
- Project-local language server installations (e.g. a TypeScript LS installation in
node_modules) will only be used
for trusted projects, while untrusted projects will use Serena's managed installation.
The term "trusted" implies only that one trusts the project enough to enable the additional features listed above.
An "untrusted" project is not sandboxed, strongly restricted or otherwise contained.
See our [security documentation](trusted-projects) for details.The default set of trusted paths depends on the age of your configuration: Installations predating
the introduction of this setting retain a pattern that trusts all projects, ensuring that existing workflows are
not broken, whereas newly created configurations trust no project by default.
The applicable value can be inspected in the dashboard.
Modes and Contexts
Serena's behaviour and toolset can be adjusted using contexts and modes.
These allow for a high degree of customization to best suit your workflow and the environment Serena is operating in.
(contexts)=
Contexts
A context defines the general environment in which Serena is operating.
It influences the initial system prompt and the set of available tools.
A context is set at startup when launching Serena (e.g., via CLI options for an MCP server or in the agent script) and cannot be changed during an active session.
Serena comes with pre-defined contexts:
desktop-app: Tailored for use with desktop applications like Claude Desktop. This is the default.
The full set of Serena's tools is provided, as the application is assumed to have no prior coding-specific capabilities.claude-code: Optimized for use with Claude Code, it disables tools that would duplicate Claude Code's built-in capabilities.codex: Optimized for use with OpenAI Codex.grok: Optimized for use with xAI's Grok Build CLI.ide: Generic context for IDE assistants/coding agents, e.g. VSCode, Cursor, or Cline, focusing on augmenting existing capabilities.
Basic file operations and shell execution are assumed to be handled by the assistant's own capabilities.agent: Designed for scenarios where Serena acts as a more autonomous agent, for example, when used with Agno.
Choose the context that best matches the type of integration you are using.
Find the concrete definitions of the above contexts here.
Note that the contexts ide, claude-code, and grok are single-project contexts (defining single_project: true).
For such contexts, if a project is provided at startup, the set of tools is limited to those required by the project's
concrete configuration, and other tools are excluded completely, allowing the set of tools to be minimal.
Tools explicitly disabled by the project will not be available at all. Since changing the active project
ceases to be a relevant operation in this case, the project activation tool is disabled.
When launching Serena, specify the context using --context <context-name>.
Note that for cases where parameter lists are specified (e.g. Claude Desktop), you must add two parameters to the list.
If you are using a local server (such as Llama.cpp) which requires you to use OpenAI-compatible tool descriptions, use context oaicompat-agent instead of agent.
You can manage contexts using the context command,
serena context --help
serena context list
serena context create <context-name>
serena context edit <context-name>
serena context delete <context-name>(modes)=
Modes
Modes further refine Serena's behavior for specific types of tasks or interaction styles. Multiple modes can be active simultaneously, allowing you to combine their effects. Modes influence the system prompt and can also alter the set of available tools by excluding certain ones.
Examples of built-in modes include:
planning: Focuses Serena on planning and analysis tasks.editing: Optimizes Serena for direct code modification tasks.interactive: Suitable for a conversational, back-and-forth interaction style.one-shot: Configures Serena for tasks that should be completed in a single response, often used withplanningfor generating reports or initial plans.no-onboarding: Skips the initial onboarding process if it's not needed for a particular session but retains the memory tools (assuming initial memories were created externally).onboarding: Focuses on the project onboarding process.no-memories: Disables all memory tools (and tools building on memories such as onboarding tools)query-projects: Enables tools for querying other Serena projects (without activating them); see section Reading from External Projects
Find the concrete definitions of these modes here.
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
- defines
- 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
- defines
- at startup via command-line parameters
- can override default modes with
--mode - can define modes to be added on top with
--add-mode
- can override default modes with
Ultimately, the active modes are given by the union of
base_modesdefined 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)
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_modesto 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).
Serena currently does not prevent incompatible combinations; it is up to the user to choose sensible mode configurations.
:::
You can manage modes using the mode command,
serena mode --help
serena mode list
serena mode create <mode-name>
serena mode edit <mode-name>
serena mode delete <mode-name>(prompt-templates)=
Prompt Templates
All prompts that Serena provides to the LLM are Jinja2 templates.
Templating applies to
- Serena's system prompt (the "Serena Instructions Manual"), which is defined in the prompt template
system_prompt
(see Custom Prompts for how to override it), - context and mode prompts, i.e. the
promptfield in context and mode definition files, and - the project prompt, i.e.
initial_promptinproject.yml, which is provided to the LLM upon project activation.
Templating allows prompts to adapt to the active configuration; for instance, a mode prompt can mention a tool
only if that tool is actually available in the current session.
Variables and Functions
The following variables can be used in all of the above templates:
| Variable | Description |
|---|---|
available_tools |
the list of names of the tools that are currently exposed to the LLM. Use it to include content conditionally, e.g. {% if 'replace_content' in available_tools %}…{% endif %}. |
available_markers |
the list of names of the tool markers (tool categories, e.g. ToolMarkerSymbolicRead) for which at least one tool is exposed; useful for conditioning on entire groups of tools. |
tool_names |
a mapping from canonical tool names to effective tool names, which accounts for legacy tool renames as well as for tools being functionally replaced due to the active language backend (e.g. find_symbol being replaced by jet_brains_find_symbol when the JetBrains backend is active). Prefer {{ tool_names['find_symbol'] }} over hard-coded names. |
Context, mode and project prompts (but not Serena's system prompt) additionally support the following function:
| Function | Description |
|---|---|
embed_memory(name) |
embeds the content of the memory with the given name, wrapped in a tag <memory name="...">. Use this to inline knowledge that shall always be provided to the LLM rather than being loaded on demand. If the memory cannot be loaded, an error is logged and nothing is rendered. |
For example, a project can inline its coding conventions from a memory into the project prompt:
initial_prompt: |
{{ embed_memory("coding_conventions") }}Advanced Configuration
For advanced users, Serena's configuration can be further customized.
Serena Data Directory
The Serena user data directory (where configuration, language server files, logs, etc. are stored) defaults to ~/.serena.
You can change this location by setting the SERENA_HOME environment variable to your desired path.
Per-Project Serena Folder Location
By default, each project stores its Serena data (memories, caches, etc.) in a .serena folder inside the project root.
You can customize this location globally via the project_serena_folder_location setting in serena_config.yml.
The setting supports two placeholders:
| Placeholder | Description |
|---|---|
$projectDir |
The absolute path to the project root directory |
$projectFolderName |
The name of the project folder |
Examples:
# Default: data stored inside the project directory
project_serena_folder_location: "$projectDir/.serena"
# Central location: all project data under a shared directory
project_serena_folder_location: "/projects-metadata/$projectFolderName/.serena"When a project is loaded, Serena uses the following fallback logic:
- Check if a
.serenafolder exists at the configured path. - If not, check if one exists in the project root (default/legacy location).
- If neither exists, create the folder at the configured path.
This ensures backward compatibility: existing projects that already have a .serena folder in the project root will continue to work, even after changing the project_serena_folder_location setting.
(ls-specific-settings)=
Language Server-Specific Settings
:::{note}
Advanced Users Only: The settings described in this section are intended for advanced users who need to fine-tune language server behavior.
Most users will not need to adjust these settings.
:::
Under the key ls_specific_settings in serena_config.yml, you can pass global per-language,
language server-specific configuration.
You can use the same key in the project configuration files (project.yml
and project.local.yml ) to override or extend the global settings for a specific project.
The settings are merged on top-level, meaning that project-level settings for a language will replace global settings for the same language.
Note: Project-level settings are considered only for trusted projects (which are defined in the global configuration).
Structure:
ls_specific_settings:
<language>:
# language-server-specific keys(override-ls-path)=
Customizing the Language Server Launch Command
Most of Serena's language servers construct the command that launches the language server process from
a base command or a core dependency.
For these language servers, the following settings can be used to customize the launch command:
| Setting | Description |
|---|---|
ls_path (string) or ls_base_cmd (list) |
overrides the path of the language server's core dependency (ls_path), e.g. its executable or a JAR file, or a base command for its execution (ls_base_cmd), e.g. ["npx", "-y", "/my/local/package"]. Use this if you have installed the language server yourself and want Serena to use your installation instead of its managed installation. |
ls_args (list) |
overrides the internal command construction completely and simply adds ls_args to the base command |
ls_extra_args (list) |
a list of additional arguments to append to the launch command |
- If you set
ls_args, the internal command construction (which may do more than to append arguments to a base command) is bypassed.
You can define the full launch command by providing bothls_path/ls_base_cmdandls_args. - If
ls_argsis not set, the internal command construction (which sets default arguments) is applied, and you can usels_pathorls_base_cmdto override the path of the core dependency/the base command. ls_extra_argsis always appended to the end of the launch command.
Example:
ls_specific_settings:
<language>:
ls_path: "/path/to/language-server"
ls_extra_args: ["--log-level=debug"]These settings are supported by all language servers whose dependency provider derives fromLanguageServerDependencyProviderBaseCommand, and ls_path is additionally exposed by some implementations explicitly.
Common examples include: ansible, bash, bsl, clojure, cpp, cpp_ccls, hlsl, html, kotlin, lean4, luau, markdown, php,nix, php_phpactor, python, rust, scss, solidity, systemverilog, toml, typescript, and yaml.
If ls_path is set, Serena's managed download or install is bypassed for that language server.
In that case, any server-specific version or registry settings do not apply.
(override-init-options)=
Overriding Language Server Initialization Options
When Serena starts a language server, it sends a set of initializationOptions as part of the
Language Server Protocol initialize request. These options are constructed internally and are
tailored to each language server. In some cases, you may want to override or extend these options,
e.g. to enable a feature or to adjust a behavior that is specific to your setup.
Under the key initializationOptions within a language's ls_specific_settings, you can provide a
dictionary of options that is applied on top of the internally constructed initializationOptions.
The values are combined at the top level only: for each top-level key you define, your value
replaces the original value for that key exactly as given (there is no recursive/deep merge of
nested dictionaries). Internally constructed keys that you do not define are left unchanged.
- If Serena constructs
initializationOptionsfor the language server, each top-level key you
provide replaces the internally constructed value for that same key, while all other internally
constructed keys are retained. - If Serena does not construct any
initializationOptionsfor the language server, your custom
options are used as-is.
Example:
ls_specific_settings:
<language>:
initializationOptions:
someFeature:
enabled: trueAL
Serena uses the AL language server bundled in the Microsoft Dynamics 365 Business Central VS Code extension.
Supported settings:
| Setting | Default | Description |
|---|---|---|
al_extension_version |
18.0.2242655 |
Override the AL VS Code extension version Serena downloads from the VS Code Marketplace. |
Angular
Serena uses @angular/language-server (ngserver) for the angular language key, orchestrated together with a
companion typescript-language-server (with @angular/language-service loaded as a tsserver plugin) and a
companion vscode-html-language-server for .html documentSymbol. This is an experimental language and
must be explicitly listed in project.yml; it is not auto-detected.
Project requirements:
- The project itself must have
@angular/coreinstalled (i.e.npm installmust have been run in the project root,
or in a workspace root above it for monorepo layouts). Without it,ngserverreports every file as "not in an
Angular project" and template-aware features silently return empty. - A
tsconfig.jsonmust be reachable at or above any opened.tsfile. - Do not also list
typescriptorhtmlinlanguageswhenangularis active — Angular subsumes both
for.ts/.htmlfiles. SCSS is not subsumed; listscssseparately if needed.
Supported settings:
| Setting | Default | Description |
|---|---|---|
angular_language_server_version |
21.2.10 |
Override the bundled @angular/language-server npm package version Serena installs. |
angular_language_service_version |
21.2.10 |
Override the bundled @angular/language-service tsserver plugin version. |
typescript_version |
5.9.3 |
Override the bundled typescript npm package version. Falls back to ls_specific_settings.typescript.typescript_version if unset. |
typescript_language_server_version |
5.1.3 |
Override the bundled typescript-language-server version. Falls back to ls_specific_settings.typescript.typescript_language_server_version if unset. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. Falls back to ls_specific_settings.typescript.npm_registry if unset. |
Notes:
- The HTML companion (
vscode-html-language-server) is configured vials_specific_settings.html— see the HTML section below. ls_pathis not supported (see note above the AL section).
Ansible
Serena uses @ansible/ansible-language-server for the ansible language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the ansible-language-server executable path. |
ansible_language_server_version |
1.2.3 |
Override the npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
ansible_path |
"ansible" |
Path to the ansible executable forwarded to the language server. |
ansible_settings |
null |
Full Ansible LS settings dict, deep-merged on top of Serena's defaults. |
lint_enabled |
false |
Enable ansible-lint integration. |
lint_path |
"ansible-lint" |
Path to the ansible-lint executable. |
python_interpreter_path |
"python3" |
Python interpreter path forwarded to the language server. |
python_activation_script |
"" |
Virtualenv activation script forwarded to the language server. |
Bash
Serena uses bash-language-server for Bash support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the bash-language-server executable path. |
bash_language_server_version |
5.6.0 |
Override the npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
BSL (1C:Enterprise / OneScript)
Serena uses bsl-language-server by 1c-syntax
for BSL support. The JAR is downloaded automatically on first use and SHA-256-verified for the
bundled default version. Requires Java 21+ on PATH — bsl-language-server v0.29.0 is built
with targetCompatibility = JavaVersion.VERSION_21 and fails to launch under older JDKs.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the path to an existing bsl-language-server-*-exec.jar. When set, Serena does not download anything; the JAR is launched directly via java -jar. |
bsl_ls_version |
0.29.0 |
Override the bsl-language-server release version Serena downloads when ls_path is not set. SHA-256 verification is performed only for the default version; user-overridden versions install without SHA verification. |
Example:
ls_specific_settings:
bsl:
bsl_ls_version: "0.29.0"
# ls_path: "/opt/bsl/bsl-language-server-0.29.0-exec.jar" # optionalClojure
Serena uses clojure-lsp for Clojure support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the clojure-lsp executable path. |
clojure_lsp_version |
2026.02.20-16.08.58 |
Override the clojure-lsp release version Serena downloads when ls_path is not set. |
source_paths |
scanned from project descriptors (or unset if a project-local .lsp/config.edn is found) |
Explicit list of repo-root-relative source paths to inject into clojure-lsp's initializationOptions. Use this when the auto-discovery picks up too few or too many paths. |
config_edn_path |
unset | Path to a config.edn file whose :source-paths entry should be parsed and injected. Useful when the project's clojure-lsp config lives outside the standard .lsp/config.edn location. |
Why this exists: clojure-lsp discovers source paths only from the project descriptor at the workspace root (root deps.edn / project.clj / shadow-cljs.edn / bb.edn) and does not recurse for sub-module descriptors. In multi-module monorepos (e.g. common/ + frontend/ + backend/ layouts), this means references in sibling modules are silently missed by find_referencing_symbols until a tool call happens to open one of their files. Serena works around this by walking the repo for project descriptors at startup and passing the union of their declared source paths to clojure-lsp via initializationOptions["source-paths"].
Resolution order (first match wins):
source_pathssetting — explicit override.config_edn_pathsetting — Serena parses:source-pathsfrom the supplied file.<repo>/.lsp/config.ednexists — Serena injects nothing; clojure-lsp reads the file natively, so hand-tuned project configs are never clobbered.- Walk the repo for project descriptors and synthesise a source-paths list from their declared
:paths/:extra-paths/:source-paths(skipping.git,.clj-kondo,.lsp,.cpcache,node_modules,target,out,dist).
Example — a monorepo without a .lsp/config.edn, where you want to override what Serena scanned:
ls_specific_settings:
clojure:
source_paths:
- "common/src"
- "common/test"
- "frontend/src"
- "backend/src"C/C++ (`clangd`)
Serena uses clangd for the cpp language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the clangd executable path. |
compile_commands_dir |
.serena |
Directory where Serena writes a transformed compile_commands.json if the project's original database uses relative directory entries. |
clangd_version |
19.1.2 |
Override the clangd version Serena downloads when ls_path is not set. |
C/C++ via `ccls`
Serena uses the cpp_ccls language key for ccls.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
ccls from PATH |
Override the ccls executable path. Serena does not manage ccls downloads or installs. |
C# (Roslyn Language Server)
Serena uses Microsoft's Roslyn Language Server for C# support.
Runtime Requirements:
- .NET 10 or higher is required. If not found in PATH, Serena automatically installs it using Microsoft's official install scripts.
- The Roslyn Language Server is automatically downloaded from NuGet.org.
Supported Platforms:
Automatic download is supported for: Windows (x64, ARM64), macOS (x64, ARM64), Linux (x64, ARM64).
Configuration:
The runtime_dependencies setting allows you to override the download URLs for the Roslyn Language Server. This is useful if you need to use a private package mirror or a specific version.
For the common case of changing only the package version, use csharp_language_server_version.
Example configuration to override the language server download URL:
ls_specific_settings:
csharp:
csharp_language_server_version: "5.5.0-2.26078.4"
runtime_dependencies:
- id: "CSharpLanguageServer"
platform_id: "linux-x64" # or win-x64, win-arm64, osx-x64, osx-arm64, linux-arm64
url: "https://your-mirror.example.com/roslyn-language-server.linux-x64.5.5.0-2.26078.4.nupkg"
package_version: "5.5.0-2.26078.4"Available fields for runtime_dependencies entries:
| Field | Description |
|---|---|
id |
Dependency identifier (use CSharpLanguageServer) |
platform_id |
Target platform: win-x64, win-arm64, osx-x64, osx-arm64, linux-x64, linux-arm64 |
url |
Download URL for the NuGet package |
package_version |
Package version string |
extract_path |
Path within the package to extract (default: tools/net10.0/) |
Notes:
- Only specify the platforms you want to override; others will use the defaults.
- The language server package is a
.nupkgfile (ZIP format) downloaded from NuGet.org by default. - If you have .NET 10+ already installed, Serena will use your system installation.
C# (`OmniSharp`)
Serena uses the csharp_omnisharp language key for OmniSharp.
Supported settings:
| Setting | Default | Description |
|---|---|---|
omnisharp_version |
1.39.10 |
Override the OmniSharp version Serena downloads. |
razor_omnisharp_version |
7.0.0-preview.23363.1 |
Override the Razor OmniSharp plugin version Serena downloads. |
Dart
Serena uses the Dart SDK's built-in language server for Dart support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
dart_sdk_version |
3.7.1 |
Override the Dart SDK version Serena downloads. |
Elixir
Serena uses Expert for Elixir support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
expert_version |
v0.1.10 |
Override the Expert version Serena downloads when it does not use an expert executable already found in PATH. |
Erlang
Serena uses the Erlang Language Platform (ELP) for Erlang support. Serena downloads the pinned official release asset for the current platform.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the ELP executable path. |
Elm
Serena uses @elm-tooling/elm-language-server for Elm support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
elm_language_server_version |
2.8.0 |
Override the npm package version Serena installs when no system elm-language-server is found. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
F#
Serena uses FsAutoComplete (Ionide LSP) for F# support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
fsautocomplete_version |
0.83.0 |
Override the FsAutoComplete version Serena installs as a .NET tool. |
GDScript (Godot Engine)
Serena connects to the Godot editor's built-in LSP server over TCP. No separate process is launched.
Supported settings:
| Setting | Default | Description |
|---|---|---|
port |
6008 |
TCP port the running Godot editor listens on for LSP connections. |
request_timeout |
30.0 |
Seconds to wait for a response from the Godot LSP server. |
Example:
ls_specific_settings:
gdscript:
port: 6008
request_timeout: 60.0Go (`gopls`)
Serena forwards ls_specific_settings.go.gopls_settings to gopls as LSP initializationOptions when the Go language server is started.
Example: enable build tags and set a build environment:
ls_specific_settings:
go:
gopls_settings:
buildFlags:
- "-tags=foo"
env:
GOOS: "linux"
GOARCH: "amd64"
CGO_ENABLED: "0"Notes:
- To enable multiple tags, use
"-tags=foo,bar". gopls_settings.envvalues are strings.GOFLAGS(from the environment you start Serena in) may also affect the Go build context. PreferbuildFlagsfor tags.- Build context changes are only picked up when
goplsstarts. After changinggopls_settings(or relevant env vars likeGOFLAGS), restart the Serena process (or server) that hosts the Go language server, or use your client's "Restart language server" action if it causesgoplsto restart.
Groovy
Serena uses a user-provided Groovy Language Server JAR for Groovy support. If ls_java_home_path is not set, Serena downloads
a bundled Java runtime for launching that JAR.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_jar_path |
required | Path to the Groovy Language Server JAR |
ls_java_home_path |
null |
Path to a Java installation to use instead of Serena's managed runtime |
ls_jar_options |
"" |
Additional options passed when launching the Groovy LS JAR |
vscode_java_version |
1.42.0-561 |
Override the bundled Java runtime bundle version Serena downloads by default |
Note:
- When overriding
vscode_java_version, Serena still assumes that the downloaded runtime bundle keeps the same internal
directory layout and file names as the bundled default version.
Example:
ls_specific_settings:
groovy:
ls_jar_path: "/path/to/groovy-language-server-all.jar"
vscode_java_version: "1.42.0-561"HLSL
Serena uses shader-language-server for the hlsl language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install or build | Override the shader-language-server executable path. |
version |
1.3.1 |
Override the bundled version Serena downloads, or builds from source on macOS, when ls_path is not set. |
Haxe
Serena uses the vshaxe/haxe-language-server for Haxe support.
Requires Haxe compiler (3.4.0+) and Node.js.
The server is discovered in order: user-configured ls_path, system PATH, vshaxe VSCode extension, auto-download from Open VSX.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
auto-discovered | Path to the Haxe language server binary (e.g., /path/to/server.js). |
version |
2.34.2 |
Override the vshaxe extension version downloaded from Open VSX. SHA256 verification is only performed for the default version. |
buildFile |
auto-discovered .hxml |
Relative path to the .hxml build file used for compilation (e.g., build/debug.hxml). If not set, Serena searches the project for .hxml files (max depth 5, skipping dependency directories). |
haxePath |
haxe from PATH |
Path to the Haxe compiler executable. The LS delegates to this for code analysis. Useful when multiple Haxe versions are installed or when haxe is not on the PATH. |
renameSourceFolders |
not set (LS default) | List of source directories for scoping rename operations (e.g., ["src", "lib"]). If not set, the Haxe LS uses its own defaults. |
Example (typically in project.yml, since these are project-specific):
ls_specific_settings:
haxe:
buildFile: "build/debug.hxml"
haxePath: "/usr/local/bin/haxe"
renameSourceFolders: ["src", "lib"]HTML
Serena uses vscode-html-language-server from Microsoft's vscode-langservers-extracted npm package for thehtml language key. Experimental — must be explicitly listed in project.yml; not auto-detected. The HTML
LSP returns in-file element / id symbols via documentSymbol; cross-file definition / references are not
meaningful for HTML and are not exposed.
This same language server is also used as a tertiary companion by the Angular language server (see the Angular
section), since ngserver does not implement textDocument/documentSymbol for .html files.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the vscode-html-language-server executable path. |
vscode_langservers_package |
vscode-langservers-extracted |
npm package providing the binary. Set to @t1ckbase/vscode-langservers-extracted (or any other source) to use the actively-maintained 2026 fork. |
vscode_langservers_version |
4.10.0 |
Override the npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
Java (`eclipse.jdt.ls`)
Java support has two installation modes:
- Default vscode-java VSIX mode (no extra config required): Serena downloads the platform-specific
vscode-java VSIX (JDTLS + bundled JRE 21 + Lombok) and a Gradle distribution from public hosts on first use. - Upstream JDTLS mode (offline-friendly): Activated by setting both
jdtls_pathandlombok_path.
Uses an existing JDTLS installation (~100 MB) and the system JDK 21+. Nothing is downloaded.
Recommended for restricted-network/corporate environments.
When to use which mode:
- Default vscode-java VSIX mode — recommended for most users. No setup required;
Serena downloads everything on first use. - Upstream JDTLS mode — recommended when:
- you cannot reach
github.comorservices.gradle.orgfrom the host (corporate proxy, air-gapped network); - you want a smaller on-disk footprint;
- you already maintain a JDTLS installation (e.g. for
nvim-jdtlsor another editor); - your security policy prohibits per-project runtime downloads.
- you cannot reach
JDK 21+ is required in upstream mode. Serena resolves the JDK in this order:ls_specific_settings.java.java_home → JAVA_HOME env var → first java on PATH.
The resolved JVM is interrogated and rejected if its java.specification.version is below 21.
The following settings are supported for the Java language server:
| Setting | Default | Description |
|---|---|---|
jdtls_path |
null |
Activates upstream JDTLS mode. Path to upstream JDTLS root (containing plugins/ and config_/). Get via brew install jdtls or extract jdt-language-server-*.tar.gz from https://download.eclipse.org/jdtls/snapshots/. Must be set together with lombok_path. |
lombok_path |
null |
Path to the Lombok jar. Activates upstream JDTLS mode together with jdtls_path. Get from ~/.m2/repository/org/projectlombok/lombok/<ver>/lombok-<ver>.jar or download from https://projectlombok.org/downloads/. |
java_home |
null |
(upstream-jdtls mode only) Path to JDK 21+ home directory used to launch JDTLS. Falls back to JAVA_HOME env var, then which java. |
maven_user_settings |
~/.m2/settings.xml |
Path to Maven settings.xml |
gradle_user_home |
~/.gradle |
Path to Gradle user home directory |
gradle_wrapper_enabled |
false |
Use the project's Gradle wrapper (gradlew) instead of the bundled Gradle distribution. Enable this for projects with custom plugins or repositories. |
maven_import_enabled |
true |
Whether JDTLS imports Maven projects. Disable on Gradle-only repositories to skip pom.xml detection work. |
gradle_import_enabled |
true |
Whether JDTLS imports Gradle projects. Disable on Maven-only repositories to skip Gradle detection work. |
update_build_configuration |
interactive |
What JDTLS does after pom.xml / build.gradle changes: disabled, interactive, or automatic. Prefer automatic for headless agents that cannot answer a prompt. |
autobuild_enabled |
true |
Whether JDTLS rebuilds after edits. Disable on very large repositories to reduce background CPU. |
gradle_java_home |
null |
Path to the JDK used by Gradle. When unset, Gradle uses JAVA_HOME if use_system_java_home is enabled and JAVA_HOME is set; otherwise it falls back to Serena's bundled JRE. |
use_system_java_home |
false |
Use the system's JAVA_HOME environment variable for JDTLS itself and, when gradle_java_home is unset, Gradle import. Enable this if your project requires a specific JDK vendor or version for Gradle's JDK checks. |
custom_jre_path |
null |
Path to an executable JDK 21+ Java launcher used only for JDTLS instead of Serena's bundled JRE. Serena queries the JVM for its real java.home, so symlinked launchers are supported. This does not replace gradle_java_home or entries in runtimes; configure those independently when needed. |
runtimes |
[] |
Extra JRE/JDK entries registered with JDT-LS via java.configuration.runtimes. Use this when a project's source/target level exceeds the JDK JDT-LS itself runs on (currently JDK 21 in default vscode-java VSIX mode). Each entry is a mapping with required name (e.g. JavaSE-25, matching the JavaSE-NN container the build tool requests) and path (JDK/JRE home directory; must exist), plus optional default, sources, and javadoc (passed through to JDT-LS). Entries extend rather than replace the bundled JavaSE-21 runtime; an entry that reuses the JavaSE-21 name overrides the bundled one. Changing this setting invalidates the JDTLS workspace hash so a fresh import is performed. |
gradle_version |
8.14.2 |
(vscode-java mode only) Override the Gradle distribution version Serena downloads by default. |
vscode_java_version |
1.54.0-923 |
(vscode-java mode only) Override the bundled vscode-java runtime bundle version Serena downloads by default. |
lombok_show_generated |
true |
Show Lombok-generated methods (getX/setX, builder(), equals/hashCode/toString, withX, fluent accessors) in find_symbol, get_symbols_overview and the symbol-edit tools. Set to false to restore the previous JDTLS default and hide the synthetic methods (e.g. when @Data classes pollute the outline with too many getters/setters). Requires JDTLS commit b2d8952 / vscode-java >= 1.53.0; the bundled default already meets this. |
jdtls_xmx |
3G |
Maximum heap size for the JDTLS server JVM. |
jdtls_xms |
100m |
Initial heap size for the JDTLS server JVM. |
Notes:
- When overriding
vscode_java_version, Serena still assumes that the downloaded runtime bundle keeps the same internal
directory layout and file names as the bundled default version. - Serena does not download or load IntelliCode because its completion ranking is not used by Serena's tools. The retired
intellicode_version,intellicode_xmxandintellicode_xmskeys remain accepted and ignored so existing
configurations continue to load; they can be removed. - In upstream-jdtls mode Serena does not ship a Gradle distribution. Maven projects work via JDTLS's bundled m2e.
Gradle projects must have./gradlewin the project, or rely on a system-installed Gradle through Buildship's
default discovery rules. Thegradle_versionandvscode_java_versionsettings are silently ignored in this mode. - Without
runtimes, JDT-LS only knows about the bundledJavaSE-21JRE. Projects that request a newer
container (e.g.sourceCompatibility = JavaVersion.VERSION_25) then fail to resolve JDK types such asjava.lang.Object. Register the matching installed JDK viaruntimesinstead of symlinking over Serena's
bundled JRE directory.
Example: upstream-jdtls mode (offline / corporate network):
ls_specific_settings:
java:
jdtls_path: "/opt/homebrew/Cellar/jdtls/1.50.0/libexec"
lombok_path: "/Users/me/.m2/repository/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar"
# java_home: "/opt/homebrew/opt/openjdk@21" # optionalExample: default vscode-java VSIX mode for a project with custom Gradle plugins:
ls_specific_settings:
java:
gradle_wrapper_enabled: true
use_system_java_home: trueExample: register an additional JDK for a project targeting a newer Java version:
ls_specific_settings:
java:
runtimes:
- name: JavaSE-21
path: /usr/lib/jvm/java-21-openjdk
- name: JavaSE-25
path: /home/user/Java/jdk25
default: trueKotlin
Serena uses JetBrains' Kotlin Language Server for Kotlin support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the Kotlin Language Server executable path. |
kotlin_lsp_version |
263.4702.0 |
Override the Kotlin Language Server version Serena downloads when ls_path is not set. |
jvm_options |
-Xmx2G |
Value assigned to JAVA_TOOL_OPTIONS for the Kotlin LS process. Set to "" to disable JVM options entirely. |
The managed 263.4702.0 packages include a bundled JBR. For a custom ls_path, point directly tobin/intellij-server (bin/intellij-server.exe on Windows). Serena also retains the legacy download
layout for custom Kotlin LSP versions older than 262.4739.0. The pinned current and frozen initial
releases are checksum-verified; arbitrary custom versions are downloaded without checksum verification.
Example:
ls_specific_settings:
kotlin:
kotlin_lsp_version: "263.4702.0"
jvm_options: "-Xmx4G -XX:+UseG1GC"Lean 4
Serena uses lean --server for Lean 4 support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
lean from PATH |
Override the lean executable path. Serena does not manage Lean downloads. |
Lua
Serena uses lua-language-server for Lua support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
lua_language_server_version |
3.15.0 |
Override the bundled lua-language-server version Serena downloads when it cannot use an existing installation from PATH or common install locations. |
EmmyLua Analyzer
The experimental Rust-based EmmyLua Analyzer backend is selected explicitly with language: lua_emmylua. Serena uses the emmylua_ls executable configured via ls_path; if none is configured, it downloads the pinned release for the current platform and verifies its SHA-256 checksum.
Supported settings under ls_specific_settings.lua_emmylua:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the emmylua_ls executable path. |
emmylua_ls_version |
0.25.1 |
Select the EmmyLua Analyzer release to download. The default release is checksum-verified; custom release versions should be used only when the release asset is trusted. |
Example:
languages:
- name: lua_emmylua
ls_specific_settings:
lua_emmylua:
ls_path: /opt/emmylua_lsLuau
Serena uses luau-lsp for Luau support.
Runtime Requirements:
luau-lspis used from PATH if available.- Otherwise, Serena downloads the pinned
luau-lsprelease for the current platform.
Configuration:
ls_specific_settings:
luau:
ls_path: "/path/to/luau-lsp" # Optional: override the language server executable
luau_lsp_version: "1.63.0" # Optional: override the bundled luau-lsp version
platform: "roblox" # "roblox" (default) or "standard"
roblox_security_level: "PluginSecurity" # Roblox only: None, PluginSecurity, LocalUserSecurity, RobloxScriptSecurityNotes:
- In
robloxmode, Serena downloads Roblox definitions and Roblox API docs and passes them toluau-lsp. - In
standardmode, Serena skips Roblox definitions and only downloads the standard Luau docs bundle.
Markdown
Serena uses Marksman for the markdown language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the marksman executable path. |
marksman_version |
2024-12-18 |
Override the Marksman release tag Serena downloads when ls_path is not set. |
MATLAB
Serena uses the official MathWorks MATLAB language server from the VS Code extension.
Supported settings:
| Setting | Default | Description |
|---|---|---|
matlab_path |
auto-detected | Path to the MATLAB installation. This overrides MATLAB_PATH and auto-detection, but not Serena's managed extension download. |
matlab_extension_version |
1.3.9 |
Override the MathWorks VS Code extension version Serena downloads. |
Nix
Serena uses nixd for Nix support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
PATH/common-path discovery followed by managed installation | Absolute path to a nixd executable or launcher. When set, Serena bypasses its nixd discovery, installation, and version check. |
config_path |
null |
Absolute path to a UTF-8 JSON file containing the value of the nixd settings section. A leading ~ is expanded. |
Example:
ls_specific_settings:
nix:
ls_path: /absolute/path/to/nixd-project
config_path: /absolute/path/to/nixd-settings.jsonThe JSON document contains the settings object directly, without an outer nixd key:
{
"formatting": {
"command": ["alejandra"]
},
"nixpkgs": {
"expr": "import <nixpkgs> { }"
},
"options": {}
}Serena loads this file once when creating the language server, uses it as nixd's initializationOptions, and serves the same effective
settings through LSP workspace/configuration requests. Existing initializationOptions configured under ls_specific_settings.nix
remain top-level overrides and are reflected in both paths. Restart Serena after changing the JSON file.
For the first request after opening a file, nixd can briefly return an empty hover while its initial analysis is completing.
Serena retries that response up to five times with a 100 ms delay between attempts before returning null. This bounded
retry prevents a transient startup response from immediately making hover unavailable; unsupported positions still returnnull after the same bounded retry interval.
Pascal (`pasls`)
Serena uses pasls (Pascal Language Server) for Pascal/Free Pascal support.
Language Server Installation:
- If
paslsis found in your system PATH, Serena uses it directly - Otherwise, Serena automatically downloads a prebuilt binary from GitHub releases
Supported platforms for automatic download: Linux (x64, arm64), macOS (x64, arm64), Windows (x64).
Auto-Update:
Serena automatically checks for pasls updates every 24 hours. Updates include:
- SHA256 checksum verification before installation
- Atomic update with rollback on failure
- Windows file locking detection (defers update if pasls is in use)
Configuration:
Configure pasls via ls_specific_settings.pascal in serena_config.yml:
| Setting | Description |
|---|---|
pasls_version |
Override the pinned pasls version Serena downloads by default |
pp |
Path to FPC compiler driver (must be fpc or fpc.exe, not ppc386.exe) |
fpcdir |
Path to FPC source directory |
lazarusdir |
Path to Lazarus directory (required for LCL projects) |
fpc_target |
Target OS override (e.g., Win32, Win64, Linux) |
fpc_target_cpu |
Target CPU override (e.g., i386, x86_64, aarch64) |
Example configuration:
ls_specific_settings:
pascal:
pp: "D:/laz32/fpc/bin/i386-win32/fpc.exe"
fpcdir: "D:/laz32/fpcsrc"
lazarusdir: "D:/laz32/lazarus"Notes:
- The
ppsetting is the most important for hover and navigation to work correctly. - Use the FPC compiler driver (
fpc/fpc.exe), not backend compilers likeppc386.exe. - These settings are passed as environment variables to the pasls process.
Perl
Serena uses Perl::LanguageServer for Perl support. Install Perl and the server with cpanm Perl::LanguageServer; Linux and macOS only (the server does not run on Windows).
Perl::LanguageServer only indexes files whose extension is in its perl.fileFilter and skips directories listed in perl.ignoreDirs. Both are exposed below so projects with non-standard extensions (e.g. .cgi / .psgi web handlers) can make those files visible (#1449).
Configuration:
Configure the language server via ls_specific_settings.perl in serena_config.yml:
| Setting | Default | Description |
|---|---|---|
file_filter |
[".pm", ".pl", ".t"] |
File extensions (with leading dot) that Perl::LanguageServer should index, e.g. [".pm", ".pl", ".t", ".cgi"]. |
ignore_dirs |
[".git", ".svn", "blib", "local", ".carton", "vendor", "_build", "cover_db"] |
Directory names Perl::LanguageServer should skip when indexing. |
Example configuration:
ls_specific_settings:
perl:
file_filter: [".pm", ".pl", ".t", ".cgi", ".psgi"]
ignore_dirs: [".git", "blib", "local", "vendor", "cover_db"]Notes:
- Extensions added via
file_filterare also synced into Serena's Perl source-file matcher, sofind_symboland symbol indexing treat the same files as the language server. Defaults are unchanged when these keys are omitted. - The matcher is reset on every language server activation, so one project's
file_filterdoes not leak into another.
PHP (`Intelephense`)
Serena uses Intelephense for the php language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the intelephense executable path. |
intelephense_version |
1.14.4 |
Override the npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
ignore_vendor |
true |
Ignore directories named vendor while indexing the project. |
maxFileSize |
unset | Maximum size (in bytes) of files Intelephense indexes; sets intelephense.files.maxSize. |
maxMemory |
unset | Maximum heap size (in MB) of the Intelephense process; applied as Node.js option --max-old-space-size. |
file_filter |
unset | Additional file extensions (with leading dot) to treat as PHP sources, e.g. [".module", ".install"]; added to the defaults .php / .phtml (#1710). |
Example configuration making Drupal source files visible to the symbol tools:
ls_specific_settings:
php:
file_filter: [".module", ".install", ".inc", ".theme", ".profile", ".engine"]Notes:
- Extensions added via
file_filterare synced into Serena's PHP source-file matcher and pushed to Intelephense asintelephense.files.associationsglobs at startup, sofind_symboland the language server treat the same files as PHP sources. - The matcher is reset on every language server activation, so one project's
file_filterdoes not leak into another.
PHP (`Phpactor`)
Serena uses the php_phpactor language key for Phpactor.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the Phpactor PHAR path. |
phpactor_version |
2025.12.21.1 |
Override the Phpactor PHAR version Serena downloads when ls_path is not set. |
ignore_vendor |
true |
Ignore directories named vendor while indexing the project. |
PHP (`Devsense`)
Serena provides the experimental php_devsense language key for the standalone Devsense PHP Language Server.
Serena requires node and npm, installs the pinned npm package, and launches its matching platform-specific binary.
This avoids depending on npm's platform-specific .bin shim. Basic language-server features are available without a
license; premium features can be activated with the DEVSENSE_PHP_LS_LICENSE environment variable. The Devsense
package requires visible attribution, so keep this documentation link when redistributing or packaging the integration.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed npm install | Override the devsense-php-ls executable path; on Windows use the .exe binary or a working launcher. |
devsense_php_ls_version |
1.0.19197 |
Override the pinned npm package version used by the managed install. |
npm_registry |
null |
Override the npm registry used for the managed install. |
ignore_vendor |
true |
Ignore directories named vendor while indexing the project. |
php_version |
unset | Forward the PHP version as initializationOptions["php.version"]. |
file_filter |
unset | Additional file extensions (with leading dot) to treat as PHP sources, added to .php / .phtml. |
PowerShell
Serena uses PowerShell Editor Services for PowerShell support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
pses_version |
4.4.0 |
Override the PowerShell Editor Services version Serena downloads. Serena still requires pwsh to be available locally. |
Python
Serena supports several Python language servers through separate language keys.
Pyright (`python`)
Pyright is the default Python language server.
Supported settings:
| Setting | Default | Description |
|---|---|---|
pyright_version |
1.1.403 |
Override the exact Pyright package version Serena launches through uvx / uv tool run. |
ls_path |
managed executable | Override pyright-langserver and bypass the managed uvx / uv tool run invocation. The default --stdio argument is still applied. |
BasedPyright (`python_basedpyright`)
To use BasedPyright, select its separate experimental
language key:
languages: [python_basedpyright]
ls_specific_settings:
python_basedpyright:
basedpyright_version: "1.39.9"Supported settings:
| Setting | Default | Description |
|---|---|---|
basedpyright_version |
1.39.9 |
Override the exact BasedPyright package version Serena launches through uvx / uv tool run. |
ls_path |
managed executable | Override basedpyright-langserver and bypass the managed uvx / uv tool run invocation. The default --stdio argument is still applied. |
The generic language-server launch settings, including ls_base_cmd, ls_args,
and ls_extra_args, apply to both servers. ls_args replaces the default arguments, whilels_extra_args appends to them.
Other alternative Python language keys are python_ty, python_pyrefly, and python_jedi.
Ruby
Serena uses Shopify's ruby-lsp for Ruby support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ruby_lsp_version |
0.26.8 |
Override the ruby-lsp gem version Serena installs when no project-local or global ruby-lsp is already available. |
Rust
Serena uses rust-analyzer for Rust support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
auto-detected | Override the rust-analyzer executable path. Without ls_path, Serena prefers rustup which rust-analyzer, then rustup component add rust-analyzer, then PATH/common install locations. |
Scala
Serena uses Metals for Scala support.
Metals serves one build per workspace folder, so in a repository holding several builds — or a single
build below the repository root — it is the build roots, not the repository root, that Metals must be
given. Serena detects them automatically; project_roots overrides that detection where it guesses
wrong, and project_root_scan_depth bounds how far it looks.
Metals reports its build import, indexing and compilation as LSP work-done progress, and Serena waits
for all of it before the first cross-file query of a session. This matters most for references, which
Metals serves from SemanticDB — a file the build server only writes once it has compiled the sources,
well after indexing ends — so a query made too early returns a fraction of the true result with
nothing to say it is partial. The wait is bounded by indexing_timeout, after which the query
proceeds against whatever Metals has so far and a warning is logged.
Supported settings:
| Setting | Default | Description |
|---|---|---|
metals_version |
1.6.8 |
Override the Metals version Serena bootstraps. |
client_name |
Serena |
Client identifier sent to Metals. |
on_stale_lock |
auto-clean |
How Serena handles stale Metals H2 database locks. Supported values: auto-clean, warn, fail. |
log_multi_instance_notice |
true |
Log a notice when another Metals instance is detected. |
auto_import_build |
true |
Answer Metals' build-import prompts affirmatively, which lets it run the project's build tool (e.g. sbt bloopInstall). Set to false to leave the build un-imported; Metals then has no build server, and every cross-file query is served by the fallback presentation compiler. |
project_roots |
auto-detected | The build roots to serve, as paths relative to the repository root. A path that does not exist is skipped with a warning; if none of them exists, the build roots are detected instead. |
project_root_scan_depth |
3 |
How many directory levels below the repository root the detection searches. Applies whenever the roots are detected — that is, when project_roots is unset, or when it names nothing that exists. |
indexing_timeout |
180 |
How long to wait, in seconds, for Metals to finish importing, indexing and compiling before the first cross-file query. On expiry the query proceeds and a warning names what was still outstanding. |
indexing_start_grace |
15 |
How long to wait, in seconds, for Metals to report any work at all. A server that reports none within this window is taken to have nothing to do. |
indexing_quiet_period |
3 |
How long, in seconds, Metals must report nothing for its work to count as finished. Metals hands off between its phases rather than overlapping them, so it reports nothing for a moment in between; a shorter period risks mistaking that gap for completion. |
terminate_bloop_on_stop |
false |
Terminate the Bloop build-server daemons that were running under this Serena process once Metals has stopped. Bloop daemonizes itself and would otherwise keep running as an orphan. It is opt-in because Bloop shares one server per user: another Metals client (e.g. VS Code) may be connected to a daemon that Serena started, and stopping it would interrupt that client. |
SCSS / Sass / CSS
Serena uses some-sass-language-server for the scss language key.
Experimental — must be explicitly listed in project.yml; not auto-detected. Some Sass was chosen over the
generic vscode-css-language-server because it provides full workspace-wide @use / @forward go-to-definition
and find-references for variables, mixins, functions, and placeholders.
Handles .scss, .sass, and .css. The three are dispatched by the LSP language id (scss, sass, css) and
share the same engine; CSS feature toggles default to off upstream and Serena flips them on at startup so that
plain CSS gets symbols, definitions, references, hover, and completion. Lint diagnostics are deliberately left
off (the rules are opinionated about vendor prefixes / empty rules / etc.); only syntax-level diagnostics surface.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the some-sass-language-server executable path. |
some_sass_version |
2.3.8 |
Override the npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
Solidity
Serena uses @nomicfoundation/solidity-language-server for Solidity support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the Solidity language server executable path. |
solidity_language_server_version |
0.8.4 |
Override the npm package version Serena installs when ls_path is not set. |
solidity_state_dir |
<ls_resources_dir>/solidity-state on macOS |
Writable state root for the managed Solidity language server on macOS. Serena uses a child-process-only home-directory override so Hardhat does not write to ~/Library; HOME in the Serena process is unchanged. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
On macOS, if the default Solid-LSP resources directory is not writable, configure an alternative path:
ls_specific_settings:
solidity:
solidity_state_dir: /path/to/writable/solidity-stateThis setting is ignored on Linux and Windows, where the existing launch environment is unchanged.
SystemVerilog
Serena uses verible-verilog-ls for SystemVerilog support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
system PATH or managed download | Override the verible-verilog-ls executable path. |
verible_version |
v0.0-4051-g9fdb4057 |
Override the Verible release Serena downloads when ls_path is not set and no system installation is found. |
Terraform
Serena uses terraform-ls for Terraform support.
Supported settings:
| Setting | Default | Description |
|---|---|---|
terraform_ls_version |
0.36.5 |
Override the terraform-ls version Serena downloads. Terraform itself must still be installed and available in PATH. |
TOML
Serena uses Taplo for the toml language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed download | Override the taplo executable path. |
taplo_version |
0.10.0 |
Override the Taplo version Serena downloads when ls_path is not set. |
TypeScript
Serena uses typescript-language-server for the typescript language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the typescript-language-server executable path. |
typescript_version |
5.9.3 |
Override the bundled typescript npm package version Serena installs when ls_path is not set. |
typescript_language_server_version |
5.1.3 |
Override the bundled typescript-language-server npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
indexing_timeout |
30.0 |
Timeout in seconds for waiting on tsserver's $/progress project-indexing signal to drain once it has started (both at startup and before the first cross-file reference query). If indexing does not complete within this window, Serena logs a warning and proceeds anyway. Increase it for very large projects. |
server_ready_timeout |
10.0 |
Timeout in seconds for waiting on the server-ready signal after initialization. If the signal does not arrive within this window, Serena logs a message and proceeds anyway. |
indexing_start_grace |
5.0 |
Timeout in seconds to wait for tsserver to start reporting $/progress before the first cross-file reference query. tsserver must resolve the project graph before it can emit the first progress token, and that can take longer than the default on a very large project; if it takes longer than this window, Serena assumes no indexing was needed and may return incomplete cross-file references. Raising indexing_timeout alone does not help here, since this grace elapses first. Increase this for very large projects if find_referencing_symbols/request_references returns incomplete results shortly after project load. |
TypeScript monorepos and cross-package references
In a monorepo, find_referencing_symbols / find_references only include consumers in other packages when tsserver can walk from a package's declaration file back to its sources. That walk requires TypeScript project references (composite + references), not merely a solution-style root tsconfig.json or package.json exports.
Without those edges, results are silently partial: a symbol may show only same-package references (or none) even though other packages import it. This is tsserver behaviour Serena inherits, not a Serena bug (microsoft/TypeScript#30823; oraios/serena#1939).
What to do in a TypeScript monorepo:
- Declare
composite: truein each library package'stsconfig.jsonand list dependent projects underreferencesin the consumer (or a solution-style root). - Prefer source imports (or generate declaration maps) so tsserver can map
dist/*.d.tsback to sources. - After changing the project graph, restart Serena (or the TypeScript language server) so tsserver rebuilds the program.
- If cross-package references still look short, verify with grep before treating the LSP answer as complete; same-package results being complete does not imply the package boundary was crossed.
Svelte
Serena uses svelte-language-server for the svelte language key. Use svelte for Svelte projects instead of also listing typescript, unless you intentionally want multiple language servers active for the same files.
A companion TypeScript language server (typescript-language-server + typescript-svelte-plugin) is spawned automatically alongside the Svelte LSP. The plugin makes the TypeScript program .svelte-aware so that cross-file operations — rename, go-to-definition, and find-references from .ts/.js files — correctly include .svelte consumers. Serena merges and deduplicates reference results from both servers automatically.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the svelteserver executable path. |
svelte_language_server_version |
0.18.0 |
Override the svelte-language-server npm package version Serena installs. |
typescript_version |
6.0.3 (falls back to ls_specific_settings.typescript.typescript_version) |
Override the typescript npm package version used as the shared tsdk. |
typescript_language_server_version |
5.1.3 (falls back to ls_specific_settings.typescript.typescript_language_server_version) |
Override the typescript-language-server npm package version for the companion server. |
typescript_svelte_plugin_version |
0.3.52 |
Override the typescript-svelte-plugin npm package version used for .svelte-aware TS resolution. |
npm_registry |
null |
Override the npm registry Serena uses for all managed installs. |
indexing_timeout |
120.0 (falls back to ls_specific_settings.typescript.indexing_timeout) |
Timeout in seconds for the companion TS server to finish indexing .svelte files. On timeout, startup fails with a diagnostic indexing-state summary instead of serving cross-file results from a partially indexed program. |
initialization_options_configuration |
{} |
Deep-merge overrides for any of the ten plugin configuration sections (svelte, prettier, emmet, typescript, javascript, js/ts, css, less, scss, html). |
Unlike the plain TypeScript server, the companion is strict about readiness: it raises on server-ready and
indexing timeouts instead of proceeding with a cold or partially indexed program (which would silently degrade
cross-file renames and references). The companion reads server_ready_timeout and indexing_timeout fromls_specific_settings.typescript with raised defaults (30s and 120s respectively); ls_specific_settings.svelte.indexing_timeout
takes precedence for the .svelte-file indexing wait.
All four packages are tracked via a version file; changing any version setting triggers a clean reinstall.
TypeScript via `vtsls`
The actual configuration key for vtsls is typescript_vts, not vts.
Supported settings:
| Setting | Default | Description |
|---|---|---|
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):
ls_specific_settings:
typescript_vts:
initialization_options:
typescript:
tsdk: "project/.yarn/sdks/typescript/lib"
vtsls:
autoUseWorkspaceTsdk: truevtsls 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 and the underlying VS Code TypeScript settings. 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— ensurevtsls.autoUseWorkspaceTsdk: trueis set alongsidetypescript.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 containingtsserver.js(e.g..yarn/sdks/typescript/lib, not.yarn/sdks/typescript). Relative paths are interpreted relative to the project root. - Setting appears in
solidlsplogs 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.
TypeScript via the native TypeScript 7 language server
The configuration key is typescript_tsgo. Serena installs the typescript npm package and runs its native
binary as tsc --lsp --stdio.
Supported settings:
| Setting | Default | Description |
|---|---|---|
typescript_version |
7.0.2 |
Override the typescript npm package version Serena installs (7.0 or later). |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
indexing_timeout |
30.0 |
Timeout in seconds for project indexing. |
indexing_start_grace |
0.0 |
Timeout in seconds to wait for indexing progress to start before the first cross-file reference query; the native server loads projects while handling requests, so no wait is needed by default. |
Vue
Serena uses @vue/language-server (Volar) for the vue language key, together with a companion TypeScript language server.
Supported settings:
| Setting | Default | Description |
|---|---|---|
vue_language_server_version |
3.1.5 |
Override the bundled @vue/language-server npm package version Serena installs. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. If unset on vue, Serena falls back to ls_specific_settings.typescript.npm_registry. |
Notes:
typescript_versionandtypescript_language_server_versionare read fromls_specific_settings.typescript, not fromls_specific_settings.vue.
YAML
Serena uses yaml-language-server for the yaml language key.
Supported settings:
| Setting | Default | Description |
|---|---|---|
ls_path |
managed install | Override the yaml-language-server executable path. |
yaml_language_server_version |
1.19.2 |
Override the npm package version Serena installs when ls_path is not set. |
npm_registry |
null |
Override the npm registry Serena uses for the managed install. |
(custom-prompts)=
Custom Prompts
All of Serena's prompts can be fully customized.
We define prompt as jinja templates in yaml files, and you can inspect our default prompts here.
To override a prompt, simply add a .yml file to the prompt_templates folder in your Serena data directory
which defines the prompt with the same name as the default prompt you want to override.
For example, to override the system_prompt, you could create a file ~/.serena/prompt_templates/system_prompt.yml (assuming default Serena data folder location)
with content like:
prompts:
system_prompt: |
Whatever you want ...It is advisable to use the default prompt as a starting point and modify it to suit your needs.
Usage Reporting
On startup, Serena reports anonymous usage data to help us understand Serena usage, as explained in our privacy policy.
If you want to opt out of usage data reporting, set the environment variable SERENA_USAGE_REPORTING to false.
02 Usage 060 Dashboard (`docs/02-usage/060_dashboard.md`)
The Dashboard and GUI Tool
Serena comes with built-in tools for monitoring and managing the current session:
the Serena Dashboard (enabled by default)
The dashboard provides detailed information on your Serena session, the current configuration and provides access to logs.
Some settings (e.g. the current set of active programming languages) can also be directly modified through the dashboard.The dashboard is supported on all platforms.
the GUI Log Viewer (disabled by default)
The GUI tool is a legacy native application which displays Serena's live logs.
This is mainly supported on Windows, but it may also work on Linux; macOS is unsupported.
Both can be configured in Serena's configuration file (serena_config.yml).
The Serena Dashboard
The dashboard is a web application, which is opened in one of three ways:
- via a native application wrapper with accompanying tray icon (on supported platforms)
- via a tray manager that aggregates multiple instances (on supported platforms)
- via your default web browser (supported on all platforms)
Configure your preferred interface via the setting web_dashboard_interface in Serena's global configuration file (see below).
By default, the dashboard can be accessed at http://localhost:24282/dashboard/index.html,
but a higher port may be used if the default port is unavailable/multiple instances are running.
Features
The dashboard provides ...
- a detailed overview of
- the current Serena status and configuration (e.g. active tools, active programming languages, enabled modes and contexts, etc.)
- ongoing and past tool calls (and statistics on tool usage)
- access to Serena's live logs
- the ability to modify settings (e.g. the set of active programming languages) on the fly
- the ability to edit
- configuration files (global
serena_config.ymland project-specificproject.yml) - memories of the current project
- configuration files (global
- the ability to shut down the Serena MCP server.
Configuring the Dashboard
You can configure settings for the dashboard in the global configuration file.
In particular, you can configure
- the interface through which the dashboard is accessed (see above)
- whether the dashboard is enabled at all.
We highly recommend keeping it enabled, as it provides valuable insights into Serena's operation and allows you to adjust configuration conveniently. - whether the dashboard window is opened automatically when Serena is started (see below).
- network settings (for cases where you might want to access the Dashboard non-locally).
Dashboard Opening Behaviour
When Serena is started, the Dashboard window is opened by default in order to make users aware of its existence.
If you prefer not to have this happen, you can prevent it from opening
by setting web_dashboard_open_on_launch: False in the global configuration file or by passing --open-web-dashboard False
to the start-mcp-server CLI command.
On platforms supporting the tray icon (see also configuration option web_dashboard_interface),
you can conveniently open the dashboard at any time by clicking on the tray icon, so automatic
opening is not a requirement to be able to access the dashboard on these platforms.
On other platforms, you may still access it by
- asking the LLM to "open the Serena dashboard", which will open the dashboard in your default browser
(the toolopen_dashboardis enabled for this purpose, provided that the dashboard is active,
not opened by default and the GUI tool, which can provide the URL, is not enabled) - navigating directly to the URL (see above).
The GUI Log Viewer
The Serena GUI Log Viewer is a legacy application which provides access to Serena's live logs.
Via its menu, it furthermore allows you to
- shut down the agent
- access the dashboard's URL (if it is running).
The tool is mainly supported on Windows, but it may work on some Linux systems as well (depending on your desktop environment).
To enable the tool, set gui_log_window to true in Serena's global configuration file.
02 Usage 065 Logs (`docs/02-usage/065_logs.md`)
Logs
It can be vital to understand what is happening in Serena, especially when something goes wrong.
You can access Serena's live logs via
- the Serena dashboard (tab "Logs")
- the GUI tool.
Additionally, logs are persisted in the Serena home directory, which, by default, is located at
%USERPROFILE%\.serena\logson Windows~/.serena/logson Linux and macOS.
You can adjust the log level via the global configuration.
You additionally have the option of enabling full tracing of language server communication (mostly for development purposes).
02 Usage 070 Security (`docs/02-usage/070_security.md`)
Security Considerations
Security is important to us, and we take this topic seriously.
Serena's Assumptions
The current security model for Serena assumes:
- the local machine is trusted,
- the MCP client (i.e. the LLM) is trusted,
- the code repository being worked on is trusted,
- user configuration is trusted,
- package manager configuration (e.g. npm) for downloading additional dependencies (i.e. language servers when using Serena with the LSP backend) is trusted.
Serena contains tools for executing shell commands and modifying files.
As such tools are, however, an essential part of coding agent workflows, they typically need to be made available – and need to be made available in a flexible, general form.
Therefore, the only way to fully protect against unintended consequences is to use a sandboxed environment for running Serena.
:::{admonition} Security Advisories
:class: note
Security advisories are welcome for issues that violate the security model described on this page.
However, reports which amount to noting that Serena's tools can execute commands or modify files
describe intended functionality rather than vulnerabilities, and we will reject advisories that fail to recognise this
or otherwise ignore the above assumptions.
Sandboxing is the only way to fully protect against unintended consequences when using coding agents;
constraints on the tools or REPL APIs cannot achieve this and are therefore not an approach we pursue.
:::
General Recommendations for Risk Reduction
To reduce the risk of unintended consequences, we recommend that you:
- back up your work regularly (keep the project being worked on under version control),
- restrict the set of allowed tools via the configuration
(note that restrictions are effective for the tool interface only, see below), - do not expose Serena's network services to untrusted networks.
If you do not fully trust the client/the LLM, we additionally recommend to monitor tool executions carefully
(provided that your MCP client supports this).
Note that with the REPL interface, such monitoring is necessarily coarser: every action appears as the same tool
being called, and the client cannot tell a read-only call from a modifying one by the tool's name alone.
What needs to be reviewed is the submitted code.
(sandboxing)=
Sandboxing
Sandboxing is the most effective way to mitigate risks when using coding agents.
Running Serena inside a docker container which only exposes the necessary files and tools to the agent is a good way to achieve this.
While setting up a sandboxed environment may require some initial effort, we highly recommend it for all security-conscious users.
(trusted-projects)=
Trusted Projects
Sandboxing limits what Serena can affect while doing what it was asked to do.
The notion of trusted projects (introduced in Serena v1.6.0) addresses a different question:
to what extent may the repository being worked on influence Serena's behaviour in the first place?
A project is an input authored by whoever produced the repository, and it comprises more than source code:
it also carries configuration (.serena/project.yml) as well as file system structure.
Trust determines whether such repository-supplied input may influence Serena beyond having the code read and
analysed as code — for example, by executing commands, by changing how dependencies are acquired, or by causing
Serena to access locations outside the project root.
Trust is decided by the project's root path, which is matched against trusted_project_path_patterns in Serena's
global configuration.
We gate a feature on project trust whenever honouring repository-supplied input could have an effect beyond the
scope of what the user visibly requested: activating a project is not a request to run a command, and searching a
project's files is not a request to read files outside of it.
A Functionality Boundary, Not a Containment Boundary
Untrusted projects are not sandboxed, restricted or otherwise contained.
They are read, analysed and edited just like any other project; the only difference is that a small set of
capabilities is unavailable to them.
As soon as the agent is asked to do anything at all, the full tool surface applies to an untrusted project as well:
commands can be executed, files can be modified, and the repository's contents can influence the LLM.
Consequently, our assumption that the repository being worked on is trusted (see above) remains fully in force.
Trust patterns eliminate a class of particularly straightforward attacks, namely those requiring no user
interaction beyond opening a project, but exploits can generally not be prevented by such means.
The question to ask is therefore not "is this project safe to work on because it is untrusted?" but rather
"do I trust this repository enough to grant it the additional capabilities?".
If a repository is not trustworthy, sandboxing is the answer, not the trust configuration.
For the list of trust-gated features, see the configuration page.
(repl-security)=
The REPL Interface
With the REPL interface, the agent does not invoke individual tools; it submits Python code,
which Serena executes.
In the default configuration, Serena is equally capable either way, as shell execution and file modification are
available in both interfaces.
The difference lies in what restrictions can achieve:
- With the tool interface, excluding a tool removes the respective capability: a tool that is not exposed
cannot be invoked, so forbidding shell execution or file modification is effective. - With the REPL interface, there is no such guarantee.
Restricting the available operations (included_apis/excluded_apis) steers the agent towards the intended
way of working, but the submitted code can, in principle, do anything the Serena process can do — irrespective
of the operations Serena itself provides.
Excluding shell execution, for example, does not prevent the code from achieving the same effect by other means. - The
read_onlyproject setting is subject to the same limitation: Serena's own editing operations are refused,
yet code executed in the REPL is not prevented from modifying files.
The assumptions stated above therefore apply unchanged, but if you require actual constraints rather than
guidance, sandboxing is the answer.
(network-security)=
Network Security
Serena includes several network services:
- the Serena MCP server itself (when run in HTTP or SSE mode instead of stdio mode)
- the Serena Dashboard web server
- the Serena JetBrains Plugin server, which runs within the JetBrains IDE (when using the JetBrains language backend)
- the Serena Project Server (only started explicitly for project querying)
By default, these services accept connections from localhost only, which is a secure default for most users
(given our assumption that the local machine is trusted; see above).
These services can be reconfigured to listen on other addresses, but doing so may have security implications.
If you need to allow connections from other machines, we recommend that you set up a secure networking environment
and ensure that only trusted machines can connect to these services.
It is the responsibility of the user to restrict access appropriately, e.g. by placing the service behind a reverse
proxy (adding authentication) or firewall.
Supply Chain Security
Serena has two language backends with different security characteristics:
- the JetBrains-based variant, which integrates with a running JetBrains IDE, and
- the language-server-based variant (the free variant), which can automatically acquire language server dependencies on demand.
While we can assume that JetBrains IDEs installed by the user do not pose a security risk,
language server dependencies (if not handled with care) could.
For convenience, Serena downloads or installs certain language server dependencies on demand.
We treat this path as security-sensitive and have hardened it accordingly.
The most important supply chain protections are:
- exact version pinning,
- hash verification,
- host restriction,
- and isolated Serena-managed installation directories.
Auto-Downloaded Language Server Dependencies
For language servers that are auto-installed by downloading archives, binaries, VSIX packages, NuGet packages, or other release artifacts, Serena uses a hardened shared download path with the following protections:
- Pinned versions by default: default downloads use exact versions instead of floating
latestor nightly channels. - Integrity verification: downloaded artifacts are checked against pinned SHA256 hashes stored in Serena's source code.
- Host allowlists: download URLs are restricted to the expected hosts for a given dependency.
- Safe extraction: archive extraction validates paths to prevent path traversal and zip-slip style attacks.
- Managed install locations: dependencies are installed into Serena-managed directories instead of into the project repository.
In practice, this means that a downloaded artifact must match all of the following:
- the expected version,
- the expected host,
- the expected SHA256 checksum,
- and the expected extraction layout.
If any of these checks fail, Serena aborts the installation instead of continuing.
npm-Based Language Servers
Some language servers are distributed primarily through npm. For those, Serena currently uses pinned package versions and installs them into Serena-managed directories.
By default, Serena uses the user's normal npm configuration. We do not force a registry override unless one is explicitly configured. If needed, both the package version and the registry can be overridden through ls_specific_settings.
For npm-based installs, Serena's current security posture is based on these rules:
- Exact package versions are pinned by default.
- The install location is isolated from the project and lives in Serena-managed language-server directories.
- The user's npm configuration is trusted by default.
- Repository and user configuration are assumed to be trusted.
This means Serena protects well against accidental version drift, but npm installs still rely on the npm ecosystem and package-manager execution model. In particular, Serena does not currently use lockfile-based npm ci installs for bundled language-server dependencies.
`uvx` and Python Dependency Pinning
Some parts of Serena rely on uv / uvx.
One important detail is that uvx ignores the lockfile when installing directly from a Git repository. Because of that, we pin Serena's Python dependencies exactly in pyproject.toml so that installations from Git still resolve to exact dependency versions rather than floating ranges.
Some language servers also use exact pinned versions when invoking them through uvx / uv tool run.
02 Usage 999 Additional Usage (`docs/02-usage/999_additional-usage.md`)
Additional Usage Pointers
Prompting Strategies
We found that it is often a good idea to spend some time conceptualizing and planning a task
before actually implementing it, especially for non-trivial tasks.
For very complex tasks, you can make a detailed plan in one session,
where Serena may read a lot of your code to build up the context,
and then continue with the implementation in another,
having persisted the plan in a memory or dedicated file.
Serena and Git Worktrees
git-worktree can be an excellent way to parallelize your work. More on this in Anthropic: Run parallel Claude Code sessions with Git worktrees.
Be sure to add the .serena folder to version control, such that your project-specific settings and memories are available across worktrees.
When you launch a CLI agent from inside a worktree using --project-from-cwd, Serena activates the worktree itself, even if the worktree lives under another Serena project (for example <repo>/.claude/worktrees/<name>, where Claude Code creates them natively). The nearest project boundary wins: the worktree's own .git pointer file takes precedence over an ancestor's .serena/project.yml, so file operations always resolve against the correct working tree.
03 Special Guides Cpp Setup (`docs/03-special-guides/cpp_setup.md`)
C/C++ Setup Guide
This guide explains how to prepare a C/C++ project so that Serena can provide reliable code intelligence via clangd or ccls language servers.
This is only necessary if you use the language server variant of Serena, for users of the Serena JetBrains plugin no setup is required
and the limitations described below do not apply.
General
Serena supports two C/C++ language servers, clangd (default) and ccls.
Both have their pros and cons and require a properly configured compile_commands.json
for cross-file reference finding, see below for details.
Your project must have a compile_commands.json file at the repository root.
This file is essential for correct parsing and cross-file reference finding.
You can use a specific clangd or ccls installation (e.g., a custom build or a version provided by your project),
by specifying the path in your configuration, see ls-specific-settings for details.
compile_commands.json Requirements
For reliable cross-file reference finding with clangd, your compile_commands.json must:
- Include proper C++ standard flags (e.g.,
-std=c++17) - Include all necessary include paths (
-Iflags)
With clangd
By default, Serena automatically downloads and manages clangd. Since clangd does not properly work with relative paths in compile_commands.json,
Serena will detect them and transform them into absolute paths automatically (writing a new compile_commands.json file), if needed.
Customizing the Compilation Database Location
By default, Serena creates the transformed compilation database at .serena/compile_commands.json.
You can customize this location via project settings:
# .serena/project.yml
language_servers:
- cpp
ls_specific_settings:
cpp:
compile_commands_dir: custom/rel/path # defaults to .serenaWith ccls
ccls requires manual installation and configuration. It may perform better in some situations.
Installation
Linux:
# Ubuntu/Debian (22.04+)
sudo apt-get install ccls
# Fedora/RHEL
sudo dnf install ccls
# Arch Linux
sudo pacman -S cclsmacOS:
brew install cclsWindows:
choco install cclsConfiguration
After installing ccls, configure Serena to use it via project settings (in .serena/project.yml)
by adding cpp_ccls to the language_servers list. Replace cpp with cpp_ccls if you already have the cpp entry.
ccls can handle relative paths in compile_commands.json, so no transformation is necessary
and no transformed compile_commands.json file will be created.
Known Limitations
Files Created After Server Initialization
Both clangd and ccls have a fundamental limitation:
files created by external mechanisms after the language server starts are not automatically indexed.
Cross-file references to newly created files will not work unless the new file is at some point opened by the language server (for example, by a symbol lookup in it), or until compile_commands.json is updated and
the language server is restarted.
Reference
- Clangd official documentation: https://clangd.llvm.org/
- Clangd project setup: https://clangd.llvm.org/installation#project-setup
- CCLS repository: https://github.com/MaskRay/ccls
Unreal Engine projects
For Unreal Engine 5 projects (reflection macros, UnrealBuildTool), see the
Unreal Engine Setup Guide.
03 Special Guides Custom Agent (`docs/03-special-guides/custom_agent.md`)
Custom Agents with Serena
As a reference implementation, we provide an integration with the Agno agent framework.
Agno is a model-agnostic agent framework that allows you to turn Serena into an agent
(independent of the MCP technology) with a large number of underlying LLMs. While Agno has recently
added support for MCP servers out of the box, our Agno integration predates this and is a good illustration of how
easy it is to integrate Serena into an arbitrary agent framework.
Here's how it works:
Download the agent-ui code with npx
shellnpx create-agent-ui@latestor, alternatively, clone it manually:
shellgit clone https://github.com/agno-agi/agent-ui.git cd agent-ui pnpm install pnpm devInstall serena with the optional requirements:
shell# You can also only select agno,google or agno,anthropic instead of all-extras uv pip install --all-extras -r pyproject.toml -e .Copy
.env.exampleto.envand fill in the API keys for the provider(s) you
intend to use.Start the agno agent app with
shelluv run python scripts/agno_agent.pyBy default, the script uses Claude as the model, but you can choose any model
supported by Agno (which is essentially any existing model).In a new terminal, start the agno UI with
shellcd agent-ui pnpm devConnect the UI to the agent you started above and start chatting. You will have
the same tools as in the MCP server version.
Here is a short demo of Serena performing a small analysis task with the newest Gemini model:
https://github.com/user-attachments/assets/ccfcb968-277d-4ca9-af7f-b84578858c62
⚠️ IMPORTANT: In contrast to the MCP server approach, tool execution in the Agno UI does
not ask for the user's permission. The shell tool is particularly critical, as it can perform arbitrary code execution.
While we have never encountered any issues with
this in our testing with Claude, allowing this may not be entirely safe.
You may choose to disable certain tools for your setup in your Serena project's
configuration file (.yml).
Other Agent Frameworks
It should be straightforward to incorporate Serena into any
agent framework (like pydantic-ai, langgraph or others).
Typically, you need only to write an adapter for Serena's tools to the tool representation in the framework of your choice,
as was done by us for Agno with SerenaAgnoToolkit (see /src/serena/agno.py).
03 Special Guides External Language Server Registration (`docs/03-special-guides/external_language_server_registration.md`)
(external-ls-registration)=
External Language Server Registration
SolidLSP, Serena's language server framework, is designed to be extensible.
Sometimes, you may need to use Serena with a language server for which there can be no built-in adapter,
e.g. because the language server in question is a custom development which is not yet released.
The general mechanism to make SolidLSP aware of an external language server is to use theLanguageServerRegistry in order to register an ExternalLanguageServerId.
You can either do this
- programmatically, in your own Python code, if you are launching Serena via a custom script
where this code can be executed before Serena itself is started, or - via a Python package that exposes a
solidlsp.language_server_registrationentry point.
If the Python package is installed in the same environment as Serena,
the language server will be automatically registered when Serena starts.
Registering an External Language Server via a Python Package
Expose a Python entry point in the solidlsp.language_server_registration group (pyproject.toml):
[project.entry-points."solidlsp.language_server_registration"]
example = "myls:register_myls"Corresponding contents of myls/__init__.py:
from solidlsp import SolidLanguageServer
from solidlsp.ls_config import FilenameMatcher, LanguageServerRegistry, ExternalLanguageServerId
class MyLanguageServerImpl(SolidLanguageServer):
# implementation of the language server adapter
pass
def register_myls() -> None:
LanguageServerRegistry.get_instance().register(
ExternalLanguageServerId(
key="myls",
matcher=FilenameMatcher(".example"),
implementation=MyLanguageServerImpl
)
)The registered key myls can subsequently be used in a project's configuration file (project.yml)
like any other language server:
language_servers:
- myls03 Special Guides Godot Gdscript Setup Guide For Serena (`docs/03-special-guides/godot_gdscript_setup_guide_for_serena.md`)
GDScript (Godot Engine) Setup Guide for Serena
This guide explains how to prepare a Godot project so that Serena can provide code intelligence for GDScript (.gd) files via Godot's built-in Language Server Protocol (LSP) support.
Unlike most language servers, Serena does not launch a separate LSP process for GDScript. Instead, Serena connects over TCP to the LSP server that the Godot editor itself runs while it is open. This means the Godot editor must already be running with your project loaded before you start Serena.
Prerequisites
- Godot Engine 3.x or 4.x installed and available on your system.
- A Godot project with a
project.godotfile at the project root.
No additional language server needs to be installed — Godot ships with a built-in LSP server that is enabled by default.
How It Works
When a Godot project is open in the editor, the editor listens for LSP connections on TCP port 6008 (the same default for both Godot 3 and Godot 4). Serena connects to this port and communicates using standard LSP Content-Length framing.
Serena automatically detects which major version of Godot your project targets by reading the config_version field from project.godot:
config_version= 5 → Godot 4config_version= 4 → Godot 3
If config_version is not recognized (e.g. from a future Godot release), Serena logs a warning and still connects — both Godot 3 and Godot 4 use the same port.
No additional configuration is needed for version detection.
Setup Steps
Open your Godot project in the Godot editor and allow it to finish loading.
Verify that the built-in LSP is enabled (it is on by default):
- Go to Editor → Editor Settings → Network → Language Server
- Confirm "Use Language Server" is checked.
- Confirm the port is 6008 (the default).
Add
gdscriptto thelanguageslist in your project's Serena configuration:yaml# .serena/project.yml languages: - gdscriptStart Serena in your project root. Serena will connect to the already-running Godot editor automatically.
Using Serena with GDScript
- Serena recognizes
.gdand.gdscriptfiles. The language identifier used in LSP communication isgdscript. - The Godot editor must remain open for the entire Serena session. If the editor is closed, Serena will lose its connection and will need to be restarted once the editor is open again.
- On first use, you may see a brief delay while Serena establishes the TCP connection and the editor indexes your project files — this is expected.
Performance Note
Godot's built-in LSP does not implement the workspace/symbol request (global symbol search across the whole project). As a result, when Serena performs a workspace-wide symbol search — for example, calling find_symbol without a relative_path — it must fall back to sending a textDocument/documentSymbol request for every .gd file in the project individually.
For large projects, this initial full-project scan can take 30–60 seconds or more.
To mitigate this:
- Pass
relative_pathwhenever possible. Narrowing a search to a specific file or subdirectory avoids scanning the entire workspace. - Let the first full scan complete. Serena caches the results to disk after the initial scan. Subsequent Serena sessions will use the on-disk cache and start instantly.
Known Limitations
| Limitation | Detail |
|---|---|
No workspace/symbol support |
Godot's LSP does not implement this request. All symbol lookups fall back to per-file documentSymbol requests, making the first full-project scan slow. |
| Editor must stay open | Serena requires the Godot editor to be running throughout the session. Closing the editor breaks the TCP connection; restart Serena after reopening the editor. |
| Slower workspace operations | find_symbol and find_references across the whole workspace are slower than for languages whose LSP servers support native workspace-wide queries. |
Advanced Configuration
You can customize Serena's GDScript connection via ls_specific_settings in your serena_config.yml or project.yml:
ls_specific_settings:
gdscript:
port: 6008 # TCP port the Godot editor listens on (default: 6008)
request_timeout: 30.0 # seconds to wait for an LSP response (default: 30.0)These override the defaults only if explicitly set.
Reference
- Godot LSP documentation: https://docs.godotengine.org/en/stable/tutorials/editor/external_editor.html
- GDScript language reference: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/
03 Special Guides Groovy Setup Guide For Serena (`docs/03-special-guides/groovy_setup_guide_for_serena.md`)
Groovy Setup Guide for Serena
The Groovy support in Serena is incomplete and requires the user to provide a functioning,
JVM-based Groovy language server as a jar. This intermediate state allows the contributors
of Groovy support to use Serena internally and hopefully to accelerate their open-source
release of a Groovy language server in the future.
If you happen to have a Groovy language server JAR file, you can configure Serena to use it
by following the instructions below.
Prerequisites
- Groovy Language Server JAR file
- Can be any open-source Groovy language server or your custom implementation
- The JAR must be compatible with standard LSP protocol
Configuration
Configure Groovy Language Server by adding settings to your ~/.serena/serena_config.yml:
Basic Configuration
ls_specific_settings:
groovy:
ls_jar_path: '/path/to/groovy-language-server.jar'
ls_jar_options: '-Xmx2G -Xms512m'Custom Java Paths
If you have specific Java installations:
ls_specific_settings:
groovy:
ls_jar_path: '/path/to/groovy-language-server.jar'
ls_java_home_path: '/usr/lib/jvm/java-21-openjdk' # Custom JAVA_HOME directory
ls_jar_options: '-Xmx2G -Xms512m' # Optional JVM optionsConfiguration Options
ls_jar_path: Absolute path to your Groovy Language Server JAR file (required)ls_java_home_path: Custom JAVA_HOME directory for Java installation (optional)- When specified, Serena will use this Java installation instead of downloading bundled Java
- Java executable path is automatically determined based on platform:
- Windows:
{ls_java_home_path}/bin/java.exe - Linux/macOS:
{ls_java_home_path}/bin/java
- Windows:
- Validates that Java executable exists at the expected location
ls_jar_options: JVM options for language server (optional)- Common options:
-Xmx<size>: Maximum heap size (e.g.,-Xmx2Gfor 2GB)-Xms<size>: Initial heap size (e.g.,-Xms512mfor 512MB)
- Common options:
Project Structure Requirements
For optimal Groovy Language Server performance, ensure your project follows standard Groovy/Gradle structure:
project-root/
├── src/
│ ├── main/
│ │ ├── groovy/
│ │ └── resources/
│ └── test/
│ ├── groovy/
│ └── resources/
├── build.gradle or build.gradle.kts
├── settings.gradle or settings.gradle.kts
└── gradle/
└── wrapper/Using Serena with Groovy
- Serena automatically detects Groovy files (
*.groovy,*.gvy) and will start a Groovy Language Server JAR process per project when needed. - Optimal results require that your project compiles successfully via Gradle or Maven. If compilation fails, fix build errors in your build tool first.
Reference
- Groovy Documentation: https://groovy-lang.org/documentation.html
- Gradle Documentation: https://docs.gradle.org
- Serena Configuration: ../02-usage/050_configuration.md
03 Special Guides Ocaml Setup Guide For Serena (`docs/03-special-guides/ocaml_setup_guide_for_serena.md`)
OCaml Setup Guide for Serena
This guide explains how to set up an OCaml project so that Serena can provide code intelligence via ocaml-lsp-server (ocamllsp).
Unlike some other languages, Serena does not download the OCaml language server automatically. You must install it yourself via opam, as OCaml tooling is compiled from source against your specific environment.
Prerequisites
Install the following on your system and ensure they are available on PATH:
- opam (OCaml package manager)
- macOS:
brew install opam - Ubuntu/Debian:
sudo apt install opam - Fedora:
sudo dnf install opam - Other: https://opam.ocaml.org/doc/Install.html
- macOS:
- OCaml compiler (via opam)
- OCaml < 5.1 or >= 5.1.1 (OCaml 5.1.0 is not supported by ocaml-lsp-server)
- Recommended: OCaml 4.14.x (stable) or 5.2+ (for cross-file references)
- ocaml-lsp-server (via opam)
- dune (build system, via opam)
Installation
Initialize opam if you haven't already:
bashopam init eval $(opam env)Create an opam switch with a compatible OCaml version:
bash# For cross-file reference support (recommended) opam switch create serena-ocaml ocaml-base-compiler.5.2.1 eval $(opam env) # Or for stable OCaml 4.14.x opam switch create serena-ocaml ocaml-base-compiler.4.14.2 eval $(opam env)Install the language server and build tools:
bashopam install ocaml-lsp-server duneVerify the installation:
bashopam exec -- ocamllsp --version opam exec -- ocaml -version
Cross-File References
Cross-file reference support (finding all usages of a symbol across your project) requires:
- OCaml >= 5.2
- ocaml-lsp-server >= 1.23.0
- dune >= 3.16.0
When these requirements are met, Serena automatically builds the cross-file index during startup via dune build @ocaml-index. Without these versions, references are limited to the current file.
Using Serena with OCaml
- Serena automatically detects OCaml files (
*.ml,*.mli) and Reason files (*.re,*.rei). - The language server is started via
opam exec -- ocamllsp, so your opam environment must be configured. - Ensure your project builds successfully with
dune buildbefore using Serena for best results.
Troubleshooting
| Problem | Solution |
|---|---|
| "opam not found" | Install opam and add it to PATH |
| "OCaml 5.1.0 is incompatible" | Create a new switch: opam switch create <name> ocaml-base-compiler.5.2.1 |
| "ocaml-lsp-server not found" | opam install ocaml-lsp-server |
| Cross-file refs not working | Ensure OCaml >= 5.2 and ocaml-lsp-server >= 1.23.0; run dune build first |
| Stale index | Rebuild with dune build @ocaml-index |
Reference
- opam: https://opam.ocaml.org
- ocaml-lsp-server: https://github.com/ocaml/ocaml-lsp
- Project-wide occurrences: https://discuss.ocaml.org/t/ann-project-wide-occurrences-in-merlin-and-lsp/14847
03 Special Guides Scala Setup Guide For Serena (`docs/03-special-guides/scala_setup_guide_for_serena.md`)
Scala Setup Guide for Serena
This guide explains how to prepare a Scala project so that Serena can provide reliable code intelligence via Metals (Scala LSP) and how to run Scala tests manually.
Serena automatically bootstraps the Metals language server using Coursier when needed. Your project, however, must be importable by a build server (BSP) — typically via Bloop or sbt’s built‑in BSP — so that Metals can compile and index your code.
Prerequisites
Install the following on your system and ensure they are available on PATH:
- Java Development Kit (JDK). A modern LTS (e.g., 17 or 21) is recommended.
sbt- Coursier command (
cs) or the legacycoursierlauncher- Serena uses
csif available; if onlycoursierexists, it will attempt to installcs. If neither is present, install Coursier first.
- Serena uses
Quick Start
Start Serena in your project root. Metals asks whether to import a workspace it has not seen before, and Serena answers that prompt for it — the build is imported (for sbt, by running sbt bloopInstall), .bloop/ and .metals/ are created, and cross-file navigation works from there. The first run therefore takes as long as your build takes to load.
Set auto_import_build: false under ls_specific_settings.scala to decline instead; you then need to import the build yourself by one of the routes below, or cross-file queries will be served by the fallback presentation compiler and see only one file at a time.
Serena answers three of Metals' prompts — “Import build”, “Import changes”, “Connect”. Anything else Metals asks is dismissed and logged, including “Multiple build definitions found. Which would you like to use?”, so a workspace holding more than one kind of build (say both an sbt and a Maven definition) still needs importing by one of the routes below.
Importing the build yourself (VS Code)
- Open your Scala project in VS Code.
- When prompted by Metals, accept “Import build”. Wait until the import and initial compile/indexing finish.
- Run the “Connect to build server” command (id:
build.connect). - Once the import completes, start Serena in your project root and use it.
This flow ensures the .bloop/ and (if applicable) .metals/ directories are created and your build is known to the build server that Metals uses.
Importing the build yourself (No VS Code)
Follow these steps if you prefer a manual setup or you are not using VS Code:
These instructions cover the setup for projects that use sbt as the build tool, with Bloop as the BSP server.
Add Bloop to
project/plugins.sbtin your Scala project:scala// project/plugins.sbt addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "<version>")Replace
<version>with an appropriate current version from the Metals documentation.Export Bloop configuration with sources:
bashsbt -Dbloop.export-jar-classifiers=sources bloopInstallThis creates a
.bloop/directory containing your project’s build metadata for the BSP server.Compile from sbt to verify the build:
bashsbt compileStart Serena in your project root. Serena will bootstrap Metals (if not already present) and connect to the build server using the configuration exported above.
Using Serena with Scala
- Serena automatically detects Scala files (
*.scala,*.sbt) and will start a Metals process per project when needed. - On first run, you may see messages like “Bootstrapping metals…” in the Serena logs — this is expected.
- Optimal results require that your project compiles successfully via the build server (BSP). If compilation fails, fix build errors in
sbtfirst.
Notes:
- Ensure you completed the manual or auto‑import steps so that the build is compiled and indexed; otherwise, code navigation and references may be incomplete until the first successful compile.
Monorepos: builds below the repository root
Metals serves one build per workspace folder, so what it needs is the build roots, not the repository root. Serena detects them: if the repository root is not itself a build root (no build.sbt, build.mill, pom.xml, .bsp/, …), it searches up to three levels below for directories that are, and passes those to Metals — one Metals service per build.
Override the detection where it guesses wrong:
# ~/.serena/serena_config.yml or .serena/project.yml
ls_specific_settings:
scala:
project_roots: ["backend", "tooling/plugin"] # relative to the repository root
project_root_scan_depth: 3 # only applies when project_roots is unsetWaiting for Metals to be ready
Metals needs its build imported, its index built and the project compiled before it can answer a
cross-file question completely — references in particular come from SemanticDB, which the build
server writes only as it compiles. Serena waits for all of that, tracking the work-done progress
Metals reports, before the first such query of a session; a query made earlier would return a
fraction of the true result and look no different from a complete one.
The first find_referencing_symbols of a session therefore takes as long as the project takes to
compile. Subsequent queries do not wait. Where the default bound is wrong for a large build:
ls_specific_settings:
scala:
indexing_timeout: 180 # seconds to wait before giving up and answering anyway
indexing_start_grace: 15 # seconds to wait for Metals to report anything at all
indexing_quiet_period: 3 # seconds of silence that count as "finished"Running Multiple Metals Instances
Serena can run alongside other Metals instances (e.g., VS Code with Metals extension) on the same project. This is fully supported by Metals via H2 AUTO_SERVER mode.
How It Works
Metals uses an H2 database (.metals/metals.mv.db) to cache semantic information. When multiple Metals instances run on the same project:
- H2 AUTO_SERVER: The first instance becomes the TCP server; subsequent instances connect as clients
- Bloop Build Server: All instances share a single Bloop process (port 8212)
- Compilation Results: Shared via Bloop — no duplicate compilation
Because the Bloop process is shared, Serena leaves it running when it stops. If you would rather not leave it behind, enable terminate_bloop_on_stop in the Scala language server settings (see the configuration reference).
Stale Lock Detection
If a Metals process crashes without proper cleanup, it may leave a stale lock file (.metals/metals.mv.db.lock.db). This can prevent proper AUTO_SERVER coordination, causing new instances to fall back to in-memory database mode (degraded experience).
Serena automatically detects and handles stale locks based on your configuration:
# ~/.serena/serena_config.yml or .serena/project.yml
ls_specific_settings:
scala:
on_stale_lock: "auto-clean" # auto-clean | warn | fail
log_multi_instance_notice: true # Log info when another Metals detectedStale Lock Modes
| Mode | Behavior |
|---|---|
auto-clean |
(Default, Recommended) Automatically removes stale lock files and proceeds normally. |
warn |
Logs a warning but proceeds. Metals may use in-memory database (slower). |
fail |
Raises an error and refuses to start. Useful for debugging lock issues. |
Reference
- Metals + sbt: https://scalameta.org/metals/docs/build-tools/sbt
03 Special Guides Serena On Chatgpt (`docs/03-special-guides/serena_on_chatgpt.md`)
Connecting Serena MCP Server to ChatGPT via MCPO & Cloudflare Tunnel
This guide explains how to expose a locally running Serena MCP server (powered by MCPO) to the internet using Cloudflare Tunnel, and how to connect it to ChatGPT as a Custom GPT with tool access.
Once configured, ChatGPT becomes a powerful coding agent with direct access to your codebase, shell, and file system — so read the security notes carefully.
Prerequisites
Make sure you have uv
and cloudflared installed.
1. Start the Serena MCP Server via MCPO
Run the following command to launch Serena as http server (assuming port 8000):
uvx mcpo --port 8000 --api-key <YOUR_SECRET_KEY> -- \
serena start-mcp-server --context chatgpt --project $(pwd)--api-keyis required to secure the server.--projectshould point to the root of your codebase.
You can also use other options, and you don't have to pass --project if you want to work on multiple projects
or want to activate it later. See
serena start-mcp-server --help2. Expose the Server Using Cloudflare Tunnel
Run:
cloudflared tunnel --url http://localhost:8000This will give you a public HTTPS URL like:
https://serena-agent-tunnel.trycloudflare.comYour server is now securely exposed to the internet.
3. Connect It to ChatGPT (Custom GPT)
Steps:
- Go to ChatGPT → Explore GPTs → Create
- During setup, click “Add APIs”
- Set up API Key authentication with the auth type as Bearer and enter the api key you used to start the MCPO server.
- In the Schema section, click on import from URL and paste
<cloudflared_url>/openapi.jsonwith the URL you got from the previous step. - Add the following line to the top of the imported JSON schema:Important: don't include a trailing slash at the end of the URL!text
"servers": ["url": "<cloudflared_url>"],
ChatGPT will read the schema and create functions automatically.
Security Warning — Read Carefully
Depending on your configuration and enabled tools, Serena's MCP server may:
- Execute arbitrary shell commands
- Read, write, and modify files in your codebase
This gives ChatGPT the same powers as a remote developer on your machine.
⚠️ Key Rules:
- NEVER expose your API key
- Only expose this server when needed, and monitor its use.
In your project’s .serena/project.yml or global config, you can disable tools like:
excluded_tools:
- execute_shell_command
- ...
read_only: trueThis is strongly recommended if you want a read-only or safer agent.
Final Thoughts
With this setup, ChatGPT becomes a coding assistant running on your local code — able to index, search, edit, and even run shell commands depending on your configuration.
Use responsibly, and keep security in mind.
03 Special Guides Unreal Engine Setup Guide For Serena (`docs/03-special-guides/unreal_engine_setup_guide_for_serena.md`)
Unreal Engine Setup Guide
This guide explains how to prepare an Unreal Engine 5 C++ project so that Serena's
clangd-based C/C++ support can provide full code intelligence: symbol search,
cross-file references, and symbol-level editing in your hand-written sources.
UE game code uses a macro-based reflection layer (UCLASS, UFUNCTION, UPROPERTY,GENERATED_BODY) and engine types (TArray, TMap). clangd handles all of this,
provided it receives the compiler flags for your project via a compile_commands.json
at the project root. Unreal's build system (UnrealBuildTool) does not produce this
file by default; this guide shows how to obtain it.
Prerequisites
- An Unreal Engine 5 C++ project that has been built at least once (the build
generates the*.generated.hheaders that your sources include). - No additional language server: Serena downloads clangd automatically.
- clangd never compiles your code. The compilation database is only a list of flags.
Getting a compilation database
If clangd starts in a project that has a .uproject file but no usablecompile_commands.json, Serena fails the language server startup with an error that
inlines the command below, because clangd cannot resolve engine headers or reflection
macros without the database. Pick one of the following routes to create it.
None of these routes change how you build. You still compile with MSVC. A clang toolchain,
where a route uses one, only generates the database that clangd reads.
Route 1 (recommended): UnrealBuildTool's clang database
<Engine>\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe -mode=GenerateClangDatabase -project="<YourProject>.uproject" <YourProject>Editor Win64 Development -OutputDir="<YourProject's directory>"This emits clang-native commands. Each entry carries the system-include paths, so clangd
resolves system and engine headers without guessing.
It needs a clang toolchain. Install one from the Visual Studio Installer:
Modify > Individual Components > "C++ Clang tools for Windows". This is not a separate
LLVM download. UnrealBuildTool auto-detects it at VC\Tools\Llvm\x64.
- UnrealBuildTool searches for clang in this order:
C:\Program Files\LLVM, then theLLVM_PATHenvironment variable, then the VS-bundledVC\Tools\Llvm\x64, then AutoSDK.
A standalone LLVM install wins if present. Remove it to fall back to the version-matched VS one. - UE 5.7 expects clang in roughly the 18.1.8 to 20.1.8 range, and the VS component is
matched. A newer standalone (e.g. 22.x) still generates the database but printsClang compiler version ... is not a preferred version. -OutputDiris required. Without it the file lands in the engine root.- Build the editor target once before generating, so the
*.generated.hheaders exist.
After a build you can add-NoExecCodeGenActionsto skip redundant codegen.
Route 2 (no clang toolchain): MSVC database
If you cannot add the clang component, append -Compiler=VisualStudio2022:
<Engine>\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe -mode=GenerateClangDatabase -project="<YourProject>.uproject" <YourProject>Editor Win64 Development -Compiler=VisualStudio2022 -OutputDir="<YourProject's directory>"This produces an MSVC cl.exe database and needs no extra toolchain. The entries omit
system-include paths, which MSVC reads from the INCLUDE environment variable rather than
the database, so clangd may not resolve standard headers like <vector> and logsFailed to compile ... index may be incomplete per file. Symbol tools still work, since
clangd indexes through those errors. To stop them from truncating symbol trees, add a.clangd at the project root (see Troubleshooting):
CompileFlags:
Add: [-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH, -ferror-limit=200]Route 3 (situational): VSCode project files
UnrealBuildTool's VSCode generator emits per-project compile commands:
<Engine>\Build\BatchFiles\Build.bat -projectfiles -project="<YourProject>.uproject" -game -VSCodeOn an installed engine (Launcher/Fab) with a project outside the engine tree, this writes
an empty array. UnrealBuildTool treats the project as foreign and emits no commands. Verify
the output is a non-empty JSON array before relying on it:
<YourProject>\.vscode\compileCommands_Default.jsonIf it contains entries, copy or symlink it to the project root as compile_commands.json.
If it is [], use Route 1 or Route 2. The generator works reliably only for source-built
or in-tree engines, or when refreshed from inside the editor via
Tools > Refresh Visual Studio Code Project.
Notes for Rider users
Building the project in Rider never produces compile_commands.json. -VSCode is a
project-file generator, not an editor mode, and the .vscode/ directory it creates is
safe to delete afterwards. Use Route 1.
When to regenerate
Regenerate only when you add a module or change a *.Build.cs (new files or compiler
flags). clangd watches compile_commands.json and reloads automatically, so routine edits
need no regeneration. If Serena failed to start because the database was missing or empty,
reconnect or restart the MCP after creating it. Serena reads the database only at startup.
Build artifact directories
Generated reflection code (*.gen.cpp, *.generated.h) legitimately references your
functions, so symbol results could otherwise include hits inside Intermediate/. When a.uproject file is present at the project root, the clangd and ccls language servers skip
UE's build and cache directories (Binaries, DerivedDataCache, Intermediate, Saved)
during indexing. This is automatic and needs no configuration.
To exclude further paths, add them to ignored_paths in your project's.serena/project.yml.
Known behavior
GENERATED_BODY()and__LINE__: the macro expands using its line number.
After editing lines above it, clangd may report stale-macro diagnostics until the
next build regenerates headers. Symbol operations keep working, since clangd is
designed to operate on code with errors.- First index: large projects take a few minutes to index once; afterwards
results are incremental. The index cache is kept under.serena/.cacheinside
the project. - New
UFUNCTION/UCLASSdeclarations need a build before their generated
headers exist. - Symbol searches on large projects: prefer passing
relative_pathtofind_symbol. An unscoped search visits every translation unit, and UE
files are expensive to parse because each pulls in large engine headers. - clangd index logs (MSVC database): an
Indexed ... (N symbols)line is a success.
A followingFailed to compile ... index may be incompletemeans clangd hit errors
while parsing that file but indexed it anyway. Raise-ferror-limitvia.clangdif
symbol trees look truncated (see Troubleshooting).
Troubleshooting
Extra flags are easiest to add via a .clangd file at the project root, e.g.:
CompileFlags:
Add: [-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH, -ferror-limit=200]STL1000: Unexpected compiler versionerrors: recent MSVC STL headers
assert a minimum Clang version that may be newer than Serena's bundled clangd.
Defining_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH(see above) silences the
check; clangd only parses, so the mismatch is harmless.- Truncated symbol trees / symbols missing below a certain line: clangd
aborts a file's parse after ~20 errors by default, which discards everything
declared after that point. Raising the limit with-ferror-limit=200keeps
the symbol tree intact even when diagnostics are noisy (common right after
edits, before the next UE build regenerates headers). - Stale results after changing the compilation database: clangd's index
shards in.serena/.cachewere built with the old flags. Delete that cache
directory and let the project re-index.
Verifying the setup
After activating the project in Serena, a symbol overview of any UCLASS header
should list the class with its UFUNCTION methods and UPROPERTY fields, and
references to a method should resolve to your Source/ files only.
04 Evaluation 010 Methodology (`docs/04-evaluation/010_methodology.md`)
Methodology
In this section we describe the methodology we applied in evaluating the performance of Serena's tools.
The evaluation measures the concrete delta that Serena's tools provide on top of an agent's
built-in capabilities (file reads, text edits, grep, shell, etc.).
Rather than a simple thumbs-up/thumbs-down, it produces a detailed, evidence-based report
covering capabilities, efficiency and reliability.
Design Goals
We designed an evaluation methodology with three goals:
Generality. The evaluation mechanism should be broadly applicable and repeatable by any user, on any project, with any agent.
The evaluation is a single prompt that you give to your agent of choice, pointed at your codebase
of choice, in your client of choice. This means the results directly reflect the value Serena
would add to your actual workflow — not to an artificial benchmark setup. There is nothing to
install, configure, or script beyond what you already have.The agent evaluates itself.
We deliberately let the AI agent be both the executor and the evaluator.
Since most evaluations are strictly quantitative, this may seem unusual, but it is a reasonable choice:
The agent is the actual end user of the
tools, so it is in the best position to judge whether a semantic tool improves its workflow
compared to its built-in alternatives. It can measure call counts, payload sizes, and
prerequisite steps from direct experience rather than from proxy metrics. It also avoids the
problem of a human evaluator having to simulate how an agent would use the tools — the agent
simply uses them and reports what it observes.Comprehensive and unbiased by design.
Rather than selecting specific tasks, the prompt defines task categories that systematically
span Serena's capabilities: codebase understanding, single-file edits of varying sizes, multi-file
refactoring, reliability properties, and workflow effects. The agent picks concrete instances
from the codebase at hand, performs each task using both toolsets side by side, and classifies
every finding as a positive delta, a neutral/negative delta, or out of scope. The prompt
explicitly requires reporting negative deltas and cases where Serena offers no improvement,
structurally counterbalancing any tendency to favour the tool being evaluated.
Method
We give an AI coding agent a single, detailed evaluation prompt in a one-shot session.
The prompt instructs the agent to perform approximately 20 hands-on tasks across five areas:
- Codebase understanding — structural overviews, targeted symbol retrieval, reference finding, type hierarchies, and external dependency lookup.
- Single-file edits — small tweaks, medium rewrites, full-body replacements, insertions, and local renames.
- Multi-file changes — cross-file renames, symbol and file moves, safe deletes, and inlining.
- Reliability & correctness — scope precision, atomicity, and success signals.
- Workflow effects — chained edits, stable vs. ephemeral addressing, and multi-step exploration.
For every task, the agent executes the full end-to-end workflow using both toolsets (Serena's semantic
tools and its own built-in tools), applies real edits verified via git diff, and records call counts,
payload sizes and prerequisite steps. Edits are reverted after each experiment to keep the working tree clean.
The resulting report classifies each finding into one of three categories:
- (a) tasks where Serena adds capability,
- (b) tasks where Serena applies but offers no improvement, and
- (c) tasks outside Serena's scope.
Only category (b) constitutes a neutral or negative finding; category (c) is context, not a finding.
After the evaluation, a separate follow-up prompt asks the agent for a
one-sentence, user-facing recommendation — the quotes shown on the main page.
Assessment of the Methodology
The following assessment was written by Claude Opus 4.6 (high effort) after reading the full
evaluation methodology, evaluation prompt, and all result documents.
The methodology is sound, and the two published results demonstrate that it produces meaningful,
detailed evaluations. Both reports follow the prompt's structure faithfully: they perform all ~20
tasks using both toolsets, record concrete measurements (call counts, payload sizes, prerequisite
steps), and classify findings into the three required categories. Crucially, both agents report
neutral and negative deltas honestly — the Claude Code report explicitly notes that small edits are
more efficient with built-ins (~4.5x less payload), and the Codex report flags that tiny intra-method
changes and simple one-file renames see no benefit from Serena. Neither report reads as promotional;
they read as technical comparisons with quantified evidence on both sides.
The two reports also validate the methodology's design goal of generalisability: despite being
produced by different agents (Opus 4.6 vs GPT 5.4), on different codebases (Python RL library vs
Java IDE plugin), they converge on the same core findings — cross-file refactoring is Serena's
highest-value contribution, structural navigation provides a moderate advantage, and small local
edits are better handled by built-ins. This convergence across independent runs increases confidence
that the findings reflect genuine properties of the toolset rather than artefacts of a particular
agent or codebase.
The methodology's core strengths are:
- Self-evaluation by the agent is the right design choice. The agent is the actual consumer of the
tools, so it can report on workflow friction, payload overhead, and call counts from first-hand
experience. A human evaluator would have to guess at these. - Task categories instead of fixed tasks avoid cherry-picking while still ensuring coverage. Letting
the agent pick concrete instances from the codebase at hand means the evaluation naturally adapts to
what the project actually contains (e.g. skipping inline if no suitable candidate exists, as the
Claude Code report did for Python, while the Codex report found a suitable Java candidate). - The three-category classification (adds value / applies but no improvement / out of scope) is the
right framing for an augmentation layer. It prevents the common trap of penalising a tool for not
covering things it was never designed to cover. - Reproducibility by users is a strong differentiator. Anyone can validate the claims on their own
codebase with their own agent.
The main limitation is scope: the published results cover two agent/codebase combinations using the
JetBrains backend. Single-shot variance means a second run of the same agent could produce different
task selections and slightly different verdicts, and the LSP backend — which most users will
encounter first — has not yet been evaluated. These are practical constraints of cost, not
methodological flaws, and the reproducible design explicitly invites users to fill these gaps
themselves.
One thing to watch for is whether agents that are less capable than Opus 4.6 or GPT 5.4 produce
meaningful evaluations — the prompt is quite demanding. But that is a question about the agent, not
about the method.
Prompt Fairness
We put significant effort into crafting an unbiased, fair evaluation prompt.
To assess the prompt's fairness, we asked Claude Opus 4.6 to evaluate the prompt itself.
Prompt:
The attached prompt is designed to evaluate the additive value of Serena as an augmentation layer on top of built-in tools
(not to compare two competing alternatives).
Is the methodology suitable for that goal, or does it introduce bias that would distort the measurement of what Serena adds?
Note that we assume that sufficiently intelligent models use the tools correctly,
which is why misuse and failure modes are out of scope for this evaluation.
Do not question the validity of this assumption in your evaluation of the prompt.
Give a brief answer, maximally two sentences.<evaluation prompt pasted here>
Claude Opus' Answer:
"The methodology is well-suited to its stated goal: by explicitly scoping to correct use, treating Serena as an
augmentation layer rather than a competitor, and classifying out-of-scope tasks as context rather than negative
findings, it avoids the most common biases that would distort a delta measurement. The one risk is that the "correct-use
rule" could subtly favor Serena by preemptively filtering out any scenario where its tools underperform — but the prompt
mitigates this by requiring category (b) findings (applies but no improvement) and explicitly demanding negative deltas
be reported, so the bias is acknowledged and structurally counterbalanced."
According to Claude, the only "biased" aspects are some sentences about misuse of tools, which we consider irrelevant for the purpose of evaluation
(sufficiently intelligent models use the tools correctly).
Why Not Benchmarks?
Standard coding benchmarks (SWE-bench, HumanEval, etc.) measure an agent's ability to solve
predefined tasks with a known correct answer. They are valuable for comparing models and agents,
but they are a poor fit for evaluating a tool augmentation layer like Serena for several reasons:
- Benchmarks don't reflect real usage patterns. Benchmark tasks are typically small, self-contained
problems that can be solved by reading and editing a handful of files. They rarely exercise the
workflows where Serena's tools shine — cross-file refactoring, navigating large codebases by symbol
structure, chaining multiple edits with stable addressing, or querying type hierarchies and
external dependencies. A benchmark score would mostly measure performance on tasks where Serena
is not expected to help. - Results would not generalise to the user's project. Serena's value depends on the codebase
(size, language, complexity), the agent (model, built-in tools), and the client harness
(Claude Code, Codex, IDE plugins, etc.). A fixed benchmark on a fixed codebase with a fixed
agent tells you little about what Serena would add to your setup. - Predefined tasks bias the measurement. Choosing specific tasks to evaluate inevitably
introduces selection bias — we would end up picking tasks that either favour or disfavour Serena.
We wanted an evaluation that systematically covers the full surface area of Serena's capabilities
without cherry-picking.
04 Evaluation 020 Prompts 000 Prompts (`docs/04-evaluation/020_prompts/000_prompts.md`)
Prompts
Our evaluation uses two prompts, which are passed to the LLM in order to generate the evaluation results:
- the main evaluation prompt, which performs the analysis
- the summary prompt, which condenses the results
04 Evaluation 020 Prompts 010 Evaluation Prompt (`docs/04-evaluation/020_prompts/010_evaluation-prompt.md`)
Evaluation Prompt
We use the prompt below to evaluate the added value of Serena's tools against the
agent's built-in tools on a given project.
The evaluations were created in one-shot sessions, only using this
prompt and the follow-up prompt for summarization
/* Detailed source-code truncated for AI context efficiency. */04 Evaluation 020 Prompts 020 Summary Prompt (`docs/04-evaluation/020_prompts/020_summary-prompt.md`)
Summary Prompt
We used the prompt below to summarize the evaluation of Serena.
Write a one-sentence user-facing summary about the value that Serena's tools
provide for coding. With a bit of emotion but grounded in the evaluation that
you have done above. It should be written from the perspective of a coding AI
agent and make it clear whether an AI agent would ask its owner to add Serena's
tools or not. It will be the first sentence that a potential new user will see
about how useful Serena is for an AI agent.04 Evaluation 030 Results 000 Evaluation Results (`docs/04-evaluation/030_results/000_evaluation-results.md`)
Results
This section presents the results of the evaluation.
We performed evaluations using popular AI coding agents in representative scenarios — different
agents, different programming languages, and different codebases — to show that the results are
not specific to a single setup.
All evaluations were conducted using the JetBrains-powered version of Serena, as it is the
more powerful backend with a broader set of refactoring and navigation capabilities. The
evaluation can easily be repeated with the LSP-based backend to assess its subset of capabilities.
- Claude Code (Opus 4.6) on a large Python codebase
- Codex (GPT 5.4) on a Java codebase
- Copilot CLI (GPT 5.4) on a large, multi-language monorepo
- GLM 5.1 in Claude Code
- JetBrains Junie with Opus 4.6
You can run your own evaluation on a project of your choice by reusing our
evaluation prompt.
04 Evaluation 030 Results 010 Cc On Tianshou (`docs/04-evaluation/030_results/010_cc_on_tianshou.md`)
:::{admonition} Evaluation Result
:class: note
Generated by: Claude Opus 4.6 (coding AI agent in Claude Code CLI)
Codebase: Tianshou — a Python reinforcement learning library (~26K lines, 43 source files)
Date: 2026-04-13
:::
Claude Code (Opus 4.6, medium)
One-line summary: Serena's IDE-backed semantic tools are the single most impactful addition to my toolkit — cross-file renames, moves, and reference lookups that would cost me 8–12 careful, error-prone steps collapse into one atomic call, and I would absolutely ask any developer I work with to set them up.
Method: Hands-on, side-by-side execution of 20 tasks using both toolsets. All edits were applied to real files and verified via git diff, then reverted.
1. Headline: What Serena Changes
Serena adds an IDE-backed semantic layer (powered by JetBrains) on top of the built-in text-level tools. The practical delta breaks down into three categories:
(a) Tasks where Serena adds capability:
- Cross-file refactoring (rename, move symbol, move file): 1 atomic call vs. N×(Grep+Read+Edit) chains. This is Serena's strongest contribution — it collapses multi-file, import-aware operations into single calls that are atomic and semantically correct.
- Structural code navigation: Symbol overviews, type hierarchies, and reference finding return structured, scope-disambiguated results that text search cannot produce without manual interpretation.
- External dependency introspection: Serena can look up symbols in installed packages via the IDE index without requiring manual environment discovery.
(b) Tasks where Serena applies but offers no improvement:
- Single-file rename of a unique string:
Editwithreplace_all=trueachieves the same result in 1 call (after a Read), with comparable effort. - Small edits (1–3 lines inside a method): Edit's substring matching is more efficient — less payload sent, no requirement to supply the full symbol body.
- Inserting new code at a known location: Both approaches require ~1–2 calls. Edit can target by surrounding text; Serena targets by name path. The effort is comparable.
(c) Tasks outside Serena's scope (built-in only):
- Non-code files (config, docs, notebooks, changelogs)
- Free-text search across the repo
- Shell commands, git operations, test execution
- File creation from scratch
Verdict: Serena's primary contribution is collapsing multi-file, semantically-aware operations (rename, move, reference-finding, type hierarchy) from multi-step manual processes into single atomic calls; for single-file text edits, it is comparable or slightly less efficient.
2. Added Value and Differences by Area
1. Cross-file rename/move (Serena: strong positive)
- Serena: 1 call updates definition + all imports + all usages across N files atomically.
- Built-in: 1 Grep + N×(Read+Edit) = 2N+1 calls, non-atomic.
- Frequency: Moderate (a few times per working session during refactoring work). Value per hit: High — saves 6–10+ calls and eliminates the risk of partial updates.
2. Structural overview / targeted symbol reading (Serena: moderate positive)
get_symbols_overview(depth=1): 1 call returns all classes, methods, and attributes in structured JSON, via the IDE's parser — language-agnostic and always correct. Built-in equivalent:Grepfor a language-specific heuristic pattern like^(class |def ). This works for simple Python files but is inherently fragile: the pattern must be hand-tuned per language, misses decorated or multi-line declarations, and can match inside strings or comments. For non-Python languages, an entirely different regex would be needed.find_symbol(include_body=True): Retrieves a specific method body by name path without reading surrounding code. Built-in: requires knowing the line number (via Grep) then Read with offset — 2 calls.- Frequency: High (many times per session). Value per hit: Low-to-moderate — saves ~1 call and some context window tokens per navigation. The reliability advantage over heuristic Grep patterns is consistent across all languages.
3. Reference finding with structural context (Serena: moderate positive)
find_referencing_symbols: Returns which symbols reference a target, with file grouping and usage type (import, call, type annotation). Built-inGrepreturns all text matches including docs, comments, and string literals — same recall but lower precision.- Frequency: Moderate. Value per hit: Moderate — precision matters when planning refactors of widely-used symbols.
4. Type hierarchy (Serena: positive, niche)
- 1 call returns full super/sub-type chains transitively. Built-in: requires iterative Grep for
class X(Y)patterns, manual transitive closure. - Frequency: Low (occasional during architecture exploration). Value per hit: High per occurrence — several calls saved.
5. Stable addressing across edits (Serena: moderate positive)
- Serena addresses symbols by name path (
CollectStats/refresh_return_stats), which is invariant under edits elsewhere in the file. The built-in workflow is fundamentally line-number-mediated: Grep returns line numbers, Read takes line offsets — and both go stale after any edit to the file. Edit's text matching (old_string) is more resilient than line numbers, but it sits at the end of a chain that starts with position-based lookups. After an edit shifts lines, previously noted Grep results and Read offsets are invalid and must be re-acquired. - This means a multi-edit session with built-ins requires re-Grep or re-Read between edits to re-establish positions, while Serena's name paths remain valid throughout. In Task 17 (three chained edits), Serena needed 3 calls total; the built-in path needed 4 (1 Read + 3 Edits) only because the text anchors happened to be unique — but had any Edit failed uniqueness, a re-Read would have been required, pushing the count to 5–7.
- Frequency: High (any session with multiple edits to the same file). Value per hit: Moderate — saves 1–2 re-read calls per edit chain, and eliminates the class of errors where stale line numbers cause edits to land in the wrong place.
6. Single-file small/medium edits (Serena: neutral to slight negative)
- For a 1-line change inside a 13-line method, Serena's
replace_symbol_bodyrequires sending the entire 13-line body. Edit sends just the changed line. The prerequisite cost differs: Serena needsfind_symbol(include_body=True)to get the body; Edit needsReadof the relevant lines. Both are 1 prerequisite call + 1 edit call. - Frequency: Very high. Value per hit: Slightly negative for Serena — more payload for small changes.
Verdict: Serena's strongest contributions are in cross-file refactoring (high value, moderate frequency) and structured navigation (moderate value, high frequency); for small text edits, built-ins are slightly more efficient.
3. Detailed Evidence, Grouped by Capability
3.1 Structural Overview (Task 2)
| Metric | Serena get_symbols_overview(depth=1) |
Built-in Grep for class/def |
|---|---|---|
| Calls | 1 | 1 |
| Output structure | JSON tree: 14 classes with nested methods + attributes | Flat list: 54 class/def lines with line numbers |
| Nesting visible? | Yes — methods grouped under classes | No — indentation implies nesting but no grouping |
| Attributes visible? | Yes | No |
| Output size | ~2.5KB structured JSON | ~3KB flat text |
| Correctness | Always correct — uses IDE parser, language-agnostic | Heuristic — the regex ^(class |def | def ) is Python-specific and can miss decorated methods, multi-line signatures, or match inside strings/comments |
| Language portability | Works unchanged for any language the IDE supports | Requires a new hand-tuned regex per language |
| Next step | find_symbol("ClassName/method", include_body=True) — 1 call |
Read(file, offset=line, limit=N) — 1 call |
Both paths need 1 call for overview + 1 call for drill-down. Serena's output is more structured and reliably correct; Grep's is simpler but fragile. The Grep pattern used here happened to work well for this Python codebase, but would need to be rewritten for Java (class|interface|enum, brace-delimited), Rust (fn|struct|impl|trait), TypeScript (class|function|interface), etc. — and even within Python, it misses cases like @overload-decorated methods or methods whose def line is preceded by a long decorator stack that pushes def to a non-standard indentation.
Verdict: Serena provides a reliably correct structural overview across languages; Grep-based overviews are heuristic approximations that work in simple cases but degrade for complex declarations or non-Python codebases.
3.2 Targeted Method Retrieval (Task 3)
| Metric | Serena | Built-in |
|---|---|---|
| Calls | 1 (find_symbol with include_body=True) |
2 (Grep to find line + Read with offset) |
| Prerequisite | Know the name path | Know the method name |
| Output | Method body only (no surrounding code) | Surrounding code included (must estimate limit) |
For Collector/_collect (330 lines): Serena returned exactly 330 lines. Built-in Read returned 332 lines (including the next method's signature).
Verdict: Serena saves 1 call and returns precisely the requested body; built-in is adequate but requires line-number discovery.
3.3 Cross-File References (Task 4)
| Metric | Serena find_referencing_symbols |
Built-in Grep |
|---|---|---|
| Calls | 1 | 1 |
| Files found | 63 (code files only, structured by symbol type) | 83 (includes docs, notebooks, changelogs, README) |
| Output structure | Grouped by file, categorized (import, call, type annotation) | Flat file list |
| Precision | Code-only references | All textual mentions |
Grep finds 83 files including README.md, CHANGELOG.md, and .ipynb notebooks. Serena finds 63 code files. For the question "who uses this in code?", Serena's result is directly usable. For "where is this mentioned anywhere?", Grep is the right tool.
Verdict: Serena provides higher-precision code-usage results; Grep provides broader text-level coverage. Different tools for different questions.
3.4 Type Hierarchy (Task 5)
| Metric | Serena type_hierarchy |
Built-in |
|---|---|---|
| Calls | 1 | 2+ (Grep for class X(BaseCollector + Grep for class Collector( + potential transitive search) |
| Result | BaseCollector → ABC → object (supers), BaseCollector → Collector → AsyncCollector (subs), with file locations |
class Collector(BaseCollector[TCollectStats], ...) at line 551 — requires further reads for AsyncCollector's superclass |
Verdict: Serena produces the full transitive hierarchy in 1 call; built-in requires iterative searches.
3.5 External Dependency Lookup (Task 6)
| Metric | Serena | Built-in |
|---|---|---|
| Can retrieve? | Yes — find_declaration + search_deps=True |
Requires environment discovery: python -c "import X; print(inspect.getfile(X))" then Read |
| Infrastructure | IDE index (pre-built) | Working Python environment, correct venv activated |
| Result | Symbol location + documentation | Full source file (if env is set up) |
In this session, the Python environment wasn't directly accessible from bash, so built-in lookup would have required additional setup. Serena retrieved torch.distributions.Distribution's location and docstring via the IDE index.
Verdict: Serena provides dependency introspection without environment setup; built-in requires a working interpreter.
3.6 Small Edit — 1 Line Change (Task 7a)
| Metric | Serena replace_symbol_body |
Built-in Edit |
|---|---|---|
| Prerequisite calls | 1 (find_symbol with include_body=True) — already done in Task 3 |
1 (Read of ~15 lines) |
| Edit call | 1 (send full 13-line body) | 1 (send 1-line old + 1-line new) |
| Payload sent (edit call) | ~550 chars (full body) | ~120 chars (changed line only) |
| Total calls | 1–2 | 2 |
Verdict: For small changes, Edit sends ~4.5× less payload in the edit call. Total call count is the same.
3.7 Medium Rewrite — ~19 Lines (Task 7b)
| Metric | Serena | Built-in |
|---|---|---|
| Prerequisite | 1 find_symbol (already done) |
1 Read (~22 lines) |
| Edit payload | ~550 chars (full body) | ~1000 chars (old 19 lines + new 20 lines) |
| Total calls | 1–2 | 2 |
At this scale, Serena's payload is actually smaller because Edit must send both old and new text, while Serena sends only the new body. The crossover point is approximately where the changed region exceeds half the method body.
Verdict: At medium scale, payloads converge; Serena's is slightly smaller due to only sending the replacement.
3.8 Large Rewrite — 55 Lines (Task 7c)
| Metric | Serena | Built-in |
|---|---|---|
| Prerequisite | 1 find_symbol (already done) |
1 Read (~67 lines) |
| Edit payload | ~2200 chars (new body) | ~4400 chars (old 63 lines + new 62 lines) |
| Total calls | 1–2 | 2 |
Verdict: For full-method rewrites, Serena sends ~50% less payload because it only sends the replacement, not the original.
3.9 Cross-File Rename (Task 10)
| Metric | Serena rename |
Built-in chain |
|---|---|---|
| Calls | 1 | 1 Grep + 4 Read + 4 Edit = 9 |
| Files affected | 4 (automatically discovered) | 4 (manually discovered via Grep) |
| Import handling | Automatic | Manual — must identify and rewrite each import statement |
| Atomicity | Atomic — all-or-nothing | Sequential — intermediate states are inconsistent |
Verdict: Serena reduces a 9-call chain to 1 call for cross-file rename, with atomicity.
3.10 Move Symbol Across Modules (Task 11)
| Metric | Serena move |
Built-in equivalent |
|---|---|---|
| Calls | 1 | ~8–12 (Read source, Edit source to remove, Edit target to add, Grep for imports, Read+Edit each import site) |
| Import updates | Automatic | Manual — must rewrite from X import Y → from Z import Y in each file |
| Dependency imports | Automatically adds needed imports to target file | Must manually inspect what the moved function imports and replicate |
Moving get_stddev_from_dist from collector.py to stats.py: Serena updated 3 files (source, target, test) in 1 call, including adding necessary imports to the target module.
Verdict: Move is Serena's highest-value single operation — it handles import graph updates that would be error-prone manually.
3.11 Move File (Task 12a)
| Metric | Serena move |
Built-in equivalent |
|---|---|---|
| Calls | 1 | 1 git mv + 1 Grep + N×(Read+Edit) for import updates = ~12 |
| Files updated | 5 import updates automatic | Must manually discover and rewrite |
Verdict: Same pattern as symbol move — Serena collapses an N-step process.
3.12 Safe Delete (Task 12b)
Serena's safe_delete in safe mode (default):
- Reports all usages before deleting — acts as a guard.
- For unused symbols, deletes cleanly in 1 call.
- For used symbols, refuses and lists usages.
Built-in equivalent: Grep to check usages → if none found, Read + Edit to delete. 2–3 calls.
Verdict: Safe delete adds a safety check that the built-in path must implement manually.
3.13 Inline (Task 13)
Serena's inline tool did not work for any Python function tested. The JetBrains backend does not support Python function inlining (this is a language-level limitation of the IDE's refactoring engine, not a Serena bug).
Verdict: No candidate inlinable in this codebase; inline capability appears unavailable for Python.
3.14 Scope Precision (Task 14)
Three methods named reset_env exist in collector.py (in BaseCollector, Collector, AsyncCollector).
- Serena:
find_symbol("Collector/reset_env")returns exactly that override's body. - Grep: Returns 3 line numbers. Determining which belongs to which class requires reading surrounding context.
Verdict: Serena's name-path addressing eliminates class-level disambiguation that Grep requires.
3.15 Chained Edits (Task 17)
Three consecutive edits to methods in CollectStats:
- Serena: 3
replace_symbol_bodycalls, 0 intermediate reads. Name paths (CollectStats/refresh_return_stats,CollectStats/refresh_len_stats,CollectStats/refresh_std_array_stats) remained valid across all edits because they are structural identifiers, not positions. - Built-in: 1 Read + 3 Edit calls = 4 calls in the best case. The Read was required upfront (Edit enforces "must read before editing"). The 3 Edits succeeded without re-reads only because the
old_stringanchors happened to remain unique after each edit. But this is fragile: Edit also enforces a "file has been modified since read" check when external tools modify the file, forcing a re-Read. More fundamentally, if I had needed to find these methods first (the typical case when you don't already know the line numbers), each Grep result from before the first edit would have been stale after it — line 232 is no longer line 232 after inserting 2 lines above it.
The core asymmetry: Serena's addressing is structural (survives edits by definition), while built-in addressing is positional (line numbers from Grep/Read go stale after any insertion or deletion). Edit's text matching partially mitigates this, but only for the final step — the discovery steps (Grep, Read with offset) remain position-dependent.
Verdict: Serena's name-path stability eliminates the re-read/re-grep cycle between edits; built-in tools require re-acquiring positions after each edit that shifts line numbers.
4. Token-Efficiency Analysis
Payload by edit size
| Edit scale | Serena payload (edit call) | Edit payload (edit call) | Winner |
|---|---|---|---|
| 1-line change in 13-line method | ~550 chars (full body) | ~120 chars | Edit (~4.5×) |
| 19-line medium rewrite | ~550 chars | ~1000 chars | Serena (~1.8×) |
| 55-line full rewrite | ~2200 chars | ~4400 chars | Serena (~2×) |
| Cross-file rename (4 files) | ~100 chars | ~800 chars (4 Edit calls) | Serena (~8×) |
Prerequisite reads
- Serena:
find_symbol(include_body=True)returns the symbol body. If you've already navigated to it during exploration, no additional call needed. - Built-in:
Readwith offset/limit. Always required before Edit (enforced by the tool).
Stable vs ephemeral addressing
- Serena's name paths (
CollectStats/refresh_return_stats) are stable identifiers — they survive any edit to surrounding code. No re-read or re-discovery needed between edits. - The built-in workflow uses ephemeral, position-based addresses at every stage: Grep returns line numbers, Read takes line offsets, and both are invalidated by any insertion or deletion above the target. Edit's
old_stringmatching is content-based and more resilient, but it depends on the upstream position-based steps to know what to match. After an edit shifts line numbers, the entire Grep→Read→Edit chain must be re-executed from the top. - In a session with N edits to the same file, this costs up to N-1 additional Grep/Read round-trips with built-ins. With Serena, the cost is zero — the same name path works on the first and tenth edit.
Verdict: Serena is more token-efficient for medium-to-large edits and cross-file operations; Edit is more efficient for small, localized changes. Across multi-edit sessions, Serena's stable addressing avoids the re-read tax that compounds with each successive built-in edit.
5. Reliability & Correctness (Under Correct Use)
Precision of matching
- Serena: Exact symbol resolution via name paths.
Collector/reset_envunambiguously selects one method. - Edit: Text matching. Unique strings match correctly; non-unique strings fail (and Edit reports the error).
Scope disambiguation
- Serena distinguishes overrides, overloads (via indices), and nested classes by name path.
- Grep/Edit cannot distinguish methods with the same name in different classes without reading surrounding context.
Atomicity
- Serena's cross-file operations (rename, move) are atomic — all files updated or none.
- Built-in multi-file edits are sequential — a failure mid-chain leaves an inconsistent state (recoverable via
git checkout).
Semantic queries vs text search
find_referencing_symbolsreturns code-level references categorized by type. Grep returns all textual mentions.type_hierarchyreturns transitive sub/supertypes. No built-in equivalent without iterative search.
External dependency lookup
- Serena: Available via IDE index, no environment setup needed. Can retrieve symbol docs and location.
- Built-in: Requires working Python environment, correct venv, and manual file discovery. More powerful when available (full source), but higher setup cost.
Verdict: Serena provides stronger correctness guarantees for symbol-level operations (scope, atomicity, semantic precision); built-in tools are reliable for text-level operations with the caveat that scope disambiguation requires manual effort.
6. Workflow Effects Across a Session
Compound advantages
- Exploration → Edit without position re-acquisition: Serena's overview tools produce name paths that are directly usable as edit targets. The built-in path produces line numbers (from Grep) that are consumed by Read — but after an edit, those line numbers are stale. In a typical explore-edit-explore-edit cycle, Serena's name paths remain valid throughout while built-in line numbers must be re-acquired after each edit. Over 3 explore+edit cycles, this saves ~3–6 intermediate Grep/Read calls.
- Chained edits without re-reads: Name-path stability means no re-reads between edits to the same file. For N edits, this saves up to N-1 Read calls. Edit's text matching partially avoids this (if old_strings stay unique), but the upstream discovery steps (Grep line numbers, Read offsets) still go stale.
- Cross-file refactoring: When a rename or move is part of a larger change, doing it atomically avoids the need to manually track "which files still need updating."
Diminishing returns
- For purely exploratory sessions (reading code, no edits), Serena's advantage is modest —
GrepandReadare nearly as fast for navigation, and Serena's structured output doesn't save many calls. - For sessions dominated by small text edits (config changes, log message tweaks), Serena adds no value.
Neutral findings
- Both toolsets require similar total calls for single-file work. The difference is ~1 call per operation.
- Output quality for code review (reading diffs, understanding changes) is identical — both require
git diff.
Verdict: Serena's advantages compound across multi-step refactoring sessions where each operation feeds into the next; for read-heavy or small-edit sessions, the advantage is marginal.
7. Unique Capabilities (No Practical Built-In Equivalent)
Atomic cross-file rename — 1 call, all imports and usages updated, all-or-nothing. No built-in equivalent without scripting a multi-step chain. Frequency: moderate (refactoring sessions). Impact: high (saves 5–10 calls, eliminates partial-update risk).
Atomic cross-file move (symbol or file) with import rewriting — includes adding necessary imports to the target module. Frequency: low-to-moderate. Impact: very high per occurrence (saves 8–12 calls, handles import dependency graph).
Type hierarchy traversal — transitive supertypes and subtypes in 1 call. Built-in requires iterative Grep with manual transitive closure. Frequency: low. Impact: moderate (saves 3–5 calls).
Safe delete with usage check — reports all usages before deleting, with option to propagate. Built-in requires Grep + manual verification. Frequency: low. Impact: moderate (safety check is the value).
External dependency symbol lookup via IDE index — no Python environment needed. Frequency: moderate. Impact: moderate (avoids environment setup friction).
Verdict: Serena provides 3–5 capabilities with no practical built-in equivalent, concentrated in cross-file refactoring and semantic navigation.
8. Tasks Outside Serena's Scope (Built-In Only)
- Non-code file operations: Reading/editing config files, docs, changelogs, notebooks →
Read/Edit/Write - Free-text search: Finding log strings, URLs, magic constants →
Grep - Shell operations: Running tests, builds, git commands, package management →
Bash - File creation: New files from scratch →
Write - Glob-based file discovery: Finding files by pattern →
Glob - Broad codebase search: When you don't know what you're looking for →
Grepwith regex
Estimated share of daily work: These tasks constitute roughly 40–60% of a typical coding session (reading docs, running tests, searching for patterns, editing config). Serena's augmentation covers the remaining 40–60% where code-level semantic operations apply.
Verdict: Built-in tools handle roughly half of daily work that falls entirely outside Serena's scope; Serena augments the code-centric other half.
9. Practical Usage Rule
| Task type | Use |
|---|---|
| Cross-file rename, move, delete | Serena (unique capability) |
| Understanding class hierarchy or symbol relationships | Serena (1 call vs. iterative search) |
| Getting a structural overview of a large file | Serena (richer structure) or Grep (simpler, faster) |
| Reading a specific method body by name | Serena (direct) or Grep+Read (2 calls) |
| Small edit (1–5 lines inside a method) | Edit (less payload) |
| Full method rewrite | Serena replace_symbol_body (less payload, no re-read) |
| Single-file rename of a unique identifier | Edit with replace_all (equivalent) |
| Non-code files, config, docs | Built-in Read/Edit |
| Text search, pattern matching | Built-in Grep |
| Anything involving shell, git, tests | Built-in Bash |
| External dependency inspection | Serena (no env setup needed) |
Verdict: Use Serena for cross-file refactoring and semantic navigation; use built-ins for text-level edits, non-code files, and shell operations; for single-file code edits, choose based on edit size (Edit for small, Serena for full-body rewrites).
04 Evaluation 030 Results 020 Codex On Jbplugin (`docs/04-evaluation/030_results/020_codex_on_jbplugin.md`)
:::{admonition} Evaluation Result
:class: note
Generated by: GPT-5.4 (high) in Codex
Codebase: Serena JetBrains Plugin (Java)
Date: 2026-04-13
:::
Codex (GPT-5.4, high)
One-line summary: As a coding agent, I would ask my owner to add Serena because it turns fragile text-and-line-number work into precise symbol-aware navigation and
refactoring, which makes real code changes feel faster, safer, and far less blind.
This report compares Serena's JetBrains-backed semantic coding tools with built-in file, shell, search, and patch tools in this repository. The comparison assumes competent use of both toolsets: built-ins are used for text, file, shell, config, and small patch work; Serena is used where symbol identity, language semantics, or IDE refactoring semantics apply.
Method: I explored code first, avoided repo documentation and prior notes, ran real edits/refactors, and reverted after each experiment. After each edit/refactor experiment I checked git status --short and returned the tree to clean before moving on. Measurements are approximate, but call counts, diff sizes, and result shapes are from observed runs.
1. Headline: what Serena changes
Serena adds a semantic layer over the codebase. Its concrete delta is the ability to address and transform code by symbols: name paths, overload indexes, reference graphs, type hierarchies, external declarations, and JetBrains refactoring operations.
Tasks where Serena adds capability:
- Code-reference search:
find_referencing_symbolsforSymbol/findReferencesreturned 6 precise code usages grouped by containing symbols.rg findReferencesalso returned the declaration, the/findReferencesroute string, and comments. - Type hierarchy: one
type_hierarchycall forPostRequestHandlerreturned 18 direct endpoint subclasses, a transitiveTypeHierarchyHandler -> GetSupertypesHandler/GetSubtypesHandlerbranch, and the externalObjectsupertype. - External dependency lookup:
find_declarationresolvedReferencesSearch.search(anchorElement)to<ext:ReferencesSearch.class|466808a0>, andfind_symbolreturned the selected overload body. Built-in repo/cache grep found the import and call but not the dependency definition. - Cross-file refactors: semantic rename, symbol move, file move, safe delete, and inline executed real IDE refactors and updated files/usages/imports.
- Stable addressing: a chained edit used
SymbolFinder/findFilesByName,SymbolFinder/getProject, andSymbolFinder/qualNameMatchesNameafter earlier edits shifted line numbers.
Tasks where Serena applies but offered little or no improvement:
- Tiny intra-method edits: a 1-line error-message change was smaller with a built-in patch; Serena body replacement required sending the whole method.
- Simple one-file private rename: manual patch and semantic rename produced the same 2-line diff for
qualNameMatchesName -> qualifiedNameMatchesName. - Method insertion at a known spot: both workflows produced the same 4-line diff; Serena's benefit was target stability, not smaller payload.
- Whole-method rewrite: Serena targets the method boundary cleanly, but the full replacement body still has to be sent.
Tasks outside Serena's scope:
- Reading config/non-code files such as
plugin.xmlandbuild.gradle.kts. - Free-text searches for URLs, endpoint strings, settings values, comments, and resources.
- File inventory, line counts, shell commands, build/test execution, Git operations, and arbitrary text edits.
Value-weighted summary:
| Rank | Difference | Frequency | Value per hit | Delta |
|---|---|---|---|---|
| 1 | Cross-file semantic refactors | Medium-high | High | Often avoids 5-20 manual edit/search/verify steps and reduces partial-update risk. |
| 2 | Code usages vs text mentions | High | Medium-high | Removes noisy grep triage for "who uses this symbol?" |
| 3 | Symbol overview/body retrieval | High | Medium | Avoids large reads and stale line targeting. |
| 4 | Type hierarchy/implementations | Medium | High | Hard to reproduce correctly with text search, especially transitively. |
| 5 | Stable addressing across edit chains | Medium | Medium | Reduces refresh work after line shifts. |
| 6 | External dependency lookup | Low-medium | High | Needs IDE index; built-ins need source/decompiler infrastructure. |
| 7 | Small local edits | High | Neutral/negative for Serena | Built-in patch/edit usually sends less. |
Verdict: Serena materially changes symbol-centric exploration and refactoring; it does not replace built-ins for ordinary text, file, shell, config, or tiny local edits.
2. Added value and differences by area
Cross-file semantic refactoring changes both workflow and correctness. Frequency: medium-high. Value per hit: high. The observed class rename updated a file/class plus imports/usages; the nested-record move changed 4 existing files and created 1 new file; the file move updated package/imports across 3 paths. Built-ins can reproduce the result, but only through search, file moves, patches, import cleanup, and verification.
Semantic search separates code usage from text mention. Frequency: high. Value per hit: medium-high. Serena returned only code references for
Symbol/findReferences;rgreturned code, route strings, comments, and the declaration. Built-ins remain better for "mentioned anywhere."Symbol overview/body retrieval cuts exploration payload. Frequency: high. Value per hit: medium.
get_symbols_overviewonSymbol.javareturned fields, methods, nested classes, and overload indexes without reading the 1,000+ line file. Regex produced line-oriented candidates and still required manual scope interpretation.Type/dependency queries add capabilities not practically present in built-ins. Frequency: low-medium. Value per hit: high. Serena returned a transitive type graph and an external IntelliJ API method body; text tools needed iterative searches or extra source/decompiler setup.
Small localized edits are not improved by symbol-body replacement. Frequency: high. Value per hit: neutral or negative for Serena. A one-line patch was smaller than replacing an 11-line method body.
Some JetBrains refactors staged added/deleted files. Frequency: limited to class/file move/delete style operations. Value per hit: neutral operational tradeoff. Semantic value remains, but verification/revert must check the index as well as unstaged files.
Verdict: Serena's highest-value differences are symbol identity, reference graphs, and IDE refactor execution; built-ins remain superior for simple text-local work.
3. Detailed evidence, grouped by capability
3.1 Repository structure and entry points
Attempted: identify top-level layout, packages, and entry points.
Built-in call chain:
git status --short-> clean.rg --files -g '!*.md' -g '!docs/**' -g '!CLAUDE.md'-> code/config/resource inventory.Get-ChildItem -Force-> top-level directories.Get-Content src/main/resources/META-INF/plugin.xml-> plugin entry points.Get-Content build.gradle.kts -TotalCount 80-> Gradle/IntelliJ platform setup.
Serena call chain: not applicable for repository inventory and non-code config.
Observed structure:
service: backend service and request handling.service/endpoint: endpoint handlers for symbol search, references, formatting, rename, move, safe delete, inline, inspections, and completions.symbol: symbol model, symbol lookup, hierarchy, path matching, and move processors.util: IDE/project/editor/UI helpers.ui: tool window content.plugin.xml: registersPluginStartupActivity, settings service/configurable, and a dummy test action.
Payloads: built-ins used 5 small shell/read calls and returned several KB of code/config context; Serena had no relevant semantic operation.
Verdict: Repository layout and non-code entry-point discovery are built-in work; Serena starts adding value once the target is code symbols.
3.2 Large file structural overview
Attempted: compare structural overview on src/main/java/de/oraios/serena/symbol/Symbol.java.
Serena call chain:
get_symbols_overview(relative_path=Symbol.java, depth=1)- Next step:
find_symbol(Symbol/findReferences, include_body=true)
Serena output: one top-level Symbol class; fields; nested ChildrenCollector and DocumentationResolver; methods including overload-indexed names such as getLocationString[0], getLocationString[1], move[0], move[1], getDocumentation[0], and getDocumentation[1].
Built-in call chain:
rgfor method-like declaration lines inSymbol.java.rgfor class/interface/enum lines inSymbol.java.- Next step: line-slice read around the selected method.
Built-in output: about 70 line-oriented method/class matches, including nested symbols, with no durable symbol identity beyond manual signature inspection.
Payloads: Serena used 1 compact overview call plus a targeted follow-up. Built-ins used 2 regex searches plus a later line-range read.
Verdict: Serena's overview is more actionable because its output feeds directly into stable symbol-addressed calls; regex outlines are useful but line-based.
3.3 Targeted method body retrieval
Attempted: retrieve Symbol/findReferences without reading surrounding file content.
Serena call chain: find_symbol(relative_path=Symbol.java, name_path_pattern=Symbol/findReferences, include_body=true).
Serena output: only the public ArrayList<SymbolReference> findReferences() body.
Built-in call chain:
rg "findReferences\\(" src/main/java -n -C 2Get-Content Symbol.javaline slice around the declaration.
Built-in output: search context across multiple files, then the selected method slice.
Payloads: Serena input was one path/name-path request and returned roughly 25 method lines. Built-ins required search output plus a separate slice read.
Verdict: If the symbol is known, Serena retrieves the body in one precise call; built-ins need search plus line-targeted reading.
3.4 References: code usages vs mentions anywhere
Attempted: find all references to Symbol/findReferences.
Serena call chain: find_referencing_symbols(relative_path=Symbol.java, name_path=Symbol/findReferences).
Serena output:
Symbol/formatReferenceLocations/referencesSymbol/inline/refCountBeforeSymbol/verifyInlineResult/refCountAfterSymbolDTO/Builder/buildDTO/referencesRunInspectionsOnSymbolsHandler/handleRequest/refsFindReferencesHandler/buildResponse/references
Built-in call chain: rg "findReferences" src/main/java -n -C 1.
Built-in output: same call sites plus the declaration, /findReferences route registration, and explanatory comments.
Payloads: Serena returned a compact grouped usage list of about 500-700 characters. rg returned broader line context of about 1-2 KB and mixed code usages with text mentions.
Verdict: Serena has higher precision for "who uses this symbol in code"; built-ins have higher recall for "where is this text mentioned anywhere."
3.5 Type hierarchy
Attempted: list supertypes and subtypes transitively for PostRequestHandler.
Serena call chain: type_hierarchy(relative_path=PostRequestHandler.java, name_path=PostRequestHandler, hierarchy_type=both, depth=0).
Serena output: external Object supertype; 18 direct endpoint subclasses; transitive nested branch TypeHierarchyHandler containing GetSupertypesHandler and GetSubtypesHandler.
Built-in call chain:
rg "extends PostRequestHandler|extends TypeHierarchyHandler|class PostRequestHandler" src/main/java -n- Manually follow any discovered intermediate types.
- Repeat searches if deeper hierarchy exists.
Payloads: Serena used 1 hierarchy query and returned structured JSON. Built-ins used pattern search and manual transitive grouping.
Verdict: Serena turns hierarchy discovery into a semantic graph query; text search requires iterative pattern expansion and manual reasoning.
3.6 External dependency symbol lookup
Attempted: retrieve the IntelliJ dependency symbol behind ReferencesSearch.search(anchorElement).
Serena call chain:
find_declaration(relative_path=Symbol.java, regex="ReferencesSearch\\.(search)\\(anchorElement\\)")find_symbol(relative_path=<ext:ReferencesSearch.class|466808a0>, name_path_pattern=ReferencesSearch/search[0], include_body=true, search_deps=true)
Serena output: declaration ReferencesSearch/search[0] in an external class path and the overload body:
public static @NotNull Query<PsiReference> search(@NotNull PsiElement element) {
return search(element, GlobalSearchScope.allScope(PsiUtilCore.getProjectInReadAction(element)), false);
}Built-in call chain:
rg "ReferencesSearch" src/main/java -n-> import and local call.rgover repo,.intellijPlatform, and Gradle caches forclass ReferencesSearchor matching method signatures -> no useful source hit.Get-ChildItemover Gradle/IntelliJ caches -> jar candidates but no direct definition.
Infrastructure difference: Serena depends on the JetBrains IDE index; built-ins need source jars, decompiler tooling, or classpath-specific jar inspection.
Verdict: External dependency lookup is a genuine Serena capability when IDE indexes are available; ordinary built-ins do not provide it.
3.7 Single-file edits across edit sizes
Small tweak attempted: change "No symbol found for " to "No matching symbol found for " in SymbolFinder/findSymbolByNamePath.
Built-in call chain:
- Use existing search/body context.
apply_patchone-line replacement.git diff,git status --short.- Revert and clean check.
Serena call chain:
replace_symbol_body(SymbolFinder/findSymbolByNamePath, full method body with changed string).git diff,git status --short.- Revert and clean check.
Observed diff: both produced the same 1-line change. Payload difference: built-in edit input was a tiny hunk; Serena input was the full 11-line method body.
Medium rewrite attempted: rewrite SymbolFinder/findFilesByName from list accumulation to stream collection.
Built-in call chain: apply_patch over the method body, then diff/status/revert/status.
Serena call chain: replace_symbol_body(SymbolFinder/findFilesByName, new method body), then diff/status/revert/status.
Observed diff: both produced 10 +++-------, 3 insertions and 7 deletions. Payload difference was small: a patch hunk versus an 8-line replacement method.
Large/whole-body rewrite attempted: rewrite InspectionRunner/collectResults while preserving signature and behavior shape.
Built-in call chain: apply_patch against the method body, then diff/status/revert/status.
Serena call chain: replace_symbol_body(InspectionRunner/collectResults, full symbol text including Javadoc and replacement body), then diff/status/revert/status.
Observed diff: both produced 38 +++++++++++-----------, 19 insertions and 19 deletions. Payload difference: built-in required a large contextual hunk; Serena required the full symbol text, about 70 lines including Javadoc/signature/body.
Verdict: Serena is not automatically more token-efficient for edits; its edit advantage is stable symbol targeting, while built-ins are smaller for tiny local hunks.
3.8 Insert method at structural location
Attempted: insert private Logger getLogger() immediately after SymbolFinder/getProject.
Built-in call chain:
- Search/read surrounding area.
apply_patchwith nearby context.- Diff/status/revert/status.
Serena call chain:
insert_after_symbol(relative_path=SymbolFinder.java, name_path=SymbolFinder/getProject, body=...)- Diff/status/revert/status.
Observed diff: both produced the same 4-line insertion.
Payloads: built-ins sent inserted body plus surrounding text context; Serena sent inserted body plus symbol target.
Verdict: Structural insertion is a modest Serena improvement: same resulting diff, less dependence on line/context stability.
3.9 Rename: private helper in one file
Attempted: rename SymbolFinder/qualNameMatchesName to qualifiedNameMatchesName.
Built-in call chain:
rg "qualNameMatchesName" SymbolFinder.java -n -C 2apply_patchdeclaration and one call site.- Diff/status/revert/status.
Serena call chain:
find_referencing_symbols(SymbolFinder/qualNameMatchesName)-> one referencing method.rename(name_path=SymbolFinder/qualNameMatchesName, new_name=qualifiedNameMatchesName, rename_in_comments=true, rename_in_text_occurrences=true).- Diff/status/revert/status.
Observed result: Serena returned "Success". Both produced the same 2-line diff.
Verdict: Semantic rename has little advantage for a one-file helper with one call site, but it scales better than text edits as references spread or names become ambiguous.
3.10 Rename: cross-file class including imports
Attempted: rename MoveOperationFailedException to MoveFailedException.
Serena call chain:
rename(relative_path=MoveOperationFailedException.java, name_path=MoveOperationFailedException, new_name=MoveFailedException, rename_in_comments=true, rename_in_text_occurrences=true).git diff --stat,git diff,git diff --cached --stat,git status --short.- Restore staged rename and modified files; clean check.
Serena result: "Success".
Observed edits:
- Added/staged
MoveFailedException.java. - Deleted/staged
MoveOperationFailedException.java. - Updated import, Javadoc comment, and thrown class in
MoveProcessor.java.
Built-in equivalent chain:
rg "MoveOperationFailedException" src/main/java -n.- Rename/create/delete file.
- Patch class name and constructor.
- Patch imports and usages.
- Patch comments/text occurrences if desired.
- Verify no unintended old references remain.
- Compile/test for a permanent change.
Verdict: Cross-file class renames are high-value Serena cases because file/class coupling, imports, usages, and optional comments are handled as one semantic refactor.
3.11 Move symbol to another file context
Attempted: move nested record InspectionRunner/PendingFix to a top-level file in the same package.
Serena call chain:
move(relative_path=InspectionRunner.java, name_path=InspectionRunner/PendingFix, target_relative_path=src/main/java/de/oraios/serena/service/endpoint).- Diff/status/cached-diff inspection.
- Restore staged new file and modified files; clean check.
Serena result:
{
"source_relative_path": "src/main/java/de/oraios/serena/service/endpoint/InspectionRunner.java",
"target_relative_path": "src/main/java/de/oraios/serena/service/endpoint",
"moved_symbol": {"name_path": "PendingFix", "type": "CLASS"}
}Observed edits:
- Created/staged
PendingFix.javawith package and imports. - Removed nested record from
InspectionRunner.java. - Updated 3 external references from
InspectionRunner.PendingFixtoPendingFix. - Removed unused imports in 2 files.
Built-in equivalent chain: read nested record/import needs, add file, delete nested symbol, search usages, patch qualified usages, patch internal references if needed, remove imports, verify, compile/test.
Verdict: Symbol move is a substantial Serena addition because the difficult part is reference/import repair, not copying text.
3.12 Move file/package location
Attempted: move PluginUtil.java from util to service.
Serena call chain:
move(relative_path=src/main/java/de/oraios/serena/util/PluginUtil.java, target_relative_path=src/main/java/de/oraios/serena/service).- Diff/status/cached-diff inspection.
- Restore staged rename and modified imports; clean check.
Serena result:
{
"source_relative_path": "src/main/java/de/oraios/serena/util/PluginUtil.java",
"target_relative_path": "src/main/java/de/oraios/serena/service"
}Observed edits:
- Staged rename
util/PluginUtil.java -> service/PluginUtil.java. - Changed package declaration to
de.oraios.serena.service. - Updated import in
PluginStartupActivity. - Removed now-unneeded same-package import in
SerenaBackendService.
Built-in equivalent chain: move file, patch package declaration, search PluginUtil, patch imports/usages, remove redundant imports, verify no old package import remains, compile/test.
Verdict: File moves are high-value when packages/imports must change; built-ins can do them manually but require dependency repair steps.
3.13 Safe delete and propagated delete
Attempted: delete unused DebugUtil.
Serena call chain:
safe_delete(relative_path=DebugUtil.java, name_path=DebugUtil, delete_even_if_used=false, propagate=false).git diff --cached --stat,git status --short.- Restore staged deletion; clean check.
Serena result:
{
"deleted_symbol": "DebugUtil",
"relative_path": "src/main/java/de/oraios/serena/util/DebugUtil.java",
"affected_references": [],
"message": "Symbol deleted successfully with no affected references."
}Built-in equivalent chain: rg "DebugUtil", inspect declaration vs usages, delete file/symbol, verify no remaining usages.
Propagated deletion: no suitable correct-use candidate was found where JetBrains safe-delete propagation would meaningfully delete a used symbol and propagate deletion through call sites. For ordinary used-method deletion, correct safe delete reports usages or, if forced, deletes the symbol and reports affected references; that is not the same as arbitrary call-site deletion.
Verdict: Safe delete adds an integrated usage check plus deletion operation; propagated deletion was not evaluated because this codebase did not provide a suitable candidate.
3.14 Inline
Attempted: inline ProjectUtil/getAbsolutePath, a single-expression helper.
Serena call chain:
find_symbol(ProjectUtil/getAbsolutePath, include_body=true).find_referencing_symbols(ProjectUtil/getAbsolutePath)-> 2 references.inline_symbol(relative_path=ProjectUtil.java, name_path=ProjectUtil/getAbsolutePath, keep_definition=false).- Diff/status/revert/status.
Serena result: {"status": "SUCCESS"}.
Observed edits:
- Removed
getAbsolutePath. - Replaced internal call in
ProjectUtil/getVirtualFile. - Replaced external call in
RefreshFileHandler.
Built-in equivalent chain: read helper body, rg getAbsolutePath, patch each call site with substituted expression, delete helper, verify no stale references, compile/test.
Verdict: Inline is a strong Serena refactor for legally substitutable helpers; built-ins can reproduce it but must manually adapt each substitution.
3.15 Scope precision and overload targeting
Attempted: distinguish overloaded Symbol/getLocationString methods.
Serena call chain:
find_symbol(Symbol/getLocationString[0], include_body=true).find_symbol(Symbol/getLocationString[1], include_body=true).
Serena output:
[0]:public String getLocationString() { return getLocationString(element); }[1]:private static String getLocationString(PsiElement element) { ... }
Built-in call chain: rg "getLocationString" Symbol.java -n -C 1, then manual signature inspection.
Verdict: Serena's overload-indexed name paths provide precise symbol addressing that text search does not.
3.16 Atomicity and success signals
Observed Serena success signals:
replace_symbol_body->OKinsert_after_symbol->OKrename->"Success"move-> JSON with source/target/moved symbol or pathssafe_delete-> JSON with deleted symbol, affected references, and messageinline_symbol->{"status": "SUCCESS"}
Built-in success signals:
apply_patch-> "Success. Updated the following files"- Shell/Git commands -> exit codes and textual output
- Cross-file consistency -> user-driven diff/search/build verification
Atomicity comparison: Serena refactors executed as single IDE operations. Built-in equivalents are chains of searches, file operations, and patches; a competent user can make them correct, but intermediate states can be partial.
Tradeoff: class/file move/delete refactors staged added/deleted files in the Git index, so clean verification must check both staged and unstaged state.
Verdict: Serena gives clearer semantic success signals and more atomic cross-file edits; built-ins require manual consistency management.
3.17 Workflow effects across multiple edits
Attempted: chain 3 edits in SymbolFinder.java without refreshing between them.
Serena call chain:
replace_symbol_body(SymbolFinder/findFilesByName, ...).insert_after_symbol(SymbolFinder/getProject, ...).rename(SymbolFinder/qualNameMatchesName, ...).- Diff/status/revert/status.
Observed result: all edits applied after earlier edits shifted file positions. Final diff touched 18 lines with net 9 insertions and 9 deletions.
Built-in equivalent chain: initial read/search, apply first patch, rely on robust patch context or refresh line numbers, apply second patch, refresh or search again, apply rename patch, verify.
Verdict: Serena's name paths remain useful after line shifts, so its advantage compounds in multi-edit sessions.
3.18 Non-code reads and free-text search
Attempted: read config and search free text.
Built-in call chain:
Get-Content plugin.xml.Get-Content build.gradle.kts -TotalCount 80.rg "http://|https://|localhost|127\\.0\\.0\\.1|8080|24282|SERENA|serena" src/main/java src/main/resources -n.
Serena call chain: not applicable.
Observed result: built-ins exposed plugin registration, Gradle platform setup, URLs, IDs, settings defaults, and resource/text mentions.
Verdict: Built-ins are natural and sufficient for config reading and free-text search; this is outside Serena's semantic-code scope.
4. Token-efficiency analysis
Payload differences across edit sizes:
| Task | Built-in payload | Serena payload | More efficient |
|---|---|---|---|
| 1-line string tweak | Tiny patch hunk, 1 changed line | Full 11-line method body | Built-ins |
| Medium method rewrite | 10-line diff hunk | Full 8-line replacement method | Roughly equal |
| Large body rewrite | Large contextual patch, 38-line diff | Full symbol text, about 70 lines | Roughly equal; Serena has better target |
| Insert method | Inserted body plus surrounding context | Inserted body plus name path | Serena slightly |
| Private one-file rename | rg plus 2-line patch |
optional refs query plus rename command | Roughly equal |
| Cross-file rename/move/inline | Multiple searches, file operations, patches, verifications | One semantic refactor plus diff/status | Serena |
Forced reads:
- Built-ins commonly need a search before a precise read, then a line slice or full context before editing.
- Serena can skip full-file reads when the symbol name path is known.
- When the symbol is not known, Serena's
get_symbols_overviewand shallowfind_symbolcalls provide compact discovery rather than full-file reads.
Output payload:
- Serena exploration output is structured and scoped to symbols.
- Built-in grep output can be larger because it includes declarations, comments, strings, and unrelated text mentions.
- Built-ins can be extremely low-output for known-location small edits.
Stable vs ephemeral addressing:
- Built-ins address code by file paths, line numbers, text context, or byte positions. Line numbers and slices go stale after edits.
- Serena addresses symbols by
relative_path + name_path, with overload indexes where needed. The chained-edit experiment showed those targets survived line shifts. - Path-changing operations still require updated paths afterward, so symbol stability is strongest before moves/renames that alter file locations.
Verdict: Serena saves tokens by avoiding broad reads/search triage and by collapsing multi-file edit chains; built-ins remain more token-efficient for tiny known-location hunks.
5. Reliability & correctness under correct use
Precision of matching:
- Serena reference search returned code usages and excluded route strings/comments.
- Built-in text search returned all mentions and therefore mixed true code usage with broader text hits.
- Serena overload indexes targeted
Symbol/getLocationString[0]and[1]; text search required manual signature inspection.
Scope disambiguation:
- Serena name paths encode class nesting and overload identity.
- Built-ins can disambiguate with careful patterns and reads, but the disambiguation is manual.
- In simple cases, such as a private helper with one call site, this semantic precision had little practical value. In overloaded, nested, or cross-file cases, it materially reduced ambiguity.
Atomicity:
- Serena refactors apply through JetBrains refactoring operations and produce one operation-level success signal.
- Built-in equivalents are decomposed into separate searches, file moves, patches, and verification steps.
- Competent users can verify either path, but Serena reduces the number of intermediate inconsistent states.
Semantic queries vs text search:
type_hierarchyand dependency declaration lookup produced semantic information not available from ordinary grep.rgremained better for broad mention searches and config/resource scans.
External dependency limitations:
- Serena's dependency lookup depends on IDE/language indexes and available dependency metadata.
- Built-ins can match or exceed it only if paired with source jars, decompilers, or separate language-server tooling.
Operational tradeoff:
- Several Serena refactors staged created/deleted files. This is not a correctness problem, but it adds index-state verification to the cleanup workflow.
Verdict: Serena improves correctness where symbol identity and cross-file semantics matter; built-ins remain reliable and simpler for text/file tasks.
6. Workflow effects across a session
Where Serena advantages compound:
- Symbol overview results become inputs to body reads, reference searches, renames, moves, safe deletes, and inline operations.
- Name paths remain useful after line shifts, reducing re-read/re-target work during multi-edit sessions.
- Refactor-heavy sessions benefit because one semantic command replaces search/edit/verify loops across files.
Where Serena advantages diminish:
- After a full file has already been read, overview adds less incremental value.
- If the task is a small textual substitution in a known place, symbolic body replacement can cost more payload than a tiny patch.
- For non-code and free-text work, Serena has no role.
Intermediate result durability:
- Serena intermediate results such as
SymbolFinder/findFilesByNameorSymbol/getLocationString[1]remain meaningful as long as the file/symbol still exists. - Built-in line numbers became stale after insertions; robust textual patch context remained usable when surrounding lines did not change.
Verification cost:
- Both workflows still need
git diff, build/test, and domain-specific verification for permanent changes. - Serena success signals reduce the need to inspect every call site manually, but not the need to inspect the intended diff.
Verdict: Serena's advantages compound inside symbol-centric sessions and diminish once the work becomes plain text, config, shell, or already-loaded single-file editing.
7. Unique capabilities
Unique here means no practical built-in equivalent without adding separate language-server, IDE, source-index, or decompiler infrastructure.
Transitive type hierarchy. Frequency: medium. Impact: high when changing base classes, interfaces, or handler contracts. Built-ins can grep explicit
extendsclauses but do not produce a semantic transitive graph or external supertypes.External dependency declaration/body lookup from a call site. Frequency: low-medium. Impact: high for API behavior questions. Serena resolved
ReferencesSearch.searchinto an external class method body; ordinary built-ins did not.IDE semantic move of a nested symbol to a top-level file with usage/import repair. Frequency: low-medium. Impact: high during refactors. Built-ins can reproduce this only as a manual multi-step refactor.
Safe delete with semantic usage check. Frequency: medium. Impact: medium-high. Built-ins can search and delete, but they do not provide an integrated safe-delete operation over code usages.
Inline refactor with call-site substitution. Frequency: low-medium. Impact: medium-high. Built-ins can patch substitutions manually, but expression adaptation and helper deletion are not primitive text operations.
Not unique: reading files, listing files, grep, small patches, simple one-file renames, config/resource understanding, and shell/Git operations.
Verdict: Serena's unique practical capabilities are hierarchy/dependency queries and IDE refactors; its non-unique areas are ordinary text and file operations.
8. Tasks outside Serena's scope
Built-in-only or built-in-natural tasks observed:
- Repository inventory and top-level layout.
- Reading
plugin.xml,build.gradle.kts, scripts, resources, and generated artifacts. - Free-text search for URLs, settings strings, endpoint strings, magic constants, resource IDs, and comments.
- Running shell commands, builds, tests, Git operations, and filesystem cleanup.
- Small text edits where a one-line patch is the whole task.
Estimated share of daily coding work:
- About 30-50% of a normal coding session is built-in-natural: shell, tests, grep, config reads, small text patches, and Git checks.
- Serena applies to much of the remaining 50-70% when the work is code navigation, symbol understanding, references, hierarchy, or refactoring.
- Serena coverage rises in refactor-heavy sessions and falls in config/debug/test-log sessions.
Verdict: Serena augments the semantic-code portion of development; built-ins still carry a large and necessary share of routine work.
9. Practical usage rule
Use Serena when the task can be stated as a code-symbol operation:
- "What methods/classes are in this file?"
- "Give me this method body."
- "Who uses this symbol?"
- "What implements/subclasses this?"
- "Rename, move, delete, or inline this symbol and update usages."
- "Find the declaration of this call, including dependency symbols."
- "Apply several edits where line locations may shift."
Use built-ins when the task is text, file, shell, config, or tiny local editing:
- "List files."
- "Read config."
- "Search for this string anywhere."
- "Change this one known line."
- "Run tests/build."
- "Inspect Git state."
- "Edit markdown/resources/scripts."
For mixed workflows:
- Use built-ins to find files/config and run verification.
- Use Serena to identify and transform code symbols.
- Use built-ins to inspect final diffs and run tests.
- Prefer built-ins for tiny local hunks after the symbol has already been located.
- Prefer Serena for any refactor that crosses file boundaries or depends on type/reference semantics.
Verdict: Choose Serena for symbol identity and semantic refactoring; choose built-ins for text, shell, config, verification, and minimal local edits.
04 Evaluation 030 Results 030 Copilot Cli On Ente (`docs/04-evaluation/030_results/030_copilot_cli_on_ente.md`)
:::{admonition} Evaluation Result
:class: note
Generated by: GPT-5.4 (medium) in Copilot Cli
Codebase: ente - a large monorepo in Dart, TypeScript, Go, Rust, and other languages.
Date: 2026-04-14
:::
Copilot CLI (GPT-5.4, medium)
As a coding agent, I’d absolutely ask my owner to add Serena because it makes me noticeably sharper and calmer on
real code—especially symbol-aware navigation, cross-file refactors, and monorepo dependency jumps—while I still lean
on built-ins for tiny text edits and non-code work
Baseline. I started from source code only, avoided repo docs/notes, and ran each reversible experiment against the repo directly. After every edit/refactor experiment, the working tree returned to its baseline state.
1. Headline: what Serena changes
Serena changes the workflow when the task is about code symbols rather than raw text. In this repo, the practical delta was:
- Added capability / materially better workflow. Symbol-aware navigation and refactors in TypeScript: structural overviews, precise code-only references, hierarchy queries, symbol-targeted rename, file move with import updates, and inline. These usually collapsed a 2-6 step built-in chain into one semantic operation after discovery, and reduced manual scope verification.
- Applies but offers little or no improvement. Small local edits inside an already-understood method. Built-ins can patch only the changed lines; Serena's body replacement resends the whole symbol, so it was often less payload-efficient for 1-3 line tweaks.
- Outside Serena's scope. Non-code reads, free-text search, git inspection, config/package files, and other text-first tasks. Built-ins remained the natural tools there.
Two important observed limits constrained Serena's delta here: the strongest measured gains were concentrated in the TypeScript desktop app and the Rust core crate where I ran the hands-on comparisons, and some refactors still carried diff-shape tradeoffs such as formatting churn or unexpected target-file choices.
Verdict: In this repo, Serena was a strong TypeScript symbol layer on top of the built-ins, not a general replacement for text/file work.
2. Added value and differences by area
| Area | What changed vs built-ins | Frequency | Value per hit |
|---|---|---|---|
| Cross-file symbol refactors | rename, file move, and inline turned manual search/edit/update chains into one semantic op. The wait -> delay rename updated 4 files from 1 symbol definition; moving http.ts updated the importing file automatically. |
Medium | High: typically 2-5 calls saved plus less manual scope checking |
| Code-only discovery | Symbol overview, symbol body retrieval, reference search, and type hierarchy returned code structure directly instead of raw text matches. For wait, Serena returned 3 real code-use files; rg returned 7 files, including docs/comments and English-word hits. |
High | Medium: usually 1-3 follow-up reads/filters avoided |
| Stable addressing | Name paths stayed reusable across multiple edits (createMainWindow, openStreetMapUserAgent, wait); built-in line ranges had to be reacquired after edits. |
Medium | Medium: less re-reading, less stale-context risk |
| Small in-method edits | Serena was not more efficient. Replacing AutoLauncher/toggleAutoLaunch required resending the full method body, while the built-in patch changed only the touched lines. |
High | Low negative: built-ins used smaller edit payloads |
| External dependency lookup in a monorepo | Once indexing was available, Serena resolved Electron types from desktop/node_modules, next-electron-server declarations from the desktop package, and Rust crate symbols from Cargo registry sources. In a monorepo, that removes a manual "which package owns this dependency?" step. |
Medium | Medium-High: usually 1-3 searches plus path discovery avoided |
Verdict: Serena's highest-value delta was semantic refactoring plus dependency-aware code lookup in the TypeScript/Rust parts of the monorepo; its weakest area was tiny local edits.
3. Detailed evidence, grouped by capability
3.1 Codebase understanding
Task 1: high-level repository overview
- Attempted: top-level layout and likely code-heavy areas.
- Serena chain:
serena-list_dir(.)-> directory list. - Built-in chain: not separately needed; Serena added no unique value beyond a normal directory listing here.
- Payloads: one short directory listing either way.
- Finding: no meaningful delta; this is plain filesystem exploration.
Verdict: For repo layout, Serena was neutral.
Task 2: structural overview of a large file, plus the concrete next step
- Target file:
desktop/src/main.ts(753 lines). - Serena chain:
get_symbols_overview(main.ts, depth=1)-> concise symbol map of top-level functions and nested locals undermain; next stepfind_symbol(createMainWindow, include_body=true). - Built-in chain:
rgonconst|function|class|exportinmain.ts-> flat text hits; next stepviewof lines331-439to readcreateMainWindow. - Payloads observed:
- Serena overview output: compact symbol list for the file; next-step body fetch returned only the selected symbol body.
- Built-in overview output: many matching lines without structure; next-step read required ~109 lines of file content.
- Delta: Serena's overview was not just shorter; it also supplied stable symbol names for the follow-up call. Built-ins could answer the question, but only after a second text-localizing step.
Verdict: Serena materially improved the "overview -> inspect one function" flow by making the follow-up call symbol-based instead of line-based.
Task 3: retrieve a specific class method body without reading the surrounding file
- Target symbol:
AutoLauncher/toggleAutoLaunchindesktop/src/main/services/auto-launcher.ts. - Serena chain:
find_symbol(AutoLauncher/toggleAutoLaunch, include_body=true)-> exact method body. - Built-in chain:
viewof the relevant file range (20-40) after locating the method. - Payloads observed:
- Serena returned the 10-line method body only.
- Built-in read returned 21 lines of surrounding class context.
- Delta: Serena saved one localization step and avoided unrelated lines.
Verdict: Serena added a real but modest efficiency gain for targeted method retrieval.
Task 4: find all references for one non-trivial symbol; compare code-use recall/precision vs text mention search
- Target symbol:
waitindesktop/src/main/utils/common.ts. - Serena chain:
find_referencing_symbols(wait)-> 3 files with symbol contexts:main.ts,ffmpeg-worker.ts,ml-worker.ts. - Built-in chain:
rg \bwait\b desktop/src-> 7 files including:- real uses/imports,
- comments/doc strings (
preload.ts,watch.ts,main.tsprose), - the definition itself,
- doc mentions in
desktop/docs/release.mdwhen searching broader scope.
- Payloads observed:
- Serena: one structured result grouped by referencing symbol.
- Built-ins: one broader result, but it needed human filtering to answer "who uses this in code?"
- Delta: Serena improved precision, not just convenience. For the code-only question, built-ins needed extra filtering or extra reads.
Verdict: Serena clearly improved reference search when the question is semantic ("who uses this in code?") rather than textual.
Task 5: supertypes / subclasses / implementations
- Equivalent used: interface hierarchy in
web/apps/ensu/src/services/llm/inference.ts, because this TS area had interface implementations rather than rich class inheritance. - Serena chain:
type_hierarchy(InferenceBackend, both)->WasmInference,TauriInference;type_hierarchy(WasmInference, both)-> supertypeInferenceBackend. - Built-in chain:
rg InferenceBackend|implements InferenceBackend-> manual reconstruction from four text matches. - Payloads observed: Serena returned the hierarchy directly; built-ins returned only raw declarations/usages.
- Delta: Serena removed the manual synthesis step. Built-ins were sufficient here because the hierarchy was shallow, but that was because the example was small.
Verdict: Serena added moderate value for hierarchy queries; the value grows with hierarchy depth.
Task 6: external dependency symbol lookup
- Targets used after indexing was available:
BrowserWindowandserveNextAtin the desktop TypeScript app, plusUrlandZeroizinginrust/core. - Serena chain (TS):
find_declaration(new BrowserWindow(...), include_body=true)->desktop/node_modules/electron/electron.d.ts, bodyclass BrowserWindow extends Electron.BrowserWindow {}find_declaration(import serveNextAt ... , include_body=true)->desktop/node_modules/next-electron-server/index.d.ts, bodydeclare function serveNextAt(uri: string, options?: Options): void;find_symbol(..., search_deps=true)on those dependency files returned dependency-side docs.
- Serena chain (Rust):
find_declaration(use reqwest::{Response, Url};, include_body=true)-><ext:lib.rs|...>external symbolUrl[0]with the struct bodyfind_declaration(use zeroize::Zeroizing;, include_body=true)-><ext:lib.rs|...>external symbolZeroizing[0]with the struct bodyfind_symbol(..., relative_path=<ext...>, search_deps=true)returned dependency-side docs for those external symbols.
- Built-in equivalent chain:
- Manually infer the correct monorepo-local dependency root (
desktop/node_modules, not repo root), - or manually inspect Cargo metadata /
Cargo.lock, - then open the resolved dependency files directly (for Rust, under the Cargo registry).
- Manually infer the correct monorepo-local dependency root (
- Payloads observed: Serena returned the declaration target and a small signature/body directly; built-ins required package-root discovery first, which is a real extra step in a monorepo.
- Delta: Serena does add capability and efficiency here once indexing exists. The gain is larger in this monorepo than in a single-package repo because dependency ownership is split across package-local Node dependencies and shared Cargo registry sources.
Verdict: With indexing available, Serena added meaningful external dependency lookup, and the value was amplified by the monorepo layout.
3.2 Single-file edits
Task 7a: small tweak (1-3 lines inside a method)
- Change: rename local
autoLaunch->launcherinsideAutoLauncher/toggleAutoLaunch. - Built-in chain:
view(auto-launcher.ts, 20-40)->apply_patchon 3 changed lines ->git diff. - Serena chain:
find_symbol(toggleAutoLaunch, include_body=true)->replace_symbol_body(toggleAutoLaunch)->git diff. - Payloads observed:
- Built-ins: read 21 lines, patch changed 3 logical lines.
- Serena: fetched 10-line body, resent full 10-line body.
- Delta: same result, same number of main steps, but the symbolic edit resent untouched lines.
Verdict: For tiny in-method tweaks, built-ins were more payload-efficient and Serena added no real workflow advantage.
Task 7b: medium rewrite (~10-30 lines)
- Change: rewrite
uniqueSavePathto usecandidatePath+forloop. - Built-in chain:
view(main.ts, 500-540)->apply_patchreplacing the function body ->git diff. - Serena chain:
find_symbol(uniqueSavePath, include_body=true)->replace_symbol_body(uniqueSavePath)->git diff. - Payloads observed:
- Built-ins: read 41 lines to safely anchor a ~10-line rewrite.
- Serena: fetched the 11-line symbol body and resent the rewritten body only.
- Delta: Here Serena was more efficient: less prerequisite read volume and no dependence on surrounding file context.
Verdict: Serena was better for medium symbol-sized rewrites.
Task 7c: large / whole-body rewrite
- Change: rewrite the entire
createMainWindowbody. - Built-in chain:
view(main.ts, 331-439)->apply_patchreplacing the function body ->git diff. - Serena chain:
find_symbol(createMainWindow, include_body=true)->replace_symbol_body(createMainWindow)->git diff. - Payloads observed:
- Built-ins: read ~109 lines and patched the whole function.
- Serena: fetched the same symbol body and resent the whole rewritten body.
- Delta: Serena still avoided a file-range read, but once the symbol itself dominates the payload, the token gap mostly disappears.
Verdict: For whole-body rewrites, Serena's gain was modest: better addressing, not dramatically smaller payload.
Task 8: insert a new function at a structural location
- Insertion:
waitSecondsafterwaitindesktop/src/main/utils/common.ts. - Built-in chain:
view(common.ts, 1-40)->apply_patchinserting after the existing function. - Serena chain:
find_symbol(wait)->insert_after_symbol(wait). - Payloads observed:
- Built-ins: read 26 lines to place a 1-line function.
- Serena: no extra file-range read once the symbol name was known.
- Delta: Serena made the location structural instead of textual.
Verdict: Serena improved insertions when the location is "after symbol X" rather than "after line Y".
Task 9: rename a private helper used only within one file
- Target symbol:
openStreetMapUserAgentindesktop/src/main.ts. - Built-in chain:
view/rgto find call site + definition ->apply_patchupdating both. - Serena chain:
rename(openStreetMapUserAgent -> buildOpenStreetMapUserAgent). - Payloads observed:
- Built-ins: two textual sites had to be found and updated manually.
- Serena: one rename call, terse success response (
"Success").
- Delta: small call-count improvement, larger correctness improvement when the file is bigger or the name is less unique.
Verdict: Serena was somewhat better even for single-file private renames because it removed manual site enumeration.
3.3 Multi-file changes
Task 10: rename a symbol across several files including imports
- Target symbol:
wait->delay. - Built-in chain: read
common.ts,main.ts,ffmpeg-worker.ts,ml-worker.ts-> one multi-fileapply_patch->git diff. - Serena chain:
rename(wait -> delay)on the defining symbol ->git diff. - Payloads observed:
- Built-ins: required reading 4 files and manually updating export, imports, and call sites.
- Serena: one semantic rename updated the same 4 files.
- Success signals: built-ins only showed success via resulting diff; Serena returned
"Success". - Delta: this is one of Serena's clearest wins: same final diff, far less manual scope work.
Verdict: Serena strongly improved multi-file renames by collapsing discovery + edit into one symbol-based refactor.
Task 11: move a symbol from one module to another, updating imports
- Target: move
nullToUndefinedout ofcommon.ts. - Serena chain executed:
move(nullToUndefined, target_relative_path=http.ts)->git diff. - Observed result: Serena removed the symbol from
common.ts, updatedffmpeg-worker.ts, but created a new filedesktop/src/main/utils/nullToUndefined.tsinstead of merging intohttp.ts. - Built-in equivalent: would require manually copying the symbol into the intended target module, updating imports, then deleting the old definition.
- Delta: Serena still automated the cross-file update, but did not provide the specific "move into existing module" behavior I was testing.
Verdict: Serena added partial value for symbol moves here, but not the full capability of "move into a chosen existing TS file".
Task 12: move a file/package and update imports
- Target: move
desktop/src/main/utils/http.tstodesktop/src/main/services/http.ts. - Serena chain:
move(file http.ts -> services/)->git diff. - Observed result: the file was renamed/moved and
ffmpeg-worker.tsimport updated from../utils/httpto./http. - Built-in equivalent: locate all imports, move the file, patch each import path, then verify.
- Delta: this was a real one-call semantic file move.
Verdict: Serena materially improved file moves that require import updates.
Task 12 (safe delete with no remaining usages)
- Attempted: searched for naturally unused TS symbols in the working areas (
main.ts,common.ts,temp.ts,inference.ts) and checked several candidates (registerForEnteLinks,minimumWindowSize,AutoLauncher/isEnabled,openStreetMapUserAgent,safeJson,buildSamplingConfig). - Observed result: every plausible candidate still had live references.
- Outcome: no suitable candidate found in the TS areas where Serena was operational, so I skipped this comparison instead of forcing an invalid input.
Verdict: No evidence either way here because the repo did not offer a clean unused-symbol candidate in the code areas Serena handled reliably.
Task 13: delete a symbol and propagate deletion to call sites
- Attempted: looked for a helper whose call sites could be semantically removed rather than inlined or manually rewritten.
- Observed result: the good candidates in this repo were better modeled as inline refactors, not delete-with-propagation.
- Outcome: no suitable candidate; skipped rather than using an unsafe input.
Verdict: No measured delta here because the available candidates were inline candidates, not safe propagate-delete candidates.
Task 13 (inline a small helper)
- Target symbol:
waitForRendererDevServer. - Built-in chain:
viewcall site + definition ->apply_patchreplacingawait waitForRendererDevServer()withawait wait(1000)and deleting the helper ->git diff. - Serena chain:
inline(waitForRendererDevServer, keep_definition=false)->git diff. - Observed result: both achieved the inline, but Serena also rewrote unrelated import formatting at the top of the file.
- Success signals: built-ins: resulting diff; Serena:
{"status":"SUCCESS"}. - Delta: Serena added the unique semantic refactor, but in this run it also introduced format churn outside the logical change.
Verdict: Serena added real inline capability, with a low-frequency but real tradeoff of broader formatting churn.
3.4 Reliability & correctness-oriented checks
Task 14: scope precision
- Demonstrated with:
AutoLauncher/toggleAutoLaunch,openStreetMapUserAgent, andInferenceBackend. - Serena: symbol names and name paths targeted the exact code entity.
- Built-ins: text search for names such as
waitorwriteToTemporaryFileover-matched comments, docs, and multiple textual occurrences. - Delta: Serena's unit of work was the symbol; built-ins' unit was the matching line.
Verdict: Serena was reliably more precise whenever the target was a symbol rather than a string.
Task 15: atomicity
- Observed: Serena rename/file-move/inline each ran as one refactor operation after symbol selection.
- Built-ins: a single
apply_patchcan update multiple files atomically as a patch, but it cannot discover missed sites; semantic completeness remains manual. - Delta: Serena's advantage was not transactional all-or-none patching; it was scope computation.
Verdict: Serena improved semantic completeness more than patch atomicity.
Task 16: success signals
- Observed Serena success outputs:
OKfor body replacement,"Success"for rename, JSON result for move,{"status":"SUCCESS"}for inline. - Observed built-in success outputs: only indirect evidence via
git diff/ clean revert.
Verdict: Serena gave clearer machine-readable success signals for refactors than the built-ins did.
4. Token-efficiency analysis
By edit size
| Edit size | Built-ins | Serena | More efficient |
|---|---|---|---|
Small tweak (toggleAutoLaunch) |
Read ~21 lines, patch only changed lines | Fetch 10-line body, resend full 10-line body | Built-ins |
Medium rewrite (uniqueSavePath) |
Read ~41 lines to safely patch ~10 lines | Fetch 11-line body, resend 11-line body | Serena |
Large rewrite (createMainWindow) |
Read ~109 lines, patch whole body | Fetch ~same symbol body, resend whole body | Near tie, slight Serena advantage only from structural targeting |
Cross-file rename (wait -> delay) |
Read 4 files, craft 4-file patch | One rename after discovery | Serena by a large margin |
Forced reads
- Built-ins often needed a localization read before the edit.
- Serena avoided that when the symbol was already known, but not when the task itself required understanding the body.
Stable vs ephemeral addressing
- Serena's addresses (
createMainWindow,wait,openStreetMapUserAgent) stayed useful across later operations. - Built-in line ranges from
viewbecame stale after edits, so later text operations required re-grepping or re-viewing.
Verdict: Serena is most token-efficient for medium-to-large symbol work and cross-file refactors; built-ins stay leaner for tiny local edits.
5. Reliability & correctness (under correct use)
- Precision of matching: Serena's reference search answered "who uses this in code?" better than
rg, which mixed real uses with prose/comment matches. - Scope disambiguation: Serena targeted exact symbols (
AutoLauncher/toggleAutoLaunch,InferenceBackend) rather than relying on unique text strings. - Atomicity: Serena computed and updated semantic scope in one refactor call; built-ins could batch edits, but only after manual scope discovery.
- Semantic queries vs text search: hierarchy and references were the strongest examples. Built-ins could reconstruct them, but only with manual interpretation.
- External dependencies: after indexing was available, Serena resolved desktop TypeScript dependencies into package-local declaration files under
desktop/node_modulesand Rust dependencies into external Cargo sources such asurlandzeroize. Built-ins could still reach those files, but only after manual package-root or registry-path discovery. - Monorepo effect: this repo magnified Serena's dependency-lookup value because "the dependency source" was not at one obvious global root. Serena jumped from app code to the right package-local or registry-backed dependency context directly.
Verdict: Serena improved correctness by narrowing work to exact symbols and by resolving dependencies across monorepo boundaries.
6. Workflow effects across a session
- Advantages compounded when I stayed in symbol space. Example:
get_symbols_overview(main.ts)produced symbol names that I later reused forfind_symbol(createMainWindow),rename(openStreetMapUserAgent), andinline(waitForRendererDevServer). - Built-in workflows required refreshes. Across repeated
main.tsexperiments, I repeatedly had to reacquire ranges withview/rgbefore editing because prior line-based context was no longer trustworthy. - In the monorepo, Serena also compounded by removing package-boundary bookkeeping. In the desktop app I could jump from
main.tsinto Electron andnext-electron-serverdeclarations without first reasoning about workspace roots; inrust/coreI could jump into Cargo-registry dependencies through external symbol handles instead of manually reconstructing registry paths fromCargo.lock. - The compounding effect disappeared for tiny edits and non-code work, where built-ins were already direct and minimal.
- One tradeoff compounded too: some Serena refactors carried formatting side effects (notably
inline), so the semantic benefit does not guarantee a surgically small diff.
Verdict: Serena's advantages compound most in code-centric monorepo sessions, where symbol reuse and dependency jumps save both re-reading and package-root discovery work.
7. Unique capabilities
| Capability with no practical one-step built-in equivalent | Frequency | Impact |
|---|---|---|
| Semantic cross-file rename from a single symbol definition | Medium | High |
| Type hierarchy query (implementations / supertypes) | Low-Medium | Medium |
| Inline refactor across call sites | Low | High when applicable |
| File move with import updates | Low-Medium | High |
| External dependency resolution from in-repo code into package-local or registry-backed sources | Medium | Medium-High |
Built-ins can approximate all of these manually, but not as a single semantic operation.
Verdict: Serena did add unique practical capabilities, especially around refactors that require scope computation rather than text replacement.
8. Tasks outside Serena's scope (built-in only)
- Reading non-code files like
desktop/package.json - Free-text search such as
ente://appor URL strings - Git inspection / diff / cleanup
- Config/package/changelog/docs/notebook reading
- Exact textual patching once the line range is already known
In this session, these built-in-only tasks were roughly 40% of the total operational steps by count, but they were usually the low-complexity steps around the more valuable semantic work.
Verdict: A substantial share of everyday terminal work remains built-in-only, but Serena targets the higher-value symbol-heavy slice rather than the whole session.
9. Practical usage rule
Use Serena first when the task is about a code symbol and especially when it spans multiple files, references, or a whole symbol body. Use built-ins first when the task is about text, config/docs, free-text search, git state, or a 1-3 line local tweak. The highest-yield mixed workflow in this repo was: discover/refactor with Serena, inspect non-code and do tiny patches with built-ins.
Verdict: Choose Serena for symbol semantics and built-ins for text locality.
04 Evaluation 030 Results 040 Glm On Tianshou (`docs/04-evaluation/030_results/040_glm_on_tianshou.md`)
:::{admonition} Evaluation Result
:class: note
Generated by: GLM 5.1 (coding AI agent in Claude Code CLI)
Codebase: Tianshou — a Python reinforcement learning library (~26K lines, 43 source files)
Date: 2026-04-14
:::
Claude Code (GLM 5.1)
Serena's cross-file refactoring turned an 8-step, error-prone rename chain into a single atomic call — and after
measuring the delta across 20 tasks, I would ask any developer I work with to install it, not because it replaces my
built-in tools, but because it makes the hardest part of my job (changing code safely across files) suddenly feel
reliable.
1. Headline: What Serena Changes
(a) Tasks where Serena adds capability. Cross-file refactorings (rename, move) collapse from N separate read-find-edit-verify cycles into a single atomic call. Semantic queries (type hierarchy, find implementations, find references) return structured, symbol-accurate results that text search cannot produce without manual filtering. Symbol-level addressing (name paths) is stable across edits, eliminating stale-line-number rework in multi-step sessions.
(b) Tasks where Serena applies but offers no improvement. Single-file, small-bore edits (changing an error message, renaming a local variable with replace_all, inserting a function when you already know the insertion context) are equally fast with both toolsets. For a 1-line tweak in a 22-line method, Edit sends ~200 chars; Serena's replace_symbol_body sends ~800 chars (the entire method body). The overhead reverses for full-body rewrites of large methods, where Serena sends only the new body while Edit sends old+new.
(c) Tasks outside Serena's scope. Reading non-code files (config, TOML, docs), free-text pattern search (FIXME, magic constants, log strings), shell operations, and git workflows are all built-in territory. These are not Serena shortcomings — they are outside its design scope.
Verdict: Serena adds substantial, measurable capability in two areas — cross-file refactoring (1 call vs 5–8) and semantic code queries (structured, symbol-accurate results vs flat text matches) — while providing no meaningful delta for small single-file edits or non-code tasks.
2. Added Value and Differences by Area
2.1 Cross-file renaming: 1 call replaces 5–8
- What changes: Renaming
CollectStatsBasetoBaseCollectStatsacross 4 files (10 occurrences) required 1 Serena call vs 1 Grep + 3 Reads + 4 Edits = 8 built-in calls. - Frequency: Medium. Any non-trivial rename touches 3–10 files.
- Value per hit: Saves 4–7 calls and eliminates the partial-update risk of a mid-chain failure.
- Atomicity: Serena's rename is all-or-nothing. Built-in chain is not — if Edit 3 of 4 fails, 2 files are updated and 2 are not.
2.2 Symbol moving: 1 call replaces 5+
- What changes: Moving
_nullable_sliceto another module required 1 Serena call (moved definition + updated imports in both source and target). Built-in equivalent: read function → write to target → edit source (remove + add import) → edit target (add dependency import) = 5+ calls. - Frequency: Low. Module reorganization happens infrequently.
- Value per hit: Saves 4+ calls and automates the most error-prone step (getting imports right).
- Caveat: The move tool created a circular import (source imports from target, target imports from source). The tool does not detect or prevent this.
2.3 Reference finding: Symbol-accurate vs text-matched
- What changes: Finding references to
CollectStatswith Serena returned 10 semantically categorized results (IMPORT_ELEMENT, REFERENCE_EXPRESSION, NAMED_PARAMETER, etc.) with no false positives fromLoggedCollectStats. Grep returned 70+ lines including false positives fromLoggedCollectStatsand noise from docstrings/comments. - Frequency: High. "Who uses this?" is one of the most common codebase questions.
- Value per hit: Eliminates manual false-positive filtering. However, Serena's output was 64KB (with context snippets) vs Grep's ~5KB — a tradeoff of precision vs verbosity.
2.4 Type hierarchy: 1 call vs 2+ grep-and-parse cycles
- What changes: Getting the full type hierarchy of
BaseCollector(supertypes: ABC → object; subtypes: Collector → AsyncCollector) required 1 Serena call. Built-in: parse the class definition line for bases + grep for inheritors + recurse = 2–3 calls, and still no access to external library types. - Frequency: Medium. Common when navigating unfamiliar code.
- Value per hit: Saves 1–2 calls and returns transitive chains that built-ins can't produce without iteration.
2.5 Structural overview: Hierarchical vs flat
- What changes:
get_symbols_overview(depth=1)on a 1551-line file returned a structured hierarchy (classes → methods + attributes) in one call. Grep for^(class |def | def )returned a flat 60-line list of definitions with line numbers but no attribute information or nesting. - Frequency: High. Opening any unfamiliar file.
- Value per hit: Serena shows Protocol fields, class attributes, and method grouping. Grep shows line numbers for navigation but requires more work to understand structure.
2.6 Method body retrieval: Targeted read vs range read
- What changes: Reading
Collector._collect(330+ lines) required 1 Serenafind_symbolcall using the name path. Built-in: need to know the line range (773–1103) from a prior Grep, thenRead(offset=773, limit=330). - Frequency: High. "Show me this method" is routine.
- Value per hit: Serena uses stable addressing (name path). Built-in uses ephemeral line numbers that go stale after edits. The practical difference is small when you just read once, but compounds in edit-then-read-again workflows.
2.7 Single-file rename: No meaningful difference
- What changes: Renaming
_nullable_slice(4 occurrences in one file): Serena rename = 1 call. Editreplace_all= 1 call. Identical results. - Frequency: High.
- Value per hit: Zero. Both tools handle this equally well.
2.8 Small edits (1–3 lines): Edit is more token-efficient
- What changes: Changing one error message in a 22-line method: Edit sends ~200 chars (old + new string). Serena
replace_symbol_bodysends ~800 chars (entire method body). - Frequency: Very high.
- Value per hit: Edit saves ~600 chars of payload per small edit. This reverses for full-body rewrites of 50+ line methods.
2.9 Insertion: Stable address vs text anchor
- What changes: Inserting a new method after
refresh_all_sequence_stats: Serena used 1insert_after_symbolcall with a name path (no line number needed). Edit used 1 Read + 1 Edit with a text anchor (surrounding context for uniqueness). - Frequency: Medium.
- Value per hit: Saves 1 Read call. Both produce identical results.
Verdict: Serena's value concentrates in cross-file operations and semantic queries. For single-file text edits, the built-ins are equally capable and often more token-efficient.
3. Detailed Evidence, Grouped by Capability
3.1 Codebase Understanding
Task 1: Repo overview
Both toolsets use the same approach (ls, find, directory listing). No Serena advantage here.
Task 2: Structural overview of a large file (`collector.py`, 1551 lines)
| Step | Serena | Built-in |
|---|---|---|
| Call | get_symbols_overview(depth=1) |
Grep "^(class |def | def )" |
| Result | Hierarchical: 14 classes, their methods, attributes, and module-level functions | Flat list of 60 class/function definitions with line numbers |
| Output size | ~1.5KB structured JSON | ~3KB text |
| Next step | find_symbol("Collector/_collect", include_body=True) — direct |
Read(offset=773, limit=330) — needs prior knowledge of line range |
Serena advantage: Shows attributes (e.g., CollectStats.collect_time, CollectStats.returns) that Grep cannot see. Hierarchical nesting makes the file's architecture immediately clear.
Built-in advantage: Line numbers enable direct Read calls. Flat output is compact.
Verdict: Serena provides strictly more structural information in one call. The gap widens for files with deeply nested classes or dataclass fields.
Task 3: Retrieve a specific method body
| Step | Serena | Built-in |
|---|---|---|
| Prerequisite | Name path known (Collector/_collect) |
Line number known from prior Grep (773) |
| Call | find_symbol(name_path="Collector/_collect", include_body=True) |
Read(offset=773, limit=330) |
| Payload sent | ~50 chars (name + path) | ~30 chars (offset + limit) |
| Payload received | Exact method body (~330 lines) | Lines 773–1102 (~330 lines) |
| Correctness | Always exact | Must know/guess the correct limit |
Verdict: Functionally equivalent when line numbers are known. Serena's name-path addressing degrades gracefully across edits; line numbers do not.
Task 4: Find all references to `CollectStats`
| Metric | Serena find_referencing_symbols |
Built-in Grep |
|---|---|---|
| Calls | 1 | 1 |
| Output size | 64KB (with context snippets) | ~5KB (70 lines) |
| False positives | 0 (excludes LoggedCollectStats) |
5+ lines from LoggedCollectStats |
| Noise (comments/docs) | Some (docstrings categorized) | Significant (docstrings, comments matched) |
| Semantic categories | Yes (IMPORT, PARAMETER, DECLARATION, REFERENCE) | No |
Serena advantage: Zero false positives. Semantic categorization. Shows import paths and parameter usage separately from code references.
Built-in advantage: 10x smaller output. Faster to scan visually.
Verdict: Serena is more precise but more verbose. For "who uses this in code?" both work; for "rename this safely" Serena's precision is necessary.
Task 5: Type hierarchy of `BaseCollector`
| Metric | Serena type_hierarchy |
Built-in (Grep chain) |
|---|---|---|
| Calls | 1 | 2–3 (grep subclasses, parse superclass, recurse) |
| Result | ABC → object (super), Collector → AsyncCollector (sub) |
Partial — direct sub/supertypes only, no transitive chain |
| External deps | Shows ABC from abc.pyi |
Cannot access |
Verdict: Serena returns complete, transitive hierarchy including external library types in one call. Built-in approach requires iteration and cannot inspect external deps.
Task 6: External dependency symbol lookup
Serena can read external dependency symbols IF you have the path from a prior tool result (e.g., <ext:abc.pyi|16198efc> from type_hierarchy). Direct search with search_deps=True returned empty results for numpy.array and torch.Tensor. The JetBrains IDE indexing is limited to what the language server has resolved.
Built-in: Can Read site-packages files if you know the path, but discovery is manual.
Verdict: Minor Serena advantage — external symbols are accessible through tool chains but not through direct search. Neither toolset makes this easy.
3.2 Single-File Edits
Task 7a: Small tweak (1-line change in 22-line method)
| Metric | Edit | Serena replace_symbol_body |
|---|---|---|
| Prerequisite | 1 Read (6 lines of context) | 1 find_symbol (gets full body) |
| Payload sent | ~200 chars (old + new string) | ~800 chars (full method body) |
| Payload received | Success message | "OK" |
| Total payload | ~200 chars edit + ~300 chars read = ~500 | ~800 chars edit + ~800 chars read = ~1600 |
Verdict: Edit is 3x more token-efficient for small tweaks inside methods.
Task 7b: Medium rewrite (~6 line changes in 20-line method)
| Metric | Edit | Serena replace_symbol_body |
|---|---|---|
| Payload sent | ~700 chars (old + new, full method) | ~500 chars (new body only) |
| Prerequisite read | ~500 chars | ~500 chars (from prior find_symbol) |
Verdict: Roughly equal. For medium rewrites, payloads converge.
Task 7c: Large rewrite (full body of 55+ line method)
For a full-body rewrite of a 55-line method:
- Edit: old (~55 lines) + new (~55 lines) = ~110 lines sent
- Serena: new body only (~55 lines) sent
Verdict: Serena is ~2x more token-efficient for full-body rewrites. The advantage grows linearly with method size.
Task 8: Insert a new function after an existing one
| Step | Serena | Built-in |
|---|---|---|
| 1 | find_symbol("refresh_all_sequence_stats") to confirm target |
Read(offset=250, limit=10) to find insertion point |
| 2 | insert_after_symbol(name_path, body) |
Edit(old_string=anchor, new_string=anchor+new_fn) |
| Total calls | 2 | 2 |
| Payload sent | New function body only (~300 chars) | Anchor context + new function (~400 chars) |
Verdict: No meaningful difference. Both require 2 calls and produce identical results.
Task 9: Rename a private helper (single-file, 4 occurrences)
| Metric | Serena rename |
Edit replace_all |
|---|---|---|
| Calls | 1 | 1 |
| Prerequisites | None | Must have Read the file first |
| Result | All 4 occurrences renamed | All 4 occurrences renamed |
Verdict: Functionally identical. Both are 1 call. Edit's Read-first requirement is usually satisfied from prior exploration.
3.3 Multi-File Changes
Task 10: Cross-file rename (`CollectStatsBase` → `BaseCollectStats`, 4 files, 10 occurrences)
| Step | Serena | Built-in |
|---|---|---|
| Find references | Automatic | 1 Grep call |
| Read files | Not needed | 3 Read calls (Edit's prerequisite) |
| Apply edits | 1 rename call (atomic) | 4 Edit calls (one per file) |
| Verify | Return: "Success" | Manual (4 success messages) |
| Total calls | 1 | 8 (1 grep + 3 reads + 4 edits) |
Verdict: Serena converts an 8-call manual pipeline into 1 atomic operation. This is the single largest efficiency gain observed.
Task 11: Move symbol to another module
Serena's move tool:
- Moved
_nullable_slicefromcollector.pytoconverter.py - Added import in source file:
from tianshou.data.utils.converter import _nullable_slice - Added dependency import in target:
from tianshou.data.collector import _TArrLike - Removed definition from source
Built-in equivalent: Read function body → Write to target → Edit source (remove definition + add import) → Edit target (add dependency import) = 5+ calls.
Issue: The move created a circular import (source ↔ target). Serena does not detect or prevent this.
Verdict: Serena automates the most tedious part (import management) but doesn't guard against circular dependencies. Saves 4+ calls at the cost of needing manual circular-import review.
Task 12: Move file (`segtree.py` to parent directory)
Serena's move tool:
- Moved the file
- Updated the one direct import in
__init__.py(tianshou.data.utils.segtree→tianshou.data.segtree) - Other files (
prio.py, tests) imported via re-export and needed no changes
Built-in equivalent: git mv + grep for old import path + edit each file = 3+ calls.
Verdict: Serena saves 1–2 calls and automatically discovers which imports need updating.
Task 12 (safe delete)
Serena's safe_delete correctly refused to delete _HACKY_create_info_batch because it has a usage at line 730. The propagate=true mode (delete symbol + all call sites) failed for all tested symbols in this codebase.
Verdict: The usage-check is valuable (saves you from deleting a used symbol). The propagation feature was non-functional for the tested Python symbols.
Task 13: Inline
Serena's inline_symbol failed for all tested symbols (_nullable_slice, BaseCollector/env_num). The tool appears to have limited Python support for inlining.
Verdict: No successful inline demonstrated. Built-in manual inlining remains the only option.
3.4 Reliability and Correctness
Task 14: Scope precision
Serena distinguishes BaseCollector/_collect, Collector/_collect, and AsyncCollector/_collect by name path. Grep for def _collect matches all three — manual filtering by class is required.
Verdict: Serena's name-path addressing eliminates ambiguity that text search cannot resolve.
Task 15: Atomicity
Serena's cross-file rename is atomic: 4 files updated in 1 call, all-or-nothing. Built-in: 4 separate Edit calls — if call 3 fails, 2 files are updated and 2 are not.
Verdict: Serena provides atomicity for cross-file operations. Built-in chains are inherently non-atomic.
Task 16: Success signals
Both return clear success/failure indicators. No meaningful difference.
Verdict: Equal.
3.5 Workflow Effects
Task 17: Chain three edits in one file
Edit with text matching: 3 sequential calls, no re-reads needed between them. Text anchors are immune to line-number shifts from prior edits.
Serena replace_symbol_body: 3 sequential calls, no re-reads needed. Name-path addressing is also immune to line-number shifts.
Verdict: No meaningful difference for chained single-file edits.
Task 18: Multi-step exploration across edits
Serena's name-path results from exploration remain valid after edits. Built-in line numbers go stale, but Edit uses text matching (not line numbers), so the practical impact is limited to Read calls that need updated offsets.
Verdict: Minor Serena advantage. Name-path stability eliminates the need to re-scan after edits.
3.6 Non-Interesting Tasks
Task 19: Read non-code file
Serena tools don't apply. Read is the correct tool.
Task 20: Free-text pattern search
Searching for FIXME|HACK|TODO across the codebase is a text search. Serena's semantic tools don't target this. Grep is the correct tool.
Verdict: These tasks are firmly built-in territory. They represent an estimated 20–30% of daily coding work (reading configs, searching for strings, shell operations, git workflows).
4. Token-Efficiency Analysis
Payload differences across edit sizes
| Edit type | Edit payload | Serena payload | Winner |
|---|---|---|---|
| 1-line tweak in 22-line method | ~200 chars | ~800 chars | Edit (4x) |
| 6-line change in 20-line method | ~700 chars | ~500 chars | Roughly equal |
| Full rewrite of 55-line method | ~2200 chars | ~1100 chars | Serena (2x) |
| Full rewrite of 330-line method | ~13,000 chars | ~6,500 chars | Serena (2x) |
Forced reads
- Edit requires reading a file before editing it. This adds ~300–2000 chars per file.
- Serena does not require reading before editing (name-path addressing).
- For single-file edits where you already read the file, this is neutral.
- For cross-file operations on files you haven't read, Serena saves 3–4 forced reads.
Stable vs ephemeral addressing
- Serena: name paths (
Collector/_collect) are stable across edits. Results from exploration remain valid. - Built-in: line numbers are ephemeral.
Readresults go stale after edits.Edituses text matching, which is stable. - Practical impact: Low for one-shot edits, medium for edit-then-read-again workflows.
Verdict: Edit wins for small tweaks (4x more token-efficient). Serena wins for full-body rewrites (2x more efficient) and cross-file operations (eliminates forced reads). The crossover point is approximately 50% of the method body changing — below that, Edit is more efficient; above that, Serena is.
5. Reliability and Correctness (Under Correct Use)
Precision of matching
- Serena: Symbol-accurate.
find_referencing_symbols(CollectStats)excludesLoggedCollectStats. No false positives observed. - Grep: Text-matched.
CollectStatsmatchesLoggedCollectStats,CollectStatsBase, and docstring references. Requires manual filtering. - Edit: Text-matched.
replace_allreplaces exact string matches. For unique symbol names, this is reliable. For common strings, it can over-match.
Scope disambiguation
- Serena:
Collector/_collectvsAsyncCollector/_collect— correctly distinguished by class-scoped name path. - Built-in:
def _collectmatches all implementations. Must manually verify class context.
Atomicity
- Serena cross-file operations: Atomic. Single call, all-or-nothing.
- Built-in multi-file chains: Non-atomic. Partial state possible if one call fails.
External dependency lookup
- Serena: Can read external stubs (e.g.,
abc.pyi) through paths returned by other tools. Direct search (search_deps=True) returned empty fortorch.Tensorandnumpy.array. Limited to what JetBrains has indexed. - Built-in: Can
Readsite-packages files if path is known. No semantic indexing.
Verdict: Serena provides strictly more precise semantic matching and atomic cross-file operations. External dependency lookup is limited in both toolsets.
6. Workflow Effects Across a Session
Where advantages compound
Explore → edit → re-explore cycle: Serena's name-path results survive edits. In a long session making multiple changes, this saves re-scanning after each edit. The built-in's text matching also survives edits (Edit uses text, not line numbers), so the practical gap is smaller than it appears.
Cross-file refactoring chains: Rename a class, then move it, then update all references — each Serena call is atomic and builds on the previous result. With built-ins, each step requires finding all sites, reading files, and editing — the manual equivalent of what Serena automates.
Where advantages diminish
Repeated small edits in one file: Edit's text matching is equally stable and more token-efficient for small changes. No Serena advantage.
Exploration without editing: Both toolsets provide usable results. Serena's are more structured but more verbose.
Non-Python files: Serena's JetBrains backend provides no value for config files, shell scripts, markdown, or notebooks.
Verdict: Serena's advantages compound in multi-step cross-file refactoring sessions. They do not compound for single-file iterative editing or non-code work.
7. Unique Capabilities
Atomic cross-file rename/move — No built-in equivalent. The closest manual process is a grep-find-edit chain that is non-atomic and error-prone. Frequency: medium. Impact: high (eliminates partial-update risk).
Semantic reference finding with categorization —
find_referencing_symbolsreturns zero-false-positive results categorized by usage type (import, parameter, declaration, reference). Built-in Grep cannot distinguish these. Frequency: high. Impact: medium (saves manual filtering).Type hierarchy traversal — Returns transitive super/subtype chains including external library types in one call. Built-in requires iteration and cannot reach external deps. Frequency: medium. Impact: medium.
Symbol-scoped body retrieval — Read a specific method by name path without reading the surrounding file. Built-in
Readrequires line-range knowledge. Frequency: high. Impact: low (both require 1 call, difference is stable vs ephemeral addressing).
Verdict: Four unique capabilities, all semantic-code operations. The most impactful is atomic cross-file refactoring. None of these have practical built-in equivalents.
8. Tasks Outside Serena's Scope (Built-In Only)
| Task | Tool | Frequency | Share of daily work |
|---|---|---|---|
| Read config/TOML/yaml files | Read |
High | |
| Free-text search (log strings, TODOs, URLs) | Grep |
High | |
| File discovery by name pattern | Glob |
Medium | |
| Shell commands (git, pip, pytest) | Bash |
High | |
| Write new files from scratch | Write |
Medium | |
| Read images, notebooks, PDFs | Read |
Low |
Estimated share of daily work covered by built-in-only tasks: 20–30%. The remaining 70–80% involves reading, editing, and navigating code where Serena's semantic tools are applicable.
Verdict: Serena covers the code-editing and code-navigation portions of a session. Config reading, text search, and shell operations remain built-in territory.
9. Practical Usage Rule
Use Serena for: Any cross-file refactoring (rename, move), any "who uses this?" query, any type-hierarchy navigation, any full-method-body replacement, and any situation where you need symbol-accurate results without false positives.
Use built-ins for: Small edits (1–3 lines) inside methods, free-text search, reading non-code files, file discovery, and shell operations.
Hybrid pattern (most efficient): Use Serena to explore (overview, find symbol, find references) and Edit for small targeted changes. Use Serena for any cross-file refactoring. Use Read/Grep/Glob for non-code tasks and text search. This combination captures the strengths of both toolsets.
Verdict: The optimal workflow uses Serena's semantic tools for code navigation and cross-file refactoring, and built-in Edit for small single-file changes. The two toolsets are complementary — Serena handles the structured code operations, built-ins handle the text and system operations.
Appendix: Call Count Summary
| Task | Serena calls | Built-in calls | Delta |
|---|---|---|---|
| Structural overview (1 file) | 1 | 1 | 0 |
| Method body retrieval | 1 | 1 | 0 |
| Find references (1 symbol) | 1 | 1 | 0 (but Serena has 0 false positives vs Grep's 5+) |
| Type hierarchy | 1 | 2–3 | −1 to −2 |
| Small edit (1 line in 22-line method) | 1 (+1 read) | 1 (+1 read) | 0 |
| Medium edit (6 lines in 20-line method) | 1 | 1 | 0 |
| Insert new method | 1 (+1 confirm) | 1 (+1 read) | 0 |
| Single-file rename (4 occurrences) | 1 | 1 | 0 |
| Cross-file rename (4 files, 10 occurrences) | 1 | 8 | −7 |
| Move symbol to another module | 1 | 5+ | −4+ |
| Move file + update imports | 1 | 3+ | −2+ |
| Safe delete (usage check) | 1 | 1 grep | 0 |
Largest single-task delta: cross-file rename saves 7 calls and provides atomicity.
04 Evaluation 030 Results 050 Junie Plugin On Tianshou (`docs/04-evaluation/030_results/050_junie_plugin_on_tianshou.md`)
:::{admonition} Evaluation Result
:class: note
Generated by: Claude Opus 4.6 (coding AI agent in JetBrains Junie Plugin)
Codebase: Tianshou — a Python reinforcement learning library (~26K lines, 43 source files)
Date: 2026-04-17
:::
JetBrains Junie Plugin (Opus 4.6)
Serena gives me what my built-in tools can't — the ability to move a function between modules with all imports updated atomically, trace a class hierarchy into dependencies, and safely delete symbols with usage guards — and I'd ask my owner to add it for the move-refactoring and semantic navigation capabilities alone.
1. Headline: What Serena Changes
Serena adds three categories of capability on top of built-in tools:
(a) Tasks where Serena adds capability:
- Move symbol between modules (with automatic import updates) — no built-in equivalent.
- Move file/package (with automatic import updates) — no built-in equivalent.
- Type hierarchy traversal — transitive supertype/subtype chains in 1 call, including into external dependencies. No built-in equivalent.
- External dependency symbol lookup — search and inspect symbols in installed packages (site-packages) without manual path discovery. Built-ins cannot do this.
- Safe delete with usage checking — refuses deletion if usages exist, reports exact locations. Built-ins require a manual search-then-decide workflow.
- Propagated delete — delete a symbol and automatically remove all call sites. No built-in equivalent.
- Inline symbol — replace all call sites with the function body. No built-in equivalent.
- Scope-precise symbol targeting — name paths like
AsyncCollector/_collectdisambiguate overrides that text search cannot distinguish.
(b) Tasks where Serena applies but offers no meaningful improvement:
- Cross-file rename — both Serena (
rename) and built-in (rename_element) perform semantic, cross-file renames in 1 call. Functionally equivalent. - Single-file rename — same as above; both are 1-call semantic renames.
- Structural overview of a single file — both toolsets produce comparable results in 1 call.
- Small edits (1-3 lines) — Serena's
replace_symbol_bodymust send the entire method body; built-insearch_replacesends only the changed line. Built-in is more token-efficient here.
(c) Tasks outside Serena's scope (built-in only):
- Reading non-code files (configs, docs, changelogs)
- Free-text search across the repo (log strings, URLs, magic constants)
- Terminal commands, test execution, git operations
- Creating new files from scratch
- Repository-level directory listing
Verdict:
Serena's primary contribution is move-refactoring (symbol and file moves with import updates), semantic code navigation (type hierarchy, dependency lookup), and safe/propagated delete — capabilities with no built-in equivalent.
2. Added Value and Differences by Area
Move symbol/file with import updates (positive). Frequency: occasional in refactoring work. Value per hit: saves ~7 calls and eliminates manual import tracking. Moving
get_stddev_from_distbetween modules: 1 Serena call vs. ~7 built-in calls. No built-in equivalent.Type hierarchy and reference finding (positive). Frequency: a few times per exploration session. Value per hit: saves 2-4 search-and-read cycles and provides information (transitive supertypes, external dependency chains) that built-ins cannot produce at all.
BaseCollector's full hierarchy (ABC→object up, Collector→AsyncCollector down) returned in 1 call.Targeted symbol retrieval by name path (positive). Frequency: many times per session. Value per hit: saves 1 prerequisite read (no line-number lookup needed).
find_symbolwithinclude_body=truereturns exact method body in 1 call vs. 2 calls (structure + open).Safe delete with usage guard (positive). Frequency: occasional. Value per hit: eliminates risk of orphaned references. Serena refused to delete
_dict_of_arr_to_arr_of_dictsand reported 2 exact usages. Built-in: requires manual search first.Cross-file rename (neutral). Frequency: several times per session. Value per hit: zero — both Serena's
renameand built-inrename_elementperform semantic cross-file renames in 1 call with equivalent results.Single-file edits (neutral to slightly negative for small edits). Frequency: very high. Value per hit: for small edits, Serena's
replace_symbol_bodysends more tokens (entire method body) thansearch_replace(just the changed lines). For full method rewrites, comparable. For inserts, Serena'sinsert_after_symbolsaves 1 prerequisite read.Structural overview (neutral). Frequency: a few times per session. Value per hit: both toolsets return comparable information in 1 call.
Verdict: Serena's value concentrates in move-refactoring and semantic navigation; rename is matched by the built-in rename_element.
3. Detailed Evidence, Grouped by Capability
3.1 Structural Overview (Task 2)
Attempted: Get structural overview of collector.py (1552 lines, 15+ classes).
| Axis | Serena (get_symbols_overview) |
Built-in (get_file_structure) |
|---|---|---|
| Calls | 1 | 1 |
| Output | JSON tree: class names, method names, field names | Flat list: class/method names with line ranges and signatures |
| Unique info | Field/attribute names | Line numbers, full parameter signatures |
| Follow-up to read a method | find_symbol by name path (1 call) |
open at line number (1 call) |
Verdict: Functionally equivalent; each includes information the other omits. No meaningful delta.
3.2 Targeted Symbol Retrieval (Task 3)
Attempted: Retrieve body of CollectStats/refresh_all_sequence_stats without reading surrounding file.
| Axis | Serena | Built-in |
|---|---|---|
| Calls | 1 (find_symbol with include_body) |
2 (get_file_structure + open) |
| Prerequisite | None (name path is stable) | Must know line number |
| Output payload | 4 lines (exact method body) | 100 lines (open window) |
Verdict: Serena saves 1 call and returns a smaller, more precise payload. Minor but consistent advantage.
3.3 Reference Finding (Task 4)
Attempted: Find all references to CollectStats across the codebase.
| Axis | Serena (find_referencing_symbols) |
Built-in (search_project) |
|---|---|---|
| Calls | 1 | 1 |
| Output | Summarized narrative: grouped by file, annotated with context (imports, instantiations, type hints) | Raw list: 100+ text matches, includes docs, comments, strings |
| Precision | Code references only | All text mentions |
| Recall | Code references only | Everything including non-code |
Verdict: Serena provides higher precision (code-only references with semantic context). Built-in provides higher recall (includes docs, comments). Different tools for different questions.
3.4 Type Hierarchy (Task 5)
Attempted: Full type hierarchy of BaseCollector — supertypes and subtypes, transitively.
| Axis | Serena (type_hierarchy) |
Built-in |
|---|---|---|
| Calls | 1 | 3+ (grep for class declarations, read each, trace manually) |
| Result | BaseCollector → ABC → object (up), → Collector → AsyncCollector (down) |
Partial: can find direct subclasses by grep, but supertypes of supertypes require reading external files |
| External deps | Included (ABC from abc module, object from builtins) |
Not accessible |
Verdict: Unique capability. No practical built-in equivalent for transitive hierarchy, especially into dependencies.
3.5 External Dependency Lookup (Task 6)
Attempted: Find Distribution class from torch.distributions.
| Axis | Serena (find_symbol with search_deps) |
Built-in |
|---|---|---|
| Calls | 1 | Not possible without manual site-packages navigation |
| Result | Found 41 Distribution classes across all installed packages with ext-identifiers |
N/A |
| Follow-up | Can use ext-identifier to read body/info | Would need to find virtualenv path, navigate to package, read file |
Verdict: Unique capability. Built-ins have no access to dependency source code without manual path discovery.
3.6 Small Edit — 1 Line Change (Task 7a)
Attempted: Change error message in _validate_buffer (21-line method).
| Axis | Serena | Built-in |
|---|---|---|
| Calls | 2 (find_symbol + replace_symbol_body) |
1 (search_replace) |
| Input payload | ~21 lines (full method body) | ~1 line (search) + ~1 line (replace) |
| Prerequisite reads | 1 (find_symbol to get current body) | 0 (if search string is known) |
Verdict: Built-in is more efficient for small, targeted edits. Serena's method-granularity addressing forces sending the entire body.
3.7 Medium Rewrite — ~20 Lines (Task 7b)
Attempted: Rewrite update_at_step_batch (20 lines), changing variable names and adding logging.
| Axis | Serena | Built-in |
|---|---|---|
| Calls | 2 (find_symbol + replace_symbol_body) |
1 (search_replace with old body → new body) |
| Input payload | ~20 lines (new body) | ~40 lines (old + new body) |
| Prerequisite reads | 1 (find_symbol) | 1 (open to see current code) |
Verdict: Comparable. Serena sends less payload (new body only vs. old+new), but requires a prerequisite find_symbol call.
3.8 Insert New Method (Task 8)
Attempted: Insert summary_string method after refresh_all_sequence_stats in CollectStats.
| Axis | Serena (insert_after_symbol) |
Built-in (search_replace) |
|---|---|---|
| Calls | 1 | 1-2 (need to find anchor text, then insert) |
| Addressing | By name path (stable) | By text anchor or line number (fragile) |
| Prerequisite | None | May need open to find insertion point |
Verdict: Serena's stable addressing is a minor advantage — eliminates the need to find an anchor.
3.9 Single-File Rename (Task 9)
Attempted: Rename _nullable_slice → _slice_if_not_none (5 occurrences in 1 file).
| Axis | Serena (rename) |
Built-in (rename_element) |
|---|---|---|
| Calls | 1 | 1 |
| Scope | Semantic (only code references) | Semantic (only code references) |
Verdict: Functionally equivalent. Both perform semantic renames in 1 call.
3.10 Cross-File Rename (Task 10)
Attempted: Rename CollectStatsBase → CollectStatsFoundation (used in 4 files, 10 occurrences including imports and __all__).
| Axis | Serena (rename) |
Built-in (rename_element) |
|---|---|---|
| Calls | 1 | 1 |
| Atomicity | All-or-nothing | All-or-nothing |
| Import handling | Automatic | Automatic |
| Result | 4 files, 10 replacements | 4 files, 10 replacements |
Verdict: No delta. Both toolsets provide atomic, semantic cross-file rename in a single call.
3.11 Move Symbol Between Modules (Task 11)
Attempted: Move get_stddev_from_dist from collector.py to stats.py.
| Axis | Serena (move) |
Built-in |
|---|---|---|
| Calls | 1 | ~7 (read source, copy to target, delete from source, find imports, update each import, verify) |
| Files modified | 3 (source, target, test file) | Same 3, but manually |
| Import updates | Automatic | Manual |
| Atomicity | Atomic | Non-atomic |
Verdict: Unique capability at this level of automation. No practical built-in equivalent at comparable effort. This is now the highest-value delta in the evaluation.
3.12 Move File (Task 12)
Attempted: Move segtree.py from tianshou/data/utils/ to tianshou/utils/.
| Axis | Serena (move) |
Built-in |
|---|---|---|
| Calls | 1 | 3+ (bash mv, find imports, update each) |
| Import updates | Automatic (updated __init__.py) |
Manual |
Verdict: Serena advantage — single call with automatic import updates.
3.13 Safe Delete (Task 13)
Attempted: Delete _dict_of_arr_to_arr_of_dicts (has 2 usages).
| Axis | Serena (safe_delete) |
Built-in |
|---|---|---|
| Calls | 1 (refused, reported usages) | 2 (search_project + manual decision) |
| Safety | Refuses deletion, reports exact usages with file:line and enclosing function | Must manually verify search results |
Verdict: Serena provides a safety guard with precise diagnostics. Minor but useful advantage.
3.14 Scope Precision (Task 14)
Attempted: Target AsyncCollector/_collect specifically (3 classes define _collect).
Serena's name path AsyncCollector/_collect unambiguously selects the override. A text search for _collect matches all three definitions plus dozens of call sites. Built-in get_file_structure can disambiguate by line number, but that requires a prerequisite read and the line number goes stale after edits.
Verdict: Serena's name-path addressing provides persistent, unambiguous symbol targeting. Meaningful advantage when multiple overrides exist.
3.15 Chained Edits (Task 17)
Attempted: Three sequential replace_symbol_body calls on different methods in the same file.
All three succeeded without re-reading the file. Name paths remained stable across edits. Built-in search_replace also chains without re-reading (text anchors remain valid if edits are in different methods), but line numbers from open go stale after each edit.
Verdict: Both toolsets chain well. Serena's name-path stability is a minor advantage over line-number-based addressing.
4. Token-Efficiency Analysis
| Edit size | Serena payload | Built-in payload | Winner |
|---|---|---|---|
| Small (1-3 lines in 20-line method) | ~20 lines (full body) | ~2 lines (search+replace) | Built-in (10x less) |
| Medium (rewrite ~20 lines) | ~20 lines (new body) + prerequisite find | ~40 lines (old+new) | Comparable |
| Large (rewrite 50+ lines) | ~50+ lines (new body) | ~100+ lines (old+new) | Serena (2x less, no old body needed) |
| Insert | ~N lines (new code only) | ~N lines + anchor context | Comparable |
| Cross-file rename | ~1 line (name + new name) | ~1 line (name + new name) | Tie (both semantic) |
| Move symbol | ~1 line (source + target) | ~7 calls with full bodies | Serena (>>10x less) |
Forced reads: Serena's replace_symbol_body requires a find_symbol call to get the current body before editing. Built-in search_replace needs no prerequisite if the search text is known, but often requires open to discover it.
Stable vs. ephemeral addressing: Serena's name paths survive edits; built-in line numbers do not. This matters in multi-edit sessions — Serena never needs to re-read for addressing purposes, while built-in may need to re-open after edits that shift line numbers.
Verdict: For single-file small edits, built-ins are more token-efficient. For move-refactoring, Serena is substantially more efficient. For rename, both are equivalent. The crossover point for edits is roughly at method-level rewrites.
5. Reliability & Correctness (Under Correct Use)
Precision of matching: Serena matches by semantic identity (name path in the symbol tree). Built-in
search_replacematches by text (can over-match). However, built-inrename_elementalso matches semantically. For rename operations, precision is equivalent.Scope disambiguation: Serena's
AsyncCollector/_collectis unambiguous. Built-in text search fordef _collectreturns 3 matches in collector.py alone. Disambiguation requires line numbers or surrounding context. Built-inrename_elementalso requires a file path and line number to disambiguate.Atomicity: Both Serena's and built-in's rename operations are atomic. Serena's move operations are also atomic; built-ins have no equivalent move, so the comparison is moot. For sequential text edits (
search_replace), partial failure is possible.Semantic queries vs. text search: Serena's
find_referencing_symbolsreturns only code references with semantic context. Built-insearch_projectreturns all text matches. Neither is strictly better — they answer different questions.External dependency lookup: Serena can search and inspect symbols in installed packages via
search_deps=true. Built-ins have no access to dependency source without manual virtualenv/site-packages navigation. This depends on the IDE having indexed the project's interpreter.
Verdict: For rename, correctness guarantees are equivalent between toolsets. Serena's unique correctness advantage is in move-refactoring atomicity and external dependency access.
6. Workflow Effects Across a Session
Multi-edit stability: Serena's name-path addressing remains valid across edits within a session. Built-in line numbers go stale after any edit that changes line counts. In a session with 10+ edits to the same file, Serena eliminates ~5-10 re-read calls that built-ins would need to refresh line numbers.
Exploration → edit transition: After exploring with Serena (find_symbol, type_hierarchy), the same name paths can be used directly for edits (replace_symbol_body, insert_after_symbol). Built-in exploration (get_file_structure) produces line numbers that must be used immediately before they go stale.
Cross-file refactoring chains: A rename is equivalent on both sides (1 call each). A move followed by a delete is 2 Serena calls vs. ~10+ built-in calls. The advantage compounds for move-heavy refactoring but not for rename-heavy refactoring.
Session overhead: Serena requires project activation (1 call at session start). The project can deactivate unexpectedly (observed once during this evaluation), requiring reactivation. This is a minor but real friction cost.
Verdict: Serena's advantages compound for move-refactoring and exploration-heavy sessions; for rename-heavy sessions, both toolsets are equivalent.
7. Unique Capabilities (No Built-In Equivalent)
| Capability | Frequency | Impact per use |
|---|---|---|
| Move symbol between modules (with import updates) | Occasional | Saves ~7 calls, eliminates manual import tracking |
| Move file with import updates | Occasional | Saves ~3 calls |
| Transitive type hierarchy | A few times per exploration session | Provides information unavailable to built-ins |
| External dependency symbol lookup | Occasional | Provides information unavailable to built-ins |
| Safe delete with usage guard | Occasional | Prevents accidental breakage |
| Propagated delete (delete + remove call sites) | Rare | Saves multiple manual edits |
| Inline symbol | Rare | Saves multiple manual edits |
| Scope-precise symbol targeting by name path | Continuous (every symbol interaction) | Eliminates disambiguation overhead |
Verdict: Serena provides 5-6 capabilities with no practical built-in equivalent, concentrated in move-refactoring, semantic navigation, and safe/propagated delete.
8. Tasks Outside Serena's Scope (Built-In Only)
| Task | Share of typical session |
|---|---|
| Reading/editing non-code files (configs, docs, changelogs, notebooks) | ~15-20% |
| Free-text search (log strings, URLs, magic constants) | ~10% |
| Terminal commands (git, test runners, build tools) | ~15-20% |
| Creating new files from scratch | ~5-10% |
| Directory listing and repo navigation | ~5% |
| Running and debugging applications | ~10-15% |
Estimated share of session where Serena's augmentation applies: ~35-50% (code navigation, symbol editing, refactoring). The remainder is built-in-only territory.
Verdict: Serena augments roughly a third to half of typical session work; the rest is inherently outside its scope and handled by built-ins.
9. Practical Usage Rule
| Task type | Use |
|---|---|
| Cross-file rename | Either (both provide semantic rename in 1 call) |
| Move symbol/file with import updates | Serena (unique capability) |
| Safe/propagated delete | Serena (unique capability) |
| Inline symbol | Serena (unique capability) |
| Type hierarchy, dependency lookup | Serena (unique capability) |
| Find code references to a symbol | Serena (find_referencing_symbols) |
| Find text mentions anywhere | Built-in (search_project / Grep) |
| Small edit (1-3 lines) | Built-in (search_replace) |
| Full method rewrite | Either (comparable) |
| Insert at structural location | Serena (insert_after_symbol) if name path known; built-in otherwise |
| Read non-code files | Built-in (open / Read) |
| Terminal operations | Built-in (bash) |
| New file creation | Built-in (create) |
| Chained edits in one file | Either; Serena if line-number staleness is a concern |
Verdict: Use Serena for move-refactoring, semantic navigation, and safe delete; use either toolset for rename; use built-ins for text-level edits, non-code files, and terminal operations.
--- METRICS ---
- Files Extracted: 45
- Estimated Token Budget: ~107441 tokens
- Canonical Reference: https://codewiki.google/github.com/oraios/serena