serena Documentation

A powerful MCP toolkit for coding, providing semantic retrieval and editing capabilities - the IDE for your agent

30,612 stars Python Python 45 files · ~107,518 tokens #agent#ai#ai-coding#claude#claude-code#codex#ide#jetbrains
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy serena on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name serena -p 9121:9121 --restart always serena:latest
Configured Services & Ports
⚙️ serena
image: serena:latest
🔌 Ports: 127.0.0.1:${SERENA_PORT:-9121}:9121, 127.0.0.1:${SERENA_DASHBOARD_PORT:-24282}:24282
docker-compose.yml
yaml (docker-compose.yml)
services:
  serena:
    image: serena:latest
    # To work with projects, you must mount them into /workspace/ in the container:
    # volumes:
      # - ./my-project:/workspace/my-project
      # - /path/to/another/project:/workspace/another-project
    build:
      context: ./
      dockerfile: Dockerfile
      target: production
    ports:
      - "127.0.0.1:${SERENA_PORT:-9121}:9121"
      - "127.0.0.1:${SERENA_DASHBOARD_PORT:-24282}:24282"
    environment:
      - SERENA_DOCKER=1
    command:
      - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0"
      # Alternatively add further arguments, e.g. a context
      # - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0 --context ide"

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 codebase
  • replace_content – agent-optimised regex-based and literal text replacement
  • list_dir / find_file – directory listing and file search
  • read_file – read files or file chunks
  • execute_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:

bash
uv tool install -p 3.13 serena-agent

After 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:

bash
serena init

By 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:


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:

  1. Elixir installed and available in your PATH

  2. Expert (optional - will be downloaded automatically if not found)

Features

The Elixir integration provides:

  • Language Server Protocol (LSP) support via Next LS
  • File extension recognition for .ex and .exs files
  • 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-versions file 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-versions file

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:

bash
pytest test/solidlsp/elixir/ -m elixir

Implementation Details

  • Main class: ElixirTools in elixir_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 point
  • Calculator.fs - Calculator functions and types
  • Models/Person.fs - Person and Employee data models

Build

bash
dotnet build

Run

bash
dotnet run

