This is page 1 of 14. Use http://codebase.md/oraios/serena?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .devcontainer │ └── devcontainer.json ├── .dockerignore ├── .env.example ├── .github │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ ├── config.yml │ │ ├── feature_request.md │ │ └── issue--bug--performance-problem--question-.md │ └── workflows │ ├── codespell.yml │ ├── docker.yml │ ├── junie.yml │ ├── lint_and_docs.yaml │ ├── publish.yml │ └── pytest.yml ├── .gitignore ├── .serena │ ├── memories │ │ ├── adding_new_language_support_guide.md │ │ ├── serena_core_concepts_and_architecture.md │ │ ├── serena_repository_structure.md │ │ └── suggested_commands.md │ └── project.yml ├── .vscode │ └── settings.json ├── CHANGELOG.md ├── CLAUDE.md ├── compose.yaml ├── CONTRIBUTING.md ├── docker_build_and_run.sh ├── DOCKER.md ├── Dockerfile ├── docs │ ├── custom_agent.md │ └── serena_on_chatgpt.md ├── flake.lock ├── flake.nix ├── lessons_learned.md ├── LICENSE ├── llms-install.md ├── public │ └── .gitignore ├── pyproject.toml ├── README.md ├── resources │ ├── serena-icons.cdr │ ├── serena-logo-dark-mode.svg │ ├── serena-logo.cdr │ ├── serena-logo.svg │ └── vscode_sponsor_logo.png ├── roadmap.md ├── scripts │ ├── agno_agent.py │ ├── demo_run_tools.py │ ├── gen_prompt_factory.py │ ├── mcp_server.py │ ├── print_mode_context_options.py │ └── print_tool_overview.py ├── src │ ├── interprompt │ │ ├── __init__.py │ │ ├── .syncCommitId.remote │ │ ├── .syncCommitId.this │ │ ├── jinja_template.py │ │ ├── multilang_prompt.py │ │ ├── prompt_factory.py │ │ └── util │ │ ├── __init__.py │ │ └── class_decorators.py │ ├── README.md │ ├── serena │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── agno.py │ │ ├── analytics.py │ │ ├── cli.py │ │ ├── code_editor.py │ │ ├── config │ │ │ ├── __init__.py │ │ │ ├── context_mode.py │ │ │ └── serena_config.py │ │ ├── constants.py │ │ ├── dashboard.py │ │ ├── generated │ │ │ └── generated_prompt_factory.py │ │ ├── gui_log_viewer.py │ │ ├── mcp.py │ │ ├── project.py │ │ ├── prompt_factory.py │ │ ├── resources │ │ │ ├── config │ │ │ │ ├── contexts │ │ │ │ │ ├── agent.yml │ │ │ │ │ ├── chatgpt.yml │ │ │ │ │ ├── codex.yml │ │ │ │ │ ├── context.template.yml │ │ │ │ │ ├── desktop-app.yml │ │ │ │ │ ├── ide-assistant.yml │ │ │ │ │ └── oaicompat-agent.yml │ │ │ │ ├── internal_modes │ │ │ │ │ └── jetbrains.yml │ │ │ │ ├── modes │ │ │ │ │ ├── editing.yml │ │ │ │ │ ├── interactive.yml │ │ │ │ │ ├── mode.template.yml │ │ │ │ │ ├── no-onboarding.yml │ │ │ │ │ ├── onboarding.yml │ │ │ │ │ ├── one-shot.yml │ │ │ │ │ └── planning.yml │ │ │ │ └── prompt_templates │ │ │ │ ├── simple_tool_outputs.yml │ │ │ │ └── system_prompt.yml │ │ │ ├── dashboard │ │ │ │ ├── dashboard.js │ │ │ │ ├── index.html │ │ │ │ ├── jquery.min.js │ │ │ │ ├── serena-icon-16.png │ │ │ │ ├── serena-icon-32.png │ │ │ │ ├── serena-icon-48.png │ │ │ │ ├── serena-logs-dark-mode.png │ │ │ │ └── serena-logs.png │ │ │ ├── project.template.yml │ │ │ └── serena_config.template.yml │ │ ├── symbol.py │ │ ├── text_utils.py │ │ ├── tools │ │ │ ├── __init__.py │ │ │ ├── cmd_tools.py │ │ │ ├── config_tools.py │ │ │ ├── file_tools.py │ │ │ ├── jetbrains_plugin_client.py │ │ │ ├── jetbrains_tools.py │ │ │ ├── memory_tools.py │ │ │ ├── symbol_tools.py │ │ │ ├── tools_base.py │ │ │ └── workflow_tools.py │ │ └── util │ │ ├── class_decorators.py │ │ ├── exception.py │ │ ├── file_system.py │ │ ├── general.py │ │ ├── git.py │ │ ├── inspection.py │ │ ├── logging.py │ │ ├── shell.py │ │ └── thread.py │ └── solidlsp │ ├── __init__.py │ ├── .gitignore │ ├── language_servers │ │ ├── al_language_server.py │ │ ├── bash_language_server.py │ │ ├── clangd_language_server.py │ │ ├── clojure_lsp.py │ │ ├── common.py │ │ ├── csharp_language_server.py │ │ ├── dart_language_server.py │ │ ├── eclipse_jdtls.py │ │ ├── elixir_tools │ │ │ ├── __init__.py │ │ │ ├── elixir_tools.py │ │ │ └── README.md │ │ ├── erlang_language_server.py │ │ ├── gopls.py │ │ ├── intelephense.py │ │ ├── jedi_server.py │ │ ├── kotlin_language_server.py │ │ ├── lua_ls.py │ │ ├── marksman.py │ │ ├── nixd_ls.py │ │ ├── omnisharp │ │ │ ├── initialize_params.json │ │ │ ├── runtime_dependencies.json │ │ │ └── workspace_did_change_configuration.json │ │ ├── omnisharp.py │ │ ├── perl_language_server.py │ │ ├── pyright_server.py │ │ ├── r_language_server.py │ │ ├── ruby_lsp.py │ │ ├── rust_analyzer.py │ │ ├── solargraph.py │ │ ├── sourcekit_lsp.py │ │ ├── terraform_ls.py │ │ ├── typescript_language_server.py │ │ ├── vts_language_server.py │ │ └── zls.py │ ├── ls_config.py │ ├── ls_exceptions.py │ ├── ls_handler.py │ ├── ls_logger.py │ ├── ls_request.py │ ├── ls_types.py │ ├── ls_utils.py │ ├── ls.py │ ├── lsp_protocol_handler │ │ ├── lsp_constants.py │ │ ├── lsp_requests.py │ │ ├── lsp_types.py │ │ └── server.py │ ├── settings.py │ └── util │ ├── subprocess_util.py │ └── zip.py ├── test │ ├── __init__.py │ ├── conftest.py │ ├── resources │ │ └── repos │ │ ├── al │ │ │ └── test_repo │ │ │ ├── app.json │ │ │ └── src │ │ │ ├── Codeunits │ │ │ │ ├── CustomerMgt.Codeunit.al │ │ │ │ └── PaymentProcessorImpl.Codeunit.al │ │ │ ├── Enums │ │ │ │ └── CustomerType.Enum.al │ │ │ ├── Interfaces │ │ │ │ └── IPaymentProcessor.Interface.al │ │ │ ├── Pages │ │ │ │ ├── CustomerCard.Page.al │ │ │ │ └── CustomerList.Page.al │ │ │ ├── TableExtensions │ │ │ │ └── Item.TableExt.al │ │ │ └── Tables │ │ │ └── Customer.Table.al │ │ ├── bash │ │ │ └── test_repo │ │ │ ├── config.sh │ │ │ ├── main.sh │ │ │ └── utils.sh │ │ ├── clojure │ │ │ └── test_repo │ │ │ ├── deps.edn │ │ │ └── src │ │ │ └── test_app │ │ │ ├── core.clj │ │ │ └── utils.clj │ │ ├── csharp │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── Models │ │ │ │ └── Person.cs │ │ │ ├── Program.cs │ │ │ ├── serena.sln │ │ │ └── TestProject.csproj │ │ ├── dart │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── lib │ │ │ │ ├── helper.dart │ │ │ │ ├── main.dart │ │ │ │ └── models.dart │ │ │ └── pubspec.yaml │ │ ├── elixir │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── lib │ │ │ │ ├── examples.ex │ │ │ │ ├── ignored_dir │ │ │ │ │ └── ignored_module.ex │ │ │ │ ├── models.ex │ │ │ │ ├── services.ex │ │ │ │ ├── test_repo.ex │ │ │ │ └── utils.ex │ │ │ ├── mix.exs │ │ │ ├── mix.lock │ │ │ ├── scripts │ │ │ │ └── build_script.ex │ │ │ └── test │ │ │ ├── models_test.exs │ │ │ └── test_repo_test.exs │ │ ├── erlang │ │ │ └── test_repo │ │ │ ├── hello.erl │ │ │ ├── ignored_dir │ │ │ │ └── ignored_module.erl │ │ │ ├── include │ │ │ │ ├── records.hrl │ │ │ │ └── types.hrl │ │ │ ├── math_utils.erl │ │ │ ├── rebar.config │ │ │ ├── src │ │ │ │ ├── app.erl │ │ │ │ ├── models.erl │ │ │ │ ├── services.erl │ │ │ │ └── utils.erl │ │ │ └── test │ │ │ ├── models_tests.erl │ │ │ └── utils_tests.erl │ │ ├── go │ │ │ └── test_repo │ │ │ └── main.go │ │ ├── java │ │ │ └── test_repo │ │ │ ├── pom.xml │ │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── test_repo │ │ │ ├── Main.java │ │ │ ├── Model.java │ │ │ ├── ModelUser.java │ │ │ └── Utils.java │ │ ├── kotlin │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── build.gradle.kts │ │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── test_repo │ │ │ ├── Main.kt │ │ │ ├── Model.kt │ │ │ ├── ModelUser.kt │ │ │ └── Utils.kt │ │ ├── lua │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── main.lua │ │ │ ├── src │ │ │ │ ├── calculator.lua │ │ │ │ └── utils.lua │ │ │ └── tests │ │ │ └── test_calculator.lua │ │ ├── markdown │ │ │ └── test_repo │ │ │ ├── api.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── guide.md │ │ │ └── README.md │ │ ├── nix │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── default.nix │ │ │ ├── flake.nix │ │ │ ├── lib │ │ │ │ └── utils.nix │ │ │ ├── modules │ │ │ │ └── example.nix │ │ │ └── scripts │ │ │ └── hello.sh │ │ ├── perl │ │ │ └── test_repo │ │ │ ├── helper.pl │ │ │ └── main.pl │ │ ├── php │ │ │ └── test_repo │ │ │ ├── helper.php │ │ │ ├── index.php │ │ │ └── simple_var.php │ │ ├── python │ │ │ └── test_repo │ │ │ ├── .gitignore │ │ │ ├── custom_test │ │ │ │ ├── __init__.py │ │ │ │ └── advanced_features.py │ │ │ ├── examples │ │ │ │ ├── __init__.py │ │ │ │ └── user_management.py │ │ │ ├── ignore_this_dir_with_postfix │ │ │ │ └── ignored_module.py │ │ │ ├── scripts │ │ │ │ ├── __init__.py │ │ │ │ └── run_app.py │ │ │ └── test_repo │ │ │ ├── __init__.py │ │ │ ├── complex_types.py │ │ │ ├── models.py │ │ │ ├── name_collisions.py │ │ │ ├── nested_base.py │ │ │ ├── nested.py │ │ │ ├── overloaded.py │ │ │ ├── services.py │ │ │ ├── utils.py │ │ │ └── variables.py │ │ ├── r │ │ │ └── test_repo │ │ │ ├── .Rbuildignore │ │ │ ├── DESCRIPTION │ │ │ ├── examples │ │ │ │ └── analysis.R │ │ │ ├── NAMESPACE │ │ │ └── R │ │ │ ├── models.R │ │ │ └── utils.R │ │ ├── ruby │ │ │ └── test_repo │ │ │ ├── .solargraph.yml │ │ │ ├── examples │ │ │ │ └── user_management.rb │ │ │ ├── lib.rb │ │ │ ├── main.rb │ │ │ ├── models.rb │ │ │ ├── nested.rb │ │ │ ├── services.rb │ │ │ └── variables.rb │ │ ├── rust │ │ │ ├── test_repo │ │ │ │ ├── Cargo.lock │ │ │ │ ├── Cargo.toml │ │ │ │ └── src │ │ │ │ ├── lib.rs │ │ │ │ └── main.rs │ │ │ └── test_repo_2024 │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ ├── lib.rs │ │ │ └── main.rs │ │ ├── swift │ │ │ └── test_repo │ │ │ ├── Package.swift │ │ │ └── src │ │ │ ├── main.swift │ │ │ └── utils.swift │ │ ├── terraform │ │ │ └── test_repo │ │ │ ├── data.tf │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ ├── typescript │ │ │ └── test_repo │ │ │ ├── .serena │ │ │ │ └── project.yml │ │ │ ├── index.ts │ │ │ ├── tsconfig.json │ │ │ └── use_helper.ts │ │ └── zig │ │ └── test_repo │ │ ├── .gitignore │ │ ├── build.zig │ │ ├── src │ │ │ ├── calculator.zig │ │ │ ├── main.zig │ │ │ └── math_utils.zig │ │ └── zls.json │ ├── serena │ │ ├── __init__.py │ │ ├── __snapshots__ │ │ │ └── test_symbol_editing.ambr │ │ ├── config │ │ │ ├── __init__.py │ │ │ └── test_serena_config.py │ │ ├── test_edit_marker.py │ │ ├── test_mcp.py │ │ ├── test_serena_agent.py │ │ ├── test_symbol_editing.py │ │ ├── test_symbol.py │ │ ├── test_text_utils.py │ │ ├── test_tool_parameter_types.py │ │ └── util │ │ ├── test_exception.py │ │ └── test_file_system.py │ └── solidlsp │ ├── al │ │ └── test_al_basic.py │ ├── bash │ │ ├── __init__.py │ │ └── test_bash_basic.py │ ├── clojure │ │ ├── __init__.py │ │ └── test_clojure_basic.py │ ├── csharp │ │ └── test_csharp_basic.py │ ├── dart │ │ ├── __init__.py │ │ └── test_dart_basic.py │ ├── elixir │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_elixir_basic.py │ │ ├── test_elixir_ignored_dirs.py │ │ ├── test_elixir_integration.py │ │ └── test_elixir_symbol_retrieval.py │ ├── erlang │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_erlang_basic.py │ │ ├── test_erlang_ignored_dirs.py │ │ └── test_erlang_symbol_retrieval.py │ ├── go │ │ └── test_go_basic.py │ ├── java │ │ └── test_java_basic.py │ ├── kotlin │ │ └── test_kotlin_basic.py │ ├── lua │ │ └── test_lua_basic.py │ ├── markdown │ │ ├── __init__.py │ │ └── test_markdown_basic.py │ ├── nix │ │ └── test_nix_basic.py │ ├── perl │ │ └── test_perl_basic.py │ ├── php │ │ └── test_php_basic.py │ ├── python │ │ ├── test_python_basic.py │ │ ├── test_retrieval_with_ignored_dirs.py │ │ └── test_symbol_retrieval.py │ ├── r │ │ ├── __init__.py │ │ └── test_r_basic.py │ ├── ruby │ │ ├── test_ruby_basic.py │ │ └── test_ruby_symbol_retrieval.py │ ├── rust │ │ ├── test_rust_2024_edition.py │ │ └── test_rust_basic.py │ ├── swift │ │ └── test_swift_basic.py │ ├── terraform │ │ └── test_terraform_basic.py │ ├── typescript │ │ └── test_typescript_basic.py │ ├── util │ │ └── test_zip.py │ └── zig │ └── test_zig_basic.py └── uv.lock ``` # Files -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- ``` 1 | ``` -------------------------------------------------------------------------------- /test/resources/repos/elixir/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | ``` -------------------------------------------------------------------------------- /test/resources/repos/kotlin/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | .gradle/ 2 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | ignore_this_dir*/ ``` -------------------------------------------------------------------------------- /src/solidlsp/.gitignore: -------------------------------------------------------------------------------- ``` 1 | language_servers/static ``` -------------------------------------------------------------------------------- /test/resources/repos/r/test_repo/.Rbuildignore: -------------------------------------------------------------------------------- ``` 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.serena$ ``` -------------------------------------------------------------------------------- /src/interprompt/.syncCommitId.remote: -------------------------------------------------------------------------------- ``` 1 | 059d50b7d4d7e8fb9e7a13df7f7f33bae1aed5e2 ``` -------------------------------------------------------------------------------- /src/interprompt/.syncCommitId.this: -------------------------------------------------------------------------------- ``` 1 | 53ee7f47c08f29ae336567bcfaf89f79e7d447a2 ``` -------------------------------------------------------------------------------- /test/resources/repos/ruby/test_repo/.solargraph.yml: -------------------------------------------------------------------------------- ```yaml 1 | --- 2 | include: 3 | - "main.rb" 4 | - "lib.rb" 5 | ``` -------------------------------------------------------------------------------- /test/resources/repos/zig/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | zig-cache/ 2 | zig-out/ 3 | .zig-cache/ 4 | build/ 5 | dist/ ``` -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- ``` 1 | GOOGLE_API_KEY=<your_google_api_key> 2 | ANTHROPIC_API_KEY=<your_key> 3 | ``` -------------------------------------------------------------------------------- /test/resources/repos/nix/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Nix specific 2 | result 3 | result-* 4 | .direnv/ 5 | 6 | # Build artifacts 7 | *.drv 8 | 9 | # IDE 10 | .vscode/ 11 | .idea/ ``` -------------------------------------------------------------------------------- /test/resources/repos/lua/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Lua specific 2 | *.luac 3 | .luarocks/ 4 | lua_modules/ 5 | luarocks/ 6 | 7 | # Build artifacts 8 | build/ 9 | dist/ 10 | 11 | # IDE 12 | .vscode/ 13 | .idea/ ``` -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- ``` 1 | data 2 | logs 3 | log 4 | test/log 5 | docs/jupyter_execute 6 | docs/.jupyter_cache 7 | docs/_build 8 | coverage.xml 9 | docker_build_and_run.sh 10 | ``` -------------------------------------------------------------------------------- /test/resources/repos/csharp/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Build results 2 | [Dd]ebug/ 3 | [Dd]ebugPublic/ 4 | [Rr]elease/ 5 | [Rr]eleases/ 6 | x64/ 7 | x86/ 8 | [Aa][Rr][Mm]/ 9 | [Aa][Rr][Mm]64/ 10 | bld/ 11 | [Bb]in/ 12 | [Oo]bj/ 13 | [Ll]og/ 14 | [Ll]ogs/ 15 | 16 | # Visual Studio temporary files 17 | .vs/ 18 | 19 | # .NET Core 20 | project.lock.json 21 | project.fragment.lock.json 22 | artifacts/ 23 | 24 | # Files built by Visual Studio 25 | *.user 26 | *.userosscache 27 | *.sln.docstates 28 | 29 | # Build results 30 | *.dll 31 | *.exe 32 | *.pdb 33 | 34 | # NuGet 35 | *.nupkg 36 | *.snupkg 37 | packages/ ``` -------------------------------------------------------------------------------- /test/resources/repos/dart/test_repo/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | pubspec.lock 5 | build/ 6 | 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | # pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | doc/api/ 12 | 13 | # dotenv environment variables file 14 | .env* 15 | 16 | # Avoid committing generated Javascript files 17 | *.dart.js 18 | *.info.json # Produced by the --dump-info flag. 19 | *.js # When generated by dart2js. Don't specify *.js if your 20 | # project includes source files written in JavaScript. 21 | *.js_ 22 | *.js.deps 23 | *.js.map ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # macOS specific files 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | Icon 9 | .fseventsd 10 | .DocumentRevisions-V100 11 | .TemporaryItems 12 | .VolumeIcon.icns 13 | .com.apple.timemachine.donotpresent 14 | 15 | # Windows specific files 16 | Thumbs.db 17 | Thumbs.db:encryptable 18 | ehthumbs.db 19 | ehthumbs_vista.db 20 | *.stackdump 21 | [Dd]esktop.ini 22 | $RECYCLE.BIN/ 23 | *.cab 24 | *.msi 25 | *.msix 26 | *.msm 27 | *.msp 28 | *.lnk 29 | 30 | # Linux specific files 31 | *~ 32 | .fuse_hidden* 33 | .directory 34 | .Trash-* 35 | .nfs* 36 | 37 | # IDE/Text Editors 38 | # VS Code 39 | .vscode/* 40 | !.vscode/settings.json 41 | !.vscode/tasks.json 42 | !.vscode/launch.json 43 | !.vscode/extensions.json 44 | *.code-workspace 45 | .history/ 46 | 47 | # JetBrains IDEs (beyond .idea/) 48 | *.iml 49 | *.ipr 50 | *.iws 51 | out/ 52 | .idea_modules/ 53 | 54 | # Sublime Text 55 | *.tmlanguage.cache 56 | *.tmPreferences.cache 57 | *.stTheme.cache 58 | *.sublime-workspace 59 | *.sublime-project 60 | 61 | # Project specific ignore 62 | .idea 63 | temp 64 | 65 | # Byte-compiled / optimized / DLL files 66 | __pycache__/ 67 | *.py[cod] 68 | *$py.class 69 | 70 | # C extensions 71 | *.so 72 | 73 | # Distribution / packaging 74 | .Python 75 | build/ 76 | develop-eggs/ 77 | dist/ 78 | downloads/ 79 | eggs/ 80 | .eggs/ 81 | lib/ 82 | lib64/ 83 | parts/ 84 | sdist/ 85 | var/ 86 | wheels/ 87 | pip-wheel-metadata/ 88 | share/python-wheels/ 89 | *.egg-info/ 90 | .installed.cfg 91 | *.egg 92 | MANIFEST 93 | 94 | # PyInstaller 95 | # Usually these files are written by a python script from a template 96 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 97 | *.manifest 98 | *.spec 99 | 100 | # Installer logs 101 | pip-log.txt 102 | pip-delete-this-directory.txt 103 | 104 | # Unit test / coverage reports 105 | htmlcov/ 106 | .tox/ 107 | .nox/ 108 | .coverage 109 | .coverage.* 110 | .cache 111 | nosetests.xml 112 | coverage.xml 113 | *.cover 114 | *.py,cover 115 | .hypothesis/ 116 | .pytest_cache/ 117 | 118 | # Translations 119 | *.mo 120 | *.pot 121 | 122 | # Django stuff: 123 | *.log 124 | local_settings.py 125 | db.sqlite3 126 | db.sqlite3-journal 127 | 128 | # Flask stuff: 129 | instance/ 130 | .webassets-cache 131 | 132 | # Scrapy stuff: 133 | .scrapy 134 | 135 | # Sphinx documentation 136 | docs/_build/ 137 | 138 | # PyBuilder 139 | target/ 140 | 141 | # Jupyter Notebook 142 | .ipynb_checkpoints 143 | 144 | # IPython 145 | profile_default/ 146 | ipython_config.py 147 | 148 | # pyenv 149 | .python-version 150 | 151 | # pipenv 152 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 153 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 154 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 155 | # install all needed dependencies. 156 | #Pipfile.lock 157 | 158 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 159 | __pypackages__/ 160 | 161 | # Celery stuff 162 | celerybeat-schedule 163 | celerybeat.pid 164 | 165 | # SageMath parsed files 166 | *.sage.py 167 | 168 | # Environments 169 | .env 170 | .venv 171 | env/ 172 | venv/ 173 | ENV/ 174 | env.bak/ 175 | venv.bak/ 176 | 177 | # Spyder project settings 178 | .spyderproject 179 | .spyproject 180 | 181 | # Rope project settings 182 | .ropeproject 183 | 184 | # mkdocs documentation 185 | /site 186 | 187 | # mypy 188 | .mypy_cache/ 189 | .dmypy.json 190 | dmypy.json 191 | 192 | # Pyre type checker 193 | .pyre/ 194 | 195 | # reports 196 | pylint.html 197 | .pylint.d 198 | 199 | # Serena-specific 200 | /*.yml 201 | !*.template.yml 202 | /agent-ui 203 | 204 | # dynamic LS installations 205 | /src/multilspy/language_servers/*/static 206 | /src/solidlsp/language_servers/*/static 207 | /src/solidlsp/language_servers/static 208 | 209 | # clojure-lsp temporary files 210 | .calva/ 211 | .clj-kondo/ 212 | .cpcache/ 213 | .lsp/ 214 | 215 | # Cache is in .multilspy 216 | **/.multilspy/** 217 | 218 | tmp/ 219 | 220 | .serena/ 221 | .vscode/ 222 | 223 | # Claude settings 224 | .claude/settings.local.json 225 | 226 | # Elixir 227 | /test/resources/repos/elixir/test_repo/deps 228 | # Exception: Don't ignore Elixir test repository lib directory (contains source code) 229 | !/test/resources/repos/elixir/test_repo/lib 230 | 231 | # Exception: Don't ignore Nix test repository lib directory (contains source code) 232 | !/test/resources/repos/nix/test_repo/lib 233 | 234 | # Swift 235 | /test/resources/repos/swift/test_repo/.build 236 | /test/resources/repos/swift/test_repo/.swiftpm 237 | ``` -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- ```markdown 1 | Serena uses (modified) versions of other libraries/packages: 2 | 3 | * solidlsp (our fork of [microsoft/multilspy](https://github.com/microsoft/multilspy) for fully synchronous language server communication) 4 | * [interprompt](https://github.com/oraios/interprompt) (our prompt templating library) 5 | ``` -------------------------------------------------------------------------------- /test/resources/repos/markdown/test_repo/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Test Repository 2 | 3 | This is a test repository for markdown language server testing. 4 | 5 | ## Overview 6 | 7 | This repository contains sample markdown files for testing LSP features. 8 | 9 | ## Features 10 | 11 | - Document symbol detection 12 | - Link navigation 13 | - Reference finding 14 | - Code completion 15 | 16 | ### Installation 17 | 18 | To use this test repository: 19 | 20 | 1. Clone the repository 21 | 2. Install dependencies 22 | 3. Run tests 23 | 24 | ### Usage 25 | 26 | See [guide.md](guide.md) for detailed usage instructions. 27 | 28 | ## Code Examples 29 | 30 | Here's a simple example: 31 | 32 | ```python 33 | def hello_world(): 34 | print("Hello, World!") 35 | ``` 36 | 37 | ### JavaScript Example 38 | 39 | ```javascript 40 | function greet(name) { 41 | console.log(`Hello, ${name}!`); 42 | } 43 | ``` 44 | 45 | ## References 46 | 47 | - [Official Documentation](https://example.com/docs) 48 | - [API Reference](api.md) 49 | - [Contributing Guide](CONTRIBUTING.md) 50 | 51 | ## License 52 | 53 | MIT License 54 | ``` -------------------------------------------------------------------------------- /src/solidlsp/language_servers/elixir_tools/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Elixir Language Server Integration 2 | 3 | This directory contains the integration for Elixir language support using [Next LS](https://github.com/elixir-tools/next-ls) from the elixir-tools project. 4 | 5 | > **⚠️ Windows Not Supported**: Next LS does not provide Windows binaries, so Elixir language server integration is only available on Linux and macOS. 6 | 7 | ## Known Issues 8 | 9 | ### Next LS v0.23.3 Timeout Enumeration Bug 10 | There is a known intermittent bug in Next LS v0.23.3 where `textDocument/definition` requests can fail with: 11 | ``` 12 | Protocol.UndefinedError: protocol Enumerable not implemented for :timeout of type Atom 13 | ``` 14 | 15 | This bug is tracked in [Next LS Issue #543](https://github.com/elixir-tools/next-ls/issues/543) and primarily occurs in CI environments. The affected test (`test_request_defining_symbol_none`) is marked as expected to fail until this upstream bug is resolved. 16 | 17 | ## Prerequisites 18 | 19 | Before using the Elixir language server integration, you need to have: 20 | 21 | 1. **Elixir** installed and available in your PATH 22 | - Install from: https://elixir-lang.org/install.html 23 | - Verify with: `elixir --version` 24 | 25 | 2. **Next LS** installed and available in your PATH 26 | - Install from: https://github.com/elixir-tools/next-ls#installation 27 | - Verify with: `nextls --version` 28 | 29 | ## Features 30 | 31 | The Elixir integration provides: 32 | 33 | - **Language Server Protocol (LSP) support** via Next LS 34 | - **File extension recognition** for `.ex` and `.exs` files 35 | - **Project structure awareness** with proper handling of Elixir-specific directories: 36 | - `_build/` - Compiled artifacts (ignored) 37 | - `deps/` - Dependencies (ignored) 38 | - `.elixir_ls/` - ElixirLS artifacts (ignored) 39 | - `cover/` - Coverage reports (ignored) 40 | - `lib/` - Source code (not ignored) 41 | - `test/` - Test files (not ignored) 42 | 43 | ## Configuration 44 | 45 | The integration uses the default Next LS configuration with: 46 | 47 | - **MIX_ENV**: `dev` 48 | - **MIX_TARGET**: `host` 49 | - **Experimental completions**: Disabled by default 50 | - **Credo extension**: Enabled by default 51 | 52 | ## Usage 53 | 54 | The Elixir language server is automatically selected when working with Elixir projects. It will be used for: 55 | 56 | - Code completion 57 | - Go to definition 58 | - Find references 59 | - Document symbols 60 | - Hover information 61 | - Code formatting 62 | - Diagnostics (via Credo integration) 63 | 64 | ### Important: Project Compilation 65 | 66 | Next LS requires your Elixir project to be **compiled** for optimal performance, especially for: 67 | - Cross-file reference resolution 68 | - Complete symbol information 69 | - Accurate go-to-definition 70 | 71 | **For production use**: Ensure your project is compiled with `mix compile` before using the language server. 72 | 73 | **For testing**: The test suite automatically compiles the test repositories before running tests to ensure optimal Next LS performance. 74 | 75 | ## Testing 76 | 77 | Run the Elixir-specific tests with: 78 | 79 | ```bash 80 | pytest test/solidlsp/elixir/ -m elixir 81 | ``` 82 | 83 | ## Implementation Details 84 | 85 | - **Main class**: `ElixirTools` in `elixir_tools.py` 86 | - **Initialization parameters**: Defined in `initialize_params.json` 87 | - **Language identifier**: `"elixir"` 88 | - **Command**: `nextls --stdio` 89 | 90 | The implementation follows the same patterns as other language servers in this project, inheriting from `SolidLanguageServer` and providing Elixir-specific configuration and behavior. ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | <p align="center" style="text-align:center"> 2 | <img src="resources/serena-logo.svg#gh-light-mode-only" style="width:500px"> 3 | <img src="resources/serena-logo-dark-mode.svg#gh-dark-mode-only" style="width:500px"> 4 | </p> 5 | 6 | * :rocket: Serena is a powerful **coding agent toolkit** capable of turning an LLM into a fully-featured agent that works **directly on your codebase**. 7 | Unlike most other tools, it is not tied to an LLM, framework or an interface, making it easy to use it in a variety of ways. 8 | * :wrench: Serena provides essential **semantic code retrieval and editing tools** that are akin to an IDE's capabilities, extracting code entities at the symbol level and exploiting relational structure. When combined with an existing coding agent, these tools greatly enhance (token) efficiency. 9 | * :free: Serena is **free & open-source**, enhancing the capabilities of LLMs you already have access to free of charge. 10 | 11 | You can think of Serena as providing IDE-like tools to your LLM/coding agent. With it, the agent no longer needs to read entire 12 | files, perform grep-like searches or string replacements to find and edit the right code. Instead, it can use code centered tools like `find_symbol`, `find_referencing_symbols` and `insert_after_symbol`. 13 | 14 | <p align="center"> 15 | <em>Serena is under active development! See the latest updates, upcoming features, and lessons learned to stay up to date.</em> 16 | </p> 17 | 18 | <p align="center"> 19 | <a href="CHANGELOG.md"> 20 | <img src="https://img.shields.io/badge/Updates-1e293b?style=flat&logo=rss&logoColor=white&labelColor=1e293b" alt="Changelog" /> 21 | </a> 22 | <a href="roadmap.md"> 23 | <img src="https://img.shields.io/badge/Roadmap-14532d?style=flat&logo=target&logoColor=white&labelColor=14532d" alt="Roadmap" /> 24 | </a> 25 | <a href="lessons_learned.md"> 26 | <img src="https://img.shields.io/badge/Lessons-Learned-7c4700?style=flat&logo=readthedocs&logoColor=white&labelColor=7c4700" alt="Lessons Learned" /> 27 | </a> 28 | </p> 29 | 30 | ### LLM Integration 31 | 32 | Serena provides the necessary [tools](#list-of-tools) for coding workflows, but an LLM is required to do the actual work, 33 | orchestrating tool use. 34 | 35 | For example, **supercharge the performance of Claude Code** with a [one-line shell command](#claude-code). 36 | 37 | In general, Serena can be integrated with an LLM in several ways: 38 | 39 | * by using the **model context protocol (MCP)**. 40 | Serena provides an MCP server which integrates with 41 | * Claude Code and Claude Desktop, 42 | * Terminal-based clients like Codex, Gemini-CLI, Qwen3-Coder, rovodev, OpenHands CLI and others, 43 | * IDEs like VSCode, Cursor or IntelliJ, 44 | * Extensions like Cline or Roo Code 45 | * Local clients like [OpenWebUI](https://docs.openwebui.com/openapi-servers/mcp), [Jan](https://jan.ai/docs/mcp-examples/browser/browserbase#enable-mcp), [Agno](https://docs.agno.com/introduction/playground) and others 46 | * by using [mcpo to connect it to ChatGPT](docs/serena_on_chatgpt.md) or other clients that don't support MCP but do support tool calling via OpenAPI. 47 | * by incorporating Serena's tools into an agent framework of your choice, as illustrated [here](docs/custom_agent.md). 48 | Serena's tool implementation is decoupled from the framework-specific code and can thus easily be adapted to any agent framework. 49 | 50 | ### Serena in Action 51 | 52 | #### Demonstration 1: Efficient Operation in Claude Code 53 | 54 | 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. 55 | 56 | https://github.com/user-attachments/assets/ab78ebe0-f77d-43cc-879a-cc399efefd87 57 | 58 | #### Demonstration 2: Serena in Claude Desktop 59 | 60 | A demonstration of Serena implementing a small feature for itself (a better log GUI) with Claude Desktop. 61 | Note how Serena's tools enable Claude to find and edit the right symbols. 62 | 63 | https://github.com/user-attachments/assets/6eaa9aa1-610d-4723-a2d6-bf1e487ba753 64 | 65 | ### Programming Language Support & Semantic Analysis Capabilities 66 | 67 | Serena's semantic code analysis capabilities build on **language servers** using the widely implemented 68 | language server protocol (LSP). The LSP provides a set of versatile code querying 69 | and editing functionalities based on symbolic understanding of the code. 70 | Equipped with these capabilities, Serena discovers and edits code just like a seasoned developer 71 | making use of an IDE's capabilities would. 72 | Serena can efficiently find the right context and do the right thing even in very large and 73 | complex projects! So not only is it free and open-source, it frequently achieves better results 74 | than existing solutions that charge a premium. 75 | 76 | Language servers provide support for a wide range of programming languages. 77 | With Serena, we provide direct, out-of-the-box support for: 78 | 79 | * Python 80 | * TypeScript/Javascript 81 | * PHP (uses Intelephense LSP; set `INTELEPHENSE_LICENSE_KEY` environment variable for premium features) 82 | * Go (requires installation of gopls) 83 | * R (requires installation of the `languageserver` R package) 84 | * Rust (requires [rustup](https://rustup.rs/) - uses rust-analyzer from your toolchain) 85 | * C/C++ (you may experience issues with finding references, we are working on it) 86 | * Zig (requires installation of ZLS - Zig Language Server) 87 | * C# 88 | * Ruby (by default, uses [ruby-lsp](https://github.com/Shopify/ruby-lsp), specify ruby_solargraph as your language to use the previous solargraph based implementation) 89 | * Swift 90 | * Kotlin (uses the pre-alpha [official kotlin LS](https://github.com/Kotlin/kotlin-lsp), some issues may appear) 91 | * Java (_Note_: startup is slow, initial startup especially so. There may be issues with java on macos and linux, we are working on it.) 92 | * Clojure 93 | * Dart 94 | * Bash 95 | * Lua (automatically downloads lua-language-server if not installed) 96 | * Nix (requires nixd installation) 97 | * Elixir (requires installation of NextLS and Elixir; **Windows not supported**) 98 | * Erlang (requires installation of beam and [erlang_ls](https://github.com/erlang-ls/erlang_ls), experimental, might be slow or hang) 99 | * Perl (requires installation of Perl::LanguageServer) 100 | * AL 101 | * Markdown (automatically downloads marksman if not installed, experimental, must be explicitly specified via `--language markdown` when generating project config - primarily useful for documentation-heavy projects) 102 | 103 | Support for further languages can easily be added by providing a shallow adapter for a new language server implementation, 104 | see Serena's [memory on that](.serena/memories/adding_new_language_support_guide.md). 105 | 106 | ### Community Feedback 107 | 108 | Most users report that Serena has strong positive effects on the results of their coding agents, even when used within 109 | very capable agents like Claude Code. Serena is often described to be a [game changer](https://www.reddit.com/r/ClaudeAI/comments/1lfsdll/try_out_serena_mcp_thank_me_later/), providing an enormous [productivity boost](https://www.reddit.com/r/ClaudeCode/comments/1mguoia/absolutely_insane_improvement_of_claude_code). 110 | 111 | Serena excels at navigating and manipulating complex codebases, providing tools that support precise code retrieval and editing in the presence of large, strongly structured codebases. 112 | However, when dealing with tasks that involve only very few/small files, you may not benefit from including Serena on top of your existing coding agent. 113 | In particular, when writing code from scratch, Serena will not provide much value initially, as the more complex structures that Serena handles more gracefully than simplistic, file-based approaches are yet to be created. 114 | 115 | Several videos and blog posts have talked about Serena: 116 | 117 | * YouTube: 118 | * [AI Labs](https://www.youtube.com/watch?v=wYWyJNs1HVk&t=1s) 119 | * [Yo Van Eyck](https://www.youtube.com/watch?v=UqfxuQKuMo8&t=45s) 120 | * [JeredBlu](https://www.youtube.com/watch?v=fzPnM3ySmjE&t=32s) 121 | 122 | * Blog posts: 123 | * [Serena's Design Principles](https://medium.com/@souradip1000/deconstructing-serenas-mcp-powered-semantic-code-understanding-architecture-75802515d116) 124 | * [Serena with Claude Code (in Japanese)](https://blog.lai.so/serena/) 125 | * [Turning Claude Code into a Development Powerhouse](https://robertmarshall.dev/blog/turning-claude-code-into-a-development-powerhouse/) 126 | 127 | ## Table of Contents 128 | 129 | <!-- Created with markdown-toc -i README.md --> 130 | <!-- Install it with npm install -g markdown-toc --> 131 | 132 | <!-- toc --> 133 | 134 | - [Quick Start](#quick-start) 135 | * [Running the Serena MCP Server](#running-the-serena-mcp-server) 136 | + [Usage](#usage) 137 | + [Using uvx](#using-uvx) 138 | + [Local Installation](#local-installation) 139 | + [Using Docker (Experimental)](#using-docker-experimental) 140 | + [Using Nix](#using-nix) 141 | + [Streamable HTTP Mode](#streamable-http-mode) 142 | + [Command-Line Arguments](#command-line-arguments) 143 | * [Configuration](#configuration) 144 | * [Project Activation & Indexing](#project-activation--indexing) 145 | * [Claude Code](#claude-code) 146 | * [Codex](#codex) 147 | * [Other Terminal-Based Clients](#other-terminal-based-clients) 148 | * [Claude Desktop](#claude-desktop) 149 | * [MCP Coding Clients (Cline, Roo-Code, Cursor, Windsurf, etc.)](#mcp-coding-clients-cline-roo-code-cursor-windsurf-etc) 150 | * [Local GUIs and Frameworks](#local-guis-and-frameworks) 151 | - [Detailed Usage and Recommendations](#detailed-usage-and-recommendations) 152 | * [Tool Execution](#tool-execution) 153 | + [Shell Execution and Editing Tools](#shell-execution-and-editing-tools) 154 | * [Modes and Contexts](#modes-and-contexts) 155 | + [Contexts](#contexts) 156 | + [Modes](#modes) 157 | + [Customization](#customization) 158 | * [Onboarding and Memories](#onboarding-and-memories) 159 | * [Prepare Your Project](#prepare-your-project) 160 | + [Structure Your Codebase](#structure-your-codebase) 161 | + [Start from a Clean State](#start-from-a-clean-state) 162 | + [Logging, Linting, and Automated Tests](#logging-linting-and-automated-tests) 163 | * [Prompting Strategies](#prompting-strategies) 164 | * [Running Out of Context](#running-out-of-context) 165 | * [Serena's Logs: The Dashboard and GUI Tool](#serenas-logs-the-dashboard-and-gui-tool) 166 | - [Comparison with Other Coding Agents](#comparison-with-other-coding-agents) 167 | * [Subscription-Based Coding Agents](#subscription-based-coding-agents) 168 | * [API-Based Coding Agents](#api-based-coding-agents) 169 | * [Other MCP-Based Coding Agents](#other-mcp-based-coding-agents) 170 | - [Acknowledgements](#acknowledgements) 171 | * [Sponsors](#sponsors) 172 | * [Community Contributions](#community-contributions) 173 | * [Technologies](#technologies) 174 | - [Customizing and Extending Serena](#customizing-and-extending-serena) 175 | - [List of Tools](#list-of-tools) 176 | 177 | <!-- tocstop --> 178 | 179 | ## Quick Start 180 | 181 | Serena can be used in various ways, below you will find instructions for selected integrations. 182 | 183 | * For coding with Claude, we recommend using Serena through [Claude Code](#claude-code) or [Claude Desktop](#claude-desktop). You can also use Serena in most other [terminal-based clients](#other-terminal-based-clients). 184 | * If you want a GUI experience outside an IDE, you can use one of the many [local GUIs](#local-guis-and-frameworks) that support MCP servers. 185 | You can also connect Serena to many web clients (including ChatGPT) using [mcpo](docs/serena_on_chatgpt.md). 186 | * If you want to use Serena integrated in your IDE, see the section on [other MCP clients](#other-mcp-clients---cline-roo-code-cursor-windsurf-etc). 187 | * You can use Serena as a library for building your own applications. We try to keep the public API stable, but you should still 188 | expect breaking changes and pin Serena to a fixed version if you use it as a dependency. 189 | 190 | Serena is managed by `uv`, so you will need to [install it](https://docs.astral.sh/uv/getting-started/installation/). 191 | 192 | ### Running the Serena MCP Server 193 | 194 | You have several options for running the MCP server, which are explained in the subsections below. 195 | 196 | #### Usage 197 | 198 | The typical usage involves the client (Claude Code, Claude Desktop, etc.) running 199 | the MCP server as a subprocess (using stdio communication), 200 | so the client needs to be provided with the command to run the MCP server. 201 | (Alternatively, you can run the MCP server in Streamable HTTP or SSE mode and tell your client 202 | how to connect to it.) 203 | 204 | Note that no matter how you run the MCP server, Serena will, by default, start a small web-based dashboard on localhost that will display logs and allow shutting down the 205 | MCP server (since many clients fail to clean up processes correctly). 206 | This and other settings can be adjusted in the [configuration](#configuration) and/or by providing [command-line arguments](#command-line-arguments). 207 | 208 | #### Using uvx 209 | 210 | `uvx` can be used to run the latest version of Serena directly from the repository, without an explicit local installation. 211 | 212 | ```shell 213 | uvx --from git+https://github.com/oraios/serena serena start-mcp-server 214 | ``` 215 | 216 | Explore the CLI to see some of the customization options that serena provides (more info on them below). 217 | 218 | #### Local Installation 219 | 220 | 1. Clone the repository and change into it. 221 | 222 | ```shell 223 | git clone https://github.com/oraios/serena 224 | cd serena 225 | ``` 226 | 227 | 2. Optionally edit the configuration file in your home directory with 228 | 229 | ```shell 230 | uv run serena config edit 231 | ``` 232 | 233 | If you just want the default config, you can skip this part, and a config file will be created when you first run Serena. 234 | 3. Run the server with `uv`: 235 | 236 | ```shell 237 | uv run serena start-mcp-server 238 | ``` 239 | 240 | When running from outside the serena installation directory, be sure to pass it, i.e., use 241 | 242 | ```shell 243 | uv run --directory /abs/path/to/serena serena start-mcp-server 244 | ``` 245 | 246 | #### Using Docker (Experimental) 247 | 248 | ⚠️ Docker support is currently experimental with several limitations. Please read the [Docker documentation](DOCKER.md) for important caveats before using it. 249 | 250 | You can run the Serena MCP server directly via docker as follows, 251 | assuming that the projects you want to work on are all located in `/path/to/your/projects`: 252 | 253 | ```shell 254 | docker run --rm -i --network host -v /path/to/your/projects:/workspaces/projects ghcr.io/oraios/serena:latest serena start-mcp-server --transport stdio 255 | ``` 256 | 257 | Replace `/path/to/your/projects` with the absolute path to your projects directory. The Docker approach provides: 258 | 259 | * Better security isolation for shell command execution 260 | * No need to install language servers and dependencies locally 261 | * Consistent environment across different systems 262 | 263 | Alternatively, use docker compose with the `compose.yml` file provided in the repository. 264 | 265 | See the [Docker documentation](DOCKER.md) for detailed setup instructions, configuration options, and known limitations. 266 | 267 | #### Using Nix 268 | 269 | 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: 270 | 271 | ```bash 272 | nix run github:oraios/serena -- start-mcp-server --transport stdio 273 | ``` 274 | 275 | 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`. 276 | 277 | #### Streamable HTTP Mode 278 | 279 | ℹ️ Note that MCP servers which use stdio as a protocol are somewhat unusual as far as client/server architectures go, as the server 280 | necessarily has to be started by the client in order for communication to take place via the server's standard input/output stream. 281 | In other words, you do not need to start the server yourself. The client application (e.g. Claude Desktop) takes care of this and 282 | therefore needs to be configured with a launch command. 283 | 284 | When using instead the *Streamable HTTP* mode, you control the server lifecycle yourself, 285 | i.e. you start the server and provide the client with the URL to connect to it. 286 | 287 | Simply provide `start-mcp-server` with the `--transport streamable-http` option and optionally provide the port. 288 | For example, to run the Serena MCP server in Streamable HTTP mode on port 9121 using a local installation, 289 | you would run this command from the Serena directory, 290 | 291 | ```shell 292 | uv run serena start-mcp-server --transport streamable-http --port 9121 293 | ``` 294 | 295 | and then configure your client to connect to `http://localhost:9121/mcp`. 296 | 297 | ℹ️ Note that SSE transport is supported as well, but its use is discouraged. 298 | Use Streamable HTTP instead. 299 | 300 | #### Command-Line Arguments 301 | 302 | The Serena MCP server supports a wide range of additional command-line options, including the option to run in Streamable HTTP or SSE mode 303 | and to adapt Serena to various [contexts and modes of operation](#modes-and-contexts). 304 | 305 | Run with parameter `--help` to get a list of available options. 306 | 307 | ### Configuration 308 | 309 | Serena is very flexible in terms of configuration. While for most users, the default configurations will work, 310 | you can fully adjust it to your needs by editing a few yaml files. You can disable tools, change Serena's instructions 311 | (what we denote as the `system_prompt`), adjust the output of tools that just provide a prompt, and even adjust tool descriptions. 312 | 313 | Serena is configured in four places: 314 | 315 | 1. The `serena_config.yml` for general settings that apply to all clients and projects. 316 | It is located in your user directory under `.serena/serena_config.yml`. 317 | If you do not explicitly create the file, it will be auto-generated when you first run Serena. 318 | You can edit it directly or use 319 | 320 | ```shell 321 | uvx --from git+https://github.com/oraios/serena serena config edit 322 | ``` 323 | 324 | (or use the `--directory` command version). 325 | 2. In the arguments passed to the `start-mcp-server` in your client's config (see below), 326 | which will apply to all sessions started by the respective client. In particular, the [context](#contexts) parameter 327 | should be set appropriately for Serena to be best adjusted to existing tools and capabilities of your client. 328 | See for a detailed explanation. You can override all entries from the `serena_config.yml` through command line arguments. 329 | 3. In the `.serena/project.yml` file within your project. This will hold project-level configuration that is used whenever 330 | that project is activated. This file will be autogenerated when you first use Serena on that project, but you can also 331 | generate it explicitly with 332 | 333 | ```shell 334 | uvx --from git+https://github.com/oraios/serena serena project generate-yml 335 | ``` 336 | 337 | (or use the `--directory` command version). 338 | 4. Through the context and modes. Explore the [modes and contexts](#modes-and-contexts) section for more details. 339 | 340 | After the initial setup, continue with one of the sections below, depending on how you 341 | want to use Serena. 342 | 343 | ### Project Activation & Indexing 344 | 345 | If you are mostly working with the same project, you can configure to always activate it at startup 346 | by passing `--project <path_or_name>` to the `start-mcp-server` command in your client's MCP config. 347 | This is especially useful for clients which configure MCP servers on a per-project basis, like Claude Code. 348 | 349 | Otherwise, the recommended way is to just ask the LLM to activate a project by providing it an absolute path to, or, 350 | in case the project was activated in the past, by its name. The default project name is the directory name. 351 | 352 | * "Activate the project /path/to/my_project" 353 | * "Activate the project my_project" 354 | 355 | All projects that have been activated will be automatically added to your `serena_config.yml`, and for each 356 | project, the file `.serena/project.yml` will be generated. You can adjust the latter, e.g., by changing the name 357 | (which you refer to during the activation) or other options. Make sure to not have two different projects with the 358 | same name. 359 | 360 | ℹ️ For larger projects, we recommend that you index your project to accelerate Serena's tools; otherwise the first 361 | tool application may be very slow. 362 | To do so, run this from the project directory (or pass the path to the project as an argument): 363 | 364 | ```shell 365 | uvx --from git+https://github.com/oraios/serena serena project index 366 | ``` 367 | 368 | (or use the `--directory` command version). 369 | 370 | ### Claude Code 371 | 372 | Serena is a great way to make Claude Code both cheaper and more powerful! 373 | 374 | From your project directory, add serena with a command like this, 375 | 376 | ```shell 377 | claude mcp add serena -- <serena-mcp-server> --context ide-assistant --project $(pwd) 378 | ``` 379 | 380 | where `<serena-mcp-server>` is your way of [running the Serena MCP server](#running-the-serena-mcp-server). 381 | For example, when using `uvx`, you would run 382 | 383 | ```shell 384 | claude mcp add serena -- uvx --from git+https://github.com/oraios/serena serena start-mcp-server --context ide-assistant --project $(pwd) 385 | ``` 386 | 387 | ℹ️ Serena comes with an instruction text, and Claude needs to read it to properly use Serena's tools. 388 | As of version `v1.0.52`, claude code reads the instructions of the MCP server, so this **is handled automatically**. 389 | If you are using an older version, or if Claude fails to read the instructions, you can ask it explicitly 390 | to "read Serena's initial instructions" or run `/mcp__serena__initial_instructions` to load the instruction text. 391 | If you want to make use of that, you will have to enable the corresponding tool explicitly by adding `initial_instructions` to the `included_optional_tools` 392 | in your config. 393 | Note that you may have to make Claude read the instructions when you start a new conversation and after any compacting operation to ensure Claude remains properly configured to use Serena's tools. 394 | 395 | ### Codex 396 | 397 | Serena works with OpenAI's Codex CLI out of the box, but you have to use the `codex` context for it to work properly. (The technical reason is that Codex doesn't fully support the MCP specifications, so some massaging of tools is required.). 398 | 399 | Unlike Claude Code, in Codex you add an MCP server globally and not per project. Add the following to 400 | `~/.codex/config.toml` (create the file if it does not exist): 401 | 402 | ```toml 403 | [mcp_servers.serena] 404 | command = "uvx" 405 | args = ["--from", "git+https://github.com/oraios/serena", "serena", "start-mcp-server", "--context", "codex"] 406 | ``` 407 | 408 | After codex has started, you need to activate the project, which you can do by saying: 409 | 410 | "Activate the current dir as project using serena" 411 | 412 | > If you don't activate the project, you will not be able to use Serena's tools! 413 | 414 | That's it! Have a look at `~/.codex/log/codex-tui.log` to see if any errors occurred. 415 | 416 | The Serena dashboard will run if you have not disabled it in the configuration, but due to Codex's sandboxing the webbrowser 417 | may not open automatically. You can open it manually by going to `http://localhost:24282/dashboard/index.html` (or a higher port, if 418 | that was already taken). 419 | 420 | > Codex will often show the tools as `failed` even though they are successfully executed. This is not a problem, seems to be a bug in Codex. Despite the error message, everything works as expected. 421 | 422 | ### Other Terminal-Based Clients 423 | 424 | There are many terminal-based coding assistants that support MCP servers, such as [Codex](https://github.com/openai/codex?tab=readme-ov-file#model-context-protocol-mcp), 425 | [Gemini-CLI](https://github.com/google-gemini/gemini-cli), [Qwen3-Coder](https://github.com/QwenLM/Qwen3-Coder), 426 | [rovodev](https://community.atlassian.com/forums/Rovo-for-Software-Teams-Beta/Introducing-Rovo-Dev-CLI-AI-Powered-Development-in-your-terminal/ba-p/3043623), 427 | the [OpenHands CLI](https://docs.all-hands.dev/usage/how-to/cli-mode) and [opencode](https://github.com/sst/opencode). 428 | 429 | They generally benefit from the symbolic tools provided by Serena. You might want to customize some aspects of Serena 430 | by writing your own context, modes or prompts to adjust it to your workflow, to other MCP servers you are using, and to 431 | the client's internal capabilities. 432 | 433 | ### Claude Desktop 434 | 435 | For [Claude Desktop](https://claude.ai/download) (available for Windows and macOS), go to File / Settings / Developer / MCP Servers / Edit Config, 436 | which will let you open the JSON file `claude_desktop_config.json`. 437 | Add the `serena` MCP server configuration, using a [run command](#running-the-serena-mcp-server) depending on your setup. 438 | 439 | * local installation: 440 | 441 | ```json 442 | { 443 | "mcpServers": { 444 | "serena": { 445 | "command": "/abs/path/to/uv", 446 | "args": ["run", "--directory", "/abs/path/to/serena", "serena", "start-mcp-server"] 447 | } 448 | } 449 | } 450 | ``` 451 | 452 | * uvx: 453 | 454 | ```json 455 | { 456 | "mcpServers": { 457 | "serena": { 458 | "command": "/abs/path/to/uvx", 459 | "args": ["--from", "git+https://github.com/oraios/serena", "serena", "start-mcp-server"] 460 | } 461 | } 462 | } 463 | ``` 464 | 465 | * docker: 466 | 467 | ```json 468 | { 469 | "mcpServers": { 470 | "serena": { 471 | "command": "docker", 472 | "args": ["run", "--rm", "-i", "--network", "host", "-v", "/path/to/your/projects:/workspaces/projects", "ghcr.io/oraios/serena:latest", "serena", "start-mcp-server", "--transport", "stdio"] 473 | } 474 | } 475 | } 476 | ``` 477 | 478 | If you are using paths containing backslashes for paths on Windows 479 | (note that you can also just use forward slashes), be sure to escape them correctly (`\\`). 480 | 481 | That's it! Save the config and then restart Claude Desktop. You are ready for activating your first project. 482 | 483 | ℹ️ You can further customize the run command using additional arguments (see [above](#command-line-arguments)). 484 | 485 | Note: on Windows and macOS there are official Claude Desktop applications by Anthropic, for Linux there is an [open-source 486 | community version](https://github.com/aaddrick/claude-desktop-debian). 487 | 488 | ⚠️ Be sure to fully quit the Claude Desktop application, as closing Claude will just minimize it to the system tray – at least on Windows. 489 | 490 | ⚠️ Some clients may leave behind zombie processes. You will have to find and terminate them manually then. 491 | With Serena, you can activate the [dashboard](#serenas-logs-the-dashboard-and-gui-tool) to prevent unnoted processes and also use the dashboard 492 | for shutting down Serena. 493 | 494 | After restarting, you should see Serena's tools in your chat interface (notice the small hammer icon). 495 | 496 | For more information on MCP servers with Claude Desktop, see [the official quick start guide](https://modelcontextprotocol.io/quickstart/user). 497 | 498 | ### MCP Coding Clients (Cline, Roo-Code, Cursor, Windsurf, etc.) 499 | 500 | Being an MCP Server, Serena can be included in any MCP Client. The same configuration as above, 501 | perhaps with small client-specific modifications, should work. Most of the popular 502 | existing coding assistants (IDE extensions or VSCode-like IDEs) support connections 503 | to MCP Servers. It is **recommended to use the `ide-assistant` context** for these integrations by adding `"--context", "ide-assistant"` to the `args` in your MCP client's configuration. Including Serena generally boosts their performance 504 | by providing them tools for symbolic operations. 505 | 506 | In this case, the billing for the usage continues to be controlled by the client of your choice 507 | (unlike with the Claude Desktop client). But you may still want to use Serena through such an approach, 508 | e.g., for one of the following reasons: 509 | 510 | 1. You are already using a coding assistant (say Cline or Cursor) and just want to make it more powerful. 511 | 2. You are on Linux and don't want to use the [community-created Claude Desktop](https://github.com/aaddrick/claude-desktop-debian). 512 | 3. You want tighter integration of Serena into your IDE and don't mind paying for that. 513 | 514 | ### Local GUIs and Frameworks 515 | 516 | Over the last months, several technologies have emerged that allow you to run a powerful local GUI 517 | and connect it to an MCP server. They will work with Serena out of the box. 518 | Some of the leading open source GUI technologies offering this are 519 | [Jan](https://jan.ai/docs/mcp), [OpenHands](https://github.com/All-Hands-AI/OpenHands/), 520 | [OpenWebUI](https://docs.openwebui.com/openapi-servers/mcp) and [Agno](https://docs.agno.com/introduction/playground). 521 | They allow combining Serena with almost any LLM (including locally running ones) and offer various other integrations. 522 | 523 | ## Detailed Usage and Recommendations 524 | 525 | ### Tool Execution 526 | 527 | Serena combines tools for semantic code retrieval with editing capabilities and shell execution. 528 | Serena's behavior can be further customized through [Modes and Contexts](#modes-and-contexts). 529 | Find the complete list of tools [below](#full-list-of-tools). 530 | 531 | The use of all tools is generally recommended, as this allows Serena to provide the most value: 532 | Only by executing shell commands (in particular, tests) can Serena identify and correct mistakes 533 | autonomously. 534 | 535 | #### Shell Execution and Editing Tools 536 | 537 | Many clients have their own shell execution tool, and by default Serena's shell tool will be disabled in them 538 | (e.g., when using the `ide-assistant` or `codex` context). However, when using Serena through something like 539 | Claude Desktop or ChatGPT, it is recommended to enable Serena's `execute_shell_command` tool to allow 540 | agentic behavior. 541 | 542 | It should be noted that the `execute_shell_command` tool allows for arbitrary code execution. 543 | When using Serena as an MCP Server, clients will typically ask the user for permission 544 | before executing a tool, so as long as the user inspects execution parameters beforehand, 545 | this should not be a problem. 546 | However, if you have concerns, you can choose to disable certain commands in your project's configuration file. 547 | If you only want to use Serena purely for analyzing code and suggesting implementations 548 | without modifying the codebase, you can enable read-only mode by setting `read_only: true` in your project configuration file. 549 | This will automatically disable all editing tools and prevent any modifications to your codebase while still 550 | allowing all analysis and exploration capabilities. 551 | 552 | In general, be sure to back up your work and use a version control system in order to avoid 553 | losing any work. 554 | 555 | ### Modes and Contexts 556 | 557 | Serena's behavior and toolset can be adjusted using contexts and modes. 558 | These allow for a high degree of customization to best suit your workflow and the environment Serena is operating in. 559 | 560 | #### Contexts 561 | 562 | A context defines the general environment in which Serena is operating. 563 | It influences the initial system prompt and the set of available tools. 564 | 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. 565 | 566 | Serena comes with pre-defined contexts: 567 | 568 | * `desktop-app`: Tailored for use with desktop applications like Claude Desktop. This is the default. 569 | * `agent`: Designed for scenarios where Serena acts as a more autonomous agent, for example, when used with Agno. 570 | * `ide-assistant`: Optimized for integration into IDEs like VSCode, Cursor, or Cline, focusing on in-editor coding assistance. 571 | Choose the context that best matches the type of integration you are using. 572 | 573 | When launching Serena, specify the context using `--context <context-name>`. 574 | Note that for cases where parameter lists are specified (e.g. Claude Desktop), you must add two parameters to the list. 575 | 576 | 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`. 577 | 578 | #### Modes 579 | 580 | 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. 581 | 582 | Examples of built-in modes include: 583 | 584 | * `planning`: Focuses Serena on planning and analysis tasks. 585 | * `editing`: Optimizes Serena for direct code modification tasks. 586 | * `interactive`: Suitable for a conversational, back-and-forth interaction style. 587 | * `one-shot`: Configures Serena for tasks that should be completed in a single response, often used with `planning` for generating reports or initial plans. 588 | * `no-onboarding`: Skips the initial onboarding process if it's not needed for a particular session. 589 | * `onboarding`: (Usually triggered automatically) Focuses on the project onboarding process. 590 | 591 | Modes can be set at startup (similar to contexts) but can also be _switched dynamically_ during a session. You can instruct the LLM to use the `switch_modes` tool to activate a different set of modes (e.g., "switch to planning and one-shot modes"). 592 | 593 | When launching Serena, specify modes using `--mode <mode-name>`; multiple modes can be specified, e.g. `--mode planning --mode no-onboarding`. 594 | 595 | :warning: **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. 596 | 597 | #### Customization 598 | 599 | You can create your own contexts and modes to precisely tailor Serena to your needs in two ways: 600 | 601 | * You can use Serena's CLI to manage modes and contexts. Check out 602 | 603 | ```shell 604 | uvx --from git+https://github.com/oraios/serena serena mode --help 605 | ``` 606 | 607 | and 608 | 609 | ```shell 610 | uvx --from git+https://github.com/oraios/serena serena context --help 611 | ``` 612 | 613 | _NOTE_: Custom contexts/modes are simply YAML files in `<home>/.serena`, they are automatically registered and available for use by their name (filename without the `.yml` extension). If you don't want to use Serena's CLI, you can create and manage them in any way you see fit. 614 | * **Using external YAML files**: When starting Serena, you can also provide an absolute path to a custom `.yml` file for a context or mode. 615 | 616 | This customization allows for deep integration and adaptation of Serena to specific project requirements or personal preferences. 617 | 618 | ### Onboarding and Memories 619 | 620 | By default, Serena will perform an **onboarding process** when 621 | it is started for the first time for a project. 622 | The goal of the onboarding is for Serena to get familiar with the project 623 | and to store memories, which it can then draw upon in future interactions. 624 | If an LLM should fail to complete the onboarding and does not actually write the 625 | respective memories to disk, you may need to ask it to do so explicitly. 626 | 627 | The onboarding will usually read a lot of content from the project, thus filling 628 | up the context. It can therefore be advisable to switch to another conversation 629 | once the onboarding is complete. 630 | After the onboarding, we recommend that you have a quick look at the memories and, 631 | if necessary, edit them or add additional ones. 632 | 633 | **Memories** are files stored in `.serena/memories/` in the project directory, 634 | which the agent can choose to read in subsequent interactions. 635 | Feel free to read and adjust them as needed; you can also add new ones manually. 636 | Every file in the `.serena/memories/` directory is a memory file. 637 | Whenever Serena starts working on a project, the list of memories is 638 | provided, and the agent can decide to read them. 639 | We found that memories can significantly improve the user experience with Serena. 640 | 641 | ### Prepare Your Project 642 | 643 | #### Structure Your Codebase 644 | 645 | Serena uses the code structure for finding, reading and editing code. This means that it will 646 | work well with well-structured code but may perform poorly on fully unstructured one (like a "God class" 647 | with enormous, non-modular functions). 648 | Furthermore, for languages that are not statically typed, type annotations are highly beneficial. 649 | 650 | #### Start from a Clean State 651 | 652 | It is best to start a code generation task from a clean git state. Not only will 653 | this make it easier for you to inspect the changes, but also the model itself will 654 | have a chance of seeing what it has changed by calling `git diff` and thereby 655 | correct itself or continue working in a followup conversation if needed. 656 | 657 | :warning: **Important**: since Serena will write to files using the system-native line endings 658 | and it might want to look at the git diff, it is important to 659 | set `git config core.autocrlf` to `true` on Windows. 660 | With `git config core.autocrlf` set to `false` on Windows, you may end up with huge diffs 661 | only due to line endings. It is generally a good idea to globally enable this git setting on Windows: 662 | 663 | ```shell 664 | git config --global core.autocrlf true 665 | ``` 666 | 667 | #### Logging, Linting, and Automated Tests 668 | 669 | Serena can successfully complete tasks in an _agent loop_, where it iteratively 670 | acquires information, performs actions, and reflects on the results. 671 | However, Serena cannot use a debugger; it must rely on the results of program executions, 672 | linting results, and test results to assess the correctness of its actions. 673 | Therefore, software that is designed to meaningful interpretable outputs (e.g. log messages) 674 | and that has a good test coverage is much easier to work with for Serena. 675 | 676 | We generally recommend to start an editing task from a state where all linting checks and tests pass. 677 | 678 | ### Prompting Strategies 679 | 680 | We found that it is often a good idea to spend some time conceptualizing and planning a task 681 | before actually implementing it, especially for non-trivial task. This helps both in achieving 682 | better results and in increasing the feeling of control and staying in the loop. You can 683 | make a detailed plan in one session, where Serena may read a lot of your code to build up the context, 684 | and then continue with the implementation in another (potentially after creating suitable memories). 685 | 686 | ### Running Out of Context 687 | 688 | For long and complicated tasks, or tasks where Serena has read a lot of content, you 689 | may come close to the limits of context tokens. In that case, it is often a good idea to continue 690 | in a new conversation. Serena has a dedicated tool to create a summary of the current state 691 | of the progress and all relevant info for continuing it. You can request to create this summary and 692 | write it to a memory. Then, in a new conversation, you can just ask Serena to read the memory and 693 | continue with the task. In our experience, this worked really well. On the up-side, since in a 694 | single session there is no summarization involved, Serena does not usually get lost (unlike some 695 | other agents that summarize under the hood), and it is also instructed to occasionally check whether 696 | it's on the right track. 697 | 698 | Moreover, Serena is instructed to be frugal with context 699 | (e.g., to not read bodies of code symbols unnecessarily), 700 | but we found that Claude is not always very good in being frugal (Gemini seemed better at it). 701 | You can explicitly instruct it to not read the bodies if you know that it's not needed. 702 | 703 | ### Serena's Logs: The Dashboard and GUI Tool 704 | 705 | Serena provides two convenient ways of accessing the logs of the current session: 706 | 707 | * via the **web-based dashboard** (enabled by default) 708 | 709 | This is supported on all platforms. 710 | By default, it will be accessible at `http://localhost:24282/dashboard/index.html`, 711 | but a higher port may be used if the default port is unavailable/multiple instances are running. 712 | 713 | * via the **GUI tool** (disabled by default) 714 | 715 | This is mainly supported on Windows, but it may also work on Linux; macOS is unsupported. 716 | 717 | Both can be enabled, configured or disabled in Serena's configuration file (`serena_config.yml`, see above). 718 | If enabled, they will automatically be opened as soon as the Serena agent/MCP server is started. 719 | The web dashboard will display usage statistics of Serena's tools if you set `record_tool_usage_stats: True` in your config. 720 | 721 | In addition to viewing logs, both tools allow to shut down the Serena agent. 722 | This function is provided, because clients like Claude Desktop may fail to terminate the MCP server subprocess 723 | when they themselves are closed. 724 | 725 | ## Comparison with Other Coding Agents 726 | 727 | To our knowledge, Serena is the first fully-featured coding agent where the 728 | entire functionality 729 | is available through an MCP server, thus not requiring API keys or 730 | subscriptions. 731 | 732 | ### Subscription-Based Coding Agents 733 | 734 | Many prominent subscription-based coding agents are parts of IDEs like 735 | Windsurf, Cursor and VSCode. 736 | Serena's functionality is similar to Cursor's Agent, Windsurf's Cascade or 737 | VSCode's agent mode. 738 | 739 | Serena has the advantage of not requiring a subscription. 740 | A potential disadvantage is that it 741 | is not directly integrated into an IDE, so the inspection of newly written code 742 | is not as seamless. 743 | 744 | More technical differences are: 745 | 746 | * Serena is not bound to a specific IDE or CLI. 747 | Serena's MCP server can be used with any MCP client (including some IDEs), 748 | and the Agno-based agent provides additional ways of applying its functionality. 749 | * Serena is not bound to a specific large language model or API. 750 | * Serena navigates and edits code using a language server, so it has a symbolic 751 | understanding of the code. 752 | IDE-based tools often use a RAG-based or purely text-based approach, which is often 753 | less powerful, especially for large codebases. 754 | * Serena is open-source and has a small codebase, so it can be easily extended 755 | and modified. 756 | 757 | ### API-Based Coding Agents 758 | 759 | An alternative to subscription-based agents are API-based agents like Claude 760 | Code, Cline, Aider, Roo Code and others, where the usage costs map directly 761 | to the API costs of the underlying LLM. 762 | Some of them (like Cline) can even be included in IDEs as an extension. 763 | They are often very powerful and their main downside are the (potentially very 764 | high) API costs. 765 | 766 | Serena itself can be used as an API-based agent (see the section on Agno above). 767 | We have not yet written a CLI tool or a 768 | dedicated IDE extension for Serena (and there is probably no need for the latter, as 769 | Serena can already be used with any IDE that supports MCP servers). 770 | If there is demand for a Serena as a CLI tool like Claude Code, we will 771 | consider writing one. 772 | 773 | The main difference between Serena and other API-based agents is that Serena can 774 | also be used as an MCP server, thus not requiring 775 | an API key and bypassing the API costs. This is a unique feature of Serena. 776 | 777 | ### Other MCP-Based Coding Agents 778 | 779 | There are other MCP servers designed for coding, like [DesktopCommander](https://github.com/wonderwhy-er/DesktopCommanderMCP) and 780 | [codemcp](https://github.com/ezyang/codemcp). 781 | However, to the best of our knowledge, none of them provide semantic code 782 | retrieval and editing tools; they rely purely on text-based analysis. 783 | It is the integration of language servers and the MCP that makes Serena unique 784 | and so powerful for challenging coding tasks, especially in the context of 785 | larger codebases. 786 | 787 | ## Acknowledgements 788 | 789 | ### Sponsors 790 | 791 | We are very grateful to our [sponsors](https://github.com/sponsors/oraios) who help us drive Serena's development. The core team 792 | (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. 793 | So far, there is no business model behind this project, and sponsors are our only source of income from it. 794 | 795 | Sponsors help us dedicating more time to the project, managing contributions, and working on larger features (like better tooling based on more advanced 796 | LSP features, VSCode integration, debugging via the DAP, and several others). 797 | If you find this project useful to your work, or would like to accelerate the development of Serena, consider becoming a sponsor. 798 | 799 | We are proud to announce that the Visual Studio Code team, together with Microsoft’s Open Source Programs Office and GitHub Open Source 800 | have decided to sponsor Serena with a one-time contribution! 801 | 802 | <p align="center"> 803 | <img src="resources/vscode_sponsor_logo.png" alt="Visual Studio Code sponsor logo" width="220"> 804 | </p> 805 | 806 | ### Community Contributions 807 | 808 | A significant part of Serena, especially support for various languages, was contributed by the open source community. 809 | We are very grateful for the many contributors who made this possible and who played an important role in making Serena 810 | what it is today. 811 | 812 | ### Technologies 813 | We built Serena on top of multiple existing open-source technologies, the most important ones being: 814 | 815 | 1. [multilspy](https://github.com/microsoft/multilspy). 816 | A library which wraps language server implementations and adapts them for interaction via Python 817 | and which provided the basis for our library Solid-LSP (src/solidlsp). 818 | Solid-LSP provides pure synchronous LSP calls and extends the original library with the symbolic logic 819 | that Serena required. 820 | 2. [Python MCP SDK](https://github.com/modelcontextprotocol/python-sdk) 821 | 3. [Agno](https://github.com/agno-agi/agno) and 822 | the associated [agent-ui](https://github.com/agno-agi/agent-ui), 823 | which we use to allow Serena to work with any model, beyond the ones 824 | supporting the MCP. 825 | 4. All the language servers that we use through Solid-LSP. 826 | 827 | Without these projects, Serena would not have been possible (or would have been significantly more difficult to build). 828 | 829 | ## Customizing and Extending Serena 830 | 831 | It is straightforward to extend Serena's AI functionality with your own ideas. 832 | Simply implement a new tool by subclassing 833 | `serena.agent.Tool` and implement the `apply` method with a signature 834 | that matches the tool's requirements. 835 | Once implemented, `SerenaAgent` will automatically have access to the new tool. 836 | 837 | It is also relatively straightforward to add [support for a new programming language](/.serena/memories/adding_new_language_support_guide.md). 838 | 839 | We look forward to seeing what the community will come up with! 840 | For details on contributing, see [contributing guidelines](/CONTRIBUTING.md). 841 | 842 | ## List of Tools 843 | 844 | Here is the list of Serena's default tools with a short description (output of `uv run serena tools list`): 845 | 846 | * `activate_project`: Activates a project by name. 847 | * `check_onboarding_performed`: Checks whether project onboarding was already performed. 848 | * `create_text_file`: Creates/overwrites a file in the project directory. 849 | * `delete_memory`: Deletes a memory from Serena's project-specific memory store. 850 | * `execute_shell_command`: Executes a shell command. 851 | * `find_file`: Finds files in the given relative paths 852 | * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). 853 | * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). 854 | * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. 855 | * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. 856 | * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. 857 | * `list_dir`: Lists files and directories in the given directory (optionally with recursion). 858 | * `list_memories`: Lists memories in Serena's project-specific memory store. 859 | * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). 860 | * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). 861 | * `read_file`: Reads a file within the project directory. 862 | * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. 863 | * `replace_regex`: Replaces content in a file by using regular expressions. 864 | * `replace_symbol_body`: Replaces the full definition of a symbol. 865 | * `search_for_pattern`: Performs a search for a pattern in the project. 866 | * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. 867 | * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. 868 | * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. 869 | * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. 870 | 871 | There are several tools that are disabled by default, and have to be enabled explicitly, e.g., through the context or modes. 872 | Note that several of our default contexts do enable some of these tools. For example, the `desktop-app` context enables the `execute_shell_command` tool. 873 | 874 | The full list of optional tools is (output of `uv run serena tools list --only-optional`): 875 | 876 | * `delete_lines`: Deletes a range of lines within a file. 877 | * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. 878 | * `initial_instructions`: Gets the initial instructions for the current project. 879 | Should only be used in settings where the system prompt cannot be set, 880 | e.g. in clients you have no control over, like Claude Desktop. 881 | * `insert_at_line`: Inserts content at a given line in a file. 882 | * `jet_brains_find_referencing_symbols`: Finds symbols that reference the given symbol 883 | * `jet_brains_find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). 884 | * `jet_brains_get_symbols_overview`: Retrieves an overview of the top-level symbols within a specified file 885 | * `remove_project`: Removes a project from the Serena configuration. 886 | * `replace_lines`: Replaces a range of lines within a file with new content. 887 | * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. 888 | * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. 889 | * `switch_modes`: Activates modes by providing a list of their names 890 | ``` -------------------------------------------------------------------------------- /test/resources/repos/markdown/test_repo/CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown 1 | # Contributing Guidelines 2 | 3 | Thank you for considering contributing to this project! 4 | 5 | ## Table of Contents 6 | 7 | - [Code of Conduct](#code-of-conduct) 8 | - [Getting Started](#getting-started) 9 | - [Development Setup](#development-setup) 10 | - [Submitting Changes](#submitting-changes) 11 | 12 | ## Code of Conduct 13 | 14 | Please be respectful and considerate in all interactions. 15 | 16 | ## Getting Started 17 | 18 | To contribute: 19 | 20 | 1. Fork the repository 21 | 2. Create a feature branch 22 | 3. Make your changes 23 | 4. Submit a pull request 24 | 25 | ## Development Setup 26 | 27 | ### Prerequisites 28 | 29 | - Git 30 | - Node.js (v16+) 31 | - npm or yarn 32 | 33 | ### Installation Steps 34 | 35 | ```bash 36 | git clone https://github.com/example/repo.git 37 | cd repo 38 | npm install 39 | ``` 40 | 41 | ## Submitting Changes 42 | 43 | ### Pull Request Process 44 | 45 | 1. Update documentation 46 | 2. Add tests for new features 47 | 3. Ensure all tests pass 48 | 4. Update the [README](README.md) 49 | 50 | ### Commit Messages 51 | 52 | Use clear and descriptive commit messages: 53 | 54 | - feat: Add new feature 55 | - fix: Bug fix 56 | - docs: Documentation changes 57 | - test: Add or update tests 58 | 59 | ## Testing 60 | 61 | Run the test suite before submitting: 62 | 63 | ```bash 64 | npm test 65 | ``` 66 | 67 | For more information, see: 68 | 69 | - [User Guide](guide.md) 70 | - [API Reference](api.md) 71 | 72 | ## Questions? 73 | 74 | Contact the maintainers or open an issue. 75 | ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown 1 | # Contributing to Serena 2 | 3 | Serena is under active development. We are just discovering what it can do and where the limitations lie. 4 | 5 | Feel free to share your learnings by opening new issues, feature requests and extensions. 6 | 7 | ## Developer Environment Setup 8 | 9 | You can have a local setup via `uv` or a docker interpreter-based setup. 10 | The repository is also configured to seamlessly work within a GitHub Codespace. See the instructions 11 | for the various setup scenarios below. 12 | 13 | Independently of how the setup was done, the virtual environment can be 14 | created and activated via `uv` (see below), and the various tasks like formatting, testing, and documentation building 15 | can be executed using `poe`. For example, `poe format` will format the code, including the 16 | notebooks. Just run `poe` to see the available commands. 17 | 18 | ### Python (uv) setup 19 | 20 | You can install a virtual environment with the required as follows 21 | 22 | 1. Create a new virtual environment: `uv venv` 23 | 2. Activate the environment: 24 | * On Linux/Unix/macOS or Windows with Git Bash: `source .venv/bin/activate` 25 | * On Windows outside of Git Bash: `.venv\Scripts\activate.bat` (in cmd/ps) or `source .venv/Scripts/activate` (in git-bash) 26 | 3. Install the required packages with all extras: `uv pip install --all-extras -r pyproject.toml -e .` 27 | 28 | ## Running Tools Locally 29 | 30 | The Serena tools (and in fact all Serena code) can be executed without an LLM, and also without 31 | any MCP specifics (though you can use the mcp inspector, if you want). 32 | 33 | An example script for running tools is provided in [scripts/demo_run_tools.py](scripts/demo_run_tools.py). 34 | 35 | ## Adding a New Supported Language 36 | 37 | See the corresponding [memory](.serena/memories/adding_new_language_support_guide.md). ``` -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- ```markdown 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Development Commands 6 | 7 | **Essential Commands (use these exact commands):** 8 | - `uv run poe format` - Format code (BLACK + RUFF) - ONLY allowed formatting command 9 | - `uv run poe type-check` - Run mypy type checking - ONLY allowed type checking command 10 | - `uv run poe test` - Run tests with default markers (excludes java/rust by default) 11 | - `uv run poe test -m "python or go"` - Run specific language tests 12 | - `uv run poe lint` - Check code style without fixing 13 | 14 | **Test Markers:** 15 | Available pytest markers for selective testing: 16 | - `python`, `go`, `java`, `rust`, `typescript`, `php`, `perl`, `csharp`, `elixir`, `terraform`, `clojure`, `swift`, `bash`, `ruby`, `ruby_solargraph` 17 | - `snapshot` - for symbolic editing operation tests 18 | 19 | **Project Management:** 20 | - `uv run serena-mcp-server` - Start MCP server from project root 21 | - `uv run index-project` - Index project for faster tool performance 22 | 23 | **Always run format, type-check, and test before completing any task.** 24 | 25 | ## Architecture Overview 26 | 27 | Serena is a dual-layer coding agent toolkit: 28 | 29 | ### Core Components 30 | 31 | **1. SerenaAgent (`src/serena/agent.py`)** 32 | - Central orchestrator managing projects, tools, and user interactions 33 | - Coordinates language servers, memory persistence, and MCP server interface 34 | - Manages tool registry and context/mode configurations 35 | 36 | **2. SolidLanguageServer (`src/solidlsp/ls.py`)** 37 | - Unified wrapper around Language Server Protocol (LSP) implementations 38 | - Provides language-agnostic interface for symbol operations 39 | - Handles caching, error recovery, and multiple language server lifecycle 40 | 41 | **3. Tool System (`src/serena/tools/`)** 42 | - **file_tools.py** - File system operations, search, regex replacements 43 | - **symbol_tools.py** - Language-aware symbol finding, navigation, editing 44 | - **memory_tools.py** - Project knowledge persistence and retrieval 45 | - **config_tools.py** - Project activation, mode switching 46 | - **workflow_tools.py** - Onboarding and meta-operations 47 | 48 | **4. Configuration System (`src/serena/config/`)** 49 | - **Contexts** - Define tool sets for different environments (desktop-app, agent, ide-assistant) 50 | - **Modes** - Operational patterns (planning, editing, interactive, one-shot) 51 | - **Projects** - Per-project settings and language server configs 52 | 53 | ### Language Support Architecture 54 | 55 | Each supported language has: 56 | 1. **Language Server Implementation** in `src/solidlsp/language_servers/` 57 | 2. **Runtime Dependencies** - Automatic language server downloads when needed 58 | 3. **Test Repository** in `test/resources/repos/<language>/` 59 | 4. **Test Suite** in `test/solidlsp/<language>/` 60 | 61 | ### Memory & Knowledge System 62 | 63 | - **Markdown-based storage** in `.serena/memories/` directories 64 | - **Project-specific knowledge** persistence across sessions 65 | - **Contextual retrieval** based on relevance 66 | - **Onboarding support** for new projects 67 | 68 | ## Development Patterns 69 | 70 | ### Adding New Languages 71 | 1. Create language server class in `src/solidlsp/language_servers/` 72 | 2. Add to Language enum in `src/solidlsp/ls_config.py` 73 | 3. Update factory method in `src/solidlsp/ls.py` 74 | 4. Create test repository in `test/resources/repos/<language>/` 75 | 5. Write test suite in `test/solidlsp/<language>/` 76 | 6. Add pytest marker to `pyproject.toml` 77 | 78 | ### Adding New Tools 79 | 1. Inherit from `Tool` base class in `src/serena/tools/tools_base.py` 80 | 2. Implement required methods and parameter validation 81 | 3. Register in appropriate tool registry 82 | 4. Add to context/mode configurations 83 | 84 | ### Testing Strategy 85 | - Language-specific tests use pytest markers 86 | - Symbolic editing operations have snapshot tests 87 | - Integration tests in `test_serena_agent.py` 88 | - Test repositories provide realistic symbol structures 89 | 90 | ## Configuration Hierarchy 91 | 92 | Configuration is loaded from (in order of precedence): 93 | 1. Command-line arguments to `serena-mcp-server` 94 | 2. Project-specific `.serena/project.yml` 95 | 3. User config `~/.serena/serena_config.yml` 96 | 4. Active modes and contexts 97 | 98 | ## Key Implementation Notes 99 | 100 | - **Symbol-based editing** - Uses LSP for precise code manipulation 101 | - **Caching strategy** - Reduces language server overhead 102 | - **Error recovery** - Automatic language server restart on crashes 103 | - **Multi-language support** - 16+ languages with LSP integration 104 | - **MCP protocol** - Exposes tools to AI agents via Model Context Protocol 105 | - **Async operation** - Non-blocking language server interactions 106 | 107 | ## Working with the Codebase 108 | 109 | - Project uses Python 3.11 with `uv` for dependency management 110 | - Strict typing with mypy, formatted with black + ruff 111 | - Language servers run as separate processes with LSP communication 112 | - Memory system enables persistent project knowledge 113 | - Context/mode system allows workflow customization ``` -------------------------------------------------------------------------------- /src/interprompt/util/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /src/serena/config/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/test_repo/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /test/solidlsp/bash/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /test/solidlsp/dart/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /src/solidlsp/language_servers/elixir_tools/__init__.py: -------------------------------------------------------------------------------- ```python 1 | 2 | ``` -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- ```python 1 | 2 | ``` -------------------------------------------------------------------------------- /test/serena/__init__.py: -------------------------------------------------------------------------------- ```python 1 | 2 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- ```yaml 1 | blank_issues_enabled: false ``` -------------------------------------------------------------------------------- /test/solidlsp/r/__init__.py: -------------------------------------------------------------------------------- ```python 1 | # Empty init file for R tests 2 | ``` -------------------------------------------------------------------------------- /test/serena/config/__init__.py: -------------------------------------------------------------------------------- ```python 1 | # Empty init file for test package 2 | ``` -------------------------------------------------------------------------------- /test/resources/repos/php/test_repo/simple_var.php: -------------------------------------------------------------------------------- ```php 1 | <?php 2 | $localVar = "test"; 3 | echo $localVar; 4 | ?> ``` -------------------------------------------------------------------------------- /src/solidlsp/__init__.py: -------------------------------------------------------------------------------- ```python 1 | # ruff: noqa 2 | from .ls import SolidLanguageServer 3 | ``` -------------------------------------------------------------------------------- /test/resources/repos/kotlin/test_repo/src/main/kotlin/test_repo/Model.kt: -------------------------------------------------------------------------------- ```kotlin 1 | package test_repo 2 | 3 | data class Model(val name: String) ``` -------------------------------------------------------------------------------- /test/solidlsp/markdown/__init__.py: -------------------------------------------------------------------------------- ```python 1 | """Tests for markdown language server functionality.""" 2 | ``` -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- ```yaml 1 | # These are supported funding model platforms 2 | 3 | github: oraios 4 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/custom_test/__init__.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | Custom test package for testing code parsing capabilities. 3 | """ 4 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/examples/__init__.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | Examples package for demonstrating test_repo module usage. 3 | """ 4 | ``` -------------------------------------------------------------------------------- /test/resources/repos/nix/test_repo/scripts/hello.sh: -------------------------------------------------------------------------------- ```bash 1 | #!/usr/bin/env bash 2 | # Simple hello script for testing 3 | echo "Hello from Nix!" ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/scripts/__init__.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | Scripts package containing entry point scripts for the application. 3 | """ 4 | ``` -------------------------------------------------------------------------------- /test/resources/repos/rust/test_repo/Cargo.toml: -------------------------------------------------------------------------------- ```toml 1 | [package] 2 | name = "rsandbox" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ``` -------------------------------------------------------------------------------- /test/resources/repos/rust/test_repo_2024/Cargo.toml: -------------------------------------------------------------------------------- ```toml 1 | [package] 2 | name = "rsandbox_2024" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] ``` -------------------------------------------------------------------------------- /test/resources/repos/php/test_repo/helper.php: -------------------------------------------------------------------------------- ```php 1 | <?php 2 | 3 | function helperFunction(): void { 4 | echo "Helper function was called."; 5 | } 6 | 7 | ?> ``` -------------------------------------------------------------------------------- /test/resources/repos/ruby/test_repo/lib.rb: -------------------------------------------------------------------------------- ```ruby 1 | class Calculator 2 | def add(a, b) 3 | a + b 4 | end 5 | 6 | def subtract(a, b) 7 | a - b 8 | end 9 | end 10 | ``` -------------------------------------------------------------------------------- /docker_build_and_run.sh: -------------------------------------------------------------------------------- ```bash 1 | #!/usr/bin/bash 2 | 3 | docker build -t serena . 4 | 5 | docker run -it --rm -v "$(pwd)":/workspace serena 6 | ``` -------------------------------------------------------------------------------- /scripts/mcp_server.py: -------------------------------------------------------------------------------- ```python 1 | from serena.cli import start_mcp_server 2 | 3 | if __name__ == "__main__": 4 | start_mcp_server() 5 | ``` -------------------------------------------------------------------------------- /test/resources/repos/kotlin/test_repo/src/main/kotlin/test_repo/Utils.kt: -------------------------------------------------------------------------------- ```kotlin 1 | package test_repo 2 | 3 | object Utils { 4 | fun printHello() { 5 | println("Hello from Utils!") 6 | } 7 | } ``` -------------------------------------------------------------------------------- /scripts/print_tool_overview.py: -------------------------------------------------------------------------------- ```python 1 | from serena.agent import ToolRegistry 2 | 3 | if __name__ == "__main__": 4 | ToolRegistry().print_tool_overview() 5 | ``` -------------------------------------------------------------------------------- /test/resources/repos/typescript/test_repo/use_helper.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {helperFunction} from "./index"; 2 | 3 | 4 | export function useHelper() { 5 | helperFunction(); 6 | } 7 | 8 | useHelper(); 9 | ``` -------------------------------------------------------------------------------- /src/interprompt/__init__.py: -------------------------------------------------------------------------------- ```python 1 | from .prompt_factory import autogenerate_prompt_factory_module 2 | 3 | __all__ = ["autogenerate_prompt_factory_module"] 4 | ``` -------------------------------------------------------------------------------- /test/resources/repos/rust/test_repo/src/lib.rs: -------------------------------------------------------------------------------- ```rust 1 | // This function returns the sum of 2 + 2 2 | pub fn add() -> i32 { 3 | let res = 2 + 2; 4 | res 5 | } 6 | pub fn multiply() -> i32 { 7 | 2 * 3 8 | } 9 | 10 | ``` -------------------------------------------------------------------------------- /test/resources/repos/java/test_repo/src/main/java/test_repo/Utils.java: -------------------------------------------------------------------------------- ```java 1 | package test_repo; 2 | 3 | public class Utils { 4 | public static void printHello() { 5 | System.out.println("Hello from Utils!"); 6 | } 7 | } 8 | ``` -------------------------------------------------------------------------------- /test/resources/repos/kotlin/test_repo/build.gradle.kts: -------------------------------------------------------------------------------- ```kotlin 1 | plugins { 2 | kotlin("jvm") version "1.9.21" 3 | application 4 | } 5 | 6 | group = "test.serena" 7 | version = "1.0-SNAPSHOT" 8 | 9 | repositories { 10 | mavenCentral() 11 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/typescript/test_repo/.serena/project.yml: -------------------------------------------------------------------------------- ```yaml 1 | excluded_tools: [] 2 | ignore_all_files_in_gitignore: true 3 | ignored_paths: [] 4 | initial_prompt: '' 5 | language: typescript 6 | project_name: test_repo 7 | read_only: false 8 | ``` -------------------------------------------------------------------------------- /test/resources/repos/kotlin/test_repo/src/main/kotlin/test_repo/ModelUser.kt: -------------------------------------------------------------------------------- ```kotlin 1 | package test_repo 2 | 3 | object ModelUser { 4 | @JvmStatic 5 | fun main(args: Array<String>) { 6 | val model = Model("Cascade") 7 | println(model.name) 8 | } 9 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/rust/test_repo/src/main.rs: -------------------------------------------------------------------------------- ```rust 1 | use rsandbox::add; 2 | 3 | fn main() { 4 | println!("Hello, World!"); 5 | println!("Good morning!"); 6 | println!("add result: {}", add()); 7 | println!("inserted line"); 8 | } 9 | ``` -------------------------------------------------------------------------------- /test/resources/repos/java/test_repo/src/main/java/test_repo/ModelUser.java: -------------------------------------------------------------------------------- ```java 1 | package test_repo; 2 | 3 | public class ModelUser { 4 | public static void main(String[] args) { 5 | Model model = new Model("Cascade"); 6 | System.out.println(model.getName()); 7 | } 8 | } 9 | ``` -------------------------------------------------------------------------------- /test/resources/repos/dart/test_repo/pubspec.yaml: -------------------------------------------------------------------------------- ```yaml 1 | name: test_repo 2 | description: A test repository for Serena Dart language server testing 3 | version: 1.0.0 4 | 5 | environment: 6 | sdk: '>=3.0.0 <4.0.0' 7 | 8 | dependencies: 9 | 10 | dev_dependencies: 11 | lints: ^3.0.0 ``` -------------------------------------------------------------------------------- /test/resources/repos/java/test_repo/src/main/java/test_repo/Model.java: -------------------------------------------------------------------------------- ```java 1 | package test_repo; 2 | 3 | public class Model { 4 | private String name; 5 | 6 | public Model(String name) { 7 | this.name = name; 8 | } 9 | 10 | public String getName() { 11 | return name; 12 | } 13 | } 14 | ``` -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "cSpell.words": [ 3 | "agno", 4 | "asyncio", 5 | "genai", 6 | "getpid", 7 | "Gopls", 8 | "langsrv", 9 | "multilspy", 10 | "pixi", 11 | "sensai", 12 | "vibing" 13 | ], 14 | } 15 | ``` -------------------------------------------------------------------------------- /test/resources/repos/go/test_repo/main.go: -------------------------------------------------------------------------------- ```go 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello, Go!") 7 | Helper() 8 | } 9 | 10 | func Helper() { 11 | fmt.Println("Helper function called") 12 | } 13 | 14 | type DemoStruct struct { 15 | Field int 16 | } 17 | 18 | func UsingHelper() { 19 | Helper() 20 | } 21 | ``` -------------------------------------------------------------------------------- /test/resources/repos/typescript/test_repo/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "skipLibCheck": true 9 | }, 10 | "include": ["**/*.ts"] 11 | } 12 | ``` -------------------------------------------------------------------------------- /src/serena/tools/__init__.py: -------------------------------------------------------------------------------- ```python 1 | # ruff: noqa 2 | from .tools_base import * 3 | from .file_tools import * 4 | from .symbol_tools import * 5 | from .memory_tools import * 6 | from .cmd_tools import * 7 | from .config_tools import * 8 | from .workflow_tools import * 9 | from .jetbrains_tools import * 10 | ``` -------------------------------------------------------------------------------- /test/resources/repos/php/test_repo/index.php: -------------------------------------------------------------------------------- ```php 1 | <?php 2 | 3 | require_once 'helper.php'; 4 | 5 | function greet(string $name): string { 6 | return "Hello, " . $name . "!"; 7 | } 8 | 9 | $userName = "PHP User"; 10 | $greeting = greet($userName); 11 | 12 | echo $greeting; 13 | 14 | helperFunction(); 15 | 16 | function useHelperFunction() { 17 | helperFunction(); 18 | } 19 | 20 | ?> ``` -------------------------------------------------------------------------------- /test/resources/repos/typescript/test_repo/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | export class DemoClass { 2 | value: number; 3 | constructor(value: number) { 4 | this.value = value; 5 | } 6 | printValue() { 7 | console.log(this.value); 8 | } 9 | } 10 | 11 | export function helperFunction() { 12 | const demo = new DemoClass(42); 13 | demo.printValue(); 14 | } 15 | 16 | helperFunction(); 17 | ``` -------------------------------------------------------------------------------- /src/interprompt/util/class_decorators.py: -------------------------------------------------------------------------------- ```python 1 | from typing import Any 2 | 3 | 4 | def singleton(cls: type[Any]) -> Any: 5 | instance = None 6 | 7 | def get_instance(*args: Any, **kwargs: Any) -> Any: 8 | nonlocal instance 9 | if instance is None: 10 | instance = cls(*args, **kwargs) 11 | return instance 12 | 13 | return get_instance 14 | ``` -------------------------------------------------------------------------------- /test/resources/repos/rust/test_repo_2024/src/main.rs: -------------------------------------------------------------------------------- ```rust 1 | fn main() { 2 | println!("Hello, Rust 2024 edition!"); 3 | let result = add(2, 3); 4 | println!("2 + 3 = {}", result); 5 | } 6 | 7 | pub fn add(a: i32, b: i32) -> i32 { 8 | a + b 9 | } 10 | 11 | #[cfg(test)] 12 | mod tests { 13 | use super::*; 14 | 15 | #[test] 16 | fn test_add() { 17 | assert_eq!(add(2, 3), 5); 18 | } 19 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/kotlin/test_repo/src/main/kotlin/test_repo/Main.kt: -------------------------------------------------------------------------------- ```kotlin 1 | package test_repo 2 | 3 | object Main { 4 | @JvmStatic 5 | fun main(args: Array<String>) { 6 | Utils.printHello() 7 | val model = Model("Cascade") 8 | println(model.name) 9 | acceptModel(model) 10 | } 11 | 12 | fun acceptModel(m: Model?) { 13 | // Do nothing, just for LSP reference 14 | } 15 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/ruby/test_repo/main.rb: -------------------------------------------------------------------------------- ```ruby 1 | require './lib.rb' 2 | 3 | class DemoClass 4 | attr_accessor :value 5 | 6 | def initialize(value) 7 | @value = value 8 | end 9 | 10 | def print_value 11 | puts @value 12 | end 13 | end 14 | 15 | def helper_function(number = 42) 16 | demo = DemoClass.new(number) 17 | Calculator.new.add(demo.value, 10) 18 | demo.print_value 19 | end 20 | 21 | helper_function 22 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/test_repo/nested.py: -------------------------------------------------------------------------------- ```python 1 | class OuterClass: 2 | class NestedClass: 3 | def find_me(self): 4 | pass 5 | 6 | def nested_test(self): 7 | class WithinMethod: 8 | pass 9 | 10 | def func_within_func(): 11 | pass 12 | 13 | a = self.NestedClass() # noqa: F841 14 | 15 | 16 | b = OuterClass().NestedClass().find_me() 17 | ``` -------------------------------------------------------------------------------- /test/resources/repos/swift/test_repo/Package.swift: -------------------------------------------------------------------------------- ```swift 1 | // swift-tools-version: 5.9 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "test_repo", 6 | products: [ 7 | .library( 8 | name: "test_repo", 9 | targets: ["test_repo"]), 10 | ], 11 | targets: [ 12 | .target( 13 | name: "test_repo", 14 | dependencies: []), 15 | ] 16 | ) ``` -------------------------------------------------------------------------------- /src/serena/resources/config/contexts/agent.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: All tools except InitialInstructionsTool for agent context 2 | prompt: | 3 | You are running in agent context where the system prompt is provided externally. You should use symbolic 4 | tools when possible for code understanding and modification. 5 | excluded_tools: 6 | - initial_instructions 7 | 8 | tool_description_overrides: {} ``` -------------------------------------------------------------------------------- /test/resources/repos/rust/test_repo_2024/src/lib.rs: -------------------------------------------------------------------------------- ```rust 1 | pub fn multiply(a: i32, b: i32) -> i32 { 2 | a * b 3 | } 4 | 5 | pub struct Calculator { 6 | pub result: i32, 7 | } 8 | 9 | impl Calculator { 10 | pub fn new() -> Self { 11 | Calculator { result: 0 } 12 | } 13 | 14 | pub fn add(&mut self, value: i32) { 15 | self.result += value; 16 | } 17 | 18 | pub fn get_result(&self) -> i32 { 19 | self.result 20 | } 21 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/java/test_repo/src/main/java/test_repo/Main.java: -------------------------------------------------------------------------------- ```java 1 | package test_repo; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | Utils.printHello(); 6 | Model model = new Model("Cascade"); 7 | System.out.println(model.getName()); 8 | acceptModel(model); 9 | } 10 | public static void acceptModel(Model m) { 11 | // Do nothing, just for LSP reference 12 | } 13 | } 14 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/test_repo/complex_types.py: -------------------------------------------------------------------------------- ```python 1 | from typing import TypedDict 2 | 3 | a: list[int] = [1] 4 | 5 | 6 | class CustomListInt(list[int]): 7 | def some_method(self): 8 | pass 9 | 10 | 11 | class CustomTypedDict(TypedDict): 12 | a: int 13 | b: str 14 | 15 | 16 | class Outer2: 17 | class InnerTypedDict(TypedDict): 18 | a: int 19 | b: str 20 | 21 | 22 | class ComplexExtension(Outer2.InnerTypedDict, total=False): 23 | c: bool 24 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/modes/mode.template.yml: -------------------------------------------------------------------------------- ```yaml 1 | # See Serena's documentation for more details on concept of modes. 2 | description: Description of the mode, not used in the code. 3 | prompt: Prompt that will form part of the message sent to the model when activating this mode. 4 | excluded_tools: [] 5 | 6 | # several tools are excluded by default and have to be explicitly included by the user 7 | included_optional_tools: [] ``` -------------------------------------------------------------------------------- /src/serena/resources/config/contexts/oaicompat-agent.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: All tools except InitialInstructionsTool for agent context, uses OpenAI compatible tool definitions 2 | prompt: | 3 | You are running in agent context where the system prompt is provided externally. You should use symbolic 4 | tools when possible for code understanding and modification. 5 | excluded_tools: 6 | - initial_instructions 7 | 8 | tool_description_overrides: {} ``` -------------------------------------------------------------------------------- /src/solidlsp/util/subprocess_util.py: -------------------------------------------------------------------------------- ```python 1 | import platform 2 | import subprocess 3 | 4 | 5 | def subprocess_kwargs(): 6 | """ 7 | Returns a dictionary of keyword arguments for subprocess calls, adding platform-specific 8 | flags that we want to use consistently. 9 | """ 10 | kwargs = {} 11 | if platform.system() == "Windows": 12 | kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW # type: ignore 13 | return kwargs 14 | ``` -------------------------------------------------------------------------------- /test/resources/repos/zig/test_repo/zls.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "enable_build_on_save": true, 3 | "build_on_save_args": ["build"], 4 | "enable_autofix": false, 5 | "semantic_tokens": "full", 6 | "enable_inlay_hints": true, 7 | "inlay_hints_show_builtin": true, 8 | "inlay_hints_exclude_single_argument": true, 9 | "inlay_hints_show_parameter_name": true, 10 | "skip_std_references": false, 11 | "max_detail_length": 1048576 12 | } 13 | ``` -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "serena Project", 3 | "dockerFile": "../Dockerfile", 4 | "workspaceFolder": "/workspaces/serena", 5 | "settings": { 6 | "terminal.integrated.shell.linux": "/bin/bash", 7 | "python.pythonPath": "/usr/local/bin/python", 8 | }, 9 | "extensions": [ 10 | "ms-python.python", 11 | "ms-toolsai.jupyter", 12 | "ms-python.vscode-pylance" 13 | ], 14 | "forwardPorts": [], 15 | "remoteUser": "root", 16 | } 17 | ``` -------------------------------------------------------------------------------- /test/solidlsp/clojure/__init__.py: -------------------------------------------------------------------------------- ```python 1 | from pathlib import Path 2 | 3 | from solidlsp.language_servers.clojure_lsp import verify_clojure_cli 4 | 5 | 6 | def _test_clojure_cli() -> bool: 7 | try: 8 | verify_clojure_cli() 9 | return False 10 | except (FileNotFoundError, RuntimeError): 11 | return True 12 | 13 | 14 | CLI_FAIL = _test_clojure_cli() 15 | TEST_APP_PATH = Path("src") / "test_app" 16 | CORE_PATH = str(TEST_APP_PATH / "core.clj") 17 | UTILS_PATH = str(TEST_APP_PATH / "utils.clj") 18 | ``` -------------------------------------------------------------------------------- /src/serena/util/class_decorators.py: -------------------------------------------------------------------------------- ```python 1 | from typing import Any 2 | 3 | 4 | # duplicate of interprompt.class_decorators 5 | # We don't want to depend on interprompt for this in serena, so we duplicate it here 6 | def singleton(cls: type[Any]) -> Any: 7 | instance = None 8 | 9 | def get_instance(*args: Any, **kwargs: Any) -> Any: 10 | nonlocal instance 11 | if instance is None: 12 | instance = cls(*args, **kwargs) 13 | return instance 14 | 15 | return get_instance 16 | ``` -------------------------------------------------------------------------------- /test/serena/test_edit_marker.py: -------------------------------------------------------------------------------- ```python 1 | from serena.tools import CreateTextFileTool, ReadFileTool, Tool 2 | 3 | 4 | class TestEditMarker: 5 | def test_tool_can_edit_method(self): 6 | """Test that Tool.can_edit() method works correctly""" 7 | # Non-editing tool should return False 8 | assert issubclass(ReadFileTool, Tool) 9 | assert not ReadFileTool.can_edit() 10 | 11 | # Editing tool should return True 12 | assert issubclass(CreateTextFileTool, Tool) 13 | assert CreateTextFileTool.can_edit() 14 | ``` -------------------------------------------------------------------------------- /.github/workflows/junie.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Junie 2 | run-name: Junie run ${{ inputs.run_id }} 3 | 4 | permissions: 5 | contents: write 6 | 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | run_id: 11 | description: "id of workflow process" 12 | required: true 13 | workflow_params: 14 | description: "stringified params" 15 | required: true 16 | 17 | jobs: 18 | call-workflow-passing-data: 19 | uses: jetbrains-junie/junie-workflows/.github/workflows/ej-issue.yml@main 20 | with: 21 | workflow_params: ${{ inputs.workflow_params }} 22 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/modes/no-onboarding.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Onboarding was already performed, exclude all onboarding tools 2 | prompt: | 3 | You have already performed onboarding, meaning that memories have already been created. 4 | Read a list of available memories using the `list_memories` tool before proceeding with the task. 5 | You don't need to read the actual memories, just remember that they exist and that you can read them later if they are relevant for your task. 6 | excluded_tools: 7 | - onboarding 8 | - check_onboarding_performed 9 | ``` -------------------------------------------------------------------------------- /test/resources/repos/python/test_repo/test_repo/name_collisions.py: -------------------------------------------------------------------------------- ```python 1 | # ruff: noqa 2 | var_will_be_overwritten = 1 3 | 4 | var_will_be_overwritten = 2 5 | 6 | 7 | def func_using_overwritten_var(): 8 | print(var_will_be_overwritten) 9 | 10 | 11 | class ClassWillBeOverwritten: 12 | def method1(self): 13 | pass 14 | 15 | 16 | class ClassWillBeOverwritten: 17 | def method2(self): 18 | pass 19 | 20 | 21 | def func_will_be_overwritten(): 22 | pass 23 | 24 | 25 | def func_will_be_overwritten(): 26 | pass 27 | 28 | 29 | def func_calling_overwritten_func(): 30 | func_will_be_overwritten() 31 | 32 | 33 | def func_calling_overwritten_class(): 34 | ClassWillBeOverwritten() 35 | ``` -------------------------------------------------------------------------------- /src/serena/prompt_factory.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | 3 | from serena.constants import PROMPT_TEMPLATES_DIR_IN_USER_HOME, PROMPT_TEMPLATES_DIR_INTERNAL 4 | from serena.generated.generated_prompt_factory import PromptFactory 5 | 6 | 7 | class SerenaPromptFactory(PromptFactory): 8 | """ 9 | A class for retrieving and rendering prompt templates and prompt lists. 10 | """ 11 | 12 | def __init__(self) -> None: 13 | os.makedirs(PROMPT_TEMPLATES_DIR_IN_USER_HOME, exist_ok=True) 14 | super().__init__(prompts_dir=[PROMPT_TEMPLATES_DIR_IN_USER_HOME, PROMPT_TEMPLATES_DIR_INTERNAL]) 15 | ``` -------------------------------------------------------------------------------- /src/serena/__init__.py: -------------------------------------------------------------------------------- ```python 1 | __version__ = "0.1.4" 2 | 3 | import logging 4 | 5 | log = logging.getLogger(__name__) 6 | 7 | 8 | def serena_version() -> str: 9 | """ 10 | :return: the version of the package, including git status if available. 11 | """ 12 | from serena.util.git import get_git_status 13 | 14 | version = __version__ 15 | try: 16 | git_status = get_git_status() 17 | if git_status is not None: 18 | version += f"-{git_status.commit[:8]}" 19 | if not git_status.is_clean: 20 | version += "-dirty" 21 | except: 22 | pass 23 | return version 24 | ``` -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- ```yaml 1 | # Codespell configuration is within pyproject.toml 2 | --- 3 | name: Codespell 4 | 5 | on: 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | codespell: 16 | name: Check for spelling errors 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 2 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | - name: Annotate locations with typos 24 | uses: codespell-project/codespell-problem-matcher@v1 25 | - name: Codespell 26 | uses: codespell-project/actions-codespell@v2 27 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/modes/planning.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Only read-only tools, focused on analysis and planning 2 | prompt: | 3 | You are operating in planning mode. Your task is to analyze code but not write any code. 4 | The user may ask you to assist in creating a comprehensive plan, or to learn something about the codebase - 5 | either a small aspect of it or about the whole project. 6 | excluded_tools: 7 | - create_text_file 8 | - replace_symbol_body 9 | - insert_after_symbol 10 | - insert_before_symbol 11 | - delete_lines 12 | - replace_lines 13 | - insert_at_line 14 | - execute_shell_command 15 | - replace_regex 16 | ``` -------------------------------------------------------------------------------- /scripts/print_mode_context_options.py: -------------------------------------------------------------------------------- ```python 1 | from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode 2 | 3 | if __name__ == "__main__": 4 | print("---------- Available modes: ----------") 5 | for mode_name in SerenaAgentMode.list_registered_mode_names(): 6 | mode = SerenaAgentMode.load(mode_name) 7 | mode.print_overview() 8 | print("\n") 9 | print("---------- Available contexts: ----------") 10 | for context_name in SerenaAgentContext.list_registered_context_names(): 11 | context = SerenaAgentContext.load(context_name) 12 | context.print_overview() 13 | print("\n") 14 | ``` -------------------------------------------------------------------------------- /test/resources/repos/erlang/test_repo/rebar.config: -------------------------------------------------------------------------------- ``` 1 | %% Rebar3 configuration for test repository 2 | {erl_opts, [ 3 | debug_info, 4 | warnings_as_errors, 5 | warn_export_all, 6 | warn_unused_import, 7 | {i, "include"} 8 | ]}. 9 | 10 | {deps, [ 11 | {eunit, ".*", {git, "https://github.com/richcarl/eunit.git", {tag, "2.3.6"}}} 12 | ]}. 13 | 14 | {profiles, [ 15 | {test, [ 16 | {erl_opts, [debug_info]}, 17 | {deps, [ 18 | {proper, "1.3.0"} 19 | ]} 20 | ]} 21 | ]}. 22 | 23 | {cover_enabled, true}. 24 | {cover_print_enabled, true}. 25 | 26 | {dialyzer, [ 27 | {warnings, [ 28 | unmatched_returns, 29 | error_handling, 30 | race_conditions, 31 | underspecs 32 | ]} 33 | ]}. ``` -------------------------------------------------------------------------------- /test/resources/repos/swift/test_repo/src/utils.swift: -------------------------------------------------------------------------------- ```swift 1 | import Foundation 2 | 3 | public struct Utils { 4 | public static func formatDate(_ date: Date) -> String { 5 | let formatter = DateFormatter() 6 | formatter.dateStyle = .medium 7 | return formatter.string(from: date) 8 | } 9 | 10 | public static func calculateArea(radius: Double) -> Double { 11 | return Double.pi * radius * radius 12 | } 13 | } 14 | 15 | public extension String { 16 | func isValidEmail() -> Bool { 17 | let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" 18 | return NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluate(with: self) 19 | } 20 | } ``` -------------------------------------------------------------------------------- /scripts/gen_prompt_factory.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | Autogenerates the `prompt_factory.py` module 3 | """ 4 | 5 | from pathlib import Path 6 | 7 | from sensai.util import logging 8 | 9 | from interprompt import autogenerate_prompt_factory_module 10 | from serena.constants import PROMPT_TEMPLATES_DIR_INTERNAL, REPO_ROOT 11 | 12 | log = logging.getLogger(__name__) 13 | 14 | 15 | def main(): 16 | autogenerate_prompt_factory_module( 17 | prompts_dir=PROMPT_TEMPLATES_DIR_INTERNAL, 18 | target_module_path=str(Path(REPO_ROOT) / "src" / "serena" / "generated" / "generated_prompt_factory.py"), 19 | ) 20 | 21 | 22 | if __name__ == "__main__": 23 | logging.run_main(main) 24 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/internal_modes/jetbrains.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: JetBrains tools replace language server-based tools 2 | prompt: | 3 | You have access to the very powerful JetBrains tools for symbolic operations: 4 | * `jet_brains_find_symbol` replaces `find_symbol` 5 | * `jet_brains_find_referencing_symbols` replaces `find_referencing_symbols` 6 | * `jet_brains_get_symbols_overview` replaces `get_symbols_overview` 7 | excluded_tools: 8 | - find_symbol 9 | - find_referencing_symbols 10 | - get_symbols_overview 11 | - restart_language_server 12 | included_optional_tools: 13 | - jet_brains_find_symbol 14 | - jet_brains_find_referencing_symbols 15 | - jet_brains_get_symbols_overview 16 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/modes/interactive.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Interactive mode for clarification and step-by-step work 2 | prompt: | 3 | You are operating in interactive mode. You should engage with the user throughout the task, asking for clarification 4 | whenever anything is unclear, insufficiently specified, or ambiguous. 5 | 6 | Break down complex tasks into smaller steps and explain your thinking at each stage. When you're uncertain about 7 | a decision, present options to the user and ask for guidance rather than making assumptions. 8 | 9 | Focus on providing informative results for intermediate steps so the user can follow along with your progress and 10 | provide feedback as needed. 11 | excluded_tools: [] ``` -------------------------------------------------------------------------------- /src/serena/resources/config/contexts/context.template.yml: -------------------------------------------------------------------------------- ```yaml 1 | # See Serena's documentation for more details on concept of contexts. 2 | description: Description of the context, not used in the code. 3 | prompt: Prompt that will form part of the system prompt/initial instructions for agents started in this context. 4 | excluded_tools: [] 5 | 6 | # several tools are excluded by default and have to be explicitly included by the user 7 | included_optional_tools: [] 8 | 9 | # mapping of tool names to an override of their descriptions (the default description is the docstring of the Tool's apply method). 10 | # Sometimes, tool descriptions are too long (e.g., for ChatGPT), or users may want to override them for another reason. 11 | tool_description_overrides: {} ``` -------------------------------------------------------------------------------- /src/serena/resources/config/modes/onboarding.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Only read-only tools, focused on analysis and planning 2 | prompt: | 3 | You are operating in onboarding mode. This is the first time you are seeing the project. 4 | Your task is to collect relevant information about it and to save memories using the tools provided. 5 | Call relevant onboarding tools for more instructions on how to do this. 6 | In this mode, you should not be modifying any existing files. 7 | If you are also in interactive mode and something about the project is unclear, ask the user for clarification. 8 | excluded_tools: 9 | - create_text_file 10 | - replace_symbol_body 11 | - insert_after_symbol 12 | - insert_before_symbol 13 | - delete_lines 14 | - replace_lines 15 | - insert_at_line 16 | - execute_shell_command 17 | ``` -------------------------------------------------------------------------------- /src/serena/util/git.py: -------------------------------------------------------------------------------- ```python 1 | import logging 2 | 3 | from sensai.util.git import GitStatus 4 | 5 | from .shell import subprocess_check_output 6 | 7 | log = logging.getLogger(__name__) 8 | 9 | 10 | def get_git_status() -> GitStatus | None: 11 | try: 12 | commit_hash = subprocess_check_output(["git", "rev-parse", "HEAD"]) 13 | unstaged = bool(subprocess_check_output(["git", "diff", "--name-only"])) 14 | staged = bool(subprocess_check_output(["git", "diff", "--staged", "--name-only"])) 15 | untracked = bool(subprocess_check_output(["git", "ls-files", "--others", "--exclude-standard"])) 16 | return GitStatus( 17 | commit=commit_hash, has_unstaged_changes=unstaged, has_staged_uncommitted_changes=staged, has_untracked_files=untracked 18 | ) 19 | except: 20 | return None 21 | ``` -------------------------------------------------------------------------------- /test/resources/repos/csharp/test_repo/Models/Person.cs: -------------------------------------------------------------------------------- ```csharp 1 | using TestProject; 2 | 3 | namespace TestProject.Models 4 | { 5 | public class Person 6 | { 7 | public string Name { get; set; } 8 | public int Age { get; set; } 9 | public string Email { get; set; } 10 | 11 | public Person(string name, int age, string email) 12 | { 13 | Name = name; 14 | Age = age; 15 | Email = email; 16 | } 17 | 18 | public override string ToString() 19 | { 20 | return $"{Name} ({Age}) - {Email}"; 21 | } 22 | 23 | public bool IsAdult() 24 | { 25 | return Age >= 18; 26 | } 27 | 28 | public int CalculateYearsUntilRetirement() 29 | { 30 | var calculator = new Calculator(); 31 | return calculator.Subtract(65, Age); 32 | } 33 | } 34 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/al/test_repo/app.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "id": "00000001-0000-0000-0000-000000000001", 3 | "name": "Test AL Project", 4 | "publisher": "Serena Test Publisher", 5 | "version": "1.0.0.0", 6 | "brief": "Test project for AL Language Server in Serena", 7 | "description": "This project contains AL code samples for testing language server features", 8 | "privacyStatement": "", 9 | "EULA": "", 10 | "help": "", 11 | "url": "https://github.com/oraios/serena", 12 | "logo": "", 13 | "dependencies": [], 14 | "screenshots": [], 15 | "platform": "1.0.0.0", 16 | "application": "26.0.0.0", 17 | "idRanges": [ 18 | { 19 | "from": 50000, 20 | "to": 50100 21 | } 22 | ], 23 | "resourceExposurePolicy": { 24 | "allowDebugging": true, 25 | "allowDownloadingSource": true, 26 | "includeSourceInSymbolFile": true 27 | }, 28 | "runtime": "15.0", 29 | "features": ["NoImplicitWith"], 30 | "target": "Cloud" 31 | } ``` -------------------------------------------------------------------------------- /test/resources/repos/ruby/test_repo/nested.rb: -------------------------------------------------------------------------------- ```ruby 1 | class OuterClass 2 | def initialize 3 | @value = "outer" 4 | end 5 | 6 | def outer_method 7 | inner_function = lambda do |x| 8 | x * 2 9 | end 10 | 11 | result = inner_function.call(5) 12 | puts "Result: #{result}" 13 | end 14 | 15 | class NestedClass 16 | def initialize(name) 17 | @name = name 18 | end 19 | 20 | def find_me 21 | "Found in NestedClass: #{@name}" 22 | end 23 | 24 | def nested_method 25 | puts "Nested method called" 26 | end 27 | 28 | class DeeplyNested 29 | def deep_method 30 | "Deep inside" 31 | end 32 | end 33 | end 34 | 35 | module NestedModule 36 | def module_method 37 | "Module method" 38 | end 39 | 40 | class ModuleClass 41 | def module_class_method 42 | "Module class method" 43 | end 44 | end 45 | end 46 | end 47 | 48 | # Test usage of nested classes 49 | outer = OuterClass.new 50 | nested = OuterClass::NestedClass.new("test") 51 | result = nested.find_me ``` -------------------------------------------------------------------------------- /test/resources/repos/csharp/test_repo/Program.cs: -------------------------------------------------------------------------------- ```csharp 1 | using System; 2 | 3 | namespace TestProject 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | Console.WriteLine("Hello, World!"); 10 | 11 | var calculator = new Calculator(); 12 | int result = calculator.Add(5, 3); 13 | Console.WriteLine($"5 + 3 = {result}"); 14 | } 15 | } 16 | 17 | public class Calculator 18 | { 19 | public int Add(int a, int b) 20 | { 21 | return a + b; 22 | } 23 | 24 | public int Subtract(int a, int b) 25 | { 26 | return a - b; 27 | } 28 | 29 | public int Multiply(int a, int b) 30 | { 31 | return a * b; 32 | } 33 | 34 | public double Divide(int a, int b) 35 | { 36 | if (b == 0) 37 | { 38 | throw new DivideByZeroException("Cannot divide by zero"); 39 | } 40 | return (double)a / b; 41 | } 42 | } 43 | } ``` -------------------------------------------------------------------------------- /src/serena/resources/config/modes/one-shot.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Focus on completely finishing a task without interaction 2 | prompt: | 3 | You are operating in one-shot mode. Your goal is to complete the entire task autonomously without further user interaction. 4 | You should assume auto-approval for all tools and continue working until the task is completely finished. 5 | 6 | If the task is planning, your final result should be a comprehensive plan. If the task is coding, your final result 7 | should be working code with all requirements fulfilled. Try to understand what the user asks you to do 8 | and to assume as little as possible. 9 | 10 | Only abort the task if absolutely necessary, such as when critical information is missing that cannot be inferred 11 | from the codebase. 12 | 13 | It may be that you have not received a task yet. In this case, wait for the user to provide a task, this will be the 14 | only time you should wait for user interaction. 15 | excluded_tools: [] 16 | ``` -------------------------------------------------------------------------------- /test/solidlsp/elixir/__init__.py: -------------------------------------------------------------------------------- ```python 1 | import platform 2 | 3 | 4 | def _test_nextls_available() -> str: 5 | """Test if Next LS is available and return error reason if not.""" 6 | # Check if we're on Windows (Next LS doesn't support Windows) 7 | if platform.system() == "Windows": 8 | return "Next LS does not support Windows" 9 | 10 | # Try to import and check Elixir availability 11 | try: 12 | from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools 13 | 14 | # Check if Elixir is installed 15 | elixir_version = ElixirTools._get_elixir_version() 16 | if not elixir_version: 17 | return "Elixir is not installed or not in PATH" 18 | 19 | return "" # No error, Next LS should be available 20 | 21 | except ImportError as e: 22 | return f"Failed to import ElixirTools: {e}" 23 | except Exception as e: 24 | return f"Error checking Next LS availability: {e}" 25 | 26 | 27 | NEXTLS_UNAVAILABLE_REASON = _test_nextls_available() 28 | NEXTLS_UNAVAILABLE = bool(NEXTLS_UNAVAILABLE_REASON) 29 | ``` -------------------------------------------------------------------------------- /scripts/demo_run_tools.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | This script demonstrates how to use Serena's tools locally, useful 3 | for testing or development. Here the tools will be operation the serena repo itself. 4 | """ 5 | 6 | import json 7 | from pprint import pprint 8 | 9 | from serena.agent import SerenaAgent 10 | from serena.config.serena_config import SerenaConfig 11 | from serena.constants import REPO_ROOT 12 | from serena.tools import FindFileTool, FindReferencingSymbolsTool, GetSymbolsOverviewTool, SearchForPatternTool 13 | 14 | if __name__ == "__main__": 15 | agent = SerenaAgent(project=REPO_ROOT, serena_config=SerenaConfig(gui_log_window_enabled=False, web_dashboard=False)) 16 | 17 | # apply a tool 18 | find_refs_tool = agent.get_tool(FindReferencingSymbolsTool) 19 | find_file_tool = agent.get_tool(FindFileTool) 20 | search_pattern_tool = agent.get_tool(SearchForPatternTool) 21 | overview_tool = agent.get_tool(GetSymbolsOverviewTool) 22 | 23 | result = agent.execute_task( 24 | lambda: overview_tool.apply("src/solidlsp/ls.py"), 25 | ) 26 | pprint(json.loads(result)) 27 | ``` -------------------------------------------------------------------------------- /scripts/agno_agent.py: -------------------------------------------------------------------------------- ```python 1 | from agno.models.anthropic.claude import Claude 2 | from agno.models.google.gemini import Gemini 3 | from agno.playground.playground import Playground 4 | from agno.playground.serve import serve_playground_app 5 | from sensai.util import logging 6 | from sensai.util.helper import mark_used 7 | 8 | from serena.agno import SerenaAgnoAgentProvider 9 | 10 | mark_used(Gemini, Claude) 11 | 12 | # initialize logging (Note: since this module is reimported by serve_playground_app and the logging configuration 13 | # is extended by SerenaAgentProvider, we must handle this here conditionally) 14 | if __name__ == "__main__": 15 | logging.configure(level=logging.INFO) 16 | 17 | # Define the model to use (see Agno documentation for supported models; these are just examples) 18 | # model = Claude(id="claude-3-7-sonnet-20250219") 19 | model = Gemini(id="gemini-2.5-pro-exp-03-25") 20 | 21 | app = Playground(agents=[SerenaAgnoAgentProvider.get_agent(model)]).get_app() 22 | 23 | if __name__ == "__main__": 24 | serve_playground_app("agno_agent:app", reload=False, log_config=None) 25 | ``` -------------------------------------------------------------------------------- /test/resources/repos/ruby/test_repo/services.rb: -------------------------------------------------------------------------------- ```ruby 1 | require './lib.rb' 2 | require './models.rb' 3 | 4 | module Services 5 | class UserService 6 | attr_reader :users 7 | 8 | def initialize 9 | @users = {} 10 | end 11 | 12 | def create_user(id, name) 13 | user = User.new(id, name) 14 | @users[id] = user 15 | user 16 | end 17 | 18 | def get_user(id) 19 | @users[id] 20 | end 21 | 22 | def delete_user(id) 23 | @users.delete(id) 24 | end 25 | 26 | private 27 | 28 | def validate_user_data(id, name) 29 | return false if id.nil? || name.nil? 30 | return false if name.empty? 31 | true 32 | end 33 | end 34 | 35 | class ItemService 36 | def initialize 37 | @items = [] 38 | end 39 | 40 | def add_item(item) 41 | @items << item 42 | end 43 | 44 | def find_item(id) 45 | @items.find { |item| item.id == id } 46 | end 47 | end 48 | end 49 | 50 | # Module-level function 51 | def create_service_container 52 | { 53 | user_service: Services::UserService.new, 54 | item_service: Services::ItemService.new 55 | } 56 | end 57 | 58 | # Variables for testing 59 | user_service_instance = Services::UserService.new 60 | item_service_instance = Services::ItemService.new ``` -------------------------------------------------------------------------------- /test/resources/repos/swift/test_repo/src/main.swift: -------------------------------------------------------------------------------- ```swift 1 | import Foundation 2 | 3 | // Main entry point 4 | func main() { 5 | let calculator = Calculator() 6 | let result = calculator.add(5, 3) 7 | print("Result: \(result)") 8 | 9 | let user = User(name: "Alice", age: 30) 10 | user.greet() 11 | 12 | let area = Utils.calculateArea(radius: 5.0) 13 | print("Circle area: \(area)") 14 | } 15 | 16 | class Calculator { 17 | func add(_ a: Int, _ b: Int) -> Int { 18 | return a + b 19 | } 20 | 21 | func multiply(_ a: Int, _ b: Int) -> Int { 22 | return a * b 23 | } 24 | } 25 | 26 | struct User { 27 | let name: String 28 | let age: Int 29 | 30 | func greet() { 31 | print("Hello, my name is \(name) and I am \(age) years old.") 32 | } 33 | 34 | func isAdult() -> Bool { 35 | return age >= 18 36 | } 37 | } 38 | 39 | enum Status { 40 | case active 41 | case inactive 42 | case pending 43 | } 44 | 45 | protocol Drawable { 46 | func draw() 47 | } 48 | 49 | class Circle: Drawable { 50 | let radius: Double 51 | 52 | init(radius: Double) { 53 | self.radius = radius 54 | } 55 | 56 | func draw() { 57 | print("Drawing a circle with radius \(radius)") 58 | } 59 | } 60 | 61 | // Call main 62 | main() ``` -------------------------------------------------------------------------------- /test/resources/repos/markdown/test_repo/guide.md: -------------------------------------------------------------------------------- ```markdown 1 | # User Guide 2 | 3 | This guide provides detailed instructions for using the test repository. 4 | 5 | ## Getting Started 6 | 7 | Welcome to the user guide. This document covers: 8 | 9 | - Basic concepts 10 | - Advanced features 11 | - Troubleshooting 12 | 13 | ### Basic Concepts 14 | 15 | The fundamental concepts you need to understand: 16 | 17 | #### Headers and Structure 18 | 19 | Markdown documents use headers to create structure. Headers are created using `#` symbols. 20 | 21 | #### Links and References 22 | 23 | Internal links can reference other documents: 24 | 25 | - [Back to README](README.md) 26 | - [See API documentation](api.md) 27 | 28 | ### Advanced Features 29 | 30 | For advanced users, we provide: 31 | 32 | 1. Custom extensions 33 | 2. Plugin support 34 | 3. Theme customization 35 | 36 | ## Configuration 37 | 38 | Configuration options are stored in `config.yaml`: 39 | 40 | ```yaml 41 | server: 42 | port: 8080 43 | host: localhost 44 | ``` 45 | 46 | ## Troubleshooting 47 | 48 | If you encounter issues: 49 | 50 | 1. Check the [README](README.md) first 51 | 2. Review [common issues](CONTRIBUTING.md) 52 | 3. Contact support 53 | 54 | ## Next Steps 55 | 56 | After reading this guide, check out: 57 | 58 | - [API Reference](api.md) 59 | - [Contributing Guidelines](CONTRIBUTING.md) 60 | ``` -------------------------------------------------------------------------------- /src/serena/util/general.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | from typing import Literal, overload 3 | 4 | from ruamel.yaml import YAML 5 | from ruamel.yaml.comments import CommentedMap 6 | 7 | 8 | def _create_YAML(preserve_comments: bool = False) -> YAML: 9 | """ 10 | Creates a YAML that can load/save with comments if preserve_comments is True. 11 | """ 12 | typ = None if preserve_comments else "safe" 13 | result = YAML(typ=typ) 14 | result.preserve_quotes = preserve_comments 15 | return result 16 | 17 | 18 | @overload 19 | def load_yaml(path: str, preserve_comments: Literal[False]) -> dict: ... 20 | @overload 21 | def load_yaml(path: str, preserve_comments: Literal[True]) -> CommentedMap: ... 22 | def load_yaml(path: str, preserve_comments: bool = False) -> dict | CommentedMap: 23 | with open(path, encoding="utf-8") as f: 24 | yaml = _create_YAML(preserve_comments) 25 | return yaml.load(f) 26 | 27 | 28 | def save_yaml(path: str, data: dict | CommentedMap, preserve_comments: bool = False) -> None: 29 | yaml = _create_YAML(preserve_comments) 30 | os.makedirs(os.path.dirname(path), exist_ok=True) 31 | with open(path, "w", encoding="utf-8") as f: 32 | yaml.dump(data, f) 33 | ``` -------------------------------------------------------------------------------- /test/resources/repos/java/test_repo/pom.xml: -------------------------------------------------------------------------------- ``` 1 | <project xmlns="http://maven.apache.org/POM/4.0.0" 2 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 | <modelVersion>4.0.0</modelVersion> 5 | <groupId>org.example</groupId> 6 | <artifactId>test_repo</artifactId> 7 | <version>1.0-SNAPSHOT</version> 8 | <packaging>jar</packaging> 9 | <name>Java Test Repo</name> 10 | <properties> 11 | <maven.compiler.source>21</maven.compiler.source> 12 | <maven.compiler.target>21</maven.compiler.target> 13 | <maven.compiler.plugin.version>3.13.0</maven.compiler.plugin.version> 14 | </properties> 15 | 16 | <build> 17 | <plugins> 18 | <plugin> 19 | <groupId>org.apache.maven.plugins</groupId> 20 | <artifactId>maven-compiler-plugin</artifactId> 21 | <version>${maven.compiler.plugin.version}</version> 22 | <configuration> 23 | <source>21</source> 24 | <target>21</target> 25 | </configuration> 26 | </plugin> 27 | </plugins> 28 | </build> 29 | </project> 30 | ``` -------------------------------------------------------------------------------- /test/resources/repos/markdown/test_repo/api.md: -------------------------------------------------------------------------------- ```markdown 1 | # API Reference 2 | 3 | Complete API documentation for the test repository. 4 | 5 | ## Classes 6 | 7 | ### Client 8 | 9 | The main client class for interacting with the API. 10 | 11 | #### Methods 12 | 13 | ##### `connect()` 14 | 15 | Establishes a connection to the server. 16 | 17 | **Parameters:** 18 | - `host`: Server hostname 19 | - `port`: Server port number 20 | 21 | **Returns:** Connection object 22 | 23 | ##### `disconnect()` 24 | 25 | Closes the connection to the server. 26 | 27 | **Returns:** None 28 | 29 | ### Server 30 | 31 | Server-side implementation. 32 | 33 | #### Configuration 34 | 35 | ```json 36 | { 37 | "host": "localhost", 38 | "port": 8080, 39 | "timeout": 30 40 | } 41 | ``` 42 | 43 | ## Functions 44 | 45 | ### `initialize(config)` 46 | 47 | Initializes the system with the provided configuration. 48 | 49 | **Parameters:** 50 | - `config`: Configuration dictionary 51 | 52 | **Example:** 53 | 54 | ```python 55 | config = { 56 | "host": "localhost", 57 | "port": 8080 58 | } 59 | initialize(config) 60 | ``` 61 | 62 | ### `shutdown()` 63 | 64 | Gracefully shuts down the system. 65 | 66 | ## Error Handling 67 | 68 | Common errors and their solutions: 69 | 70 | - `ConnectionError`: Check network connectivity 71 | - `TimeoutError`: Increase timeout value 72 | - `ConfigError`: Validate configuration file 73 | 74 | ## See Also 75 | 76 | - [User Guide](guide.md) 77 | - [README](README.md) 78 | - [Contributing](CONTRIBUTING.md) 79 | ``` -------------------------------------------------------------------------------- /.github/workflows/lint_and_docs.yaml: -------------------------------------------------------------------------------- ```yaml 1 | name: Linting, Types and Docs Check 2 | 3 | on: 4 | workflow_dispatch: # Manual trigger only - workflow disabled from auto-running 5 | 6 | jobs: 7 | check: 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 5 10 | permissions: 11 | id-token: write 12 | pages: write 13 | actions: write 14 | contents: read 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: 3.11 21 | 22 | 23 | # use uv package manager 24 | - name: Install uv 25 | run: pip install uv 26 | - uses: actions/cache@v3 27 | name: Cache dependencies 28 | with: 29 | path: ~/.cache/uv 30 | key: uv-${{ hashFiles('pyproject.toml') }} 31 | - name: Install dependencies 32 | run: | 33 | uv venv 34 | uv pip install -e ".[dev]" 35 | - name: Lint 36 | run: uv run poe lint 37 | - name: Types 38 | run: uv run poe type-check 39 | - name: Docs 40 | run: uv run poe doc-build 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v2 43 | with: 44 | path: "docs/_build" 45 | - name: Deploy to GitHub Pages 46 | id: deployment 47 | uses: actions/deploy-pages@v2 ``` -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- ```yaml 1 | services: 2 | serena: 3 | image: serena:latest 4 | # To work with projects, you must mount them as volumes: 5 | # volumes: 6 | # - ./my-project:/workspace/my-project 7 | # - /path/to/another/project:/workspace/another-project 8 | build: 9 | context: ./ 10 | dockerfile: Dockerfile 11 | target: production 12 | ports: 13 | - "${SERENA_PORT:-9121}:9121" # MCP server port 14 | - "${SERENA_DASHBOARD_PORT:-24282}:24282" # Dashboard port (default 0x5EDA = 24282) 15 | environment: 16 | - SERENA_DOCKER=1 17 | command: 18 | - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0" 19 | # Add the context for the IDE assistant 20 | # - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0 --context ide-assistant" 21 | 22 | serena-dev: 23 | image: serena:dev 24 | build: 25 | context: ./ 26 | dockerfile: Dockerfile 27 | target: development 28 | tty: true 29 | stdin_open: true 30 | environment: 31 | - SERENA_DOCKER=1 32 | volumes: 33 | - .:/workspaces/serena 34 | ports: 35 | - "${SERENA_PORT:-9121}:9121" # MCP server port 36 | - "${SERENA_DASHBOARD_PORT:-24282}:24282" # Dashboard port 37 | command: 38 | - "uv run --directory . serena-mcp-server" 39 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/contexts/desktop-app.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: All tools included for desktop app context 2 | prompt: | 3 | You are running in desktop app context where the tools give you access to the code base as well as some 4 | access to the file system, if configured. You interact with the user through a chat interface that is separated 5 | from the code base. As a consequence, if you are in interactive mode, your communication with the user should 6 | involve high-level thinking and planning as well as some summarization of any code edits that you make. 7 | For viewing the code edits the user will view them in a separate code editor window, and the back-and-forth 8 | between the chat and the code editor should be minimized as well as facilitated by you. 9 | If complex changes have been made, advise the user on how to review them in the code editor. 10 | If complex relationships that the user asked for should be visualized or explained, consider creating 11 | a diagram in addition to your text-based communication. Note that in the chat interface you have various rendering 12 | options for text, html, and mermaid diagrams, as has been explained to you in your initial instructions. 13 | excluded_tools: [] 14 | included_optional_tools: 15 | - switch_modes 16 | 17 | tool_description_overrides: {} 18 | ``` -------------------------------------------------------------------------------- /test/solidlsp/erlang/__init__.py: -------------------------------------------------------------------------------- ```python 1 | import platform 2 | 3 | 4 | def _test_erlang_ls_available() -> str: 5 | """Test if Erlang LS is available and return error reason if not.""" 6 | # Check if we're on Windows (Erlang LS doesn't support Windows) 7 | if platform.system() == "Windows": 8 | return "Erlang LS does not support Windows" 9 | 10 | # Try to import and check Erlang availability 11 | try: 12 | from solidlsp.language_servers.erlang_language_server import ErlangLanguageServer 13 | 14 | # Check if Erlang/OTP is installed 15 | erlang_version = ErlangLanguageServer._get_erlang_version() 16 | if not erlang_version: 17 | return "Erlang/OTP is not installed or not in PATH" 18 | 19 | # Check if rebar3 is available (commonly used build tool) 20 | rebar3_available = ErlangLanguageServer._check_rebar3_available() 21 | if not rebar3_available: 22 | return "rebar3 is not installed or not in PATH (required for project compilation)" 23 | 24 | return "" # No error, Erlang LS should be available 25 | 26 | except ImportError as e: 27 | return f"Failed to import ErlangLanguageServer: {e}" 28 | except Exception as e: 29 | return f"Error checking Erlang LS availability: {e}" 30 | 31 | 32 | ERLANG_LS_UNAVAILABLE_REASON = _test_erlang_ls_available() 33 | ERLANG_LS_UNAVAILABLE = bool(ERLANG_LS_UNAVAILABLE_REASON) 34 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue--bug--performance-problem--question-.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | name: Issue (bug, performance problem, question) 3 | about: General Issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | I have: 11 | 12 | - [ ] Read the readme and verified that the issue cannot be solved by adjusting configuration 13 | - [ ] Understood that Serena's dashboard can be disabled through the config 14 | - [ ] Understood that by default a client session will start a separate instance of a Serena server. 15 | - [ ] Understood that for multi-agent setups, the SSE mode should be used. 16 | - [ ] Verified that non-project files are ignored using either gitignore or the corresponding setting in `.serena/project.yml` 17 | - [ ] Have looked for similar issues and discussions, including closed ones 18 | - [ ] Made sure it's an actual issue and not a question - those should be opened as discussion instead. 19 | 20 | If you have encountered a real and new issue: 21 | 22 | - [ ] I performed `<uv invocation> serena project health-check` 23 | - [ ] I indexed the project as described in the readme 24 | - [ ] Added sufficient explanation of my setup: the MCP client, the OS, the programming language, any config adjustments or relevant project specifics 25 | - [ ] Explained how the issue arose, added instructions on how to reproduce it (if possible) 26 | - [ ] If the issue happens on an open source project, I have added the link 27 | - [ ] Wrote a meaningful title and description 28 | ``` -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Publish Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | description: 'Tag name for the release (e.g., v0.1.0)' 11 | required: true 12 | default: 'v0.1.0' 13 | 14 | env: 15 | # Set this to true manually in the GitHub workflow UI if you want to publish to PyPI 16 | # Will always publish to testpypi 17 | PUBLISH_TO_PYPI: true 18 | 19 | jobs: 20 | publish: 21 | name: Publish the serena-agent package 22 | runs-on: ubuntu-latest 23 | 24 | permissions: 25 | id-token: write # Required for trusted publishing 26 | contents: write # Required for updating artifact 27 | 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v4 31 | 32 | - name: Install the latest version of uv 33 | uses: astral-sh/setup-uv@v6 34 | with: 35 | version: "latest" 36 | 37 | - name: Build package 38 | run: uv build 39 | 40 | - name: Upload artifacts to GitHub Release 41 | if: env.PUBLISH_TO_PYPI == 'true' 42 | uses: softprops/action-gh-release@v2 43 | with: 44 | tag_name: ${{ github.event.inputs.tag || github.ref_name }} 45 | files: | 46 | dist/*.tar.gz 47 | dist/*.whl 48 | 49 | - name: Publish to TestPyPI 50 | run: uv publish --index testpypi 51 | 52 | - name: Publish to PyPI (conditional) 53 | if: env.PUBLISH_TO_PYPI == 'true' 54 | run: uv publish 55 | ``` -------------------------------------------------------------------------------- /test/resources/repos/ruby/test_repo/models.rb: -------------------------------------------------------------------------------- ```ruby 1 | class User 2 | attr_accessor :id, :name, :email 3 | 4 | def initialize(id, name, email = nil) 5 | @id = id 6 | @name = name 7 | @email = email 8 | end 9 | 10 | def full_info 11 | info = "User: #{@name} (ID: #{@id})" 12 | info += ", Email: #{@email}" if @email 13 | info 14 | end 15 | 16 | def to_hash 17 | { 18 | id: @id, 19 | name: @name, 20 | email: @email 21 | } 22 | end 23 | 24 | def self.from_hash(hash) 25 | new(hash[:id], hash[:name], hash[:email]) 26 | end 27 | 28 | class << self 29 | def default_user 30 | new(0, "Guest") 31 | end 32 | end 33 | end 34 | 35 | class Item 36 | attr_reader :id, :name, :price 37 | 38 | def initialize(id, name, price) 39 | @id = id 40 | @name = name 41 | @price = price 42 | end 43 | 44 | def discounted_price(discount_percent) 45 | @price * (1 - discount_percent / 100.0) 46 | end 47 | 48 | def description 49 | "#{@name}: $#{@price}" 50 | end 51 | end 52 | 53 | module ItemHelpers 54 | def format_price(price) 55 | "$#{sprintf('%.2f', price)}" 56 | end 57 | 58 | def calculate_tax(price, tax_rate = 0.08) 59 | price * tax_rate 60 | end 61 | end 62 | 63 | class Order 64 | include ItemHelpers 65 | 66 | def initialize 67 | @items = [] 68 | @total = 0 69 | end 70 | 71 | def add_item(item, quantity = 1) 72 | @items << { item: item, quantity: quantity } 73 | calculate_total 74 | end 75 | 76 | def total_with_tax 77 | tax = calculate_tax(@total) 78 | @total + tax 79 | end 80 | 81 | private 82 | 83 | def calculate_total 84 | @total = @items.sum { |entry| entry[:item].price * entry[:quantity] } 85 | end 86 | end ``` -------------------------------------------------------------------------------- /src/serena/resources/config/contexts/ide-assistant.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Non-symbolic editing tools and general shell tool are excluded 2 | prompt: | 3 | You are running in IDE assistant context where file operations, basic (line-based) edits and reads, 4 | and shell commands are handled by your own, internal tools. 5 | The initial instructions and the current config inform you on which tools are available to you, 6 | and how to use them. 7 | Don't attempt to use any excluded tools, instead rely on your own internal tools 8 | for achieving the basic file or shell operations. 9 | 10 | If serena's tools can be used for achieving your task, 11 | you should prioritize them. In particular, it is important that you avoid reading entire source code files, 12 | unless it is strictly necessary! Instead, for exploring and reading code in a token-efficient manner, 13 | you should use serena's overview and symbolic search tools. 14 | Before reading a full file, it is usually best to first explore the file using the symbol_overview tool, 15 | and then make targeted reads using find_symbol and other symbolic tools. 16 | For non-code files or for reads where you don't know the symbol's name path, you can use the pattern searching tool, 17 | and read full files only as a last resort. 18 | 19 | excluded_tools: 20 | - create_text_file 21 | - read_file 22 | - execute_shell_command 23 | - prepare_for_new_conversation 24 | - replace_regex 25 | 26 | tool_description_overrides: {} 27 | 28 | ``` -------------------------------------------------------------------------------- /.serena/memories/suggested_commands.md: -------------------------------------------------------------------------------- ```markdown 1 | # Suggested Commands 2 | 3 | ## Development Tasks (using uv and poe) 4 | 5 | The following tasks should generally be executed using `uv run poe <task_name>`. 6 | 7 | - `format`: This is the **only** allowed command for formatting. Run as `uv run poe format`. 8 | - `type-check`: This is the **only** allowed command for type checking. Run as `uv run poe type-check`. 9 | - `test`: This is the preferred command for running tests (`uv run poe test [args]`). You can select subsets of tests with markers, 10 | the current markers are 11 | ```toml 12 | markers = [ 13 | "python: language server running for Python", 14 | "go: language server running for Go", 15 | "java: language server running for Java", 16 | "rust: language server running for Rust", 17 | "typescript: language server running for TypeScript", 18 | "php: language server running for PHP", 19 | "snapshot: snapshot tests for symbolic editing operations", 20 | ] 21 | ``` 22 | By default, `uv run poe test` uses the markers set in the env var `PYTEST_MARKERS`, or, if it unset, uses `-m "not java and not rust and not isolated process"`. 23 | You can override this behavior by simply passing the `-m` option to `uv run poe test`, e.g. `uv run poe test -m "python or go"`. 24 | 25 | For finishing a task, make sure format, type-check and test pass! Run them at the end of the task 26 | and if needed fix any issues that come up and run them again until they pass. ``` -------------------------------------------------------------------------------- /src/solidlsp/ls_exceptions.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | This module contains the exceptions raised by the framework. 3 | """ 4 | 5 | 6 | class SolidLSPException(Exception): 7 | def __init__(self, message: str, cause: Exception | None = None): 8 | """ 9 | Initializes the exception with the given message. 10 | 11 | :param message: the message describing the exception 12 | :param cause: the original exception that caused this exception, if any. 13 | For exceptions raised during request handling, this is typically 14 | * an LSPError for errors returned by the LSP server 15 | * LanguageServerTerminatedException for errors due to the language server having terminated. 16 | """ 17 | self.cause = cause 18 | super().__init__(message) 19 | 20 | def is_language_server_terminated(self): 21 | """ 22 | :return: True if the exception is caused by the language server having terminated as indicated 23 | by the causing exception being an instance of LanguageServerTerminatedException. 24 | """ 25 | from .ls_handler import LanguageServerTerminatedException 26 | 27 | return isinstance(self.cause, LanguageServerTerminatedException) 28 | 29 | def __str__(self) -> str: 30 | """ 31 | Returns a string representation of the exception. 32 | """ 33 | s = super().__str__() 34 | if self.cause: 35 | if "\n" in s: 36 | s += "\n" 37 | else: 38 | s += " " 39 | s += f"(caused by {self.cause})" 40 | return s 41 | ``` -------------------------------------------------------------------------------- /src/serena/generated/generated_prompt_factory.py: -------------------------------------------------------------------------------- ```python 1 | # ruff: noqa 2 | # black: skip 3 | # mypy: ignore-errors 4 | 5 | # NOTE: This module is auto-generated from interprompt.autogenerate_prompt_factory_module, do not edit manually! 6 | 7 | from interprompt.multilang_prompt import PromptList 8 | from interprompt.prompt_factory import PromptFactoryBase 9 | from typing import Any 10 | 11 | 12 | class PromptFactory(PromptFactoryBase): 13 | """ 14 | A class for retrieving and rendering prompt templates and prompt lists. 15 | """ 16 | 17 | def create_onboarding_prompt(self, *, system: Any) -> str: 18 | return self._render_prompt("onboarding_prompt", locals()) 19 | 20 | def create_think_about_collected_information(self) -> str: 21 | return self._render_prompt("think_about_collected_information", locals()) 22 | 23 | def create_think_about_task_adherence(self) -> str: 24 | return self._render_prompt("think_about_task_adherence", locals()) 25 | 26 | def create_think_about_whether_you_are_done(self) -> str: 27 | return self._render_prompt("think_about_whether_you_are_done", locals()) 28 | 29 | def create_summarize_changes(self) -> str: 30 | return self._render_prompt("summarize_changes", locals()) 31 | 32 | def create_prepare_for_new_conversation(self) -> str: 33 | return self._render_prompt("prepare_for_new_conversation", locals()) 34 | 35 | def create_system_prompt( 36 | self, *, available_markers: Any, available_tools: Any, context_system_prompt: Any, mode_system_prompts: Any 37 | ) -> str: 38 | return self._render_prompt("system_prompt", locals()) 39 | ``` -------------------------------------------------------------------------------- /test/resources/repos/bash/test_repo/main.sh: -------------------------------------------------------------------------------- ```bash 1 | #!/bin/bash 2 | 3 | # Main script demonstrating various bash features 4 | 5 | # Global variables 6 | readonly SCRIPT_NAME="Main Script" 7 | COUNTER=0 8 | declare -a ITEMS=("item1" "item2" "item3") 9 | 10 | # Function definitions 11 | function greet_user() { 12 | local username="$1" 13 | local greeting_type="${2:-default}" 14 | 15 | case "$greeting_type" in 16 | "formal") 17 | echo "Good day, ${username}!" 18 | ;; 19 | "casual") 20 | echo "Hey ${username}!" 21 | ;; 22 | *) 23 | echo "Hello, ${username}!" 24 | ;; 25 | esac 26 | } 27 | 28 | function process_items() { 29 | local -n items_ref=$1 30 | local operation="$2" 31 | 32 | for item in "${items_ref[@]}"; do 33 | case "$operation" in 34 | "count") 35 | ((COUNTER++)) 36 | echo "Processing item $COUNTER: $item" 37 | ;; 38 | "uppercase") 39 | echo "${item^^}" 40 | ;; 41 | *) 42 | echo "Unknown operation: $operation" 43 | return 1 44 | ;; 45 | esac 46 | done 47 | } 48 | 49 | # Main execution 50 | main() { 51 | echo "Starting $SCRIPT_NAME" 52 | 53 | if [[ $# -eq 0 ]]; then 54 | echo "Usage: $0 <username> [greeting_type]" 55 | exit 1 56 | fi 57 | 58 | local user="$1" 59 | local greeting="${2:-default}" 60 | 61 | greet_user "$user" "$greeting" 62 | 63 | echo "Processing items..." 64 | process_items ITEMS "count" 65 | 66 | echo "Script completed successfully" 67 | } 68 | 69 | # Run main function with all arguments 70 | main "$@" 71 | ``` -------------------------------------------------------------------------------- /src/solidlsp/settings.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | Defines settings for Solid-LSP 3 | """ 4 | 5 | import os 6 | import pathlib 7 | from dataclasses import dataclass, field 8 | from typing import TYPE_CHECKING, Any 9 | 10 | if TYPE_CHECKING: 11 | from solidlsp.ls_config import Language 12 | 13 | 14 | @dataclass 15 | class SolidLSPSettings: 16 | solidlsp_dir: str = str(pathlib.Path.home() / ".solidlsp") 17 | """ 18 | Path to the directory in which to store global Solid-LSP data (which is not project-specific) 19 | """ 20 | project_data_relative_path: str = ".solidlsp" 21 | """ 22 | Relative path within each project directory where Solid-LSP can store project-specific data, e.g. cache files. 23 | For instance, if this is ".solidlsp" and the project is located at "/home/user/myproject", 24 | then Solid-LSP will store project-specific data in "/home/user/myproject/.solidlsp". 25 | """ 26 | ls_specific_settings: dict["Language", Any] = field(default_factory=dict) 27 | """ 28 | Advanced configuration option allowing to configure language server implementation specific options. 29 | Have a look at the docstring of the constructors of the corresponding LS implementations within solidlsp to see which options are available. 30 | No documentation on options means no options are available. 31 | """ 32 | 33 | def __post_init__(self): 34 | os.makedirs(str(self.solidlsp_dir), exist_ok=True) 35 | os.makedirs(str(self.ls_resources_dir), exist_ok=True) 36 | 37 | @property 38 | def ls_resources_dir(self): 39 | return os.path.join(str(self.solidlsp_dir), "language_servers", "static") 40 | ``` -------------------------------------------------------------------------------- /src/serena/resources/config/contexts/codex.yml: -------------------------------------------------------------------------------- ```yaml 1 | description: Non-symbolic editing tools and general shell tool are excluded 2 | prompt: | 3 | You are running in IDE assistant context where file operations, basic (line-based) edits and reads, 4 | and shell commands are handled by your own, internal tools. 5 | The initial instructions and the current config inform you on which tools are available to you, 6 | and how to use them. 7 | Don't attempt to use any excluded tools, instead rely on your own internal tools 8 | for achieving the basic file or shell operations. 9 | 10 | If serena's tools can be used for achieving your task, 11 | you should prioritize them. In particular, it is important that you avoid reading entire source code files, 12 | unless it is strictly necessary! Instead, for exploring and reading code in a token-efficient manner, 13 | you should use serena's overview and symbolic search tools. The call of the read_file tool on an entire source code 14 | file should only happen in exceptional cases, usually you should first explore the file (by itself or as part of exploring 15 | the directory containing it) using the symbol_overview tool, and then make targeted reads using find_symbol and other symbolic tools. 16 | For non-code files or for reads where you don't know the symbol's name path you can use the patterns searching tool, 17 | using the read_file as a last resort. 18 | 19 | excluded_tools: 20 | - create_text_file 21 | - read_file 22 | - execute_shell_command 23 | - prepare_for_new_conversation 24 | - replace_regex 25 | 26 | 27 | tool_description_overrides: {} 28 | 29 | ``` -------------------------------------------------------------------------------- /src/interprompt/jinja_template.py: -------------------------------------------------------------------------------- ```python 1 | from typing import Any 2 | 3 | import jinja2 4 | import jinja2.meta 5 | import jinja2.nodes 6 | import jinja2.visitor 7 | 8 | from interprompt.util.class_decorators import singleton 9 | 10 | 11 | class ParameterizedTemplateInterface: 12 | def get_parameters(self) -> list[str]: ... 13 | 14 | 15 | @singleton 16 | class _JinjaEnvProvider: 17 | def __init__(self) -> None: 18 | self._env: jinja2.Environment | None = None 19 | 20 | def get_env(self) -> jinja2.Environment: 21 | if self._env is None: 22 | self._env = jinja2.Environment() 23 | return self._env 24 | 25 | 26 | class JinjaTemplate(ParameterizedTemplateInterface): 27 | def __init__(self, template_string: str) -> None: 28 | self._template_string = template_string 29 | self._template = _JinjaEnvProvider().get_env().from_string(self._template_string) 30 | parsed_content = self._template.environment.parse(self._template_string) 31 | self._parameters = sorted(jinja2.meta.find_undeclared_variables(parsed_content)) 32 | 33 | def render(self, **params: Any) -> str: 34 | """Renders the template with the given kwargs. You can find out which parameters are required by calling get_parameter_names().""" 35 | return self._template.render(**params) 36 | 37 | def get_parameters(self) -> list[str]: 38 | """A sorted list of parameter names that are extracted from the template string. It is impossible to know the types of the parameter 39 | values, they can be primitives, dicts or dict-like objects. 40 | 41 | :return: the list of parameter names 42 | """ 43 | return self._parameters 44 | ``` -------------------------------------------------------------------------------- /test/serena/test_tool_parameter_types.py: -------------------------------------------------------------------------------- ```python 1 | import logging 2 | 3 | import pytest 4 | 5 | from serena.config.serena_config import SerenaConfig 6 | from serena.mcp import SerenaMCPFactorySingleProcess 7 | from serena.tools.tools_base import ToolRegistry 8 | 9 | 10 | @pytest.mark.parametrize("context", ("chatgpt", "codex", "oaicompat-agent")) 11 | def test_all_tool_parameters_have_type(context): 12 | """ 13 | For every tool exposed by Serena, ensure that the generated 14 | Open‑AI schema contains a ``type`` entry for each parameter. 15 | """ 16 | cfg = SerenaConfig(gui_log_window_enabled=False, web_dashboard=False, log_level=logging.ERROR) 17 | registry = ToolRegistry() 18 | cfg.included_optional_tools = tuple(registry.get_tool_names_optional()) 19 | factory = SerenaMCPFactorySingleProcess(context=context) 20 | # Initialize the agent so that the tools are available 21 | factory._instantiate_agent(cfg, []) 22 | tools = list(factory._iter_tools()) 23 | 24 | for tool in tools: 25 | mcp_tool = factory.make_mcp_tool(tool, openai_tool_compatible=True) 26 | params = mcp_tool.parameters 27 | 28 | # Collect any parameter that lacks a type 29 | issues = [] 30 | print(f"Checking tool {tool}") 31 | 32 | if "properties" not in params: 33 | issues.append(f"Tool {tool.get_name()!r} missing properties section") 34 | else: 35 | for pname, prop in params["properties"].items(): 36 | if "type" not in prop: 37 | issues.append(f"Tool {tool.get_name()!r} parameter {pname!r} missing 'type'") 38 | if issues: 39 | raise AssertionError("\n".join(issues)) 40 | ``` -------------------------------------------------------------------------------- /llms-install.md: -------------------------------------------------------------------------------- ```markdown 1 | # MCP Installation instructions 2 | 3 | This document is mainly used as instructions for AI-assistants like Cline and others that 4 | try to do an automatic install based on freeform instructions. 5 | 6 | 0. Make sure `uv` is installed. If not, install it using either `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS, Linux) or 7 | `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"` (Windows). Find the path to the `uv` executable, 8 | you'll need it later. 9 | 1. Clone the repo with `git clone [email protected]:oraios/serena.git` and change into its dir (e.g., `cd serena`) 10 | 2. Check if `serena_config.yml` exists. If not, create it with `cp serena_config.template.yml serena_config.yml`. Read the instructions in the config. 11 | 3. In the config, check if the path to your project was added. If not, add it to the `projects` section 12 | 4. In your project, create a `.serena` if needed and check whether `project.yml` exists there. 13 | 5. If no `project.yml` was found, create it using `cp /path/to/serena/myproject.template.yml /path/to/your/project/.serena/project.yml` 14 | 6. Read the instructions in `project.yml`. Make sure the `project.yml` has the correct project language configured. 15 | Remove the project_root entry there. 16 | 7. Finally, add the Serena MCP server config like this: 17 | 18 | ```json 19 | { 20 | "mcpServers": { 21 | ... 22 | "serena": { 23 | "command": "/abs/path/to/uv", 24 | "args": ["run", "--directory", "/abs/path/to/serena", "serena-mcp-server", "/path/to/your/project/.serena/project.yml"] 25 | } 26 | } 27 | } 28 | 29 | ``` 30 | ``` -------------------------------------------------------------------------------- /src/serena/util/shell.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | import subprocess 3 | 4 | from pydantic import BaseModel 5 | 6 | from solidlsp.util.subprocess_util import subprocess_kwargs 7 | 8 | 9 | class ShellCommandResult(BaseModel): 10 | stdout: str 11 | return_code: int 12 | cwd: str 13 | stderr: str | None = None 14 | 15 | 16 | def execute_shell_command(command: str, cwd: str | None = None, capture_stderr: bool = False) -> ShellCommandResult: 17 | """ 18 | Execute a shell command and return the output. 19 | 20 | :param command: The command to execute. 21 | :param cwd: The working directory to execute the command in. If None, the current working directory will be used. 22 | :param capture_stderr: Whether to capture the stderr output. 23 | :return: The output of the command. 24 | """ 25 | if cwd is None: 26 | cwd = os.getcwd() 27 | 28 | process = subprocess.Popen( 29 | command, 30 | shell=True, 31 | stdin=subprocess.DEVNULL, 32 | stdout=subprocess.PIPE, 33 | stderr=subprocess.PIPE if capture_stderr else None, 34 | text=True, 35 | encoding="utf-8", 36 | errors="replace", 37 | cwd=cwd, 38 | **subprocess_kwargs(), 39 | ) 40 | 41 | stdout, stderr = process.communicate() 42 | return ShellCommandResult(stdout=stdout, stderr=stderr, return_code=process.returncode, cwd=cwd) 43 | 44 | 45 | def subprocess_check_output(args: list[str], encoding: str = "utf-8", strip: bool = True, timeout: float | None = None) -> str: 46 | output = subprocess.check_output(args, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, timeout=timeout, env=os.environ.copy(), **subprocess_kwargs()).decode(encoding) # type: ignore 47 | if strip: 48 | output = output.strip() 49 | return output 50 | ``` -------------------------------------------------------------------------------- /test/solidlsp/go/test_go_basic.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | 3 | import pytest 4 | 5 | from solidlsp import SolidLanguageServer 6 | from solidlsp.ls_config import Language 7 | from solidlsp.ls_utils import SymbolUtils 8 | 9 | 10 | @pytest.mark.go 11 | class TestGoLanguageServer: 12 | @pytest.mark.parametrize("language_server", [Language.GO], indirect=True) 13 | def test_find_symbol(self, language_server: SolidLanguageServer) -> None: 14 | symbols = language_server.request_full_symbol_tree() 15 | assert SymbolUtils.symbol_tree_contains_name(symbols, "main"), "main function not found in symbol tree" 16 | assert SymbolUtils.symbol_tree_contains_name(symbols, "Helper"), "Helper function not found in symbol tree" 17 | assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoStruct"), "DemoStruct not found in symbol tree" 18 | 19 | @pytest.mark.parametrize("language_server", [Language.GO], indirect=True) 20 | def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: 21 | file_path = os.path.join("main.go") 22 | symbols = language_server.request_document_symbols(file_path) 23 | helper_symbol = None 24 | for sym in symbols[0]: 25 | if sym.get("name") == "Helper": 26 | helper_symbol = sym 27 | break 28 | assert helper_symbol is not None, "Could not find 'Helper' function symbol in main.go" 29 | sel_start = helper_symbol["selectionRange"]["start"] 30 | refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) 31 | assert any( 32 | "main.go" in ref.get("relativePath", "") for ref in refs 33 | ), "main.go should reference Helper (tried all positions in selectionRange)" 34 | ``` -------------------------------------------------------------------------------- /test/solidlsp/typescript/test_typescript_basic.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | 3 | import pytest 4 | 5 | from solidlsp import SolidLanguageServer 6 | from solidlsp.ls_config import Language 7 | from solidlsp.ls_utils import SymbolUtils 8 | 9 | 10 | @pytest.mark.typescript 11 | class TestTypescriptLanguageServer: 12 | @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) 13 | def test_find_symbol(self, language_server: SolidLanguageServer) -> None: 14 | symbols = language_server.request_full_symbol_tree() 15 | assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoClass"), "DemoClass not found in symbol tree" 16 | assert SymbolUtils.symbol_tree_contains_name(symbols, "helperFunction"), "helperFunction not found in symbol tree" 17 | assert SymbolUtils.symbol_tree_contains_name(symbols, "printValue"), "printValue method not found in symbol tree" 18 | 19 | @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) 20 | def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: 21 | file_path = os.path.join("index.ts") 22 | symbols = language_server.request_document_symbols(file_path) 23 | helper_symbol = None 24 | for sym in symbols[0]: 25 | if sym.get("name") == "helperFunction": 26 | helper_symbol = sym 27 | break 28 | assert helper_symbol is not None, "Could not find 'helperFunction' symbol in index.ts" 29 | sel_start = helper_symbol["selectionRange"]["start"] 30 | refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) 31 | assert any( 32 | "index.ts" in ref.get("relativePath", "") for ref in refs 33 | ), "index.ts should reference helperFunction (tried all positions in selectionRange)" 34 | ``` -------------------------------------------------------------------------------- /test/resources/repos/bash/test_repo/utils.sh: -------------------------------------------------------------------------------- ```bash 1 | #!/bin/bash 2 | 3 | # Utility functions for bash scripting 4 | 5 | # String manipulation functions 6 | function to_uppercase() { 7 | echo "${1^^}" 8 | } 9 | 10 | function to_lowercase() { 11 | echo "${1,,}" 12 | } 13 | 14 | function trim_whitespace() { 15 | local var="$1" 16 | var="${var#"${var%%[![:space:]]*}"}" 17 | var="${var%"${var##*[![:space:]]}"}" 18 | echo "$var" 19 | } 20 | 21 | # File operations 22 | function backup_file() { 23 | local file="$1" 24 | local backup_dir="${2:-./backups}" 25 | 26 | if [[ ! -f "$file" ]]; then 27 | echo "Error: File '$file' does not exist" >&2 28 | return 1 29 | fi 30 | 31 | mkdir -p "$backup_dir" 32 | cp "$file" "${backup_dir}/$(basename "$file").$(date +%Y%m%d_%H%M%S).bak" 33 | echo "Backup created for $file" 34 | } 35 | 36 | # Array operations 37 | function contains_element() { 38 | local element="$1" 39 | shift 40 | local array=("$@") 41 | 42 | for item in "${array[@]}"; do 43 | if [[ "$item" == "$element" ]]; then 44 | return 0 45 | fi 46 | done 47 | return 1 48 | } 49 | 50 | # Logging functions 51 | function log_message() { 52 | local level="$1" 53 | local message="$2" 54 | local timestamp=$(date '+%Y-%m-%d %H:%M:%S') 55 | 56 | case "$level" in 57 | "ERROR") 58 | echo "[$timestamp] ERROR: $message" >&2 59 | ;; 60 | "WARN") 61 | echo "[$timestamp] WARN: $message" >&2 62 | ;; 63 | "INFO") 64 | echo "[$timestamp] INFO: $message" 65 | ;; 66 | "DEBUG") 67 | [[ "${DEBUG:-false}" == "true" ]] && echo "[$timestamp] DEBUG: $message" 68 | ;; 69 | *) 70 | echo "[$timestamp] $message" 71 | ;; 72 | esac 73 | } 74 | 75 | # Validation functions 76 | function is_valid_email() { 77 | local email="$1" 78 | [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]] 79 | } 80 | 81 | function is_number() { 82 | [[ $1 =~ ^[0-9]+$ ]] 83 | } 84 | ``` -------------------------------------------------------------------------------- /test/solidlsp/erlang/test_erlang_basic.py: -------------------------------------------------------------------------------- ```python 1 | """ 2 | Basic integration tests for the Erlang language server functionality. 3 | 4 | These tests validate the functionality of the language server APIs 5 | like request_references using the test repository. 6 | """ 7 | 8 | import pytest 9 | 10 | from solidlsp import SolidLanguageServer 11 | from solidlsp.ls_config import Language 12 | 13 | from . import ERLANG_LS_UNAVAILABLE, ERLANG_LS_UNAVAILABLE_REASON 14 | 15 | 16 | @pytest.mark.erlang 17 | @pytest.mark.skipif(ERLANG_LS_UNAVAILABLE, reason=f"Erlang LS not available: {ERLANG_LS_UNAVAILABLE_REASON}") 18 | class TestErlangLanguageServerBasics: 19 | """Test basic functionality of the Erlang language server.""" 20 | 21 | @pytest.mark.parametrize("language_server", [Language.ERLANG], indirect=True) 22 | def test_language_server_initialization(self, language_server: SolidLanguageServer) -> None: 23 | """Test that the Erlang language server initializes properly.""" 24 | assert language_server is not None 25 | assert language_server.language == Language.ERLANG 26 | 27 | @pytest.mark.parametrize("language_server", [Language.ERLANG], indirect=True) 28 | def test_document_symbols(self, language_server: SolidLanguageServer) -> None: 29 | """Test document symbols retrieval for Erlang files.""" 30 | try: 31 | file_path = "hello.erl" 32 | symbols_tuple = language_server.request_document_symbols(file_path) 33 | assert isinstance(symbols_tuple, tuple) 34 | assert len(symbols_tuple) == 2 35 | 36 | all_symbols, root_symbols = symbols_tuple 37 | assert isinstance(all_symbols, list) 38 | assert isinstance(root_symbols, list) 39 | except Exception as e: 40 | if "not fully initialized" in str(e): 41 | pytest.skip("Erlang language server not fully initialized") 42 | else: 43 | raise 44 | ```