# Technical Documentation: oraios/serena

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/oraios/serena)

### Overview (`README.md`)

<p align="center" style="text-align:center;">
  <img src="resources/serena-logo.svg#gh-light-mode-only" style="width:500px">
  <img src="resources/serena-logo-dark-mode.svg#gh-dark-mode-only" style="width:500px">
</p>

<h3 align="center">
    The IDE for Your Coding Agent
</h3>

<div align="center">
  <a href="https://discord.com/invite/cVUNQmnV4r"><img src="https://img.shields.io/badge/discord-join-5865F2?style=flat-square&labelColor=0a0e14&logo=discord&logoColor=5865F2" alt="discord"></a>
  <a href="https://github.com/oraios/serena/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-GPL--3.0--or--later-b0e8ff?style=flat-square&labelColor=0a0e14" alt="license: GPL-3.0-or-later"></a>
</div>
<br>


* 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](https://oraios.github.io/serena/01-about/035_tools.html#serena-s-repl-code-execution-based-interface) 
> about the new interface and how to [configure it](https://oraios.github.io/serena/02-usage/050_configuration.html#agent-interfaces).

## Video

<a href="https://www.youtube.com/watch?v=5QN7gN1KYLA"><img src="resources/serena-video-placeholder.png" style="width:500px"></a>

## 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](https://oraios.github.io/serena/04-evaluation/000_evaluation-intro.html) 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](https://oraios.github.io/serena/01-about/035_tools.html) 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.

<img src="resources/serena-block-diagram.svg">

:tv: See also: [Introduction to Serena in 5 Minutes (YouTube)](https://www.youtube.com/watch?v=5QN7gN1KYLA)

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](#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).

<a href="https://plugins.jetbrains.com/plugin/28946-serena/"><img src="docs/_static/images/jetbrains-marketplace-button.png"></a>

See our [documentation page](https://oraios.github.io/serena/02-usage/025_jetbrains_plugin.html) 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>
<summary>Details</summary>

### 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. <br>
**: 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)

</details>

## 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](https://docs.astral.sh/uv/getting-started/installation/) 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](https://oraios.github.io/serena/01-about/020_programming-languages.html) 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](https://oraios.github.io/serena/02-usage/025_jetbrains_plugin.html) 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](https://oraios.github.io/serena/02-usage/030_clients.html).
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](https://oraios.github.io/serena/02-usage/000_intro.html) to get the most out of Serena.
> 
> Specifically, we recommend to read about ...
>   * [Serena's project-based workflow](https://oraios.github.io/serena/02-usage/040_workflow.html) and
>   * [configuring Serena](https://oraios.github.io/serena/02-usage/050_configuration.html).

## User Guide

Please refer to the [user guide](https://oraios.github.io/serena/02-usage/000_intro.html) 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](LICENSES/MIT.txt),
the **Serena application** (everything else) under [GPL-3.0-or-later](LICENSES/GPL-3.0-or-later.txt).
Distributions combining both are as a whole subject to the GPL.

See [LICENSE](LICENSE) for the authoritative overview and the
[license documentation](https://oraios.github.io/serena/01-about/060_license.html) for the background.
Contributions require acceptance of our [CLA](CLA.md); see [CONTRIBUTING.md](CONTRIBUTING.md).

---

### Overview (`src/README.md`)

Serena uses (modified) versions of other libraries/packages:

 * solidlsp (our fork of [microsoft/multilspy](https://github.com/microsoft/multilspy) for fully synchronous language server communication)
 * [interprompt](https://github.com/oraios/interprompt) (our prompt templating library)

---

### Overview (`src/solidlsp/language_servers/elixir_tools/README.md`)

# Elixir Language Server Integration

This directory contains the integration for Elixir language support using [Expert](https://github.com/elixir-lang/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
   - Install from: https://elixir-lang.org/install.html
   - Verify with: `elixir --version`

2. **Expert** (optional - will be downloaded automatically if not found)
   - Expert binaries are automatically downloaded from GitHub releases
   - Manual installation: https://github.com/elixir-lang/expert#installation
   - If installed manually, ensure `expert` is in your PATH

## 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](https://github.com/elixir-lang/expert/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](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

- [Official Documentation](https://example.com/docs)
- [API Reference](api.md)
- [Contributing Guide](CONTRIBUTING.md)

## License

MIT License

---

### Index (`docs/index.md`)

<meta http-equiv="refresh" content="0; url=01-about/000_intro.html">
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](030_results/000_evaluation-results.md) 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](030_results/050_junie_plugin_on_tianshou.md).

---

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

repository:
  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](025_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)](https://github.com/AdaCore/ada_language_server),
  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/ansible-language-server@1.2.3` 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](https://github.com/1c-syntax/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+](https://dotnet.microsoft.com/en-us/download/dotnet) and, on Windows, `pwsh` ([PowerShell 7+](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5));
  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](../03-special-guides/cpp_setup) for details;
  for Unreal Engine 5 projects, see the [Unreal Engine Setup Guide](../03-special-guides/unreal_engine_setup_guide_for_serena).)
* **Clojure**
* **Crystal**  
  (requires [Crystalline](https://github.com/elbywan/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](https://github.com/WhatsApp/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+](https://dotnet.microsoft.com/en-us/download/dotnet); 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](../03-special-guides/godot_gdscript_setup_guide_for_serena) for details)
* **Gleam**  
  (requires the [Gleam compiler](https://gleam.run) 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](../03-special-guides/groovy_setup_guide_for_serena) 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](https://github.com/vshaxe/haxe-language-server);
  automatically downloaded from Open VSX, or discovered from the vshaxe VSCode extension)
* **HLSL / GLSL / WGSL**
  (uses [shader-language-server](https://github.com/antaalt/shader-sense) (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](https://github.com/jolars/fatou) alternative (language `julia_fatou`) is installed
  automatically and requires `uv`/`uvx` in PATH)
* **Kotlin**  
  (uses the pre-alpha [official kotlin LS](https://github.com/Kotlin/kotlin-lsp), some issues may appear)
* **LaTeX**  
  (experimental; must be explicitly enabled via language `latex`; uses [texlab](https://github.com/latex-lsp/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](https://github.com/leanprover/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](https://github.com/EmmyLuaLs/emmylua-analyzer-rust)
  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](https://github.com/nextflow-io/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](../03-special-guides/ocaml_setup_guide_for_serena.md))
* **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](https://github.com/phpactor/phpactor) (language `php_phpactor`), which requires PHP 8.1+;
  the experimental [PHPantom](https://github.com/PHPantom-dev/phpantom_lsp) backend (language `php_phpantom`);
  and the experimental standalone [Devsense PHP Language Server](https://www.npmjs.com/package/devsense-php-ls)
  (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](https://github.com/microsoft/pyright) (language `python`);
  alternatives: [BasedPyright](https://github.com/DetachHead/basedpyright) (language `python_basedpyright`),
  [ty](https://github.com/astral-sh/ty) (language `python_ty`),
  [pyrefly](https://github.com/facebook/pyrefly) (language `python_pyrefly`),
  [Jedi](https://github.com/pappasam/jedi-language-server) (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](https://doc.qt.io/qt-6/qtqml-tool-qmlls.html))
* **R**  
  (requires installation of the `languageserver` R package)
* **Rego**  
  (requires the [Regal](https://github.com/open-policy-agent/regal) language server on PATH)
* **Ruby**  
  (by default, uses [ruby-lsp](https://github.com/Shopify/ruby-lsp) (language `ruby`); use language `ruby_solargraph` to use Solargraph instead.)
* **Rust**  
  (requires [rustup](https://rustup.rs/) - uses rust-analyzer from your toolchain)
* **Scala**  
  (uses Metals LSP, which imports the build on first use — see the [setup guide](../03-special-guides/scala_setup_guide_for_serena))
* **SCSS / Sass / CSS**
  (experimental; requires Node.js + npm; uses [some-sass-language-server](https://github.com/wkillerud/some-sass) 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](https://github.com/LaunchCG/roslyn-vbnet-languageserver) (language `vbnet`), requiring [.NET v10+](https://dotnet.microsoft.com/en-us/download/dotnet) and, on Windows, `pwsh` ([PowerShell 7+](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5));
  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](https://github.com/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](https://github.com/oraios/serena/blob/main/CONTRIBUTING.md).
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](external-ls-registration).

### 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](https://plugins.jetbrains.com/plugin/28946-serena/) 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](../02-usage/025_jetbrains_plugin) for a high-level overview of its benefits and usage details.
* See the [Features](025_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.

<video src="https://github.com/user-attachments/assets/ab78ebe0-f77d-43cc-879a-cc399efefd87"
controls
preload="metadata"
style="max-width: 100%; height: auto;">
Your browser does not support the video tag.
</video>

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

<video src="https://github.com/user-attachments/assets/6eaa9aa1-610d-4723-a2d6-bf1e487ba753"
controls
preload="metadata"
style="max-width: 100%; height: auto;">
Your browser does not support the video tag.
</video>

---

### 01 About 050 Acknowledgements (`docs/01-about/050_acknowledgements.md`)

# Acknowledgements

## Sponsors

We are very grateful to our [sponsors](https://github.com/sponsors/oraios), who help us drive Serena's development. 
The core team (the founders of [Oraios AI](https://oraios-ai.de/)) 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](https://github.com/microsoft/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](https://github.com/modelcontextprotocol/python-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`](https://github.com/oraios/serena/blob/main/LICENSE) file in the repository,
and the full license texts are in [`LICENSES/`](https://github.com/oraios/serena/tree/main/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](https://github.com/oraios/serena/blob/main/LICENSES/MIT.txt) |
| **Serena application** – everything else (agent, tools, MCP server, prompts, scripts, tests, docs) | `src/serena`, `src/interprompt`, `test/serena`, `scripts`, … | [GPL-3.0-or-later](https://github.com/oraios/serena/blob/main/LICENSES/GPL-3.0-or-later.txt) |

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](https://github.com/oraios/serena/blob/main/CLA.md), 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](https://github.com/oraios/serena/blob/main/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](contexts)

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](https://docs.astral.sh/uv/getting-started/installation/).

**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](language-servers) page for the list of supported languages.
Many dependencies are installed by Serena on the fly, but if a language requires dependencies 
to be provided manually, this is mentioned in the notes below the respective language.

(install-serena)=
## Installing and Initialising Serena

With `uv` installed and on your PATH, install Serena with this command:

    uv tool install -p 3.13 serena-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](025_jetbrains_plugin))

Note that you can switch backends at any time via Serena's [configuration](050_configuration). 

## Updating Serena

To update Serena to the latest version, run:

    uv tool upgrade serena-agent

:::{tip}
To keep informed about updates, make sure you regularly open [Serena's Dashboard](060_dashboard),
where we will announce releases along with the new features and improvements they bring.
:::

## Uninstalling Serena

Serena can be uninstalled with the following command:

    uv tool uninstall serena-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](install-serena),
which should be available in your system PATH as `serena.`

In general, to get help, append `--help` to the command, i.e.

    serena --help
    serena <command> --help


(start-mcp-server)=
## Running the MCP Server

Given your preferred method of running Serena, you can start the MCP server using the `start-mcp-server` command:

    serena start-mcp-server [options]  

Note that no matter how you run the MCP server, Serena will, by default, start a web-based dashboard on localhost that will allow you to inspect
the server's operations, logs, and configuration.

:::{tip}
By default, Serena will use language servers for code understanding and analysis.    
With the [Serena JetBrains Plugin](025_jetbrains_plugin), we recently introduced a powerful alternative,
which has several advantages over the language server-based approach.
:::

### Standard I/O Mode

The typical usage involves the client (e.g. Claude Code, Codex or Cursor) running
the MCP server as a subprocess and using the process' stdin/stdout streams to communicate with it.
In order to launch the server, the client thus needs to be provided with the command to run the MCP server.

:::{note}
MCP servers which use stdio as a protocol are somewhat unusual as far as client/server architectures go, as the server
necessarily has to be started by the client in order for communication to take place via the server's standard input/output streams.
In other words, you do not need to start the server yourself. The client application (e.g. Claude Desktop) takes care of this and
therefore needs to be configured with a launch command.
:::

Communication over stdio is the default for the Serena MCP server, so in the simplest
case, you can simply run the `start-mcp-server` command without any additional options.
 
    serena start-mcp-server

See the section ["Configuring Your MCP Client"](030_clients) for specific information on how to configure your MCP client (e.g. Claude Code, Codex, Cursor, etc.)
to use such a launch command.

(streamable-http)=
### Streamable HTTP Mode

When using *Streamable HTTP* mode, you control the server lifecycle yourself,
i.e. you start the server and provide the client with the URL to connect to it.

Simply provide `start-mcp-server` with the `--transport streamable-http` option and optionally provide the desired port
via the `--port` option.
For example, to start the server on port 9121, run

    serena start-mcp-server --transport streamable-http --port <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](040_workflow) for more information on how to manage projects in Serena.

The legacy SSE transport is also supported (via `--transport sse` with corresponding /sse endpoint), its use is discouraged.

(mcp-args)=
### MCP Server Command-Line Arguments

The Serena MCP server supports a wide range of additional command-line options.
Use the command

    <serena> start-mcp-server --help

to get a list of all available options.

Some useful options include:

  * `--project <path|name>`: 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](contexts) in which Serena shall operate
  * `--mode <mode>`: specify one or more [modes](modes) to enable (can be passed several times)
  * `--open-web-dashboard <true|false>`: whether to open the web dashboard on startup (enabled by default)

## Other Commands

Serena provides several other commands in addition to `start-mcp-server`, 
most of which are related to project setup and configuration.

To get a list of available commands, run:

    <serena> --help

To get help on a specific command, run:

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

    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](010_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](https://github.com/oraios/serena/blob/main/DOCKER.md) documentation for more detailed instructions, configuration options, and limitations.

:::{note}
Docker usage is subject to limitations; see the [advanced Docker usage](https://github.com/oraios/serena/blob/main/DOCKER.md) 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](https://nixos.wiki/wiki/flakes), 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](https://plugins.jetbrains.com/plugin/28946-serena/) 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](020_running.md) and connect the MCP server to your 
LLM-based client as described in [client setup](030_clients.md) 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](configure-jetbrains))
3. Open the project you want to work on in your JetBrains IDE and activate it in Serena (see [below](jetbrains-workflow))
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](../01-about/035_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](../01-about/035_tools)), 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](global-config) appropriately.
2. Activate the project's root folder as a project in Serena (see [Project Creation](project-creation-indexing) and [Project Activation](project-activation)).
3. Start using Serena's tools as usual.

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](050_configuration) 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
    * a [run command](start-mcp-server) that allows the client to start the MCP server in stdio mode as a subprocess, or
    * the URL of the HTTP/SSE endpoint, having started the [Serena MCP server in HTTP/SSE mode](streamable-http) beforehand.

Find concrete examples for popular clients below.

Depending on your needs, you might want to further customize Serena's behaviour by
* [adding command-line arguments](mcp-args)
* [adjusting configuration](050_configuration).

**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 <path>` argument with a single-project [context](#contexts) (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](#contexts) when starting Serena.

(clients-common-pitfalls)=
### Common Pitfalls

**Discoverability of the `serena` command**.
Your client may not find the `serena` CLI command, even if it is on your system PATH.
In this case, a workaround is to provide the full path to the `serena` executable.

**Serena's tools not being used**.
With some clients, you may experience that Serena's tools are not being used.
This is mainly due to problems in the client itself (like a poorly implemented tool discovery). To counteract this,
Serena comes with a set of commands that can be used in _hooks_. See the sections on hooks for Claude Code and VSCode below.

**Environment Variables**.
Some language servers may require additional environment variables to be set (e.g. F# on macOS with Homebrew),
which you may need to explicitly add to the MCP server configuration.
Note that for some clients (e.g. Claude Desktop), the spawned MCP server process may not inherit environment variables that
are only configured in your shell profile (e.g. `.bashrc`, `.zshrc`, etc.); they would need to be set system-wide instead.
An easy fix is to add them explicitly to the MCP server entry.
For example, in Claude Desktop and other clients, you can simply add an `env` key to the `serena`
object, e.g.

```
"env": {
    "DOTNET_ROOT": "/opt/homebrew/Cellar/dotnet/9.0.8/libexec"
}
```

## Copilot in JetBrains

Open the settings of your JetBrains IDE and go to Tools / GitHub Copilot / Model Context Protocol (MCP). Then click
on the Configure button. This will open your global `mcp.json` file, where you can add the following entry for Serena:

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

    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](https://github.com/oraios/serena/issues) 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](https://code.claude.com/docs/en/hooks).

**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](https://github.com/microsoft/vscode/issues/245905),
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](https://developers.openai.com/codex/hooks) for details. Create
`~/.codex/hooks.json` with the following content:

```
/* Detailed source-code truncated for AI context efficiency. */
```

These timeout values are specific to Codex. Without an explicit timeout, Codex allows most hooks
to run for 600 seconds, so the five-second reminder and activation timeouts prevent a stuck hook
from blocking a `Bash` call or session startup for that entire default. `SessionEnd` instead
defaults to one second and supports at most three seconds, so the cleanup hook uses that maximum.
Codex reports timed-out commands as hook failures. Other clients may have different timeout
behaviour; in particular, the Grok cleanup example below intentionally remains at five seconds.

The `SessionEnd` cleanup hook requires Codex 0.145.0 or newer. Older Codex versions only support
`Stop` for cleanup, which currently has a [known compatibility issue](https://github.com/oraios/serena/issues/1533).
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](https://github.com/openai/codex/issues/27052)
tracks richer hook failure diagnostics. Use Codex's `/hooks` command first to inspect hook sources,
trust state, and enabled hooks.

While that issue remains open, an advanced local wrapper can add an inner timeout and capture a
bounded, redacted tail of stdout and stderr. Keep wrappers out of the standard setup: they add
another script and executable path, platform-specific timeout behaviour, log rotation and redaction
concerns, and another hook definition hash to review and trust. Such wrappers work around Codex's
diagnostic limitations; Serena does not require them.

## Grok

Serena provides native support for xAI's Grok Build CLI. To set up the Serena MCP server for Grok,
simply run:

    serena setup 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](https://claude.ai/download); for Linux, there is an [open-source
community version](https://github.com/aaddrick/claude-desktop-debian).

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](#clients-common-pitfalls)).

**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 `<project>/.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:

    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](https://zcode.z.ai), 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](streamable-http) 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](#clients-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

 * [Gemini-CLI](https://github.com/google-gemini/gemini-cli), 
 * [Qwen3-Coder](https://github.com/QwenLM/Qwen3-Coder),
 * [rovodev](https://community.atlassian.com/forums/Rovo-for-Software-Teams-Beta/Introducing-Rovo-Dev-CLI-AI-Powered-Development-in-your-terminal/ba-p/3043623),
 * [OpenHands CLI](https://docs.all-hands.dev/usage/how-to/cli-mode),
 * [opencode](https://github.com/sst/opencode) and
 * [CodeBuddy-Code](https://www.codebuddy.cn/cli/).

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

  * [Jan](https://jan.ai/docs/mcp), 
  * [OpenHands](https://github.com/All-Hands-AI/OpenHands/),
  * [OpenWebUI](https://docs.openwebui.com/openapi-servers/mcp) and 
  * [Agno](https://docs.agno.com/introduction/playground).

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:

    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](../03-special-guides/external_language_server_registration.md).
    The [Dashboard](060_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](per-project-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](https://github.com/oraios/serena/blob/main/src/serena/resources/project.template.yml).

:::{note}
Many settings in project.yml *extend* or *override* settings in [Serena's global configuration](global-config).
So use the project configuration specifically for aspects that apply only to the particular project.
:::

**Local Overrides**. The `project.yml` file is intended to be versioned together with the project.
You can specify local overrides for the settings in a `project.local.yml` file in the same directory
(which, by default, is ignored by git). 
Any keys defined therein will override the respective key in `project.yml`.

(indexing)=
## Indexing

:::{note}
Indexing is not a relevant operation when using the JetBrains plugin, as indexing is handled by the IDE.
:::

Especially for larger project, it can be advisable to index the project after creation, pre-caching 
symbol information provided by the language server(s). This will avoid delays during the first tool invocation
that requires symbol information.

While in the project directory, run this command:
   
    serena project 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](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 <path|name>`

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](045_memories).


## 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](modes) `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

      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](streamable-http)
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](memories) (committed alongside the code) and [global](global-memories)
   (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](ignoring-memories) 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](memory-cli)) 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](onboarding) 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](050_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](050_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](060_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](050_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](project-config))
 * **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](mcp-args) 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](060_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](agent-interfaces))
  * the language backend to use by default (i.e., the JetBrains plugin or language servers);
    this can also be [overridden per project](per-project-language-backend)
  * UI settings affecting the [Serena Dashboard and GUI tool](060_dashboard.md)
  * the set of tools or REPL API functions to enable/disable by default
  * the set of [modes](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](ls-specific-settings))
  * 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](https://github.com/oraios/serena/blob/main/src/serena/resources/serena_config.template.yml).

(agent-interfaces)=
#### Agent Interfaces

Serena provides its functionality to the agent (LLM) through one of two interfaces
(see [Tools and APIs](../01-about/035_tools) 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](https://oraios.github.io/serena/01-about/035_tools.html#repl-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](project-config) 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](https://github.com/oraios/serena/tree/main/src/serena/resources/config/contexts).

Note that the contexts `ide`, `claude-code`, and `grok` are **single-project contexts** (defining `single_project: true`).
For such contexts, if a project is provided at startup, the set of tools is limited to those required by the project's
concrete configuration, and other tools are excluded completely, allowing the set of tools to be minimal.
Tools explicitly disabled by the project will not be available at all. Since changing the active project
ceases to be a relevant operation in this case, the project activation tool is disabled.

When launching Serena, specify the context using `--context <context-name>`.
Note that for cases where parameter lists are specified (e.g. Claude Desktop), you must add two parameters to the list.

If you are using a local server (such as Llama.cpp) which requires you to use OpenAI-compatible tool descriptions, use context `oaicompat-agent` instead of `agent`.

You can manage contexts using the `context` command,

    serena context --help
    serena context list
    serena context create <context-name>
    serena context edit <context-name>
    serena context delete <context-name>


(modes)=
### Modes

Modes further refine Serena's behavior for specific types of tasks or interaction styles. Multiple modes can be active simultaneously, allowing you to combine their effects. Modes influence the system prompt and can also alter the set of available tools by excluding certain ones.

Examples of built-in modes include:

* `planning`: Focuses Serena on planning and analysis tasks.
* `editing`: Optimizes Serena for direct code modification tasks.
* `interactive`: Suitable for a conversational, back-and-forth interaction style.
* `one-shot`: Configures Serena for tasks that should be completed in a single response, often used 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](query-projects) 

Find the concrete definitions of these modes [here](https://github.com/oraios/serena/tree/main/src/serena/resources/config/modes).

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,

    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](https://jinja.palletsprojects.com/) templates.
Templating applies to

 * **Serena's system prompt** (the "Serena Instructions Manual"), which is defined in the prompt template `system_prompt`
   (see [Custom Prompts](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](global-config)).

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](https://github.com/1c-syntax/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](https://github.com/dotnet/roslyn) 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/<platform>`)    |

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](https://github.com/elixir-lang/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](https://github.com/WhatsApp/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](https://github.com/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_<platform>/`). 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](https://github.com/Kotlin/kotlin-lsp) 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`](https://github.com/JohnnyMorganz/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](https://github.com/artempyanykh/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](https://github.com/nix-community/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](https://github.com/genericptr/pascal-language-server) (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](https://metacpan.org/pod/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](https://www.npmjs.com/package/devsense-php-ls).
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](https://github.com/DetachHead/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](override-ls-path), 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`](https://github.com/wkillerud/some-sass) 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](https://github.com/tamasfe/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](https://www.typescriptlang.org/docs/handbook/project-references.html) (`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](https://github.com/microsoft/TypeScript/issues/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](https://github.com/yioneko/vtsls/blob/main/packages/service/configuration.schema.json) and the underlying [VS Code TypeScript settings](https://code.visualstudio.com/docs/languages/typescript). `null` (the default) and `{}` are both treated as "unset": no `initializationOptions` are sent and no `workspace/didChangeConfiguration` notification is pushed. A non-dict value (e.g. a string or list) raises an error at server start.

**Troubleshooting:**

- *vtsls keeps using its bundled TypeScript and ignores `tsdk`* — ensure `vtsls.autoUseWorkspaceTsdk: true` is set alongside `typescript.tsdk`. Without it vtsls does not auto-switch to the workspace TS in a headless LSP.
- *tsserver fails to start after pointing at a custom `tsdk`* — verify the path resolves to a directory containing `tsserver.js` (e.g. `.yarn/sdks/typescript/lib`, not `.yarn/sdks/typescript`). Relative paths are interpreted relative to the project root.
- *Setting appears in `solidlsp` logs but vtsls does not react* — cross-check the key against the vtsls configuration schema linked above. The dict is forwarded as-is, so an unknown or wrong-typed key is silently ignored by vtsls.
- *Need to inspect what Serena is actually forwarding* — the dict is logged at INFO level via the `Forwarding user-provided initializationOptions to vtsls: …` line at language server startup.

#### 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](https://github.com/oraios/serena/tree/main/src/serena/resources/config/prompt_templates).

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](privacy).

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](050_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](global-config) (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](global-config).

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](global-config) 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](global-config).