Overview (`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:

  1. Clone the repository
  2. Install dependencies
  3. Run tests

Usage

See guide.md for detailed usage instructions.

Code Examples

Here's a simple example:

python
def hello_world():
    print("Hello, World!")

JavaScript Example

javascript
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 .ada files;
    works best with a .gpr GNAT project file at the repository root;
    SPARK is handled by the same server transparently — set language ada for both.
    To use a pre-installed ALS (e.g. from Alire, GNAT Studio, or the VS Code Ada extension),
    set ls_specific_settings.ada.ls_path.)

  • AL

  • Angular
    (experimental; requires Node.js + npm, plus npm install having been run in the project root so that @angular/core
    is resolvable — without it, template-aware features silently return empty;
    subsumes typescript and html for .ts/.html files, so do not also list those)

  • Ansible
    (experimental; requires Node.js and npm; automatically installs @ansible/ansible-language-server;
    must be explicitly specified in the language_servers entry in the project.yml; requires ansible in 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 .astro files plus TypeScript/JavaScript files via @astrojs/language-server; a companion typescript-language-server + @astrojs/ts-plugin is spawned automatically for cross-file rename, go-to-definition, and references across .ts/.js and .astro files; use language astro for Astro projects instead of also enabling typescript)

  • 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 .bsl and .os files; configure optional ls_path or bsl_ls_version under ls_specific_settings.bsl)

  • C#
    (by default, uses the Roslyn language server (language csharp), requiring .NET v10+ and, on Windows, pwsh (PowerShell 7+);
    set language to csharp_omnisharp to use OmniSharp instead)

  • C/C++
    (by default, uses the clangd language server (language cpp) but we also support ccls (language cpp_ccls);
    for best results, provide a compile_commands.json at 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 as name#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 via gleam lsp)

  • Go
    (requires installation of gopls)

  • Groovy
    (requires a local groovy-language-server.jar, configured via ls_jar_path in ls_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 (language hlsl); 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 language typescript for both JavaScript and TypeScript)

  • Julia
    (by default, uses LanguageServer.jl (language julia); the
    Fatou alternative (language julia_fatou) is installed
    automatically and requires uv/uvx in PATH)

  • Kotlin
    (uses the pre-alpha official kotlin LS, some issues may appear)

  • LaTeX
    (experimental; must be explicitly enabled via language latex; uses texlab,
    auto-downloaded as a SHA-256-verified prebuilt binary; supports .tex, .bib, .sty, and .cls files; texlab is
    GPL-3.0 and runs as a separate downloaded process)

  • Lean 4
    (requires lean and lake installed via elan; uses the built-in Lean 4 LSP;
    the project must be a Lake project with lake build run before use)

  • Lua
    (by default, uses lua-language-server; the experimental Rust-based EmmyLua Analyzer
    can be selected with language lua_emmylua; Serena manages a verified download unless ls_path is set;
    references from other files are not found if the file defining the symbol uses CRLF line endings)

  • Luau

  • Markdown
    (must explicitly enable language markdown, 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 .mrc files)

  • Nextflow
    (uses the official Nextflow language server, which is automatically
    downloaded; requires a Java 17+ runtime, discovered via ls_specific_settings.nextflow.java_home, JAVA_HOME or java on PATH;
    covers .nf scripts — Nextflow .config files 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. GREET for process 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; set PP and FPCDIR environment variables for source navigation)

  • Perl
    (requires installation of Perl::LanguageServer)

  • PHP
    (by default, uses the Intelephense language server (language php), set INTELEPHENSE_LICENSE_KEY environment variable for premium features;
    we also support Phpactor (language php_phpactor), which requires PHP 8.1+;
    the experimental PHPantom backend (language php_phpantom);
    and the experimental standalone Devsense PHP Language Server
    (language php_devsense), which is installed with npm when needed

  • PowerShell
    (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 via Save-Module from your configured PowerShell repository)

  • Python
    (by default, uses Pyright (language python);
    alternatives: BasedPyright (language python_basedpyright),
    ty (language python_ty),
    pyrefly (language python_pyrefly),
    Jedi (language python_jedi);
    Pyright, BasedPyright, ty, and pyrefly require uv/uvx in PATH)

  • QML
    (requires Qt 6, provides qmlls or qmlls6 on PATH; see the Qt qmlls documentation)

  • R
    (requires installation of the languageserver R package)

  • Rego
    (requires the Regal language server on PATH)

  • Ruby
    (by default, uses ruby-lsp (language ruby); use language ruby_solargraph to 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 a foundry.toml or hardhat.config.js in the project root)

  • Svelte
    (requires Node.js v18+ and npm; supports .svelte Single File Components plus TypeScript/JavaScript files via svelte-language-server; a companion typescript-language-server + typescript-svelte-plugin is spawned automatically for cross-file rename, go-to-definition, and references across .ts/.js and .svelte files; use language svelte for Svelte projects instead of also enabling typescript)

  • Swift

  • SystemVerilog
    (uses verible-verilog-ls, taken from PATH if present, otherwise version v0.0-4051-g9fdb4057 is downloaded automatically)

  • Terraform
    (uses terraform-ls 0.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 (language typescript); the native language server of
    TypeScript 7 (language typescript_tsgo) is installed automatically via npm, requiring Node.js and npm
    only for the installation)

  • Deno
    (experimental; requires the deno CLI on PATH — it bundles the language server used here;
    serves Deno TypeScript/JavaScript and understands npm: / jsr: / https: imports and the Deno.*
    globals, which the plain TypeScript language server does not; overlaps typescript on file extensions,
    so it is not auto-detected and must be set as the language explicitly — do not also enable typescript
    for the same files)

  • VB.NET
    (uses the community Roslyn-based roslyn-vbnet-languageserver (language vbnet), requiring .NET v10+ and, on Windows, pwsh (PowerShell 7+);
    also handles .cs files so mixed VB.NET/C# solutions share one language server instance; for pure C# projects, csharp takes 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:

shell
serena download-ls-dependencies python typescript

Language 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.
{raw} html
<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:

  1. 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.
  2. Python MCP SDK
  3. 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 the
LICENSE 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 LICENSE were 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:

text
uv tool install -p 3.13 serena-agent

Upon 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:

text
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:

text
uv tool uninstall serena-agent

02 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.

text
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:

text
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.

text
serena start-mcp-server

See 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

text
serena start-mcp-server --transport streamable-http --port

and 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

text
<serena> start-mcp-server --help

to 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:

text
<serena> --help

To get help on a specific command, run:

text
<serena> <command> --help

In 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:

bash
# 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-name

Explore 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.

text
uvx -p 3.13 --from git+https://github.com/oraios/serena serena

This 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

  1. Clone the repository and change into it.

    shell
    git clone https://github.com/oraios/serena
    cd serena
  2. Run Serena via

    shell
    uv run serena

    when within the serena installation directory.
    From other directories, run it with the --directory option, i.e.

    shell
    uv 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:

shell
docker run --rm -i --network host -v /path/to/your/projects:/workspaces/projects ghcr.io/oraios/serena:latest serena

This 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:

bash
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.

{raw} html
<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:

  1. Install the plugin in your JetBrains IDE
  2. Configure Serena to use the JetBrains language backend (see below)
  3. Open the project you want to work on in your JetBrains IDE and activate it in Serena (see below)
  4. Start coding via your MCP client as usual
{admonition} *note:* the plugin is a language intelligence backend for the serena mcp server.
: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

shell
serena init -b JetBrains

to 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

yaml
language_backend: JetBrains

Note 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:

yaml
language_backend: JetBrains

If 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:

  1. Use the --project flag to activate the project at startup, which will use its configured backend.
  2. 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:

  1. Open the project you want to work on in your JetBrains IDE.
    Note that the project must be appropriately set up in your IDE, i.e. symbol lookups for all relevant programming languages and frameworks should work in the IDE.

    You can optionally make Serena open an IDE instance for your project root folder automatically upon project activation, allowing you to skip this step for a project that was previously set up correctly.
    To enable this, configure jetbrains_launch_command in Serena's global configuration file appropriately.

  2. Activate the project's root folder as a project in Serena (see Project Creation and Project Activation).

  3. 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 use 0.0.0.0 to 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

  1. determine how to add a custom MCP server to your client (refer to the client's documentation).
  2. add a new MCP server entry by specifying either

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
    --project argument with a single-project context (e.g. ide or claude-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, the activate_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.

text
"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:

json
{
    "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:

text
serena setup claude-code

Find 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:

shell
claude mcp add --scope user serena -- serena start-mcp-server --context claude-code --project-from-cwd

Per-Project Configuration. Alternatively, to add Serena only for the current project in the current directory,
use the command:

shell
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):

json
{
    "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 consecutive
    grep or read_file calls without using any Serena tools in between. Searches and source-file reads
    run via the Bash tool (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 (acceptEdits or auto), 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.

shell
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):

yaml
- dsh-hooks-claude-code:
    # Use an absolute path; relative paths resolve against DSH's process launch directory.
    configPath: /absolute/path/to/.dsh/hooks.json

The 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:

json
{
  "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 same
hookSpecificOutput shape as Claude Code, so the bridge can consume permissionDecision and
permissionDecisionReason without a client-specific wrapper. The bridge supports deny/ask responses for
PreToolUse, 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 a
command 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 consecutive
    grep or read_file calls 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:

json
{
    "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, and
serena 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):

toml
[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:

text
/* 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 support
Stop 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 sees
mcp__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:

text
serena setup grok

Manual 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:

bash
grok mcp add --scope user serena -- serena start-mcp-server --context=grok --project-from-cwd

Alternatively, add the following to ~/.grok/config.toml:

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:

bash
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).

json
{
  "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

json
{
  "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):

json
{
  "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:

json
{
  "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:

json
{
  "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:

json
{
  "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:

text
serena setup codebuddy

Manual 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:

bash
codebuddy mcp add --scope user serena -- serena start-mcp-server --context codebuddy --project-from-cwd

Project-Level Configuration. To add the Serena MCP server for a single project only:

bash
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):

json
{
  "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-in grep and read tools.
  • 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 (acceptEdits or auto), 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):

json
{
  "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), under
hooks.events, with hooks.enabled set to true:

json
{
  "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 to serena-hooks auto-approve --client=zcode the same way as
    remind above; 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:

  1. Project creation: Configuring project settings for Serena (and indexing the project, if desired)
  2. Project activation: Making Serena aware of the project you want to work with
  3. Onboarding: Getting Serena familiar with the project (creating memories)
  4. 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:

text
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 --language parameter
    (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:

text
serena project index

Indexing 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_project tool to be active,
    which it isn't in single-project contexts like ide or claude-code if 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 like ide or claude-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:

shell
git config --global core.autocrlf true

Logging, 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

    text
    serena 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:

  1. 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.
  2. Versionable with the project. Project memories live alongside the code and can
    be committed, reviewed in PRs, and reverted like any other repository artifact.
  3. 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 a mem:core entry point pointing at focused
    memories. The agent decides what to read based on names plus the references it has
    already seen.
  4. 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.
  5. 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.
  6. Framework-agnostic. The storage format is plain Markdown files in
    a simple directory layout. The only Serena-specific convention is the mem:
    prefix for references to memories, which does not prevent using the memory files outside
    of Serena.
  7. 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.md and 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 with
mem: 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 by read_only_memory_patterns, which the agent cannot
    write; serena memories check reports such a reference as stale.
    References that do not use the mem: 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 across
core 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:

yaml
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

  1. When a project is activated, Serena checks whether onboarding was already
    performed (by checking if any memories exist).
  2. If no memories are found, Serena triggers the onboarding process, which
    reads key files and directories to understand the project.
  3. Before any project memory is written, Serena materializes a project-local
    memory_maintenance memory (see below). The agent is then instructed to read it
    first and follow the conventions it describes.
  4. The gathered information is written into project-specific memory files following
    the onboarding prompt instructions and the conventions outlined in memory_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, the
mem: 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:

  1. If you already maintain a global/memory_maintenance memory, 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.
  2. Otherwise, if the project already has a memory_maintenance memory, it is left
    untouched.
  3. 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. Run serena memories check --help for the full flag list.
  • serena memories auto-prefix-references — heuristic rewrite of bare occurrences to add
    the mem: prefix; supports --dry-run.
  • serena memories initialize will seed the memory_maintenance memory 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:

shell
serena memories --help
serena memories <subcommand> --help

Disabling 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-memories to the base_modes
    in Serena's global configuration.
  • Similarly, to disable only onboarding, add no-onboarding to the base_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-server server 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

    shell
    serena 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 via included_apis/excluded_apis instead,
    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).
{note}
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_command is a shell command that is to be run whenever the project is activated.
    • ls_specific_settings defines 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.
{note}
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,

text
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 with planning for 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
  • 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
  • at startup via command-line parameters
    • can override default modes with --mode
    • can define modes to be added on top with --add-mode

Ultimately, the active modes are given by the union of

  • base_modes defined 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_modes to 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,

text
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 prompt field in context and mode definition files, and
  • the project prompt, i.e. initial_prompt in project.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:

yaml
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:

yaml
# 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:

  1. Check if a .serena folder exists at the configured path.
  2. If not, check if one exists in the project root (default/legacy location).
  3. 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:

yaml
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 both ls_path/ls_base_cmd and ls_args.
  • If ls_args is not set, the internal command construction (which sets default arguments) is applied, and you can use ls_path or ls_base_cmd to override the path of the core dependency/the base command.
  • ls_extra_args is always appended to the end of the launch command.

Example:

yaml
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 from
LanguageServerDependencyProviderBaseCommand, 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 initializationOptions for 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 initializationOptions for the language server, your custom
    options are used as-is.

Example:

yaml
ls_specific_settings:
  <language>:
    initializationOptions:
      someFeature:
        enabled: true

AL

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/core installed (i.e. npm install must have been run in the project root,
    or in a workspace root above it for monorepo layouts). Without it, ngserver reports every file as "not in an
    Angular project" and template-aware features silently return empty.
  • A tsconfig.json must be reachable at or above any opened .ts file.
  • Do not also list typescript or html in languages when angular is active — Angular subsumes both
    for .ts / .html files. SCSS is not subsumed; list scss separately 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 via ls_specific_settings.html — see the HTML section below.
  • ls_path is 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:

yaml
ls_specific_settings:
  bsl:
    bsl_ls_version: "0.29.0"
    # ls_path: "/opt/bsl/bsl-language-server-0.29.0-exec.jar"  # optional

Clojure

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):

  1. source_paths setting — explicit override.
  2. config_edn_path setting — Serena parses :source-paths from the supplied file.
  3. <repo>/.lsp/config.edn exists — Serena injects nothing; clojure-lsp reads the file natively, so hand-tuned project configs are never clobbered.
  4. 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:

yaml
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:

yaml
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 .nupkg file (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:

yaml
ls_specific_settings:
  gdscript:
    port: 6008
    request_timeout: 60.0

Go (`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:

yaml
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.env values are strings.
  • GOFLAGS (from the environment you start Serena in) may also affect the Go build context. Prefer buildFlags for tags.
  • Build context changes are only picked up when gopls starts. After changing gopls_settings (or relevant env vars like GOFLAGS), restart the Serena process (or server) that hosts the Go language server, or use your client's "Restart language server" action if it causes gopls to 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:

yaml
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):

