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 PermissionGrant(perm permission.PermissionRequest)
93 PermissionGrantPersistent(perm permission.PermissionRequest)
94 PermissionDeny(perm permission.PermissionRequest)
95 PermissionSkipRequests() bool
96 PermissionSetSkipRequests(skip bool)
97
98 // FileTracker
99 FileTrackerRecordRead(ctx context.Context, sessionID, path string)
100 FileTrackerLastReadTime(ctx context.Context, sessionID, path string) time.Time
101 FileTrackerListReadFiles(ctx context.Context, sessionID string) ([]string, error)
102
103 // History
104 ListSessionHistory(ctx context.Context, sessionID string) ([]history.File, error)
105
106 // LSP
107 LSPStart(ctx context.Context, path string)
108 LSPStopAll(ctx context.Context)
109 LSPGetStates() map[string]LSPClientInfo
110 LSPGetClient(name string) (*lsp.Client, bool)
111
112 // Config (read-only data)
113 Config() *config.Config
114 WorkingDir() string
115 Resolver() config.VariableResolver
116
117 // Config mutations (proxied to server in client mode)
118 UpdatePreferredModel(scope config.Scope, modelType config.SelectedModelType, model config.SelectedModel) error
119 SetCompactMode(scope config.Scope, enabled bool) error
120 SetProviderAPIKey(scope config.Scope, providerID string, apiKey any) error
121 SetConfigField(scope config.Scope, key string, value any) error
122 RemoveConfigField(scope config.Scope, key string) error
123 ImportCopilot() (*oauth.Token, bool)
124 RefreshOAuthToken(ctx context.Context, scope config.Scope, providerID string) error
125
126 // Project lifecycle
127 ProjectNeedsInitialization() (bool, error)
128 MarkProjectInitialized() error
129 InitializePrompt() (string, error)
130
131 // MCP operations (server-side in client mode)
132 MCPGetStates() map[string]mcptools.ClientInfo
133 MCPRefreshPrompts(ctx context.Context, name string)
134 MCPRefreshResources(ctx context.Context, name string)
135 RefreshMCPTools(ctx context.Context, name string)
136 ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
137 GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
138
139 // Events
140 Subscribe(program *tea.Program)
141 Shutdown()
142}
143
144// MCPResourceContents holds the contents of an MCP resource.
145type MCPResourceContents struct {
146 URI string `json:"uri"`
147 MIMEType string `json:"mime_type,omitempty"`
148 Text string `json:"text,omitempty"`
149 Blob []byte `json:"blob,omitempty"`
150}