1// Package workspace defines the Workspace interface used by all
2// frontends (TUI, CLI) to interact with a running workspace. Two
3// implementations exist: one wrapping a local app.App instance and one
4// wrapping the HTTP client SDK.
5package workspace
6
7import (
8 "context"
9 "time"
10
11 tea "charm.land/bubbletea/v2"
12 "charm.land/catwalk/pkg/catwalk"
13 mcptools "github.com/charmbracelet/crush/internal/agent/tools/mcp"
14 "github.com/charmbracelet/crush/internal/config"
15 "github.com/charmbracelet/crush/internal/history"
16 "github.com/charmbracelet/crush/internal/lsp"
17 "github.com/charmbracelet/crush/internal/message"
18 "github.com/charmbracelet/crush/internal/oauth"
19 "github.com/charmbracelet/crush/internal/permission"
20 "github.com/charmbracelet/crush/internal/session"
21)
22
23// LSPClientInfo holds information about an LSP client's state. This is
24// the frontend-facing type; implementations translate from the
25// underlying app or proto representation.
26type LSPClientInfo struct {
27 Name string
28 State lsp.ServerState
29 Error error
30 DiagnosticCount int
31 ConnectedAt time.Time
32}
33
34// LSPEventType represents the type of LSP event.
35type LSPEventType string
36
37const (
38 LSPEventStateChanged LSPEventType = "state_changed"
39 LSPEventDiagnosticsChanged LSPEventType = "diagnostics_changed"
40)
41
42// LSPEvent represents an LSP event forwarded to the TUI.
43type LSPEvent struct {
44 Type LSPEventType
45 Name string
46 State lsp.ServerState
47 Error error
48 DiagnosticCount int
49}
50
51// AgentModel holds the model information exposed to the UI.
52type AgentModel struct {
53 CatwalkCfg catwalk.Model
54 ModelCfg config.SelectedModel
55}
56
57// Workspace is the main abstraction consumed by the TUI and CLI. It
58// groups every operation a frontend needs to perform against a running
59// workspace, regardless of whether the workspace is in-process or
60// remote.
61type Workspace interface {
62 // Sessions
63 CreateSession(ctx context.Context, title string) (session.Session, error)
64 GetSession(ctx context.Context, sessionID string) (session.Session, error)
65 ListSessions(ctx context.Context) ([]session.Session, error)
66 SaveSession(ctx context.Context, sess session.Session) (session.Session, error)
67 DeleteSession(ctx context.Context, sessionID string) error
68 UpdateSessionModels(ctx context.Context, sessionID string, models map[config.SelectedModelType]config.SelectedModel) error
69 CreateAgentToolSessionID(messageID, toolCallID string) string
70 ParseAgentToolSessionID(sessionID string) (messageID string, toolCallID string, ok bool)
71
72 // Messages
73 ListMessages(ctx context.Context, sessionID string) ([]message.Message, error)
74 ListUserMessages(ctx context.Context, sessionID string) ([]message.Message, error)
75 ListAllUserMessages(ctx context.Context) ([]message.Message, error)
76
77 // Agent
78 AgentRun(ctx context.Context, sessionID, prompt string, attachments ...message.Attachment) error
79 AgentCancel(sessionID string)
80 AgentIsBusy() bool
81 AgentIsSessionBusy(sessionID string) bool
82 AgentModel() AgentModel
83 AgentIsReady() bool
84 AgentQueuedPrompts(sessionID string) int
85 AgentQueuedPromptsList(sessionID string) []string
86 AgentClearQueue(sessionID string)
87 AgentSummarize(ctx context.Context, sessionID string) error
88 UpdateAgentModel(ctx context.Context) error
89 InitCoderAgent(ctx context.Context) error
90 GetDefaultSmallModel(providerID string) config.SelectedModel
91
92 // Permissions
93 PermissionGrant(perm permission.PermissionRequest)
94 PermissionGrantPersistent(perm permission.PermissionRequest)
95 PermissionDeny(perm permission.PermissionRequest)
96 PermissionSkipRequests() bool
97 PermissionSetSkipRequests(skip bool)
98
99 // FileTracker
100 FileTrackerRecordRead(ctx context.Context, sessionID, path string)
101 FileTrackerLastReadTime(ctx context.Context, sessionID, path string) time.Time
102 FileTrackerListReadFiles(ctx context.Context, sessionID string) ([]string, error)
103
104 // History
105 ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error)
106
107 // LSP
108 LSPStart(ctx context.Context, path string)
109 LSPStopAll(ctx context.Context)
110 LSPGetStates() map[string]LSPClientInfo
111 LSPGetDiagnosticCounts(name string) lsp.DiagnosticCounts
112
113 // Config (read-only data)
114 Config() *config.Config
115 WorkingDir() string
116 Resolver() config.VariableResolver
117
118 // Config mutations (proxied to server in client mode)
119 UpdatePreferredModel(scope config.Scope, modelType config.SelectedModelType, model config.SelectedModel) error
120 SetCompactMode(scope config.Scope, enabled bool) error
121 SetProviderAPIKey(scope config.Scope, providerID string, apiKey any) error
122 SetConfigField(scope config.Scope, key string, value any) error
123 RemoveConfigField(scope config.Scope, key string) error
124 ImportCopilot() (*oauth.Token, bool)
125 RefreshOAuthToken(ctx context.Context, scope config.Scope, providerID string) error
126
127 // Project lifecycle
128 ProjectNeedsInitialization() (bool, error)
129 MarkProjectInitialized() error
130 InitializePrompt() (string, error)
131
132 // MCP operations (server-side in client mode)
133 MCPGetStates() map[string]mcptools.ClientInfo
134 MCPRefreshPrompts(ctx context.Context, name string)
135 MCPRefreshResources(ctx context.Context, name string)
136 RefreshMCPTools(ctx context.Context, name string)
137 ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
138 GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
139 EnableDockerMCP(ctx context.Context) error
140 DisableDockerMCP() error
141
142 // Events
143 Subscribe(program *tea.Program)
144 Shutdown()
145}
146
147// MCPResourceContents holds the contents of an MCP resource.
148type MCPResourceContents struct {
149 URI string `json:"uri"`
150 MIMEType string `json:"mime_type,omitempty"`
151 Text string `json:"text,omitempty"`
152 Blob []byte `json:"blob,omitempty"`
153}