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 "github.com/charmbracelet/crush/internal/skills"
22)
23
24// LSPClientInfo holds information about an LSP client's state. This is
25// the frontend-facing type; implementations translate from the
26// underlying app or proto representation.
27type LSPClientInfo struct {
28 Name string
29 State lsp.ServerState
30 Error error
31 DiagnosticCount int
32 ConnectedAt time.Time
33}
34
35// LSPEventType represents the type of LSP event.
36type LSPEventType string
37
38const (
39 LSPEventStateChanged LSPEventType = "state_changed"
40 LSPEventDiagnosticsChanged LSPEventType = "diagnostics_changed"
41)
42
43// LSPEvent represents an LSP event forwarded to the TUI.
44type LSPEvent struct {
45 Type LSPEventType
46 Name string
47 State lsp.ServerState
48 Error error
49 DiagnosticCount int
50}
51
52// AgentModel holds the model information exposed to the UI.
53type AgentModel struct {
54 CatwalkCfg catwalk.Model
55 ModelCfg config.SelectedModel
56}
57
58// Workspace is the main abstraction consumed by the TUI and CLI. It
59// groups every operation a frontend needs to perform against a running
60// workspace, regardless of whether the workspace is in-process or
61// remote.
62type Workspace interface {
63 // Sessions
64 CreateSession(ctx context.Context, title string) (session.Session, error)
65 GetSession(ctx context.Context, sessionID string) (session.Session, error)
66 ListSessions(ctx context.Context) ([]session.Session, error)
67 SaveSession(ctx context.Context, sess session.Session) (session.Session, error)
68 DeleteSession(ctx context.Context, sessionID string) 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 ListSkills(ctx context.Context) ([]skills.CatalogEntry, error)
132 ReadSkill(ctx context.Context, skillID string) ([]byte, skills.SkillReadResult, error)
133
134 // MCP operations (server-side in client mode)
135 MCPGetStates() map[string]mcptools.ClientInfo
136 MCPRefreshPrompts(ctx context.Context, name string)
137 MCPRefreshResources(ctx context.Context, name string)
138 RefreshMCPTools(ctx context.Context, name string)
139 ReadMCPResource(ctx context.Context, name, uri string) ([]MCPResourceContents, error)
140 GetMCPPrompt(clientID, promptID string, args map[string]string) (string, error)
141 EnableDockerMCP(ctx context.Context) error
142 DisableDockerMCP() error
143
144 // Events
145 Subscribe(program *tea.Program)
146 Shutdown()
147}
148
149// MCPResourceContents holds the contents of an MCP resource.
150type MCPResourceContents struct {
151 URI string `json:"uri"`
152 MIMEType string `json:"mime_type,omitempty"`
153 Text string `json:"text,omitempty"`
154 Blob []byte `json:"blob,omitempty"`
155}