---

### 02 Usage 065 Logs (`docs/02-usage/065_logs.md`)

# Logs

It can be vital to understand what is happening in Serena, especially when something goes wrong. 

You can access Serena's live logs via 
  * the [Serena dashboard](060_dashboard) (tab "Logs")
  * the [GUI tool](060_dashboard).

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](global-config).
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](sandboxing) 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](050_configuration)
  (note that restrictions are effective for the tool interface only, see [below](repl-security)),
- do not expose [Serena's network services](network-security) 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](docker) 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](global-config).

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](sandboxing) is the answer, not the trust configuration.

For the list of trust-gated features, see the [configuration page](trusted-projects-config).

(repl-security)=
## The REPL Interface

With the [REPL interface](agent-interfaces), 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](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](streamable-http) 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](query-projects)) 

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](https://git-scm.com/docs/git-worktree) can be an excellent way to parallelize your work. More on this in [Anthropic: Run parallel Claude Code sessions with Git worktrees](https://docs.claude.com/en/docs/claude-code/common-workflows#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](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

- Clangd official documentation: https://clangd.llvm.org/
- Clangd project setup: https://clangd.llvm.org/installation#project-setup
- CCLS repository: https://github.com/MaskRay/ccls

## Unreal Engine projects

For Unreal Engine 5 projects (reflection macros, UnrealBuildTool), see the
[Unreal Engine Setup Guide](unreal_engine_setup_guide_for_serena.md).

---

### 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](https://docs.agno.com/introduction/playground) 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](https://ai.pydantic.dev/), [langgraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/) 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

- Godot LSP documentation: [https://docs.godotengine.org/en/stable/tutorials/editor/external_editor.html](https://docs.godotengine.org/en/stable/tutorials/editor/external_editor.html)
- GDScript language reference: [https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/)

---

### 03 Special Guides Groovy Setup Guide For Serena (`docs/03-special-guides/groovy_setup_guide_for_serena.md`)

# Groovy Setup Guide for Serena

The Groovy support in Serena is incomplete and requires the user to provide a functioning,
JVM-based Groovy language server as a jar. This intermediate state allows the contributors
of Groovy support to use Serena internally and hopefully to accelerate their open-source
release of a Groovy language server in the future.

If you happen to have a Groovy language server JAR file, you can configure Serena to use it
by following the instructions below.

---
## Prerequisites

- Groovy Language Server JAR file
    - Can be any open-source Groovy language server or your custom implementation
    - The JAR must be compatible with standard LSP protocol

---
## Configuration

Configure Groovy Language Server by adding settings to your `~/.serena/serena_config.yml`:

### Basic Configuration

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

```
project-root/
├── src/
│   ├── main/
│   │   ├── groovy/
│   │   └── resources/
│   └── test/
│       ├── groovy/
│       └── resources/
├── build.gradle or build.gradle.kts
├── settings.gradle or settings.gradle.kts
└── gradle/
    └── wrapper/
```

---
## Using Serena with Groovy

- Serena automatically detects Groovy files (`*.groovy`, `*.gvy`) and will start a Groovy Language Server JAR process per project when needed.
- Optimal results require that your project compiles successfully via Gradle or Maven. If compilation fails, fix build errors in your build tool first.

## Reference

- **Groovy Documentation**: [https://groovy-lang.org/documentation.html](https://groovy-lang.org/documentation.html)
- **Gradle Documentation**: [https://docs.gradle.org](https://docs.gradle.org)
- **Serena Configuration**: [../02-usage/050_configuration.md](../02-usage/050_configuration.md)

---

### 03 Special Guides Ocaml Setup Guide For Serena (`docs/03-special-guides/ocaml_setup_guide_for_serena.md`)

# OCaml Setup Guide for Serena

This guide explains how to set up an OCaml project so that Serena can provide code intelligence via ocaml-lsp-server (ocamllsp).

Unlike some other languages, Serena does not download the OCaml language server automatically. You must install it yourself via opam, as OCaml tooling is compiled from source against your specific environment.

---
## Prerequisites

Install the following on your system and ensure they are available on `PATH`:

- **opam** (OCaml package manager)
  - macOS: `brew install opam`
  - Ubuntu/Debian: `sudo apt install opam`
  - Fedora: `sudo dnf install opam`
  - Other: https://opam.ocaml.org/doc/Install.html
- **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

- opam: https://opam.ocaml.org
- ocaml-lsp-server: https://github.com/ocaml/ocaml-lsp
- Project-wide occurrences: https://discuss.ocaml.org/t/ann-project-wide-occurrences-in-merlin-and-lsp/14847

---

### 03 Special Guides Scala Setup Guide For Serena (`docs/03-special-guides/scala_setup_guide_for_serena.md`)

# Scala Setup Guide for Serena

This guide explains how to prepare a Scala project so that Serena can provide reliable code intelligence via Metals (Scala LSP) and how to run Scala tests manually.

Serena automatically bootstraps the Metals language server using Coursier when needed. Your project, however, must be importable by a build server (BSP) — typically via Bloop or sbt’s built‑in BSP — so that Metals can compile and index your code.

---
## Prerequisites

Install the following on your system and ensure they are available on `PATH`:

- Java Development Kit (JDK). A modern LTS (e.g., 17 or 21) is recommended.
- `sbt`
- Coursier command (`cs`) or the 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](../02-usage/050_configuration.md)).

### 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 
- Metals + sbt: [https://scalameta.org/metals/docs/build-tools/sbt](https://scalameta.org/metals/docs/build-tools/sbt)

---

### 03 Special Guides Serena On Chatgpt (`docs/03-special-guides/serena_on_chatgpt.md`)

# Connecting Serena MCP Server to ChatGPT via MCPO & Cloudflare Tunnel

This guide explains how to expose a **locally running Serena MCP server** (powered by MCPO) to the internet using **Cloudflare Tunnel**, and how to connect it to **ChatGPT as a Custom GPT with tool access**.

Once configured, ChatGPT becomes a powerful **coding agent** with direct access to your codebase, shell, and file system — so **read the security notes carefully**.

---
## Prerequisites

Make sure you have [uv](https://docs.astral.sh/uv/getting-started/installation/) 
and [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) 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:

```
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](https://chat.openai.com/gpts/editor)
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:
    ```
     "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.

### Route 1 (recommended): UnrealBuildTool's clang database

    <Engine>\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe -mode=GenerateClangDatabase -project="<YourProject>.uproject" <YourProject>Editor Win64 Development -OutputDir="<YourProject's directory>"

This emits clang-native commands. Each entry carries the system-include paths, so clangd
resolves system and engine headers without guessing.

It needs a clang toolchain. Install one from the Visual Studio Installer:
**Modify > Individual Components > "C++ Clang tools for Windows"**. This is not a separate
LLVM download. UnrealBuildTool auto-detects it at `VC\Tools\Llvm\x64`.

- UnrealBuildTool searches for clang in this order: `C:\Program Files\LLVM`, then 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`:

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

    CompileFlags:
      Add: [-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH, -ferror-limit=200]

### Route 3 (situational): VSCode project files

UnrealBuildTool's VSCode generator emits per-project compile commands:

    <Engine>\Build\BatchFiles\Build.bat -projectfiles -project="<YourProject>.uproject" -game -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:

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

    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](020_prompts/010_evaluation-prompt.md) 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](020_prompts/020_summary-prompt.md) asks the agent for a
one-sentence, user-facing recommendation — the quotes shown on the [main page](https://github.com/oraios/serena).

## 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](010_evaluation-prompt), which performs the analysis
2. the [summary prompt](020_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](020_summary-prompt)

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### 04 Evaluation 020 Prompts 020 Summary Prompt (`docs/04-evaluation/020_prompts/020_summary-prompt.md`)

# Summary Prompt

We used the prompt below to summarize the evaluation of Serena.

```
Write a one-sentence user-facing summary about the value that Serena's tools
provide for coding. With a bit of emotion but grounded in the evaluation that
you have done above. It should be written from the perspective of a coding AI
agent and make it clear whether an AI agent would ask its owner to add Serena's
tools or not. It will be the first sentence that a potential new user will see
about how useful Serena is for an AI agent.
```

---

### 04 Evaluation 030 Results 000 Evaluation Results (`docs/04-evaluation/030_results/000_evaluation-results.md`)

# Results

This section presents the results of the evaluation.

We performed evaluations using popular AI coding agents in representative scenarios — different
agents, different programming languages, and different codebases — to show that the results are
not specific to a single setup.

All evaluations were conducted using the **JetBrains-powered version** of Serena, as it is the
more powerful backend with a broader set of refactoring and navigation capabilities. The
evaluation can easily be repeated with the LSP-based backend to assess its subset of capabilities.

- [Claude Code (Opus 4.6) on a large Python codebase](010_cc_on_tianshou)
- [Codex (GPT 5.4) on a Java codebase](020_codex_on_jbplugin)
- [Copilot CLI (GPT 5.4) on a large, multi-language monorepo](030_copilot_cli_on_ente.md)
- [GLM 5.1 in Claude Code](040_glm_on_tianshou)
- [JetBrains Junie with Opus 4.6](050_junie_plugin_on_tianshou.md)

You can run your own evaluation on a project of your choice by reusing our
[evaluation prompt](../020_prompts/010_evaluation-prompt.md).

---

### 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](https://github.com/thu-ml/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`).

### Semantic queries vs text search
- `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.

### 3.18 Non-code reads and free-text search

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](https://github.com/ente-io/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.

#### Task 4: find all references for one non-trivial symbol; compare code-use recall/precision vs text mention search

- **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](https://github.com/thu-ml/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.

#### Task 20: Free-text pattern search

Searching for `FIXME|HACK|TODO` across the codebase is a text search. Serena's semantic tools don't target this. `Grep` is the correct tool.

**Verdict:** These tasks are firmly built-in territory. They represent an estimated 20–30% of daily coding work (reading configs, searching for strings, shell operations, git workflows).

---

## 4. Token-Efficiency Analysis

### Payload differences across edit sizes

| Edit type | Edit payload | Serena payload | Winner |
|-----------|-------------|---------------|--------|
| 1-line tweak in 22-line method | ~200 chars | ~800 chars | Edit (4x) |
| 6-line change in 20-line method | ~700 chars | ~500 chars | Roughly equal |
| Full rewrite of 55-line method | ~2200 chars | ~1100 chars | Serena (2x) |
| Full rewrite of 330-line method | ~13,000 chars | ~6,500 chars | Serena (2x) |

### Forced reads

- Edit requires reading a file before editing it. This adds ~300–2000 chars per file.
- Serena does not require reading before editing (name-path addressing).
- For single-file edits where you already read the file, this is neutral.
- For cross-file operations on files you haven't read, Serena saves 3–4 forced reads.

### Stable vs ephemeral addressing

- Serena: name paths (`Collector/_collect`) are stable across edits. Results from exploration remain valid.
- Built-in: line numbers are ephemeral. `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](https://github.com/thu-ml/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 ---
- Files Extracted: 45
- Estimated Token Budget: ~107441 tokens
- Canonical Reference: https://codewiki.google/github.com/oraios/serena
