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 CreateAgentToolSessionID(messageID, toolCallID string) string
69 ParseAgentToolSessionID(sessionID string) (messageID string, toolCallID string, ok bool)
70
71 // Messages
72 ListMessages(ctx context.Context, sessionID string) ([]message.Message, error)
73 ListUserMessages(ctx context.Context, sessionID string) ([]message.Message, error)
74 ListAllUserMessages(ctx context.Context) ([]message.Message, error)
75
76 // Agent
77 AgentRun(ctx context.Context, sessionID, prompt string, attachments ...message.Attachment) error
78 AgentCancel(sessionID string)
79 AgentIsBusy() bool
80 AgentIsSessionBusy(sessionID string) bool
81 AgentModel() AgentModel
82 AgentIsReady() bool
83 AgentQueuedPrompts(sessionID string) int
84 AgentQueuedPromptsList(sessionID string) []string
85 AgentClearQueue(sessionID string)
86 AgentSummarize(ctx context.Context, sessionID string) error
87 UpdateAgentModel(ctx context.Context) error
88 InitCoderAgent(ctx context.Context) error
89 GetDefaultSmallModel(providerID string) config.SelectedModel
90
91 // Permissions
92 //
93 // PermissionGrant, PermissionGrantPersistent, and PermissionDeny
94 // return true if the call resolved the pending request and false if
95 // it had already been resolved by another subscriber (or is no
96 // longer pending). A false return is not an error; the modal can
97 // still close locally because the resolution will arrive via the
98 // PermissionNotification event stream regardless of which client
99 // won the race.
100 PermissionGrant(perm permission.PermissionRequest) bool
101 PermissionGrantPersistent(perm permission.PermissionRequest) bool
102 PermissionDeny(perm permission.PermissionRequest) bool
103 PermissionSkipRequests() bool
104 PermissionSetSkipRequests(skip bool)
105
106 // FileTracker
107 FileTrackerRecordRead(ctx context.Context, sessionID, path string)
108 FileTrackerLastReadTime(ctx context.Context, sessionID, path string) time.Time
109 FileTrackerListReadFiles(ctx context.Context, sessionID string) ([]string, error)
110
111 // History
112 ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error)
113
114 // LSP
115 LSPStart(ctx context.Context, path string)
116 LSPStopAll(ctx context.Context)
117 LSPGetStates() map[string]LSPClientInfo
118 LSPGetDiagnosticCounts(name string) lsp.DiagnosticCounts
119
120 // Config (read-only data)
121 Config() *config.Config
122 WorkingDir() string
123 Resolver() config.VariableResolver
124
125 // Config mutations (proxied to server in client mode)
126 UpdatePreferredModel(scope config.Scope, modelType config.SelectedModelType, model config.SelectedModel) error
127 SetCompactMode(scope config.Scope, enabled bool) error
128 SetProviderAPIKey(scope config.Scope, providerID string, apiKey any) error
129 SetConfigField(scope config.Scope, key string, value any) error
130 RemoveConfigField(scope config.Scope, key string) error
131 ImportCopilot() (*oauth.Token, bool)
132 RefreshOAuthToken(ctx context.Context, scope config.Scope, providerID string) error
133
134 // Project lifecycle
135 ProjectNeedsInitialization() (bool, error)
136 MarkProjectInitialized() error
137 InitializePrompt() (string, error)
138
139 // MCP operations (server-side in client mode)
140 MCPGetStates() map[string]mcptools.ClientInfo
141 MCPRefreshPrompts(ctx context.Context, name string)
142 MCPRefreshResources(ctx context.Context, name string)
143 RefreshMCPTools(ctx context.Context, name string)
144 ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
145 GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
146 EnableDockerMCP(ctx context.Context) error
147 DisableDockerMCP() error
148
149 // Events
150 Subscribe(program *tea.Program)
151 Shutdown()
152}
153
154// MCPResourceContents holds the contents of an MCP resource.
155type MCPResourceContents struct {
156 URI string `json:"uri"`
157 MIMEType string `json:"mime_type,omitempty"`
158 Text string `json:"text,omitempty"`
159 Blob []byte `json:"blob,omitempty"`
160}