@caretive/caret-cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmrc.tmp +2 -0
- package/README.md +72 -0
- package/cmd/cline/main.go +348 -0
- package/cmd/cline-host/main.go +71 -0
- package/e2e/default_update_test.go +154 -0
- package/e2e/helpers_test.go +378 -0
- package/e2e/main_test.go +47 -0
- package/e2e/mixed_stress_test.go +120 -0
- package/e2e/sqlite_helper.go +161 -0
- package/e2e/start_list_test.go +178 -0
- package/go.mod +64 -0
- package/go.sum +162 -0
- package/man/cline.1 +331 -0
- package/man/cline.1.md +332 -0
- package/package.json +54 -0
- package/pkg/cli/auth/auth_cline_provider.go +285 -0
- package/pkg/cli/auth/auth_menu.go +323 -0
- package/pkg/cli/auth/auth_subscription.go +130 -0
- package/pkg/cli/auth/byo_quick_setup.go +247 -0
- package/pkg/cli/auth/models_cline.go +141 -0
- package/pkg/cli/auth/models_list_fetch.go +156 -0
- package/pkg/cli/auth/models_list_static.go +69 -0
- package/pkg/cli/auth/providers_byo.go +184 -0
- package/pkg/cli/auth/providers_list.go +517 -0
- package/pkg/cli/auth/update_api_configurations.go +647 -0
- package/pkg/cli/auth/wizard_byo.go +764 -0
- package/pkg/cli/auth/wizard_byo_bedrock.go +193 -0
- package/pkg/cli/auth/wizard_byo_oca.go +366 -0
- package/pkg/cli/auth.go +43 -0
- package/pkg/cli/clerror/cline_error.go +187 -0
- package/pkg/cli/config/manager.go +208 -0
- package/pkg/cli/config/settings_renderer.go +198 -0
- package/pkg/cli/config.go +152 -0
- package/pkg/cli/display/ansi.go +27 -0
- package/pkg/cli/display/banner.go +211 -0
- package/pkg/cli/display/deduplicator.go +95 -0
- package/pkg/cli/display/markdown_renderer.go +139 -0
- package/pkg/cli/display/renderer.go +304 -0
- package/pkg/cli/display/segment_streamer.go +212 -0
- package/pkg/cli/display/streaming.go +134 -0
- package/pkg/cli/display/system_renderer.go +269 -0
- package/pkg/cli/display/tool_renderer.go +455 -0
- package/pkg/cli/display/tool_result_parser.go +371 -0
- package/pkg/cli/display/typewriter.go +210 -0
- package/pkg/cli/doctor.go +65 -0
- package/pkg/cli/global/cline-clients.go +501 -0
- package/pkg/cli/global/global.go +113 -0
- package/pkg/cli/global/registry.go +304 -0
- package/pkg/cli/handlers/ask_handlers.go +339 -0
- package/pkg/cli/handlers/handler.go +130 -0
- package/pkg/cli/handlers/say_handlers.go +521 -0
- package/pkg/cli/instances.go +506 -0
- package/pkg/cli/logs.go +382 -0
- package/pkg/cli/output/coordinator.go +167 -0
- package/pkg/cli/output/input_model.go +497 -0
- package/pkg/cli/sqlite/locks.go +366 -0
- package/pkg/cli/task/history_handler.go +72 -0
- package/pkg/cli/task/input_handler.go +577 -0
- package/pkg/cli/task/manager.go +1283 -0
- package/pkg/cli/task/settings_parser.go +754 -0
- package/pkg/cli/task/stream_coordinator.go +60 -0
- package/pkg/cli/task.go +675 -0
- package/pkg/cli/terminal/keyboard.go +695 -0
- package/pkg/cli/tui/HELP_WANTED.md +1 -0
- package/pkg/cli/types/history.go +17 -0
- package/pkg/cli/types/messages.go +329 -0
- package/pkg/cli/types/state.go +59 -0
- package/pkg/cli/updater/updater.go +409 -0
- package/pkg/cli/version.go +43 -0
- package/pkg/common/constants.go +6 -0
- package/pkg/common/schema.go +54 -0
- package/pkg/common/types.go +54 -0
- package/pkg/common/utils.go +185 -0
- package/pkg/generated/field_overrides.go +39 -0
- package/pkg/generated/providers.go +1584 -0
- package/pkg/hostbridge/diff.go +351 -0
- package/pkg/hostbridge/disabled/watch.go +39 -0
- package/pkg/hostbridge/disabled/window.go +63 -0
- package/pkg/hostbridge/disabled/workspace.go +66 -0
- package/pkg/hostbridge/env.go +166 -0
- package/pkg/hostbridge/grpc_server.go +113 -0
- package/pkg/hostbridge/simple.go +43 -0
- package/pkg/hostbridge/simple_workspace.go +85 -0
- package/pkg/hostbridge/window.go +129 -0
- package/scripts/publish-caret-cli.sh +39 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
package handlers
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"github.com/cline/cli/pkg/cli/display"
|
|
5
|
+
"github.com/cline/cli/pkg/cli/types"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// MessageHandler defines the interface for handling different message types
|
|
9
|
+
type MessageHandler interface {
|
|
10
|
+
// CanHandle returns true if this handler can process the given message
|
|
11
|
+
CanHandle(msg *types.ClineMessage) bool
|
|
12
|
+
|
|
13
|
+
// Handle processes the message and renders it using the display context
|
|
14
|
+
Handle(msg *types.ClineMessage, dc *DisplayContext) error
|
|
15
|
+
|
|
16
|
+
// GetPriority returns the priority of this handler (higher = more priority)
|
|
17
|
+
GetPriority() int
|
|
18
|
+
|
|
19
|
+
// GetName returns a human-readable name for this handler
|
|
20
|
+
GetName() string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// DisplayContext provides context and utilities for message handlers
|
|
24
|
+
type DisplayContext struct {
|
|
25
|
+
State *types.ConversationState
|
|
26
|
+
Renderer *display.Renderer
|
|
27
|
+
ToolRenderer *display.ToolRenderer
|
|
28
|
+
SystemRenderer *display.SystemMessageRenderer
|
|
29
|
+
IsLast bool
|
|
30
|
+
IsPartial bool
|
|
31
|
+
Verbose bool
|
|
32
|
+
MessageIndex int
|
|
33
|
+
IsStreamingMode bool
|
|
34
|
+
IsInteractive bool
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// BaseHandler provides common functionality for message handlers
|
|
38
|
+
type BaseHandler struct {
|
|
39
|
+
name string
|
|
40
|
+
priority int
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// NewBaseHandler creates a new base handler
|
|
44
|
+
func NewBaseHandler(name string, priority int) *BaseHandler {
|
|
45
|
+
return &BaseHandler{
|
|
46
|
+
name: name,
|
|
47
|
+
priority: priority,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// GetName returns the handler name
|
|
52
|
+
func (h *BaseHandler) GetName() string {
|
|
53
|
+
return h.name
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// GetPriority returns the handler priority
|
|
57
|
+
func (h *BaseHandler) GetPriority() int {
|
|
58
|
+
return h.priority
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// HandlerRegistry manages a collection of message handlers
|
|
62
|
+
type HandlerRegistry struct {
|
|
63
|
+
handlers []MessageHandler
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// NewHandlerRegistry creates a new handler registry
|
|
67
|
+
func NewHandlerRegistry() *HandlerRegistry {
|
|
68
|
+
return &HandlerRegistry{
|
|
69
|
+
handlers: make([]MessageHandler, 0),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Register adds a handler to the registry
|
|
74
|
+
func (r *HandlerRegistry) Register(handler MessageHandler) {
|
|
75
|
+
r.handlers = append(r.handlers, handler)
|
|
76
|
+
|
|
77
|
+
// Sort handlers by priority (highest first)
|
|
78
|
+
for i := len(r.handlers) - 1; i > 0; i-- {
|
|
79
|
+
if r.handlers[i].GetPriority() > r.handlers[i-1].GetPriority() {
|
|
80
|
+
r.handlers[i], r.handlers[i-1] = r.handlers[i-1], r.handlers[i]
|
|
81
|
+
} else {
|
|
82
|
+
break
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle finds the appropriate handler and processes the message
|
|
88
|
+
func (r *HandlerRegistry) Handle(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
89
|
+
for _, handler := range r.handlers {
|
|
90
|
+
if handler.CanHandle(msg) {
|
|
91
|
+
return handler.Handle(msg, dc)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// If no specific handler found, use default text handler
|
|
96
|
+
return r.handleDefault(msg, dc)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// handleDefault provides default handling for unrecognized messages
|
|
100
|
+
func (r *HandlerRegistry) handleDefault(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
101
|
+
if msg.Text == "" {
|
|
102
|
+
return nil
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
prefix := "RESPONSE:"
|
|
106
|
+
|
|
107
|
+
return dc.Renderer.RenderMessage(prefix, msg.Text, true)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// GetHandlers returns all registered handlers
|
|
111
|
+
func (r *HandlerRegistry) GetHandlers() []MessageHandler {
|
|
112
|
+
return r.handlers
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// GetHandlerByName finds a handler by name
|
|
116
|
+
func (r *HandlerRegistry) GetHandlerByName(name string) MessageHandler {
|
|
117
|
+
for _, handler := range r.handlers {
|
|
118
|
+
if handler.GetName() == name {
|
|
119
|
+
return handler
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return nil
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// HandlerPriorities defines standard priority levels for handlers
|
|
126
|
+
const (
|
|
127
|
+
PriorityHigh = 100
|
|
128
|
+
PriorityNormal = 50
|
|
129
|
+
PriorityLow = 10
|
|
130
|
+
)
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
package handlers
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
"github.com/cline/cli/pkg/cli/clerror"
|
|
9
|
+
"github.com/cline/cli/pkg/cli/types"
|
|
10
|
+
"github.com/cline/cli/pkg/cli/output"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// SayHandler handles SAY type messages
|
|
14
|
+
type SayHandler struct {
|
|
15
|
+
*BaseHandler
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// NewSayHandler creates a new SAY handler
|
|
19
|
+
func NewSayHandler() *SayHandler {
|
|
20
|
+
return &SayHandler{
|
|
21
|
+
BaseHandler: NewBaseHandler("say", PriorityNormal),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// CanHandle returns true if this is a SAY message
|
|
26
|
+
func (h *SayHandler) CanHandle(msg *types.ClineMessage) bool {
|
|
27
|
+
return msg.IsSay()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Handle processes SAY messages
|
|
31
|
+
func (h *SayHandler) Handle(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
32
|
+
timestamp := msg.GetTimestamp()
|
|
33
|
+
|
|
34
|
+
switch msg.Say {
|
|
35
|
+
case string(types.SayTypeTask):
|
|
36
|
+
return h.handleTask(msg, dc)
|
|
37
|
+
case string(types.SayTypeError):
|
|
38
|
+
return h.handleError(msg, dc)
|
|
39
|
+
case string(types.SayTypeAPIReqStarted):
|
|
40
|
+
return h.handleAPIReqStarted(msg, dc)
|
|
41
|
+
case string(types.SayTypeAPIReqFinished):
|
|
42
|
+
return h.handleAPIReqFinished(msg, dc)
|
|
43
|
+
case string(types.SayTypeText):
|
|
44
|
+
return h.handleText(msg, dc)
|
|
45
|
+
case string(types.SayTypeReasoning):
|
|
46
|
+
return h.handleReasoning(msg, dc)
|
|
47
|
+
case string(types.SayTypeCompletionResult):
|
|
48
|
+
return h.handleCompletionResult(msg, dc)
|
|
49
|
+
case string(types.SayTypeUserFeedback):
|
|
50
|
+
return h.handleUserFeedback(msg, dc)
|
|
51
|
+
case string(types.SayTypeUserFeedbackDiff):
|
|
52
|
+
return h.handleUserFeedbackDiff(msg, dc)
|
|
53
|
+
case string(types.SayTypeAPIReqRetried):
|
|
54
|
+
return h.handleAPIReqRetried(msg, dc)
|
|
55
|
+
case string(types.SayTypeErrorRetry):
|
|
56
|
+
return h.handleErrorRetry(msg, dc)
|
|
57
|
+
case string(types.SayTypeCommand):
|
|
58
|
+
return h.handleCommand(msg, dc)
|
|
59
|
+
case string(types.SayTypeCommandOutput):
|
|
60
|
+
return h.handleCommandOutput(msg, dc)
|
|
61
|
+
case string(types.SayTypeTool):
|
|
62
|
+
return h.handleTool(msg, dc)
|
|
63
|
+
case string(types.SayTypeShellIntegrationWarning):
|
|
64
|
+
return h.handleShellIntegrationWarning(msg, dc)
|
|
65
|
+
case string(types.SayTypeBrowserActionLaunch):
|
|
66
|
+
return h.handleBrowserActionLaunch(msg, dc)
|
|
67
|
+
case string(types.SayTypeBrowserAction):
|
|
68
|
+
return h.handleBrowserAction(msg, dc)
|
|
69
|
+
case string(types.SayTypeBrowserActionResult):
|
|
70
|
+
return h.handleBrowserActionResult(msg, dc)
|
|
71
|
+
case string(types.SayTypeMcpServerRequestStarted):
|
|
72
|
+
return h.handleMcpServerRequestStarted(msg, dc)
|
|
73
|
+
case string(types.SayTypeMcpServerResponse):
|
|
74
|
+
return h.handleMcpServerResponse(msg, dc)
|
|
75
|
+
case string(types.SayTypeMcpNotification):
|
|
76
|
+
return h.handleMcpNotification(msg, dc)
|
|
77
|
+
case string(types.SayTypeUseMcpServer):
|
|
78
|
+
return h.handleUseMcpServer(msg, dc)
|
|
79
|
+
case string(types.SayTypeDiffError):
|
|
80
|
+
return h.handleDiffError(msg, dc)
|
|
81
|
+
case string(types.SayTypeDeletedAPIReqs):
|
|
82
|
+
return h.handleDeletedAPIReqs(msg, dc)
|
|
83
|
+
case string(types.SayTypeClineignoreError):
|
|
84
|
+
return h.handleClineignoreError(msg, dc)
|
|
85
|
+
case string(types.SayTypeCheckpointCreated):
|
|
86
|
+
return h.handleCheckpointCreated(msg, dc, timestamp)
|
|
87
|
+
case string(types.SayTypeLoadMcpDocumentation):
|
|
88
|
+
return h.handleLoadMcpDocumentation(msg, dc)
|
|
89
|
+
case string(types.SayTypeInfo):
|
|
90
|
+
return h.handleInfo(msg, dc)
|
|
91
|
+
case string(types.SayTypeTaskProgress):
|
|
92
|
+
return h.handleTaskProgress(msg, dc)
|
|
93
|
+
default:
|
|
94
|
+
return h.handleDefault(msg, dc)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// handleTask handles task messages
|
|
99
|
+
func (h *SayHandler) handleTask(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
100
|
+
return nil
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// handleError handles error messages
|
|
104
|
+
func (h *SayHandler) handleError(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
105
|
+
return dc.Renderer.RenderMessage("ERROR", msg.Text, true)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// handleAPIReqStarted handles API request started messages
|
|
109
|
+
func (h *SayHandler) handleAPIReqStarted(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
110
|
+
// Parse API request info
|
|
111
|
+
apiInfo := types.APIRequestInfo{Cost: -1}
|
|
112
|
+
if err := json.Unmarshal([]byte(msg.Text), &apiInfo); err != nil {
|
|
113
|
+
return dc.Renderer.RenderMessage("API INFO", msg.Text, true)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check for streaming failed message with error details
|
|
117
|
+
if apiInfo.StreamingFailedMessage != "" {
|
|
118
|
+
clineErr, _ := clerror.ParseClineError(apiInfo.StreamingFailedMessage)
|
|
119
|
+
if clineErr != nil {
|
|
120
|
+
return h.renderClineError(clineErr, dc)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Handle different API request states
|
|
125
|
+
if apiInfo.CancelReason != "" {
|
|
126
|
+
if apiInfo.CancelReason == "user_cancelled" {
|
|
127
|
+
return dc.Renderer.RenderMessage("API INFO", "Request Cancelled", true)
|
|
128
|
+
} else if apiInfo.CancelReason == "retries_exhausted" {
|
|
129
|
+
return dc.Renderer.RenderMessage("API INFO", "Request Failed (Retries Exhausted)", true)
|
|
130
|
+
}
|
|
131
|
+
return dc.Renderer.RenderMessage("API INFO", "Streaming Failed", true)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if apiInfo.Cost >= 0 {
|
|
135
|
+
return dc.Renderer.RenderAPI("request completed", &apiInfo)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check for retry status
|
|
139
|
+
if apiInfo.RetryStatus != nil {
|
|
140
|
+
return dc.Renderer.RenderRetry(
|
|
141
|
+
apiInfo.RetryStatus.Attempt,
|
|
142
|
+
apiInfo.RetryStatus.MaxAttempts,
|
|
143
|
+
apiInfo.RetryStatus.DelaySec)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return dc.Renderer.RenderAPI("processing request", &apiInfo)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// renderClineError renders a ClineError with appropriate formatting based on type
|
|
150
|
+
func (h *SayHandler) renderClineError(err *clerror.ClineError, dc *DisplayContext) error {
|
|
151
|
+
if dc.SystemRenderer == nil {
|
|
152
|
+
return dc.Renderer.RenderMessage("ERROR", err.Message, true)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
switch err.GetErrorType() {
|
|
156
|
+
case clerror.ErrorTypeBalance:
|
|
157
|
+
return dc.SystemRenderer.RenderBalanceError(err)
|
|
158
|
+
case clerror.ErrorTypeAuth:
|
|
159
|
+
return dc.SystemRenderer.RenderAuthError(err)
|
|
160
|
+
case clerror.ErrorTypeRateLimit:
|
|
161
|
+
return dc.SystemRenderer.RenderRateLimitError(err)
|
|
162
|
+
default:
|
|
163
|
+
return dc.SystemRenderer.RenderAPIError(err)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// handleAPIReqFinished handles API request finished messages
|
|
168
|
+
func (h *SayHandler) handleAPIReqFinished(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
169
|
+
// This message type is typically not displayed as it's handled by the started message
|
|
170
|
+
return nil
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// handleText handles regular text messages
|
|
174
|
+
func (h *SayHandler) handleText(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
175
|
+
if msg.Text == "" {
|
|
176
|
+
return nil
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Special case for the user's task input
|
|
180
|
+
if dc.MessageIndex == 0 {
|
|
181
|
+
markdown := formatUserMessage(msg.Text)
|
|
182
|
+
rendered := dc.Renderer.RenderMarkdown(markdown)
|
|
183
|
+
output.Printf("%s", rendered)
|
|
184
|
+
output.Printf("\n")
|
|
185
|
+
return nil
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Regular Cline text response
|
|
189
|
+
var rendered string
|
|
190
|
+
if dc.IsStreamingMode {
|
|
191
|
+
// In streaming mode, header already shown by partial stream
|
|
192
|
+
rendered = dc.Renderer.RenderMarkdown(msg.Text)
|
|
193
|
+
output.Printf("%s\n", rendered)
|
|
194
|
+
} else {
|
|
195
|
+
// In non-streaming mode, render header + body together
|
|
196
|
+
markdown := fmt.Sprintf("### Cline responds\n\n%s", msg.Text)
|
|
197
|
+
rendered = dc.Renderer.RenderMarkdown(markdown)
|
|
198
|
+
output.Printf("\n%s\n", rendered)
|
|
199
|
+
}
|
|
200
|
+
return nil
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// handleReasoning handles reasoning messages
|
|
204
|
+
func (h *SayHandler) handleReasoning(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
205
|
+
if msg.Text == "" {
|
|
206
|
+
return nil
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
var rendered string
|
|
210
|
+
if dc.IsStreamingMode {
|
|
211
|
+
// In streaming mode, header already shown by partial stream
|
|
212
|
+
rendered = dc.Renderer.RenderMarkdown(msg.Text)
|
|
213
|
+
output.Printf("%s\n", rendered)
|
|
214
|
+
} else {
|
|
215
|
+
// In non-streaming mode, render header + body together
|
|
216
|
+
markdown := fmt.Sprintf("### Cline is thinking\n\n%s", msg.Text)
|
|
217
|
+
rendered = dc.Renderer.RenderMarkdown(markdown)
|
|
218
|
+
output.Printf("\n%s\n", rendered)
|
|
219
|
+
}
|
|
220
|
+
return nil
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
func (h *SayHandler) handleCompletionResult(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
224
|
+
text := msg.Text
|
|
225
|
+
|
|
226
|
+
if strings.HasSuffix(text, "HAS_CHANGES") {
|
|
227
|
+
text = strings.TrimSuffix(text, "HAS_CHANGES")
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var rendered string
|
|
231
|
+
if dc.IsStreamingMode {
|
|
232
|
+
// In streaming mode, header already shown by partial stream
|
|
233
|
+
rendered = dc.Renderer.RenderMarkdown(text)
|
|
234
|
+
output.Printf("%s\n", rendered)
|
|
235
|
+
} else {
|
|
236
|
+
// In non-streaming mode, render header + body together
|
|
237
|
+
markdown := fmt.Sprintf("### Task completed\n\n%s", text)
|
|
238
|
+
rendered = dc.Renderer.RenderMarkdown(markdown)
|
|
239
|
+
output.Printf("\n%s\n", rendered)
|
|
240
|
+
}
|
|
241
|
+
return nil
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
func formatUserMessage(text string) string {
|
|
245
|
+
lines := strings.Split(text, "\n")
|
|
246
|
+
|
|
247
|
+
// Wrap each line in backticks
|
|
248
|
+
for i, line := range lines {
|
|
249
|
+
if line != "" {
|
|
250
|
+
lines[i] = fmt.Sprintf("`%s`", line)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return strings.Join(lines, "\n")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
// handleUserFeedback handles user feedback messages
|
|
259
|
+
func (h *SayHandler) handleUserFeedback(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
260
|
+
if msg.Text != "" {
|
|
261
|
+
markdown := formatUserMessage(msg.Text)
|
|
262
|
+
rendered := dc.Renderer.RenderMarkdown(markdown)
|
|
263
|
+
output.Printf("%s", rendered)
|
|
264
|
+
return nil
|
|
265
|
+
} else {
|
|
266
|
+
return dc.Renderer.RenderMessage("USER", "[Provided feedback without text]", true)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// handleUserFeedbackDiff handles user feedback diff messages
|
|
271
|
+
func (h *SayHandler) handleUserFeedbackDiff(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
272
|
+
var toolMsg types.ToolMessage
|
|
273
|
+
if err := json.Unmarshal([]byte(msg.Text), &toolMsg); err != nil {
|
|
274
|
+
return dc.Renderer.RenderMessage("USER DIFF", msg.Text, true)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
message := fmt.Sprintf("User manually edited: %s\n\nDiff:\n%s",
|
|
278
|
+
toolMsg.Path,
|
|
279
|
+
toolMsg.Diff)
|
|
280
|
+
|
|
281
|
+
return dc.Renderer.RenderMessage("USER DIFF", message, true)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// handleAPIReqRetried handles API request retry messages
|
|
285
|
+
func (h *SayHandler) handleAPIReqRetried(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
286
|
+
return dc.Renderer.RenderMessage("API INFO", "Retrying request", true)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// handleErrorRetry handles error retry status messages
|
|
290
|
+
func (h *SayHandler) handleErrorRetry(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
291
|
+
// Parse retry info from message text
|
|
292
|
+
type ErrorRetryInfo struct {
|
|
293
|
+
Attempt int `json:"attempt"`
|
|
294
|
+
MaxAttempts int `json:"maxAttempts"`
|
|
295
|
+
DelaySeconds int `json:"delaySeconds"`
|
|
296
|
+
Failed bool `json:"failed"`
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
var retryInfo ErrorRetryInfo
|
|
300
|
+
if err := json.Unmarshal([]byte(msg.Text), &retryInfo); err != nil {
|
|
301
|
+
// Fallback to simple message if parsing fails
|
|
302
|
+
return dc.Renderer.RenderMessage("API INFO", "Auto-retry in progress", true)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if retryInfo.Failed {
|
|
306
|
+
// Retry failed after max attempts
|
|
307
|
+
message := fmt.Sprintf("Auto-retry failed after %d attempts. Manual intervention required.", retryInfo.MaxAttempts)
|
|
308
|
+
if dc.SystemRenderer != nil {
|
|
309
|
+
return dc.SystemRenderer.RenderWarning("Auto-Retry Failed", message)
|
|
310
|
+
}
|
|
311
|
+
return dc.Renderer.RenderMessage("WARNING", message, true)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Retry in progress
|
|
315
|
+
message := fmt.Sprintf("Attempt %d/%d - Retrying in %d seconds...",
|
|
316
|
+
retryInfo.Attempt, retryInfo.MaxAttempts, retryInfo.DelaySeconds)
|
|
317
|
+
return dc.Renderer.RenderMessage("API INFO", message, true)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// handleCommand handles command execution announcements
|
|
321
|
+
func (h *SayHandler) handleCommand(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
322
|
+
if msg.Text == "" {
|
|
323
|
+
return nil
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Use unified ToolRenderer
|
|
327
|
+
rendered := dc.ToolRenderer.RenderCommandExecution(msg.Text)
|
|
328
|
+
output.Print(rendered)
|
|
329
|
+
|
|
330
|
+
return nil
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// handleCommandOutput handles command output messages
|
|
334
|
+
func (h *SayHandler) handleCommandOutput(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
335
|
+
if msg.Text == "" {
|
|
336
|
+
return nil
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Use unified ToolRenderer
|
|
340
|
+
rendered := dc.ToolRenderer.RenderCommandOutput(msg.Text)
|
|
341
|
+
output.Print(rendered)
|
|
342
|
+
|
|
343
|
+
return nil
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
func (h *SayHandler) handleTool(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
347
|
+
var tool types.ToolMessage
|
|
348
|
+
if err := json.Unmarshal([]byte(msg.Text), &tool); err != nil {
|
|
349
|
+
return dc.Renderer.RenderMessage("TOOL", msg.Text, true)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Use unified ToolRenderer
|
|
353
|
+
rendered := dc.ToolRenderer.RenderToolExecution(&tool)
|
|
354
|
+
output.Print(rendered)
|
|
355
|
+
|
|
356
|
+
return nil
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// handleShellIntegrationWarning handles shell integration warning messages
|
|
360
|
+
func (h *SayHandler) handleShellIntegrationWarning(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
361
|
+
return dc.Renderer.RenderMessage("WARNING", "Shell Integration Unavailable - Cline won't be able to view the command's output.", true)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// handleBrowserActionLaunch handles browser action launch messages
|
|
365
|
+
func (h *SayHandler) handleBrowserActionLaunch(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
366
|
+
url := msg.Text
|
|
367
|
+
if url == "" {
|
|
368
|
+
return nil
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return dc.Renderer.RenderMessage("BROWSER", fmt.Sprintf("Launching browser at: %s", url), true)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// handleBrowserAction handles browser action messages
|
|
375
|
+
func (h *SayHandler) handleBrowserAction(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
376
|
+
if msg.Text == "" {
|
|
377
|
+
return nil
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
type BrowserActionData struct {
|
|
381
|
+
Action string `json:"action"`
|
|
382
|
+
Coordinate string `json:"coordinate,omitempty"`
|
|
383
|
+
Text string `json:"text,omitempty"`
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
var actionData BrowserActionData
|
|
387
|
+
if err := json.Unmarshal([]byte(msg.Text), &actionData); err != nil {
|
|
388
|
+
return dc.Renderer.RenderMessage("BROWSER", msg.Text, true)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Special handling for type action
|
|
392
|
+
if actionData.Action == "type" && actionData.Text != "" {
|
|
393
|
+
actionText := fmt.Sprintf("type '%s'", actionData.Text)
|
|
394
|
+
return dc.Renderer.RenderMessage("BROWSER", fmt.Sprintf("Next action: %s", actionText), true)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Special handling for click action
|
|
398
|
+
if actionData.Action == "click" && actionData.Coordinate != "" {
|
|
399
|
+
actionText := fmt.Sprintf("click (%s)", actionData.Coordinate)
|
|
400
|
+
return dc.Renderer.RenderMessage("BROWSER", fmt.Sprintf("Next action: %s", actionText), true)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Generic handling for all other actions
|
|
404
|
+
return dc.Renderer.RenderMessage("BROWSER", fmt.Sprintf("Next action: %s", actionData.Action), true)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// handleBrowserActionResult handles browser action result messages
|
|
408
|
+
func (h *SayHandler) handleBrowserActionResult(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
409
|
+
if msg.Text == "" {
|
|
410
|
+
return nil
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
type BrowserActionResult struct {
|
|
414
|
+
Screenshot string `json:"screenshot,omitempty"`
|
|
415
|
+
Logs string `json:"logs,omitempty"`
|
|
416
|
+
CurrentUrl string `json:"currentUrl,omitempty"`
|
|
417
|
+
CurrentMousePosition string `json:"currentMousePosition,omitempty"`
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
var result BrowserActionResult
|
|
421
|
+
if err := json.Unmarshal([]byte(msg.Text), &result); err != nil {
|
|
422
|
+
return dc.Renderer.RenderMessage("BROWSER", "Action completed", true)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// If we have logs, include them in the message
|
|
426
|
+
if result.Logs != "" {
|
|
427
|
+
return dc.Renderer.RenderMessage("BROWSER", fmt.Sprintf("Action completed with logs: '%s'", result.Logs), true)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Default case
|
|
431
|
+
return dc.Renderer.RenderMessage("BROWSER", "Action completed", true)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// handleMcpServerRequestStarted handles MCP server request started messages
|
|
435
|
+
func (h *SayHandler) handleMcpServerRequestStarted(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
436
|
+
return dc.Renderer.RenderMessage("MCP", "Sending request to server", true)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// handleMcpServerResponse handles MCP server response messages
|
|
440
|
+
func (h *SayHandler) handleMcpServerResponse(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
441
|
+
return dc.Renderer.RenderMessage("MCP", fmt.Sprintf("Server response: %s", msg.Text), true)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// handleMcpNotification handles MCP notification messages
|
|
445
|
+
func (h *SayHandler) handleMcpNotification(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
446
|
+
return dc.Renderer.RenderMessage("MCP", fmt.Sprintf("Server notification: %s", msg.Text), true)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// handleUseMcpServer handles MCP server usage messages
|
|
450
|
+
func (h *SayHandler) handleUseMcpServer(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
451
|
+
return dc.Renderer.RenderMessage("MCP", "Server operation approved", true)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// handleDiffError handles diff error messages
|
|
455
|
+
func (h *SayHandler) handleDiffError(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
456
|
+
if dc.SystemRenderer != nil {
|
|
457
|
+
return dc.SystemRenderer.RenderWarning(
|
|
458
|
+
"Diff Edit Failure",
|
|
459
|
+
"The model used search patterns that don't match anything in the file. Retrying...",
|
|
460
|
+
)
|
|
461
|
+
}
|
|
462
|
+
return dc.Renderer.RenderMessage("WARNING", "Diff Edit Failure - The model used an invalid diff edit format or used search patterns that don't match anything in the file.", true)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// handleDeletedAPIReqs handles deleted API requests messages
|
|
466
|
+
func (h *SayHandler) handleDeletedAPIReqs(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
467
|
+
// Don't render - this is internal metadata (aggregated API metrics from deleted checkpoint messages)
|
|
468
|
+
return nil
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// handleClineignoreError handles .clineignore error messages
|
|
472
|
+
func (h *SayHandler) handleClineignoreError(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
473
|
+
if dc.SystemRenderer != nil {
|
|
474
|
+
return dc.SystemRenderer.RenderInfo(
|
|
475
|
+
"Access Denied",
|
|
476
|
+
fmt.Sprintf("Cline tried to access `%s` which is blocked by the .clineignore file.", msg.Text),
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
return dc.Renderer.RenderMessage("WARNING", fmt.Sprintf("Access Denied - Cline tried to access %s which is blocked by the .clineignore file", msg.Text), true)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
func (h *SayHandler) handleCheckpointCreated(msg *types.ClineMessage, dc *DisplayContext, timestamp string) error {
|
|
483
|
+
if dc.SystemRenderer != nil {
|
|
484
|
+
return dc.SystemRenderer.RenderCheckpoint(timestamp, msg.Timestamp)
|
|
485
|
+
}
|
|
486
|
+
// Fallback to basic renderer if SystemRenderer not available
|
|
487
|
+
markdown := fmt.Sprintf("## [%s] Checkpoint created `%d`", timestamp, msg.Timestamp)
|
|
488
|
+
rendered := dc.Renderer.RenderMarkdown(markdown)
|
|
489
|
+
output.Print(rendered)
|
|
490
|
+
return nil
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// handleLoadMcpDocumentation handles load MCP documentation messages
|
|
494
|
+
func (h *SayHandler) handleLoadMcpDocumentation(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
495
|
+
if dc.SystemRenderer != nil {
|
|
496
|
+
return dc.SystemRenderer.RenderInfo("MCP", "Loading MCP documentation")
|
|
497
|
+
}
|
|
498
|
+
return dc.Renderer.RenderMessage("INFO", "Loading MCP documentation", true)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// handleInfo handles info messages
|
|
502
|
+
func (h *SayHandler) handleInfo(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
503
|
+
return nil
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// handleTaskProgress handles task progress messages
|
|
507
|
+
func (h *SayHandler) handleTaskProgress(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
508
|
+
if msg.Text == "" {
|
|
509
|
+
return nil
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
markdown := fmt.Sprintf("### Progress\n\n%s", msg.Text)
|
|
513
|
+
rendered := dc.Renderer.RenderMarkdown(markdown)
|
|
514
|
+
output.Printf("\n%s\n", rendered)
|
|
515
|
+
return nil
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// handleDefault handles unknown SAY message types
|
|
519
|
+
func (h *SayHandler) handleDefault(msg *types.ClineMessage, dc *DisplayContext) error {
|
|
520
|
+
return dc.Renderer.RenderMessage("SAY", msg.Text, true)
|
|
521
|
+
}
|