yaml
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 the
html 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:

  1. 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.
  2. Upstream JDTLS mode (offline-friendly): Activated by setting both jdtls_path and lombok_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.com or services.gradle.org from the host (corporate proxy, air-gapped network);
    • you want a smaller on-disk footprint;
    • you already maintain a JDTLS installation (e.g. for nvim-jdtls or another editor);
    • your security policy prohibits per-project runtime downloads.

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_xmx and intellicode_xms keys 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 ./gradlew in the project, or rely on a system-installed Gradle through Buildship's
    default discovery rules. The gradle_version and vscode_java_version settings are silently ignored in this mode.
  • Without runtimes, JDT-LS only knows about the bundled JavaSE-21 JRE. Projects that request a newer
    container (e.g. sourceCompatibility = JavaVersion.VERSION_25) then fail to resolve JDK types such as
    java.lang.Object. Register the matching installed JDK via runtimes instead of symlinking over Serena's
    bundled JRE directory.

Example: upstream-jdtls mode (offline / corporate network):

yaml
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"  # optional

Example: default vscode-java VSIX mode for a project with custom Gradle plugins:

yaml
ls_specific_settings:
  java:
    gradle_wrapper_enabled: true
    use_system_java_home: true

Example: register an additional JDK for a project targeting a newer Java version:

