From 2c9670a890c216626e5f9212fe9cf7099525b2da Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Fri, 6 Feb 2026 11:57:59 +0100 Subject: [PATCH] refactor: remove global config (#2132) * refactor: remove global config from ui * refactor: remove global config from lsp * refactor: remove global config --- internal/agent/coordinator.go | 2 +- internal/agent/tools/mcp-tools.go | 7 +++++-- internal/agent/tools/mcp/init.go | 5 ++--- internal/agent/tools/mcp/prompts.go | 5 +++-- internal/agent/tools/mcp/tools.go | 15 +++++++-------- internal/app/lsp.go | 2 +- internal/cmd/login.go | 10 ++++------ internal/cmd/root.go | 6 +++--- internal/commands/commands.go | 4 ++-- internal/config/init.go | 25 ++++++++----------------- internal/lsp/client.go | 22 ++++++++++++---------- internal/lsp/client_test.go | 2 +- internal/lsp/handlers.go | 6 ------ internal/ui/chat/messages.go | 8 +++++--- internal/ui/dialog/api_key_input.go | 2 +- internal/ui/model/header.go | 4 ++-- internal/ui/model/onboarding.go | 2 +- internal/ui/model/ui.go | 12 ++++++------ 18 files changed, 64 insertions(+), 75 deletions(-) diff --git a/internal/agent/coordinator.go b/internal/agent/coordinator.go index 09313f363d5d692971801354e0f5d609a20015ca..ad57b20c4470aa0180120798034db1bdb1de601a 100644 --- a/internal/agent/coordinator.go +++ b/internal/agent/coordinator.go @@ -446,7 +446,7 @@ func (c *coordinator) buildTools(ctx context.Context, agent config.Agent) ([]fan } } - for _, tool := range tools.GetMCPTools(c.permissions, c.cfg.WorkingDir()) { + for _, tool := range tools.GetMCPTools(c.permissions, c.cfg, c.cfg.WorkingDir()) { if agent.AllowedMCP == nil { // No MCP restrictions filteredTools = append(filteredTools, tool) diff --git a/internal/agent/tools/mcp-tools.go b/internal/agent/tools/mcp-tools.go index fa55f03728639a09e6bd2f150338238d30120883..429cadaf6b686b83e170ef35976881d839b07e17 100644 --- a/internal/agent/tools/mcp-tools.go +++ b/internal/agent/tools/mcp-tools.go @@ -6,11 +6,12 @@ import ( "charm.land/fantasy" "github.com/charmbracelet/crush/internal/agent/tools/mcp" + "github.com/charmbracelet/crush/internal/config" "github.com/charmbracelet/crush/internal/permission" ) // GetMCPTools gets all the currently available MCP tools. -func GetMCPTools(permissions permission.Service, wd string) []*Tool { +func GetMCPTools(permissions permission.Service, cfg *config.Config, wd string) []*Tool { var result []*Tool for mcpName, tools := range mcp.Tools() { for _, tool := range tools { @@ -19,6 +20,7 @@ func GetMCPTools(permissions permission.Service, wd string) []*Tool { tool: tool, permissions: permissions, workingDir: wd, + cfg: cfg, }) } } @@ -29,6 +31,7 @@ func GetMCPTools(permissions permission.Service, wd string) []*Tool { type Tool struct { mcpName string tool *mcp.Tool + cfg *config.Config permissions permission.Service workingDir string providerOptions fantasy.ProviderOptions @@ -107,7 +110,7 @@ func (m *Tool) Run(ctx context.Context, params fantasy.ToolCall) (fantasy.ToolRe return fantasy.ToolResponse{}, permission.ErrorPermissionDenied } - result, err := mcp.RunTool(ctx, m.mcpName, m.tool.Name, params.Input) + result, err := mcp.RunTool(ctx, m.cfg, m.mcpName, m.tool.Name, params.Input) if err != nil { return fantasy.NewTextErrorResponse(err.Error()), nil } diff --git a/internal/agent/tools/mcp/init.go b/internal/agent/tools/mcp/init.go index c37f238e6d915d265153518b6df27f07bb6e456e..3138e07d57d96a25569a48dab5b79fb46f52759e 100644 --- a/internal/agent/tools/mcp/init.go +++ b/internal/agent/tools/mcp/init.go @@ -189,7 +189,7 @@ func Initialize(ctx context.Context, permissions permission.Service, cfg *config return } - toolCount := updateTools(name, tools) + toolCount := updateTools(cfg, name, tools) updatePrompts(name, prompts) sessions.Set(name, session) @@ -214,13 +214,12 @@ func WaitForInit(ctx context.Context) error { } } -func getOrRenewClient(ctx context.Context, name string) (*mcp.ClientSession, error) { +func getOrRenewClient(ctx context.Context, cfg *config.Config, name string) (*mcp.ClientSession, error) { sess, ok := sessions.Get(name) if !ok { return nil, fmt.Errorf("mcp '%s' not available", name) } - cfg := config.Get() m := cfg.MCP[name] state, _ := states.Get(name) diff --git a/internal/agent/tools/mcp/prompts.go b/internal/agent/tools/mcp/prompts.go index ea208a57716d2a273fde1b6faa3988ca2e57b012..76338b4a8e349c9177ecaa216be217e241ec402d 100644 --- a/internal/agent/tools/mcp/prompts.go +++ b/internal/agent/tools/mcp/prompts.go @@ -5,6 +5,7 @@ import ( "iter" "log/slog" + "github.com/charmbracelet/crush/internal/config" "github.com/charmbracelet/crush/internal/csync" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -19,8 +20,8 @@ func Prompts() iter.Seq2[string, []*Prompt] { } // GetPromptMessages retrieves the content of an MCP prompt with the given arguments. -func GetPromptMessages(ctx context.Context, clientName, promptName string, args map[string]string) ([]string, error) { - c, err := getOrRenewClient(ctx, clientName) +func GetPromptMessages(ctx context.Context, cfg *config.Config, clientName, promptName string, args map[string]string) ([]string, error) { + c, err := getOrRenewClient(ctx, cfg, clientName) if err != nil { return nil, err } diff --git a/internal/agent/tools/mcp/tools.go b/internal/agent/tools/mcp/tools.go index 65ef5a9d8b3e7304a49bd708ecdd53a3cc400b17..da4b463bbc850ea8bfa0c3400defecf05507951d 100644 --- a/internal/agent/tools/mcp/tools.go +++ b/internal/agent/tools/mcp/tools.go @@ -32,13 +32,13 @@ func Tools() iter.Seq2[string, []*Tool] { } // RunTool runs an MCP tool with the given input parameters. -func RunTool(ctx context.Context, name, toolName string, input string) (ToolResult, error) { +func RunTool(ctx context.Context, cfg *config.Config, name, toolName string, input string) (ToolResult, error) { var args map[string]any if err := json.Unmarshal([]byte(input), &args); err != nil { return ToolResult{}, fmt.Errorf("error parsing parameters: %s", err) } - c, err := getOrRenewClient(ctx, name) + c, err := getOrRenewClient(ctx, cfg, name) if err != nil { return ToolResult{}, err } @@ -108,7 +108,7 @@ func RunTool(ctx context.Context, name, toolName string, input string) (ToolResu // RefreshTools gets the updated list of tools from the MCP and updates the // global state. -func RefreshTools(ctx context.Context, name string) { +func RefreshTools(ctx context.Context, cfg *config.Config, name string) { session, ok := sessions.Get(name) if !ok { slog.Warn("Refresh tools: no session", "name", name) @@ -121,7 +121,7 @@ func RefreshTools(ctx context.Context, name string) { return } - toolCount := updateTools(name, tools) + toolCount := updateTools(cfg, name, tools) prev, _ := states.Get(name) prev.Counts.Tools = toolCount @@ -139,8 +139,8 @@ func getTools(ctx context.Context, session *mcp.ClientSession) ([]*Tool, error) return result.Tools, nil } -func updateTools(name string, tools []*Tool) int { - tools = filterDisabledTools(name, tools) +func updateTools(cfg *config.Config, name string, tools []*Tool) int { + tools = filterDisabledTools(cfg, name, tools) if len(tools) == 0 { allTools.Del(name) return 0 @@ -150,8 +150,7 @@ func updateTools(name string, tools []*Tool) int { } // filterDisabledTools removes tools that are disabled via config. -func filterDisabledTools(mcpName string, tools []*Tool) []*Tool { - cfg := config.Get() +func filterDisabledTools(cfg *config.Config, mcpName string, tools []*Tool) []*Tool { mcpCfg, ok := cfg.MCP[mcpName] if !ok || len(mcpCfg.DisabledTools) == 0 { return tools diff --git a/internal/app/lsp.go b/internal/app/lsp.go index a93fadbd1869f46bb153e19fa15428f74293b7fc..2bb20fad3878a771ce8b6a2a4dc3688de44ba5dd 100644 --- a/internal/app/lsp.go +++ b/internal/app/lsp.go @@ -114,7 +114,7 @@ func (app *App) createAndStartLSPClient(ctx context.Context, name string, config updateLSPState(name, lsp.StateStarting, nil, nil, 0) // Create LSP client. - lspClient, err := lsp.New(ctx, name, config, app.config.Resolver()) + lspClient, err := lsp.New(ctx, name, config, app.config.Resolver(), app.config.Options.DebugLSP) if err != nil { if !userConfigured { slog.Warn("Default LSP config skipped due to error", "name", name, "error", err) diff --git a/internal/cmd/login.go b/internal/cmd/login.go index b38eaeed00ad1def862d83145f256bc219c27fda..bdad4547d6f583b5ae7e5a97bbbbd88a1421e6ee 100644 --- a/internal/cmd/login.go +++ b/internal/cmd/login.go @@ -52,17 +52,16 @@ crush login copilot } switch provider { case "hyper": - return loginHyper() + return loginHyper(app.Config()) case "copilot", "github", "github-copilot": - return loginCopilot() + return loginCopilot(app.Config()) default: return fmt.Errorf("unknown platform: %s", args[0]) } }, } -func loginHyper() error { - cfg := config.Get() +func loginHyper(cfg *config.Config) error { if !hyperp.Enabled() { return fmt.Errorf("hyper not enabled") } @@ -124,10 +123,9 @@ func loginHyper() error { return nil } -func loginCopilot() error { +func loginCopilot(cfg *config.Config) error { ctx := getLoginContext() - cfg := config.Get() if cfg.HasConfigField("providers.copilot.oauth") { fmt.Println("You are already logged in to GitHub Copilot.") return nil diff --git a/internal/cmd/root.go b/internal/cmd/root.go index c6dac2e7801779e359c939e7d595323a8ac22e49..cf6fd0909ebfdf1643e2ad4fc2de868a8b1e1c1a 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -227,21 +227,21 @@ func setupApp(cmd *cobra.Command) (*app.App, error) { return nil, err } - if shouldEnableMetrics() { + if shouldEnableMetrics(cfg) { event.Init() } return appInstance, nil } -func shouldEnableMetrics() bool { +func shouldEnableMetrics(cfg *config.Config) bool { if v, _ := strconv.ParseBool(os.Getenv("CRUSH_DISABLE_METRICS")); v { return false } if v, _ := strconv.ParseBool(os.Getenv("DO_NOT_TRACK")); v { return false } - if config.Get().Options.DisableMetrics { + if cfg.Options.DisableMetrics { return false } return true diff --git a/internal/commands/commands.go b/internal/commands/commands.go index b3fd3915182fa293aefc1fe60ec54e5b369fa591..aeb2ca305dc984c2c450d249d51028858e4e9802 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -227,9 +227,9 @@ func isMarkdownFile(name string) bool { return strings.HasSuffix(strings.ToLower(name), ".md") } -func GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error) { +func GetMCPPrompt(cfg *config.Config, clientID, promptID string, args map[string]string) (string, error) { // TODO: we should pass the context down - result, err := mcp.GetPromptMessages(context.Background(), clientID, promptID, args) + result, err := mcp.GetPromptMessages(context.Background(), cfg, clientID, promptID, args) if err != nil { return "", err } diff --git a/internal/config/init.go b/internal/config/init.go index 36742ed96ea91a1cb8834a2ddabfc8dfc8e56f38..5a4683f77485f54409d4372a33d1933b47abd33f 100644 --- a/internal/config/init.go +++ b/internal/config/init.go @@ -6,7 +6,6 @@ import ( "path/filepath" "slices" "strings" - "sync/atomic" "github.com/charmbracelet/crush/internal/fsext" ) @@ -19,25 +18,15 @@ type ProjectInitFlag struct { Initialized bool `json:"initialized"` } -// TODO: we need to remove the global config instance keeping it now just until everything is migrated -var instance atomic.Pointer[Config] - func Init(workingDir, dataDir string, debug bool) (*Config, error) { cfg, err := Load(workingDir, dataDir, debug) if err != nil { return nil, err } - instance.Store(cfg) - return instance.Load(), nil -} - -func Get() *Config { - cfg := instance.Load() - return cfg + return cfg, nil } -func ProjectNeedsInitialization() (bool, error) { - cfg := Get() +func ProjectNeedsInitialization(cfg *Config) (bool, error) { if cfg == nil { return false, fmt.Errorf("config not loaded") } @@ -110,8 +99,7 @@ func dirHasNoVisibleFiles(dir string) (bool, error) { return len(files) == 0, nil } -func MarkProjectInitialized() error { - cfg := Get() +func MarkProjectInitialized(cfg *Config) error { if cfg == nil { return fmt.Errorf("config not loaded") } @@ -126,10 +114,13 @@ func MarkProjectInitialized() error { return nil } -func HasInitialDataConfig() bool { +func HasInitialDataConfig(cfg *Config) bool { + if cfg == nil { + return false + } cfgPath := GlobalConfigData() if _, err := os.Stat(cfgPath); err != nil { return false } - return Get().IsConfigured() + return cfg.IsConfigured() } diff --git a/internal/lsp/client.go b/internal/lsp/client.go index 6c0059250c062c01ab3d541f4b0ca55ebf0b0cb6..6420cec050e283b3061b2f87275606b4bf9720a1 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -35,6 +35,7 @@ type DiagnosticCounts struct { type Client struct { client *powernap.Client name string + debug bool // Working directory this LSP is scoped to. workDir string @@ -68,7 +69,7 @@ type Client struct { } // New creates a new LSP client using the powernap implementation. -func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config.VariableResolver) (*Client, error) { +func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config.VariableResolver, debug bool) (*Client, error) { client := &Client{ name: name, fileTypes: cfg.FileTypes, @@ -76,6 +77,7 @@ func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config openFiles: csync.NewMap[string, *OpenFileInfo](), config: cfg, ctx: ctx, + debug: debug, resolver: resolver, } client.serverState.Store(StateStarting) @@ -174,7 +176,11 @@ func (c *Client) registerHandlers() { c.RegisterServerRequestHandler("workspace/applyEdit", HandleApplyEdit) c.RegisterServerRequestHandler("workspace/configuration", HandleWorkspaceConfiguration) c.RegisterServerRequestHandler("client/registerCapability", HandleRegisterCapability) - c.RegisterNotificationHandler("window/showMessage", HandleServerMessage) + c.RegisterNotificationHandler("window/showMessage", func(ctx context.Context, method string, params json.RawMessage) { + if c.debug { + HandleServerMessage(ctx, method, params) + } + }) c.RegisterNotificationHandler("textDocument/publishDiagnostics", func(_ context.Context, _ string, params json.RawMessage) { HandleDiagnostics(c, params) }) @@ -262,8 +268,6 @@ func (c *Client) SetDiagnosticsCallback(callback func(name string, count int)) { // WaitForServerReady waits for the server to be ready func (c *Client) WaitForServerReady(ctx context.Context) error { - cfg := config.Get() - // Set initial state c.SetServerState(StateStarting) @@ -275,7 +279,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error { ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() - if cfg != nil && cfg.Options.DebugLSP { + if c.debug { slog.Debug("Waiting for LSP server to be ready...") } @@ -289,7 +293,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error { case <-ticker.C: // Check if client is running if !c.client.IsRunning() { - if cfg != nil && cfg.Options.DebugLSP { + if c.debug { slog.Debug("LSP server not ready yet", "server", c.name) } continue @@ -297,7 +301,7 @@ func (c *Client) WaitForServerReady(ctx context.Context) error { // Server is ready c.SetServerState(StateReady) - if cfg != nil && cfg.Options.DebugLSP { + if c.debug { slog.Debug("LSP server is ready") } return nil @@ -416,10 +420,8 @@ func (c *Client) IsFileOpen(filepath string) bool { // CloseAllFiles closes all currently open files. func (c *Client) CloseAllFiles(ctx context.Context) { - cfg := config.Get() - debugLSP := cfg != nil && cfg.Options.DebugLSP for uri := range c.openFiles.Seq2() { - if debugLSP { + if c.debug { slog.Debug("Closing file", "file", uri) } if err := c.client.NotifyDidCloseTextDocument(ctx, uri); err != nil { diff --git a/internal/lsp/client_test.go b/internal/lsp/client_test.go index 7cc9f2f4ba230a4c6896e7ccef367a450c1c55c7..1de51997f973909a616dc9b07283622b7839a3cb 100644 --- a/internal/lsp/client_test.go +++ b/internal/lsp/client_test.go @@ -23,7 +23,7 @@ func TestClient(t *testing.T) { // but we can still test the basic structure client, err := New(ctx, "test", cfg, config.NewEnvironmentVariableResolver(env.NewFromMap(map[string]string{ "THE_CMD": "echo", - }))) + })), false) if err != nil { // Expected to fail with echo command, skip the rest t.Skipf("Powernap client creation failed as expected with dummy command: %v", err) diff --git a/internal/lsp/handlers.go b/internal/lsp/handlers.go index b386e0780f6f6db6db13be380496c60a6e3c457e..9674ab22c226a4662beb08daa813325b52c079af 100644 --- a/internal/lsp/handlers.go +++ b/internal/lsp/handlers.go @@ -5,7 +5,6 @@ import ( "encoding/json" "log/slog" - "github.com/charmbracelet/crush/internal/config" "github.com/charmbracelet/crush/internal/lsp/util" "github.com/charmbracelet/x/powernap/pkg/lsp/protocol" ) @@ -80,11 +79,6 @@ func notifyFileWatchRegistration(id string, watchers []protocol.FileSystemWatche // HandleServerMessage handles server messages func HandleServerMessage(_ context.Context, method string, params json.RawMessage) { - cfg := config.Get() - if !cfg.Options.DebugLSP { - return - } - var msg protocol.ShowMessageParams if err := json.Unmarshal(params, &msg); err != nil { slog.Debug("Server message", "type", msg.Type, "message", msg.Message) diff --git a/internal/ui/chat/messages.go b/internal/ui/chat/messages.go index 0c5668a20d52c5975dc63cb37da8090e9aa0ca7f..5dac49c08d32ae2315f9d8096f0410b2511ecb04 100644 --- a/internal/ui/chat/messages.go +++ b/internal/ui/chat/messages.go @@ -186,16 +186,18 @@ type AssistantInfoItem struct { id string message *message.Message sty *styles.Styles + cfg *config.Config lastUserMessageTime time.Time } // NewAssistantInfoItem creates a new AssistantInfoItem. -func NewAssistantInfoItem(sty *styles.Styles, message *message.Message, lastUserMessageTime time.Time) MessageItem { +func NewAssistantInfoItem(sty *styles.Styles, message *message.Message, cfg *config.Config, lastUserMessageTime time.Time) MessageItem { return &AssistantInfoItem{ cachedMessageItem: &cachedMessageItem{}, id: AssistantInfoID(message.ID), message: message, sty: sty, + cfg: cfg, lastUserMessageTime: lastUserMessageTime, } } @@ -231,13 +233,13 @@ func (a *AssistantInfoItem) renderContent(width int) string { duration := finishTime.Sub(a.lastUserMessageTime) infoMsg := a.sty.Chat.Message.AssistantInfoDuration.Render(duration.String()) icon := a.sty.Chat.Message.AssistantInfoIcon.Render(styles.ModelIcon) - model := config.Get().GetModel(a.message.Provider, a.message.Model) + model := a.cfg.GetModel(a.message.Provider, a.message.Model) if model == nil { model = &catwalk.Model{Name: "Unknown Model"} } modelFormatted := a.sty.Chat.Message.AssistantInfoModel.Render(model.Name) providerName := a.message.Provider - if providerConfig, ok := config.Get().Providers.Get(a.message.Provider); ok { + if providerConfig, ok := a.cfg.Providers.Get(a.message.Provider); ok { providerName = providerConfig.Name } provider := a.sty.Chat.Message.AssistantInfoProvider.Render(fmt.Sprintf("via %s", providerName)) diff --git a/internal/ui/dialog/api_key_input.go b/internal/ui/dialog/api_key_input.go index f06d9ff8f1af19d6dc04564bf82c8d523eee6525..9677763b2f4f2436376f5bf16ab58aed79140c68 100644 --- a/internal/ui/dialog/api_key_input.go +++ b/internal/ui/dialog/api_key_input.go @@ -296,7 +296,7 @@ func (m *APIKeyInput) verifyAPIKey() tea.Msg { Type: m.provider.Type, BaseURL: m.provider.APIEndpoint, } - err := providerConfig.TestConnection(config.Get().Resolver()) + err := providerConfig.TestConnection(m.com.Config().Resolver()) // intentionally wait for at least 750ms to make sure the user sees the spinner elapsed := time.Since(start) diff --git a/internal/ui/model/header.go b/internal/ui/model/header.go index 3d576e85d022192bb8435909915b8d1d7c5a04ee..2f6e093027783dca62f3d6cde12d61126c6061bb 100644 --- a/internal/ui/model/header.go +++ b/internal/ui/model/header.go @@ -117,8 +117,8 @@ func renderHeaderDetails( parts = append(parts, t.LSP.ErrorDiagnostic.Render(fmt.Sprintf("%s%d", styles.LSPErrorIcon, errorCount))) } - agentCfg := config.Get().Agents[config.AgentCoder] - model := config.Get().GetModelByType(agentCfg.Model) + agentCfg := com.Config().Agents[config.AgentCoder] + model := com.Config().GetModelByType(agentCfg.Model) percentage := (float64(session.CompletionTokens+session.PromptTokens) / float64(model.ContextWindow)) * 100 formattedPercentage := t.Header.Percentage.Render(fmt.Sprintf("%d%%", int(percentage))) parts = append(parts, formattedPercentage) diff --git a/internal/ui/model/onboarding.go b/internal/ui/model/onboarding.go index 0a6ec0775b9f21da9bac4ed5ac2a7013457176a1..075067d75333fc539152f0041b4e5a3c2eed1c5e 100644 --- a/internal/ui/model/onboarding.go +++ b/internal/ui/model/onboarding.go @@ -19,7 +19,7 @@ import ( // markProjectInitialized marks the current project as initialized in the config. func (m *UI) markProjectInitialized() tea.Msg { // TODO: handle error so we show it in the tui footer - err := config.MarkProjectInitialized() + err := config.MarkProjectInitialized(m.com.Config()) if err != nil { slog.Error(err.Error()) } diff --git a/internal/ui/model/ui.go b/internal/ui/model/ui.go index a01fafc2905f84e31fce9ce1914bdd8274e26ad4..7f4f01c5bdc2e7240716cc5c41a27892a4bcedde 100644 --- a/internal/ui/model/ui.go +++ b/internal/ui/model/ui.go @@ -298,7 +298,7 @@ func New(com *common.Common) *UI { desiredFocus := uiFocusEditor if !com.Config().IsConfigured() { desiredState = uiOnboarding - } else if n, _ := config.ProjectNeedsInitialization(); n { + } else if n, _ := config.ProjectNeedsInitialization(com.Config()); n { desiredState = uiInitialize } @@ -776,7 +776,7 @@ func (m *UI) setSessionMessages(msgs []message.Message) tea.Cmd { case message.Assistant: items = append(items, chat.ExtractMessageItems(m.com.Styles, msg, toolResultMap)...) if msg.FinishPart() != nil && msg.FinishPart().Reason == message.FinishReasonEndTurn { - infoItem := chat.NewAssistantInfoItem(m.com.Styles, msg, time.Unix(m.lastUserMessageTime, 0)) + infoItem := chat.NewAssistantInfoItem(m.com.Styles, msg, m.com.Config(), time.Unix(m.lastUserMessageTime, 0)) items = append(items, infoItem) } default: @@ -906,7 +906,7 @@ func (m *UI) appendSessionMessage(msg message.Message) tea.Cmd { } } if msg.FinishPart() != nil && msg.FinishPart().Reason == message.FinishReasonEndTurn { - infoItem := chat.NewAssistantInfoItem(m.com.Styles, &msg, time.Unix(m.lastUserMessageTime, 0)) + infoItem := chat.NewAssistantInfoItem(m.com.Styles, &msg, m.com.Config(), time.Unix(m.lastUserMessageTime, 0)) m.chat.AppendMessages(infoItem) if atBottom { if cmd := m.chat.ScrollToBottomAndAnimate(); cmd != nil { @@ -977,7 +977,7 @@ func (m *UI) updateSessionMessage(msg message.Message) tea.Cmd { if shouldRenderAssistant && msg.FinishPart() != nil && msg.FinishPart().Reason == message.FinishReasonEndTurn { if infoItem := m.chat.MessageItem(chat.AssistantInfoID(msg.ID)); infoItem == nil { - newInfoItem := chat.NewAssistantInfoItem(m.com.Styles, &msg, time.Unix(m.lastUserMessageTime, 0)) + newInfoItem := chat.NewAssistantInfoItem(m.com.Styles, &msg, m.com.Config(), time.Unix(m.lastUserMessageTime, 0)) m.chat.AppendMessages(newInfoItem) } } @@ -1249,7 +1249,7 @@ func (m *UI) handleDialogMsg(msg tea.Msg) tea.Cmd { // Attempt to import GitHub Copilot tokens from VSCode if available. if isCopilot && !isConfigured() { - config.Get().ImportCopilot() + m.com.Config().ImportCopilot() } if !isConfigured() { @@ -3063,7 +3063,7 @@ func (m *UI) drawSessionDetails(scr uv.Screen, area uv.Rectangle) { func (m *UI) runMCPPrompt(clientID, promptID string, arguments map[string]string) tea.Cmd { load := func() tea.Msg { - prompt, err := commands.GetMCPPrompt(clientID, promptID, arguments) + prompt, err := commands.GetMCPPrompt(m.com.Config(), clientID, promptID, arguments) if err != nil { // TODO: make this better return util.ReportError(err)()