diff --git a/internal/agent/tools/diagnostics.go b/internal/agent/tools/diagnostics.go index f44c25b3f4afc61a03972297cf0bddf0ccfdb590..de1fc9a13c95296bb8e637f56b4d0abc4f25c34b 100644 --- a/internal/agent/tools/diagnostics.go +++ b/internal/agent/tools/diagnostics.go @@ -94,7 +94,7 @@ func getDiagnostics(filePath string, lsps *csync.Map[string, *lsp.Client]) strin } out := output.String() - slog.Info("Diagnostics", "output", out) + slog.Debug("Diagnostics", "output", out) return out } diff --git a/internal/agent/tools/mcp/init.go b/internal/agent/tools/mcp/init.go index 734c95e6a80fac4d2ed211bcb886b8c4377e9052..6ad77bcedbf528e9c355bb0533093455ed12bcee 100644 --- a/internal/agent/tools/mcp/init.go +++ b/internal/agent/tools/mcp/init.go @@ -109,14 +109,26 @@ func GetState(name string) (ClientInfo, bool) { // Close closes all MCP clients. This should be called during application shutdown. func Close() error { var errs []error + var wg sync.WaitGroup for name, session := range sessions.Seq2() { - if err := session.Close(); err != nil && - !errors.Is(err, io.EOF) && - !errors.Is(err, context.Canceled) && - err.Error() != "signal: killed" { - errs = append(errs, fmt.Errorf("close mcp: %s: %w", name, err)) - } + wg.Go(func() { + done := make(chan bool, 1) + go func() { + if err := session.Close(); err != nil && + !errors.Is(err, io.EOF) && + !errors.Is(err, context.Canceled) && + err.Error() != "signal: killed" { + errs = append(errs, fmt.Errorf("close mcp: %s: %w", name, err)) + } + done <- true + }() + select { + case <-done: + case <-time.After(time.Millisecond * 250): + } + }) } + wg.Wait() broker.Shutdown() return errors.Join(errs...) } @@ -279,9 +291,8 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve }) }, LoggingMessageHandler: func(_ context.Context, req *mcp.LoggingMessageRequest) { - slog.Info("mcp log", "name", name, "data", req.Params.Data) + slog.Info("MCP log", "name", name, "data", req.Params.Data) }, - KeepAlive: time.Minute * 10, }, ) @@ -289,14 +300,14 @@ func createSession(ctx context.Context, name string, m config.MCPConfig, resolve if err != nil { err = maybeStdioErr(err, transport) updateState(name, StateError, maybeTimeoutErr(err, timeout), nil, Counts{}) - slog.Error("error starting mcp client", "error", err, "name", name) + slog.Error("MCP client failed to initialize", "error", err, "name", name) cancel() cancelTimer.Stop() return nil, err } cancelTimer.Stop() - slog.Info("Initialized mcp client", "name", name) + slog.Info("MCP client initialized", "name", name) return session, nil } diff --git a/internal/app/app.go b/internal/app/app.go index 8519f258502ad10f146870b89c7bb5f0f50994e4..4154cb459ef0d39234c8ce5dd023e75bdb432573 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -370,30 +370,42 @@ func (app *App) Subscribe(program *tea.Program) { // Shutdown performs a graceful shutdown of the application. func (app *App) Shutdown() { + start := time.Now() + defer func() { slog.Info("Shutdown took " + time.Since(start).String()) }() + var wg sync.WaitGroup if app.AgentCoordinator != nil { - app.AgentCoordinator.CancelAll() + wg.Go(func() { + app.AgentCoordinator.CancelAll() + }) } // Kill all background shells. - shell.GetBackgroundShellManager().KillAll() + wg.Go(func() { + shell.GetBackgroundShellManager().KillAll() + }) // Shutdown all LSP clients. for name, client := range app.LSPClients.Seq2() { - shutdownCtx, cancel := context.WithTimeout(app.globalCtx, 5*time.Second) - if err := client.Close(shutdownCtx); err != nil { - slog.Error("Failed to shutdown LSP client", "name", name, "error", err) - } - cancel() + wg.Go(func() { + shutdownCtx, cancel := context.WithTimeout(app.globalCtx, 5*time.Second) + defer cancel() + if err := client.Close(shutdownCtx); err != nil { + slog.Error("Failed to shutdown LSP client", "name", name, "error", err) + } + }) } // Call call cleanup functions. for _, cleanup := range app.cleanupFuncs { if cleanup != nil { - if err := cleanup(); err != nil { - slog.Error("Failed to cleanup app properly on shutdown", "error", err) - } + wg.Go(func() { + if err := cleanup(); err != nil { + slog.Error("Failed to cleanup app properly on shutdown", "error", err) + } + }) } } + wg.Wait() } // checkForUpdates checks for available updates. diff --git a/internal/app/lsp.go b/internal/app/lsp.go index f4c26af2f4ed369a94c7078600ce9639874dc643..1a1ac86ca9ab35040e274b1c1f9b0ff9bb35a17e 100644 --- a/internal/app/lsp.go +++ b/internal/app/lsp.go @@ -23,11 +23,11 @@ func (app *App) initLSPClients(ctx context.Context) { // createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher func (app *App) createAndStartLSPClient(ctx context.Context, name string, config config.LSPConfig) { - slog.Info("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args) + slog.Debug("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args) // Check if any root markers exist in the working directory (config now has defaults) if !lsp.HasRootMarkers(app.config.WorkingDir(), config.RootMarkers) { - slog.Info("Skipping LSP client - no root markers found", "name", name, "rootMarkers", config.RootMarkers) + slog.Debug("Skipping LSP client: no root markers found", "name", name, "rootMarkers", config.RootMarkers) updateLSPState(name, lsp.StateDisabled, nil, nil, 0) return } @@ -53,7 +53,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config // Initialize LSP client. _, err = lspClient.Initialize(initCtx, app.config.WorkingDir()) if err != nil { - slog.Error("Initialize failed", "name", name, "error", err) + slog.Error("LSP client initialization failed", "name", name, "error", err) updateLSPState(name, lsp.StateError, err, lspClient, 0) lspClient.Close(ctx) return @@ -68,7 +68,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config updateLSPState(name, lsp.StateError, err, lspClient, 0) } else { // Server reached a ready state scuccessfully. - slog.Info("LSP server is ready", "name", name) + slog.Debug("LSP server is ready", "name", name) lspClient.SetServerState(lsp.StateReady) updateLSPState(name, lsp.StateReady, nil, lspClient, 0) }