yaml
ls_specific_settings:
  java:
    runtimes:
      - name: JavaSE-21
        path: /usr/lib/jvm/java-21-openjdk
      - name: JavaSE-25
        path: /home/user/Java/jdk25
        default: true

Kotlin

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 to
bin/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:

yaml
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:

yaml
languages:
  - name: lua_emmylua
ls_specific_settings:
  lua_emmylua:
    ls_path: /opt/emmylua_ls

Luau

Serena uses luau-lsp for Luau support.

Runtime Requirements:

  • luau-lsp is used from PATH if available.
  • Otherwise, Serena downloads the pinned luau-lsp release for the current platform.

Configuration:

yaml
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, RobloxScriptSecurity

Notes:

  • In roblox mode, Serena downloads Roblox definitions and Roblox API docs and passes them to luau-lsp.
  • In standard mode, 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:

yaml
ls_specific_settings:
  nix:
    ls_path: /absolute/path/to/nixd-project
    config_path: /absolute/path/to/nixd-settings.json

The JSON document contains the settings object directly, without an outer nixd key:

json
{
  "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 return
null after the same bounded retry interval.

Pascal (`pasls`)

Serena uses pasls (Pascal Language Server) for Pascal/Free Pascal support.

Language Server Installation:

  1. If pasls is found in your system PATH, Serena uses it directly
  2. 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:

yaml
ls_specific_settings:
  pascal:
    pp: "D:/laz32/fpc/bin/i386-win32/fpc.exe"
    fpcdir: "D:/laz32/fpcsrc"
    lazarusdir: "D:/laz32/lazarus"

Notes:

  • The pp setting is the most important for hover and navigation to work correctly.
  • Use the FPC compiler driver (fpc/fpc.exe), not backend compilers like ppc386.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:

yaml
ls_specific_settings:
  perl:
    file_filter: [".pm", ".pl", ".t", ".cgi", ".psgi"]
    ignore_dirs: [".git", "blib", "local", "vendor", "cover_db"]

Notes:

  • Extensions added via file_filter are also synced into Serena's Perl source-file matcher, so find_symbol and 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_filter does 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:

yaml
ls_specific_settings:
  php:
    file_filter: [".module", ".install", ".inc", ".theme", ".profile", ".engine"]

Notes:

  • Extensions added via file_filter are synced into Serena's PHP source-file matcher and pushed to Intelephense as intelephense.files.associations globs at startup, so find_symbol and the language server treat the same files as PHP sources.
  • The matcher is reset on every language server activation, so one project's file_filter does 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:

yaml
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, while
ls_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:

yaml
ls_specific_settings:
  solidity:
    solidity_state_dir: /path/to/writable/solidity-state

This 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: true in each library package's tsconfig.json and list dependent projects under references in the consumer (or a solution-style root).
  • Prefer source imports (or generate declaration maps) so tsserver can map dist/*.d.ts back 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 from
ls_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):

yaml
ls_specific_settings:
  typescript_vts:
    initialization_options:
      typescript:
        tsdk: "project/.yarn/sdks/typescript/lib"
      vtsls:
        autoUseWorkspaceTsdk: true

vtsls reads typescript.tsdk through the workspace/configuration pull, not through initializationOptions, so Serena answers those pulls from the same dict (and also pushes it on workspace/didChangeConfiguration for compatibility with servers that expect the notification). Without autoUseWorkspaceTsdk: true, vtsls falls back to its bundled TypeScript and ignores tsdk (there is no UI prompt to confirm the switch in a headless LSP).

The dict is forwarded to vtsls verbatim — Serena does not validate its structure. For the list of supported keys and their expected types, refer to the vtsls configuration schema 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 — ensure vtsls.autoUseWorkspaceTsdk: true is set alongside typescript.tsdk. Without it vtsls does not auto-switch to the workspace TS in a headless LSP.
  • tsserver fails to start after pointing at a custom tsdk — verify the path resolves to a directory containing tsserver.js (e.g. .yarn/sdks/typescript/lib, not .yarn/sdks/typescript). Relative paths are interpreted relative to the project root.
  • Setting appears in solidlsp logs but vtsls does not react — cross-check the key against the vtsls configuration schema linked above. The dict is forwarded as-is, so an unknown or wrong-typed key is silently ignored by vtsls.
  • Need to inspect what Serena is actually forwarding — the dict is logged at INFO level via the Forwarding user-provided initializationOptions to vtsls: … line at language server startup.

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_version and typescript_language_server_version are read from ls_specific_settings.typescript, not from ls_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:

yaml
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.yml and project-specific project.yml)
    • memories of the current project
  • 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 tool open_dashboard is 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

Additionally, logs are persisted in the Serena home directory, which, by default, is located at

  • %USERPROFILE%\.serena\logs on Windows
  • ~/.serena/logs on 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_only project 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 latest or 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:

  1. Include proper C++ standard flags (e.g., -std=c++17)
  2. Include all necessary include paths (-I flags)

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:

yaml
# .serena/project.yml
language_servers:
  - cpp
ls_specific_settings:
  cpp:
    compile_commands_dir: custom/rel/path  # defaults to .serena

With ccls

ccls requires manual installation and configuration. It may perform better in some situations.

Installation

Linux:

bash
# Ubuntu/Debian (22.04+)
sudo apt-get install ccls

# Fedora/RHEL
sudo dnf install ccls

# Arch Linux
sudo pacman -S ccls

macOS:

bash
brew install ccls

Windows:

bash
choco install ccls

Configuration

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

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:

  1. Download the agent-ui code with npx

    shell
    npx create-agent-ui@latest

    or, alternatively, clone it manually:

    shell
    git clone https://github.com/agno-agi/agent-ui.git
    cd agent-ui 
    pnpm install 
    pnpm dev
  2. Install 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 .
  3. Copy .env.example to .env and fill in the API keys for the provider(s) you
    intend to use.

  4. Start the agno agent app with

    shell
    uv run python scripts/agno_agent.py

    By default, the script uses Claude as the model, but you can choose any model
    supported by Agno (which is essentially any existing model).

  5. In a new terminal, start the agno UI with

    shell
    cd agent-ui 
    pnpm dev

    Connect 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 the
LanguageServerRegistry 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_registration entry 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):

toml
[project.entry-points."solidlsp.language_server_registration"]
example = "myls:register_myls"

Corresponding contents of myls/__init__.py:

python
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:

yaml
language_servers:
  - myls

03 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.godot file 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 4
  • config_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

  1. Open your Godot project in the Godot editor and allow it to finish loading.

  2. 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).
  3. Add gdscript to the languages list in your project's Serena configuration:

    yaml
    # .serena/project.yml
    languages:
      - gdscript
  4. Start Serena in your project root. Serena will connect to the already-running Godot editor automatically.


Using Serena with GDScript

  • Serena recognizes .gd and .gdscript files. The language identifier used in LSP communication is gdscript.
  • 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_path whenever 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:

yaml
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


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

yaml
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:

yaml
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 options

Configuration 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
    • 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., -Xmx2G for 2GB)
      • -Xms<size>: Initial heap size (e.g., -Xms512m for 512MB)

Project Structure Requirements

For optimal Groovy Language Server performance, ensure your project follows standard Groovy/Gradle structure:

text
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


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)
  • 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

  1. Initialize opam if you haven't already:

    bash
    opam init
    eval $(opam env)
  2. 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)
  3. Install the language server and build tools:

    bash
    opam install ocaml-lsp-server dune
  4. Verify the installation:

    bash
    opam 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 build before 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


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 legacy coursier launcher
    • Serena uses cs if available; if only coursier exists, it will attempt to install cs. If neither is present, install Coursier first.

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)

  1. Open your Scala project in VS Code.
  2. When prompted by Metals, accept “Import build”. Wait until the import and initial compile/indexing finish.
  3. Run the “Connect to build server” command (id: build.connect).
  4. 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.

  1. Add Bloop to project/plugins.sbt in 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.

  2. Export Bloop configuration with sources:

    bash
    sbt -Dbloop.export-jar-classifiers=sources bloopInstall

    This creates a .bloop/ directory containing your project’s build metadata for the BSP server.

  3. Compile from sbt to verify the build:

    bash
    sbt compile
  4. Start 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 sbt first.

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:

yaml
# ~/.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 unset

Waiting 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:

yaml
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:

yaml
# ~/.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 detected

Stale 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


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):

bash
uvx mcpo --port 8000 --api-key <YOUR_SECRET_KEY> -- \
  serena start-mcp-server --context chatgpt --project $(pwd)
  • --api-key is required to secure the server.
  • --project should 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

shell
serena start-mcp-server --help

2. Expose the Server Using Cloudflare Tunnel

Run:

bash
cloudflared tunnel --url http://localhost:8000

This will give you a public HTTPS URL like:

text
https://serena-agent-tunnel.trycloudflare.com

Your server is now securely exposed to the internet.


3. Connect It to ChatGPT (Custom GPT)

Steps:

  1. Go to ChatGPT → Explore GPTs → Create
  2. During setup, click “Add APIs”
  3. Set up API Key authentication with the auth type as Bearer and enter the api key you used to start the MCPO server.
  4. In the Schema section, click on import from URL and paste <cloudflared_url>/openapi.json with the URL you got from the previous step.
  5. Add the following line to the top of the imported JSON schema:
    text
    "servers": ["url": "<cloudflared_url>"],
    Important: don't include a trailing slash at the end of the 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:

yaml
excluded_tools:
  - execute_shell_command
  - ...
read_only: true

This 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.h headers 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 usable
compile_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.

text
<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 the
    LLVM_PATH environment variable, then the VS-bundled VC\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 prints
    Clang compiler version ... is not a preferred version.
  • -OutputDir is required. Without it the file lands in the engine root.
  • Build the editor target once before generating, so the *.generated.h headers exist.
    After a build you can add -NoExecCodeGenActions to skip redundant codegen.

Route 2 (no clang toolchain): MSVC database

If you cannot add the clang component, append -Compiler=VisualStudio2022:

text
<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 logs
Failed 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):

text
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:

text
<Engine>\Build\BatchFiles\Build.bat -projectfiles -project="<YourProject>.uproject" -game -VSCode

On 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:

text
<YourProject>\.vscode\compileCommands_Default.json

If 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/.cache inside
    the project.
  • New UFUNCTION/UCLASS declarations need a build before their generated
    headers exist.
  • Symbol searches on large projects: prefer passing relative_path to
    find_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 following Failed to compile ... index may be incomplete means clangd hit errors
    while parsing that file but indexed it anyway. Raise -ferror-limit via .clangd if
    symbol trees look truncated (see Troubleshooting).

Troubleshooting

Extra flags are easiest to add via a .clangd file at the project root, e.g.:

text
CompileFlags:
  Add: [-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH, -ferror-limit=200]
  • STL1000: Unexpected compiler version errors: 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=200 keeps
    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/.cache were 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:

  1. 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.

  2. 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.

  3. 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:

  1. Codebase understanding — structural overviews, targeted symbol retrieval, reference finding, type hierarchies, and external dependency lookup.
  2. Single-file edits — small tweaks, medium rewrites, full-body replacements, insertions, and local renames.
  3. Multi-file changes — cross-file renames, symbol and file moves, safe deletes, and inlining.
  4. Reliability & correctness — scope precision, atomicity, and success signals.
  5. 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:

  1. the main evaluation prompt, which performs the analysis
  2. 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

text
/* 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.

text
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.

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: Edit with replace_all=true achieves 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: Grep for 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-in Grep returns 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_body requires sending the entire 13-line body. Edit sends just the changed line. The prerequisite cost differs: Serena needs find_symbol(include_body=True) to get the body; Edit needs Read of 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_body calls, 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_string anchors 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: Read with 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_string matching 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_env unambiguously 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).
  • find_referencing_symbols returns code-level references categorized by type. Grep returns all textual mentions.
  • type_hierarchy returns 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 — Grep and Read are 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)

  1. 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).

  2. 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).

  3. 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).

  4. 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).

  5. 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 → Grep with 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_symbols for Symbol/findReferences returned 6 precise code usages grouped by containing symbols. rg findReferences also returned the declaration, the /findReferences route string, and comments.
  • Type hierarchy: one type_hierarchy call for PostRequestHandler returned 18 direct endpoint subclasses, a transitive TypeHierarchyHandler -> GetSupertypesHandler/GetSubtypesHandler branch, and the external Object supertype.
  • External dependency lookup: find_declaration resolved ReferencesSearch.search(anchorElement) to <ext:ReferencesSearch.class|466808a0>, and find_symbol returned 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, and SymbolFinder/qualNameMatchesName after 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.xml and build.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; rg returned 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_overview on Symbol.java returned 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:

  1. git status --short -> clean.
  2. rg --files -g '!*.md' -g '!docs/**' -g '!CLAUDE.md' -> code/config/resource inventory.
  3. Get-ChildItem -Force -> top-level directories.
  4. Get-Content src/main/resources/META-INF/plugin.xml -> plugin entry points.
  5. 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: registers PluginStartupActivity, 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:

  1. get_symbols_overview(relative_path=Symbol.java, depth=1)
  2. 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:

  1. rg for method-like declaration lines in Symbol.java.
  2. rg for class/interface/enum lines in Symbol.java.
  3. 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:

  1. rg "findReferences\\(" src/main/java -n -C 2
  2. Get-Content Symbol.java line 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/references
  • Symbol/inline/refCountBefore
  • Symbol/verifyInlineResult/refCountAfter
  • SymbolDTO/Builder/buildDTO/references
  • RunInspectionsOnSymbolsHandler/handleRequest/refs
  • FindReferencesHandler/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:

  1. rg "extends PostRequestHandler|extends TypeHierarchyHandler|class PostRequestHandler" src/main/java -n
  2. Manually follow any discovered intermediate types.
  3. 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:

  1. find_declaration(relative_path=Symbol.java, regex="ReferencesSearch\\.(search)\\(anchorElement\\)")
  2. 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:

java
public static @NotNull Query<PsiReference> search(@NotNull PsiElement element) {
    return search(element, GlobalSearchScope.allScope(PsiUtilCore.getProjectInReadAction(element)), false);
}

Built-in call chain:

  1. rg "ReferencesSearch" src/main/java -n -> import and local call.
  2. rg over repo, .intellijPlatform, and Gradle caches for class ReferencesSearch or matching method signatures -> no useful source hit.
  3. Get-ChildItem over 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:

  1. Use existing search/body context.
  2. apply_patch one-line replacement.
  3. git diff, git status --short.
  4. Revert and clean check.

Serena call chain:

  1. replace_symbol_body(SymbolFinder/findSymbolByNamePath, full method body with changed string).
  2. git diff, git status --short.
  3. 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:

  1. Search/read surrounding area.
  2. apply_patch with nearby context.
  3. Diff/status/revert/status.

Serena call chain:

  1. insert_after_symbol(relative_path=SymbolFinder.java, name_path=SymbolFinder/getProject, body=...)
  2. 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:

  1. rg "qualNameMatchesName" SymbolFinder.java -n -C 2
  2. apply_patch declaration and one call site.
  3. Diff/status/revert/status.

Serena call chain:

  1. find_referencing_symbols(SymbolFinder/qualNameMatchesName) -> one referencing method.
  2. rename(name_path=SymbolFinder/qualNameMatchesName, new_name=qualifiedNameMatchesName, rename_in_comments=true, rename_in_text_occurrences=true).
  3. 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:

  1. rename(relative_path=MoveOperationFailedException.java, name_path=MoveOperationFailedException, new_name=MoveFailedException, rename_in_comments=true, rename_in_text_occurrences=true).
  2. git diff --stat, git diff, git diff --cached --stat, git status --short.
  3. 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:

  1. rg "MoveOperationFailedException" src/main/java -n.
  2. Rename/create/delete file.
  3. Patch class name and constructor.
  4. Patch imports and usages.
  5. Patch comments/text occurrences if desired.
  6. Verify no unintended old references remain.
  7. 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:

  1. move(relative_path=InspectionRunner.java, name_path=InspectionRunner/PendingFix, target_relative_path=src/main/java/de/oraios/serena/service/endpoint).
  2. Diff/status/cached-diff inspection.
  3. Restore staged new file and modified files; clean check.

Serena result:

json
{
  "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.java with package and imports.
  • Removed nested record from InspectionRunner.java.
  • Updated 3 external references from InspectionRunner.PendingFix to PendingFix.
  • 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:

  1. move(relative_path=src/main/java/de/oraios/serena/util/PluginUtil.java, target_relative_path=src/main/java/de/oraios/serena/service).
  2. Diff/status/cached-diff inspection.
  3. Restore staged rename and modified imports; clean check.

Serena result:

json
{
  "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:

  1. safe_delete(relative_path=DebugUtil.java, name_path=DebugUtil, delete_even_if_used=false, propagate=false).
  2. git diff --cached --stat, git status --short.
  3. Restore staged deletion; clean check.

Serena result:

json
{
  "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:

  1. find_symbol(ProjectUtil/getAbsolutePath, include_body=true).
  2. find_referencing_symbols(ProjectUtil/getAbsolutePath) -> 2 references.
  3. inline_symbol(relative_path=ProjectUtil.java, name_path=ProjectUtil/getAbsolutePath, keep_definition=false).
  4. 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:

  1. find_symbol(Symbol/getLocationString[0], include_body=true).
  2. 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 -> OK
  • insert_after_symbol -> OK
  • rename -> "Success"
  • move -> JSON with source/target/moved symbol or paths
  • safe_delete -> JSON with deleted symbol, affected references, and message
  • inline_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:

  1. replace_symbol_body(SymbolFinder/findFilesByName, ...).
  2. insert_after_symbol(SymbolFinder/getProject, ...).
  3. rename(SymbolFinder/qualNameMatchesName, ...).
  4. 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.

Attempted: read config and search free text.

Built-in call chain:

  1. Get-Content plugin.xml.
  2. Get-Content build.gradle.kts -TotalCount 80.
  3. 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_overview and shallow find_symbol calls 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_hierarchy and dependency declaration lookup produced semantic information not available from ordinary grep.
  • rg remained 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/findFilesByName or Symbol/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 extends clauses 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.search into 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:

  1. Use built-ins to find files/config and run verification.
  2. Use Serena to identify and transform code symbols.
  3. Use built-ins to inspect final diffs and run tests.
  4. Prefer built-ins for tiny local hunks after the symbol has already been located.
  5. 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:

  1. 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.
  2. 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.
  3. 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 under main; next step find_symbol(createMainWindow, include_body=true).
  • Built-in chain: rg on const|function|class|export in main.ts -> flat text hits; next step view of lines 331-439 to read createMainWindow.
  • 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/toggleAutoLaunch in desktop/src/main/services/auto-launcher.ts.
  • Serena chain: find_symbol(AutoLauncher/toggleAutoLaunch, include_body=true) -> exact method body.
  • Built-in chain: view of 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.

  • Target symbol: wait in desktop/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.ts prose),
    • the definition itself,
    • doc mentions in desktop/docs/release.md when 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) -> supertype InferenceBackend.
  • 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: BrowserWindow and serveNextAt in the desktop TypeScript app, plus Url and Zeroizing in rust/core.
  • Serena chain (TS):
    • find_declaration(new BrowserWindow(...), include_body=true) -> desktop/node_modules/electron/electron.d.ts, body class BrowserWindow extends Electron.BrowserWindow {}
    • find_declaration(import serveNextAt ... , include_body=true) -> desktop/node_modules/next-electron-server/index.d.ts, body declare 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 symbol Url[0] with the struct body
    • find_declaration(use zeroize::Zeroizing;, include_body=true) -> <ext:lib.rs|...> external symbol Zeroizing[0] with the struct body
    • find_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).
  • 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 -> launcher inside AutoLauncher/toggleAutoLaunch.
  • Built-in chain: view(auto-launcher.ts, 20-40) -> apply_patch on 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 uniqueSavePath to use candidatePath + for loop.
  • Built-in chain: view(main.ts, 500-540) -> apply_patch replacing 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 createMainWindow body.
  • Built-in chain: view(main.ts, 331-439) -> apply_patch replacing 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: waitSeconds after wait in desktop/src/main/utils/common.ts.
  • Built-in chain: view(common.ts, 1-40) -> apply_patch inserting 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: openStreetMapUserAgent in desktop/src/main.ts.
  • Built-in chain: view/rg to find call site + definition -> apply_patch updating 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-file apply_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 nullToUndefined out of common.ts.
  • Serena chain executed: move(nullToUndefined, target_relative_path=http.ts) -> git diff.
  • Observed result: Serena removed the symbol from common.ts, updated ffmpeg-worker.ts, but created a new file desktop/src/main/utils/nullToUndefined.ts instead of merging into http.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.ts to desktop/src/main/services/http.ts.
  • Serena chain: move(file http.ts -> services/) -> git diff.
  • Observed result: the file was renamed/moved and ffmpeg-worker.ts import updated from ../utils/http to ./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: view call site + definition -> apply_patch replacing await waitForRendererDevServer() with await 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, and InferenceBackend.
  • Serena: symbol names and name paths targeted the exact code entity.
  • Built-ins: text search for names such as wait or writeToTemporaryFile over-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_patch can 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: OK for 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 view became 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_modules and Rust dependencies into external Cargo sources such as url and zeroize. 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 for find_symbol(createMainWindow), rename(openStreetMapUserAgent), and inline(waitForRendererDevServer).
  • Built-in workflows required refreshes. Across repeated main.ts experiments, I repeatedly had to reacquire ranges with view/rg before 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.ts into Electron and next-electron-server declarations without first reasoning about workspace roots; in rust/core I could jump into Cargo-registry dependencies through external symbol handles instead of manually reconstructing registry paths from Cargo.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://app or 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 CollectStatsBase to BaseCollectStats across 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_slice to 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 CollectStats with Serena returned 10 semantically categorized results (IMPORT_ELEMENT, REFERENCE_EXPRESSION, NAMED_PARAMETER, etc.) with no false positives from LoggedCollectStats. Grep returned 70+ lines including false positives from LoggedCollectStats and 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 Serena find_symbol call using the name path. Built-in: need to know the line range (773–1103) from a prior Grep, then Read(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. Edit replace_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_body sends ~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 1 insert_after_symbol call 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:

  1. Moved _nullable_slice from collector.py to converter.py
  2. Added import in source file: from tianshou.data.utils.converter import _nullable_slice
  3. Added dependency import in target: from tianshou.data.collector import _TArrLike
  4. 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:

  1. Moved the file
  2. Updated the one direct import in __init__.py (tianshou.data.utils.segtree → tianshou.data.segtree)
  3. 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.

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. Read results go stale after edits. Edit uses 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) excludes LoggedCollectStats. No false positives observed.
  • Grep: Text-matched. CollectStats matches LoggedCollectStats, CollectStatsBase, and docstring references. Requires manual filtering.
  • Edit: Text-matched. replace_all replaces exact string matches. For unique symbol names, this is reliable. For common strings, it can over-match.

Scope disambiguation

  • Serena: Collector/_collect vs AsyncCollector/_collect — correctly distinguished by class-scoped name path.
  • Built-in: def _collect matches 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 for torch.Tensor and numpy.array. Limited to what JetBrains has indexed.
  • Built-in: Can Read site-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

  1. 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.

  2. 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

  1. Repeated small edits in one file: Edit's text matching is equally stable and more token-efficient for small changes. No Serena advantage.

  2. Exploration without editing: Both toolsets provide usable results. Serena's are more structured but more verbose.

  3. 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

  1. 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).

  2. Semantic reference finding with categorization — find_referencing_symbols returns 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).

  3. 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.

  4. Symbol-scoped body retrieval — Read a specific method by name path without reading the surrounding file. Built-in Read requires 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/_collect disambiguate 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_body must send the entire method body; built-in search_replace sends 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_dist between 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_symbol with include_body=true returns 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_dicts and 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 rename and built-in rename_element perform 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_body sends more tokens (entire method body) than search_replace (just the changed lines). For full method rewrites, comparable. For inserts, Serena's insert_after_symbol saves 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_replace matches by text (can over-match). However, built-in rename_element also matches semantically. For rename operations, precision is equivalent.

  • Scope disambiguation: Serena's AsyncCollector/_collect is unambiguous. Built-in text search for def _collect returns 3 matches in collector.py alone. Disambiguation requires line numbers or surrounding context. Built-in rename_element also 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_symbols returns only code references with semantic context. Built-in search_project returns 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 ---