Added an indexing command as uv script

This commit is contained in:
Michael Panchenko committed 2025-06-16 15:59:40 +02:00
1 parent 2e3eda72ba
commit 918ff32dcc
3 files changed
+27 -3

No files matched your search

+1
View File
@@ -40,6 +40,7 @@ dependencies = [
[project.scripts]
serena-mcp-server = "serena.mcp:start_mcp_server"
index-project = "serena.agent:index_project"
[project.license]
text = "MIT"
+7 -3
View File
@@ -1607,7 +1607,7 @@ class LanguageServer:
"""
parsed_files = await self.request_parsed_files()
files_processed = 0
for relative_file_path in tqdm.tqdm(parsed_files, disable=not progress_bar):
for relative_file_path in tqdm.tqdm(parsed_files, disable=not progress_bar, desc="Indexing (files)"):
await self.request_document_symbols(relative_file_path, include_body=False)
await self.request_document_symbols(relative_file_path, include_body=True)
files_processed += 1
@@ -2205,7 +2205,7 @@ class SyncLanguageServer:
self.language_server.logger.log("Async shutdown tasks completed, returning to main thread", logging.DEBUG)
# Note: NOT calling loop.stop() here - let main thread do it
def stop(self, shutdown_timeout: float = 5.0) -> None:
def stop(self, shutdown_timeout: float = 2.0) -> None:
"""
Stops the language server and robustly cleans up all associated resources,
including the asyncio event loop, to prevent hangs on process exit.
@@ -2230,26 +2230,30 @@ class SyncLanguageServer:
if not is_windows:
# 2. Graceful shutdown for Linux/Unix - try proper async cleanup first
self.language_server.logger.log("Starting graceful shutdown", logging.INFO)
shutdown_future = None
if self.loop and self.loop.is_running():
shutdown_future = asyncio.run_coroutine_threadsafe(self._shutdown_and_stop_loop(), self.loop)
# 3. Wait for the background thread to exit
if self.loop_thread:
self.language_server.logger.log("Waiting for event loop thread to exit", logging.INFO)
self.loop_thread.join(timeout=shutdown_timeout)
if self.loop_thread.is_alive():
self.language_server.logger.log("Event loop thread did not terminate within timeout", logging.WARNING)
# 4. Wait for the shutdown coroutine to complete if it was scheduled
if shutdown_future:
self.language_server.logger.log("Waiting for async shutdown to complete", logging.INFO)
try:
shutdown_future.result(timeout=2.0)
shutdown_future.result(timeout=shutdown_timeout)
self.language_server.logger.log("Graceful async shutdown completed", logging.DEBUG)
except Exception as e:
self.language_server.logger.log(f"Async shutdown failed: {e}", logging.WARNING)
# 5. Close the loop properly after graceful shutdown
if self.loop and not self.loop.is_closed():
self.language_server.logger.log("Closing event loop", logging.INFO)
try:
self.loop.close()
self.language_server.logger.log("Event loop closed successfully", logging.DEBUG)
+19
View File
@@ -23,6 +23,7 @@ from pathlib import Path
from types import TracebackType
from typing import TYPE_CHECKING, Any, Literal, Self, TypeVar, Union, cast
import click
import yaml
from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
from overrides import override
@@ -656,6 +657,7 @@ def create_ls_for_project(
trace_lsp_communication=trace_lsp_communication,
)
ls_logger = MultilspyLogger(log_level=log_level)
logging.basicConfig(format=LOG_DEFAULT_FORMAT, level=log_level, force=True)
log.info(f"Creating language server instance for {project_instance.project_root}.")
return SyncLanguageServer.create(
multilspy_config,
@@ -665,6 +667,23 @@ def create_ls_for_project(
)
@click.command()
@click.argument("project", type=click.Path(exists=True))
@click.option("--log-level", type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]), default="WARNING")
def index_project(project: str, log_level: str = "INFO") -> None:
"""
Index a project by saving the symbols of files to Serena's language server cache.
:param project: the project to index
"""
log_level_int = logging.getLevelNamesMapping()[log_level.upper()]
project = os.path.abspath(project)
log.info(f"Indexing project {project}")
ls = create_ls_for_project(project, log_level=log_level_int)
with ls.start_server():
ls.index_repository()
class SerenaAgent:
def __init__(
self,