1package proto
2
3import (
4 "encoding/json"
5 "errors"
6 "time"
7
8 "charm.land/catwalk/pkg/catwalk"
9 "github.com/charmbracelet/crush/internal/config"
10 "github.com/charmbracelet/crush/internal/lsp"
11)
12
13// Workspace represents a running app.App workspace with its associated
14// resources and state.
15type Workspace struct {
16 ID string `json:"id"`
17 Path string `json:"path"`
18 YOLO bool `json:"yolo,omitempty"`
19 Debug bool `json:"debug,omitempty"`
20 DataDir string `json:"data_dir,omitempty"`
21 Version string `json:"version,omitempty"`
22 Config *config.Config `json:"config,omitempty"`
23 Env []string `json:"env,omitempty"`
24 // Skills carries the snapshot of skill discovery state at workspace
25 // creation time. Subsequent updates flow through the SSE event
26 // stream.
27 Skills []SkillState `json:"skills,omitempty"`
28}
29
30// Error represents an error response.
31type Error struct {
32 Message string `json:"message"`
33}
34
35// AgentInfo represents information about the agent.
36type AgentInfo struct {
37 IsBusy bool `json:"is_busy"`
38 IsReady bool `json:"is_ready"`
39 Model catwalk.Model `json:"model"`
40 ModelCfg config.SelectedModel `json:"model_cfg"`
41}
42
43// IsZero checks if the AgentInfo is zero-valued.
44func (a AgentInfo) IsZero() bool {
45 return !a.IsBusy && !a.IsReady && a.Model.ID == ""
46}
47
48// AgentMessage represents a message sent to the agent.
49type AgentMessage struct {
50 SessionID string `json:"session_id"`
51 Prompt string `json:"prompt"`
52 Attachments []Attachment `json:"attachments,omitempty"`
53}
54
55// AgentSession represents a session with its busy status.
56type AgentSession struct {
57 Session
58 IsBusy bool `json:"is_busy"`
59}
60
61// IsZero checks if the AgentSession is zero-valued.
62func (a AgentSession) IsZero() bool {
63 return a.ID == "" && !a.IsBusy
64}
65
66// PermissionAction represents an action taken on a permission request.
67type PermissionAction string
68
69const (
70 PermissionAllow PermissionAction = "allow"
71 PermissionAllowForSession PermissionAction = "allow_session"
72 PermissionDeny PermissionAction = "deny"
73)
74
75// MarshalText implements the [encoding.TextMarshaler] interface.
76func (p PermissionAction) MarshalText() ([]byte, error) {
77 return []byte(p), nil
78}
79
80// UnmarshalText implements the [encoding.TextUnmarshaler] interface.
81func (p *PermissionAction) UnmarshalText(text []byte) error {
82 *p = PermissionAction(text)
83 return nil
84}
85
86// PermissionGrant represents a permission grant request.
87type PermissionGrant struct {
88 Permission PermissionRequest `json:"permission"`
89 Action PermissionAction `json:"action"`
90}
91
92// PermissionSkipRequest represents a request to skip permission prompts.
93type PermissionSkipRequest struct {
94 Skip bool `json:"skip"`
95}
96
97// LSPEventType represents the type of LSP event.
98type LSPEventType string
99
100const (
101 LSPEventStateChanged LSPEventType = "state_changed"
102 LSPEventDiagnosticsChanged LSPEventType = "diagnostics_changed"
103)
104
105// MarshalText implements the [encoding.TextMarshaler] interface.
106func (e LSPEventType) MarshalText() ([]byte, error) {
107 return []byte(e), nil
108}
109
110// UnmarshalText implements the [encoding.TextUnmarshaler] interface.
111func (e *LSPEventType) UnmarshalText(data []byte) error {
112 *e = LSPEventType(data)
113 return nil
114}
115
116// LSPEvent represents an event in the LSP system.
117type LSPEvent struct {
118 Type LSPEventType `json:"type"`
119 Name string `json:"name"`
120 State lsp.ServerState `json:"state"`
121 Error error `json:"error,omitempty"`
122 DiagnosticCount int `json:"diagnostic_count,omitempty"`
123}
124
125// MarshalJSON implements the [json.Marshaler] interface.
126func (e LSPEvent) MarshalJSON() ([]byte, error) {
127 type Alias LSPEvent
128 return json.Marshal(&struct {
129 Error string `json:"error,omitempty"`
130 Alias
131 }{
132 Error: func() string {
133 if e.Error != nil {
134 return e.Error.Error()
135 }
136 return ""
137 }(),
138 Alias: Alias(e),
139 })
140}
141
142// UnmarshalJSON implements the [json.Unmarshaler] interface.
143func (e *LSPEvent) UnmarshalJSON(data []byte) error {
144 type Alias LSPEvent
145 aux := &struct {
146 Error string `json:"error,omitempty"`
147 Alias
148 }{
149 Alias: Alias(*e),
150 }
151 if err := json.Unmarshal(data, &aux); err != nil {
152 return err
153 }
154 *e = LSPEvent(aux.Alias)
155 if aux.Error != "" {
156 e.Error = errors.New(aux.Error)
157 }
158 return nil
159}
160
161// LSPClientInfo holds information about an LSP client's state.
162type LSPClientInfo struct {
163 Name string `json:"name"`
164 State lsp.ServerState `json:"state"`
165 Error error `json:"error,omitempty"`
166 DiagnosticCount int `json:"diagnostic_count,omitempty"`
167 ConnectedAt time.Time `json:"connected_at"`
168}
169
170// MarshalJSON implements the [json.Marshaler] interface.
171func (i LSPClientInfo) MarshalJSON() ([]byte, error) {
172 type Alias LSPClientInfo
173 return json.Marshal(&struct {
174 Error string `json:"error,omitempty"`
175 Alias
176 }{
177 Error: func() string {
178 if i.Error != nil {
179 return i.Error.Error()
180 }
181 return ""
182 }(),
183 Alias: Alias(i),
184 })
185}
186
187// UnmarshalJSON implements the [json.Unmarshaler] interface.
188func (i *LSPClientInfo) UnmarshalJSON(data []byte) error {
189 type Alias LSPClientInfo
190 aux := &struct {
191 Error string `json:"error,omitempty"`
192 Alias
193 }{
194 Alias: Alias(*i),
195 }
196 if err := json.Unmarshal(data, &aux); err != nil {
197 return err
198 }
199 *i = LSPClientInfo(aux.Alias)
200 if aux.Error != "" {
201 i.Error = errors.New(aux.Error)
202 }
203 return nil
204}