config.go

  1package backend
  2
  3import (
  4	"context"
  5
  6	"github.com/charmbracelet/crush/internal/agent"
  7	mcptools "github.com/charmbracelet/crush/internal/agent/tools/mcp"
  8	"github.com/charmbracelet/crush/internal/commands"
  9	"github.com/charmbracelet/crush/internal/config"
 10	"github.com/charmbracelet/crush/internal/oauth"
 11)
 12
 13// MCPResourceContents holds the contents of an MCP resource returned
 14// by the backend.
 15type MCPResourceContents struct {
 16	URI      string `json:"uri"`
 17	MIMEType string `json:"mime_type,omitempty"`
 18	Text     string `json:"text,omitempty"`
 19	Blob     []byte `json:"blob,omitempty"`
 20}
 21
 22// SetConfigField sets a key/value pair in the config file for the
 23// given scope.
 24func (b *Backend) SetConfigField(workspaceID string, scope config.Scope, key string, value any) error {
 25	ws, err := b.GetWorkspace(workspaceID)
 26	if err != nil {
 27		return err
 28	}
 29	return ws.Cfg.SetConfigField(scope, key, value)
 30}
 31
 32// RemoveConfigField removes a key from the config file for the given
 33// scope.
 34func (b *Backend) RemoveConfigField(workspaceID string, scope config.Scope, key string) error {
 35	ws, err := b.GetWorkspace(workspaceID)
 36	if err != nil {
 37		return err
 38	}
 39	return ws.Cfg.RemoveConfigField(scope, key)
 40}
 41
 42// UpdatePreferredModel updates the preferred model for the given type
 43// and persists it to the config file at the given scope.
 44func (b *Backend) UpdatePreferredModel(workspaceID string, scope config.Scope, modelType config.SelectedModelType, model config.SelectedModel) error {
 45	ws, err := b.GetWorkspace(workspaceID)
 46	if err != nil {
 47		return err
 48	}
 49	return ws.Cfg.UpdatePreferredModel(scope, modelType, model)
 50}
 51
 52// SetCompactMode sets the compact mode setting and persists it.
 53func (b *Backend) SetCompactMode(workspaceID string, scope config.Scope, enabled bool) error {
 54	ws, err := b.GetWorkspace(workspaceID)
 55	if err != nil {
 56		return err
 57	}
 58	return ws.Cfg.SetCompactMode(scope, enabled)
 59}
 60
 61// SetProviderAPIKey sets the API key for a provider and persists it.
 62func (b *Backend) SetProviderAPIKey(workspaceID string, scope config.Scope, providerID string, apiKey any) error {
 63	ws, err := b.GetWorkspace(workspaceID)
 64	if err != nil {
 65		return err
 66	}
 67	return ws.Cfg.SetProviderAPIKey(scope, providerID, apiKey)
 68}
 69
 70// ImportCopilot attempts to import a GitHub Copilot token from disk.
 71func (b *Backend) ImportCopilot(workspaceID string) (*oauth.Token, bool, error) {
 72	ws, err := b.GetWorkspace(workspaceID)
 73	if err != nil {
 74		return nil, false, err
 75	}
 76	token, ok := ws.Cfg.ImportCopilot()
 77	return token, ok, nil
 78}
 79
 80// RefreshOAuthToken refreshes the OAuth token for a provider.
 81func (b *Backend) RefreshOAuthToken(ctx context.Context, workspaceID string, scope config.Scope, providerID string) error {
 82	ws, err := b.GetWorkspace(workspaceID)
 83	if err != nil {
 84		return err
 85	}
 86	return ws.Cfg.RefreshOAuthToken(ctx, scope, providerID)
 87}
 88
 89// ProjectNeedsInitialization checks whether the project in this
 90// workspace needs initialization.
 91func (b *Backend) ProjectNeedsInitialization(workspaceID string) (bool, error) {
 92	ws, err := b.GetWorkspace(workspaceID)
 93	if err != nil {
 94		return false, err
 95	}
 96	return config.ProjectNeedsInitialization(ws.Cfg)
 97}
 98
 99// MarkProjectInitialized marks the project as initialized.
100func (b *Backend) MarkProjectInitialized(workspaceID string) error {
101	ws, err := b.GetWorkspace(workspaceID)
102	if err != nil {
103		return err
104	}
105	return config.MarkProjectInitialized(ws.Cfg)
106}
107
108// InitializePrompt builds the initialization prompt for the workspace.
109func (b *Backend) InitializePrompt(workspaceID string) (string, error) {
110	ws, err := b.GetWorkspace(workspaceID)
111	if err != nil {
112		return "", err
113	}
114	return agent.InitializePrompt(ws.Cfg)
115}
116
117// RefreshMCPTools refreshes the tools for a named MCP server.
118func (b *Backend) RefreshMCPTools(ctx context.Context, workspaceID, name string) error {
119	ws, err := b.GetWorkspace(workspaceID)
120	if err != nil {
121		return err
122	}
123	mcptools.RefreshTools(ctx, ws.Cfg, name)
124	return nil
125}
126
127// ReadMCPResource reads a resource from a named MCP server.
128func (b *Backend) ReadMCPResource(ctx context.Context, workspaceID, name, uri string) ([]MCPResourceContents, error) {
129	ws, err := b.GetWorkspace(workspaceID)
130	if err != nil {
131		return nil, err
132	}
133	contents, err := mcptools.ReadResource(ctx, ws.Cfg, name, uri)
134	if err != nil {
135		return nil, err
136	}
137	result := make([]MCPResourceContents, len(contents))
138	for i, c := range contents {
139		result[i] = MCPResourceContents{
140			URI:      c.URI,
141			MIMEType: c.MIMEType,
142			Text:     c.Text,
143			Blob:     c.Blob,
144		}
145	}
146	return result, nil
147}
148
149// GetMCPPrompt retrieves a prompt from a named MCP server.
150func (b *Backend) GetMCPPrompt(workspaceID, clientID, promptID string, args map[string]string) (string, error) {
151	ws, err := b.GetWorkspace(workspaceID)
152	if err != nil {
153		return "", err
154	}
155	return commands.GetMCPPrompt(ws.Cfg, clientID, promptID, args)
156}
157
158// GetWorkingDir returns the working directory for a workspace.
159func (b *Backend) GetWorkingDir(workspaceID string) (string, error) {
160	ws, err := b.GetWorkspace(workspaceID)
161	if err != nil {
162		return "", err
163	}
164	return ws.Cfg.WorkingDir(), nil
165}