@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,455 @@
|
|
|
1
|
+
package display
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
"github.com/cline/cli/pkg/cli/types"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// ToolRenderer provides unified rendering for tool and command messages
|
|
12
|
+
type ToolRenderer struct {
|
|
13
|
+
mdRenderer *MarkdownRenderer
|
|
14
|
+
outputFormat string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// NewToolRenderer creates a new tool renderer
|
|
18
|
+
func NewToolRenderer(mdRenderer *MarkdownRenderer, outputFormat string) *ToolRenderer {
|
|
19
|
+
return &ToolRenderer{
|
|
20
|
+
mdRenderer: mdRenderer,
|
|
21
|
+
outputFormat: outputFormat,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// RenderToolApprovalRequest renders a tool approval request ("Cline wants to...")
|
|
26
|
+
func (tr *ToolRenderer) RenderToolApprovalRequest(tool *types.ToolMessage) string {
|
|
27
|
+
var output strings.Builder
|
|
28
|
+
|
|
29
|
+
// Generate header
|
|
30
|
+
header := tr.generateToolHeader(tool, "wants to")
|
|
31
|
+
rendered := tr.renderMarkdown(header)
|
|
32
|
+
output.WriteString(rendered)
|
|
33
|
+
output.WriteString("\n")
|
|
34
|
+
|
|
35
|
+
// Add content preview for relevant tools
|
|
36
|
+
contentPreview := tr.GenerateToolContentPreview(tool)
|
|
37
|
+
if contentPreview != "" {
|
|
38
|
+
output.WriteString("\n")
|
|
39
|
+
output.WriteString(contentPreview)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return output.String()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// RenderToolExecution renders a completed tool execution ("Cline is ...ing")
|
|
46
|
+
func (tr *ToolRenderer) RenderToolExecution(tool *types.ToolMessage) string {
|
|
47
|
+
var output strings.Builder
|
|
48
|
+
|
|
49
|
+
// Generate header
|
|
50
|
+
header := tr.generateToolHeader(tool, "is")
|
|
51
|
+
rendered := tr.renderMarkdown(header)
|
|
52
|
+
output.WriteString("\n")
|
|
53
|
+
output.WriteString(rendered)
|
|
54
|
+
output.WriteString("\n")
|
|
55
|
+
|
|
56
|
+
// Add content body for relevant tools
|
|
57
|
+
contentBody := tr.GenerateToolContentBody(tool)
|
|
58
|
+
if contentBody != "" {
|
|
59
|
+
output.WriteString("\n")
|
|
60
|
+
output.WriteString(contentBody)
|
|
61
|
+
output.WriteString("\n")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return output.String()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// RenderToolExecutionHeader renders just the header for streaming (no body)
|
|
68
|
+
func (tr *ToolRenderer) RenderToolExecutionHeader(tool *types.ToolMessage) string {
|
|
69
|
+
header := tr.generateToolHeader(tool, "is")
|
|
70
|
+
return header
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// RenderToolApprovalHeader renders just the header for approval requests (no body)
|
|
74
|
+
func (tr *ToolRenderer) RenderToolApprovalHeader(tool *types.ToolMessage) string {
|
|
75
|
+
header := tr.generateToolHeader(tool, "wants to")
|
|
76
|
+
return header
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// generateToolHeader generates the markdown header for a tool message
|
|
80
|
+
func (tr *ToolRenderer) generateToolHeader(tool *types.ToolMessage, verbTense string) string {
|
|
81
|
+
var verb string
|
|
82
|
+
var action string
|
|
83
|
+
|
|
84
|
+
switch tool.Tool {
|
|
85
|
+
case string(types.ToolTypeEditedExistingFile):
|
|
86
|
+
if verbTense == "wants to" {
|
|
87
|
+
action = "wants to edit"
|
|
88
|
+
} else {
|
|
89
|
+
action = "is editing"
|
|
90
|
+
}
|
|
91
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
92
|
+
|
|
93
|
+
case string(types.ToolTypeNewFileCreated):
|
|
94
|
+
if verbTense == "wants to" {
|
|
95
|
+
action = "wants to write"
|
|
96
|
+
} else {
|
|
97
|
+
action = "is writing"
|
|
98
|
+
}
|
|
99
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
100
|
+
|
|
101
|
+
case string(types.ToolTypeReadFile):
|
|
102
|
+
if verbTense == "wants to" {
|
|
103
|
+
action = "wants to read"
|
|
104
|
+
} else {
|
|
105
|
+
action = "is reading"
|
|
106
|
+
}
|
|
107
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
108
|
+
|
|
109
|
+
case string(types.ToolTypeFileDeleted):
|
|
110
|
+
if verbTense == "wants to" {
|
|
111
|
+
action = "wants to delete"
|
|
112
|
+
} else {
|
|
113
|
+
action = "is deleting"
|
|
114
|
+
}
|
|
115
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
116
|
+
|
|
117
|
+
case string(types.ToolTypeListFilesTopLevel):
|
|
118
|
+
if verbTense == "wants to" {
|
|
119
|
+
action = "wants to list files in"
|
|
120
|
+
} else {
|
|
121
|
+
action = "is listing files in"
|
|
122
|
+
}
|
|
123
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
124
|
+
|
|
125
|
+
case string(types.ToolTypeListFilesRecursive):
|
|
126
|
+
if verbTense == "wants to" {
|
|
127
|
+
action = "wants to recursively list files in"
|
|
128
|
+
} else {
|
|
129
|
+
action = "is recursively listing files in"
|
|
130
|
+
}
|
|
131
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
132
|
+
|
|
133
|
+
case string(types.ToolTypeSearchFiles):
|
|
134
|
+
if tool.Regex != "" && tool.Path != "" {
|
|
135
|
+
if verbTense == "wants to" {
|
|
136
|
+
action = "wants to search for"
|
|
137
|
+
} else {
|
|
138
|
+
action = "is searching for"
|
|
139
|
+
}
|
|
140
|
+
return fmt.Sprintf("### Cline %s `%s` in `%s`", action, tool.Regex, tool.Path)
|
|
141
|
+
} else if tool.Regex != "" {
|
|
142
|
+
if verbTense == "wants to" {
|
|
143
|
+
action = "wants to search for"
|
|
144
|
+
} else {
|
|
145
|
+
action = "is searching for"
|
|
146
|
+
}
|
|
147
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Regex)
|
|
148
|
+
} else {
|
|
149
|
+
if verbTense == "wants to" {
|
|
150
|
+
return "### Cline wants to search files"
|
|
151
|
+
} else {
|
|
152
|
+
return "### Cline is searching files"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
case string(types.ToolTypeWebFetch):
|
|
157
|
+
if verbTense == "wants to" {
|
|
158
|
+
action = "wants to fetch"
|
|
159
|
+
} else {
|
|
160
|
+
action = "is fetching"
|
|
161
|
+
}
|
|
162
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
163
|
+
|
|
164
|
+
case string(types.ToolTypeListCodeDefinitionNames):
|
|
165
|
+
if verbTense == "wants to" {
|
|
166
|
+
action = "wants to list code definitions in"
|
|
167
|
+
} else {
|
|
168
|
+
action = "is listing code definitions in"
|
|
169
|
+
}
|
|
170
|
+
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
|
171
|
+
|
|
172
|
+
case string(types.ToolTypeSummarizeTask):
|
|
173
|
+
if verbTense == "wants to" {
|
|
174
|
+
return "### Cline wants to condense the conversation"
|
|
175
|
+
} else {
|
|
176
|
+
return "### Cline condensed the conversation"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
default:
|
|
180
|
+
if verbTense == "wants to" {
|
|
181
|
+
verb = "wants to use"
|
|
182
|
+
} else {
|
|
183
|
+
verb = "is using"
|
|
184
|
+
}
|
|
185
|
+
return fmt.Sprintf("### Cline %s tool: %s", verb, tool.Tool)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// GenerateToolContentPreview generates content preview for approval requests
|
|
190
|
+
func (tr *ToolRenderer) GenerateToolContentPreview(tool *types.ToolMessage) string {
|
|
191
|
+
if tool.Content == "" {
|
|
192
|
+
return ""
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
switch tool.Tool {
|
|
196
|
+
case string(types.ToolTypeEditedExistingFile):
|
|
197
|
+
// Show diff for edits
|
|
198
|
+
diffMarkdown := fmt.Sprintf("```diff\n%s\n```", tool.Content)
|
|
199
|
+
return tr.renderMarkdown(diffMarkdown)
|
|
200
|
+
|
|
201
|
+
case string(types.ToolTypeNewFileCreated):
|
|
202
|
+
// Show content preview for new files (truncated)
|
|
203
|
+
preview := strings.TrimSpace(tool.Content)
|
|
204
|
+
if len(preview) > 500 {
|
|
205
|
+
preview = preview[:500] + "..."
|
|
206
|
+
}
|
|
207
|
+
previewMd := fmt.Sprintf("```\n%s\n```", preview)
|
|
208
|
+
return tr.renderMarkdown(previewMd)
|
|
209
|
+
|
|
210
|
+
case string(types.ToolTypeReadFile), string(types.ToolTypeWebFetch), string(types.ToolTypeFileDeleted):
|
|
211
|
+
// No preview for read/fetch operations
|
|
212
|
+
return ""
|
|
213
|
+
|
|
214
|
+
default:
|
|
215
|
+
// For other tools, show truncated content if available
|
|
216
|
+
preview := strings.TrimSpace(tool.Content)
|
|
217
|
+
if len(preview) > 200 {
|
|
218
|
+
preview = preview[:200] + "..."
|
|
219
|
+
}
|
|
220
|
+
if preview != "" {
|
|
221
|
+
return fmt.Sprintf("Preview: %s", preview)
|
|
222
|
+
}
|
|
223
|
+
return ""
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// GenerateToolContentBody generates full content for completed executions
|
|
228
|
+
func (tr *ToolRenderer) GenerateToolContentBody(tool *types.ToolMessage) string {
|
|
229
|
+
if tool.Content == "" {
|
|
230
|
+
return ""
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Use enhanced tool result parser for supported tools
|
|
234
|
+
toolParser := NewToolResultParser(tr.mdRenderer)
|
|
235
|
+
|
|
236
|
+
switch tool.Tool {
|
|
237
|
+
case string(types.ToolTypeReadFile),
|
|
238
|
+
string(types.ToolTypeFileDeleted):
|
|
239
|
+
// readFile: show header only, no body
|
|
240
|
+
return ""
|
|
241
|
+
|
|
242
|
+
case string(types.ToolTypeListFilesTopLevel),
|
|
243
|
+
string(types.ToolTypeListFilesRecursive),
|
|
244
|
+
string(types.ToolTypeListCodeDefinitionNames),
|
|
245
|
+
string(types.ToolTypeSearchFiles),
|
|
246
|
+
string(types.ToolTypeWebFetch):
|
|
247
|
+
// Use parser for structured output
|
|
248
|
+
preview := toolParser.ParseToolResult(tool)
|
|
249
|
+
return tr.renderMarkdown(preview)
|
|
250
|
+
|
|
251
|
+
case string(types.ToolTypeEditedExistingFile):
|
|
252
|
+
// Show the diff
|
|
253
|
+
diffMarkdown := fmt.Sprintf("```diff\n%s\n```", tool.Content)
|
|
254
|
+
return tr.renderMarkdown(diffMarkdown)
|
|
255
|
+
|
|
256
|
+
case string(types.ToolTypeNewFileCreated):
|
|
257
|
+
// Show file content preview
|
|
258
|
+
preview := strings.TrimSpace(tool.Content)
|
|
259
|
+
if len(preview) > 1000 {
|
|
260
|
+
preview = preview[:1000] + "..."
|
|
261
|
+
}
|
|
262
|
+
contentMd := fmt.Sprintf("```\n%s\n```", preview)
|
|
263
|
+
return tr.renderMarkdown(contentMd)
|
|
264
|
+
|
|
265
|
+
default:
|
|
266
|
+
// For unknown tools, show content as-is
|
|
267
|
+
if len(tool.Content) > 500 {
|
|
268
|
+
return tool.Content[:500] + "..."
|
|
269
|
+
}
|
|
270
|
+
return tool.Content
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// RenderCommandApprovalRequest renders a command approval request
|
|
275
|
+
func (tr *ToolRenderer) RenderCommandApprovalRequest(command string, autoApprovalConflict bool) string {
|
|
276
|
+
var output strings.Builder
|
|
277
|
+
|
|
278
|
+
// Clean command
|
|
279
|
+
command = strings.TrimSpace(command)
|
|
280
|
+
if strings.HasSuffix(command, "REQ_APP") {
|
|
281
|
+
command = strings.TrimSuffix(command, "REQ_APP")
|
|
282
|
+
command = strings.TrimSpace(command)
|
|
283
|
+
autoApprovalConflict = true
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Generate header
|
|
287
|
+
header := fmt.Sprintf("### Cline wants to run `%s`", command)
|
|
288
|
+
rendered := tr.renderMarkdown(header)
|
|
289
|
+
output.WriteString(rendered)
|
|
290
|
+
output.WriteString("\n")
|
|
291
|
+
|
|
292
|
+
// Show command in code block
|
|
293
|
+
cmdBlock := fmt.Sprintf("```shell\n%s\n```", command)
|
|
294
|
+
cmdRendered := tr.renderMarkdown(cmdBlock)
|
|
295
|
+
output.WriteString("\n")
|
|
296
|
+
output.WriteString(cmdRendered)
|
|
297
|
+
|
|
298
|
+
// Add warning if needed
|
|
299
|
+
if autoApprovalConflict {
|
|
300
|
+
output.WriteString("\nWARNING: The model has determined this command requires explicit approval.\n")
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return output.String()
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// RenderCommandExecution renders a command execution announcement
|
|
307
|
+
func (tr *ToolRenderer) RenderCommandExecution(command string) string {
|
|
308
|
+
command = strings.TrimSpace(command)
|
|
309
|
+
header := fmt.Sprintf("### Cline is running `%s`", command)
|
|
310
|
+
rendered := tr.renderMarkdown(header)
|
|
311
|
+
return "\n" + rendered + "\n"
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// RenderCommandOutput renders command output
|
|
315
|
+
func (tr *ToolRenderer) RenderCommandOutput(output string) string {
|
|
316
|
+
var result strings.Builder
|
|
317
|
+
|
|
318
|
+
header := "### Terminal output"
|
|
319
|
+
rendered := tr.renderMarkdown(header)
|
|
320
|
+
result.WriteString("\n")
|
|
321
|
+
result.WriteString(rendered)
|
|
322
|
+
result.WriteString("\n\n")
|
|
323
|
+
|
|
324
|
+
// Show output in code block
|
|
325
|
+
outputBlock := fmt.Sprintf("```\n%s\n```", strings.TrimSpace(output))
|
|
326
|
+
outputRendered := tr.renderMarkdown(outputBlock)
|
|
327
|
+
result.WriteString(outputRendered)
|
|
328
|
+
result.WriteString("\n")
|
|
329
|
+
|
|
330
|
+
return result.String()
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// RenderUserResponse renders user approval/rejection feedback
|
|
334
|
+
func (tr *ToolRenderer) RenderUserResponse(approved bool, feedback string) string {
|
|
335
|
+
var symbol, status string
|
|
336
|
+
|
|
337
|
+
if approved {
|
|
338
|
+
symbol = "✓"
|
|
339
|
+
status = "Approved"
|
|
340
|
+
} else {
|
|
341
|
+
symbol = "✗"
|
|
342
|
+
status = "Rejected"
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if feedback != "" {
|
|
346
|
+
return fmt.Sprintf("%s %s with feedback: %s\n", symbol, status, feedback)
|
|
347
|
+
}
|
|
348
|
+
return fmt.Sprintf("%s %s\n", symbol, status)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// renderMarkdown renders markdown if not in plain mode and in a TTY
|
|
352
|
+
func (tr *ToolRenderer) renderMarkdown(markdown string) string {
|
|
353
|
+
// Skip markdown rendering if plain mode or not in TTY
|
|
354
|
+
if tr.outputFormat == "plain" || !isTTY() {
|
|
355
|
+
return markdown
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if tr.mdRenderer == nil {
|
|
359
|
+
return markdown
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
rendered, err := tr.mdRenderer.Render(markdown)
|
|
363
|
+
if err != nil {
|
|
364
|
+
return markdown
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return rendered
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// GenerateAskFollowupHeader generates the header for followup questions
|
|
371
|
+
func (tr *ToolRenderer) GenerateAskFollowupHeader() string {
|
|
372
|
+
return "### Cline has a question\n"
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// GenerateAskFollowupBody generates the body content for followup questions
|
|
376
|
+
func (tr *ToolRenderer) GenerateAskFollowupBody(messageText string) string {
|
|
377
|
+
var question string
|
|
378
|
+
var options []string
|
|
379
|
+
|
|
380
|
+
// Try to parse as JSON
|
|
381
|
+
var askData types.AskData
|
|
382
|
+
if err := json.Unmarshal([]byte(messageText), &askData); err == nil {
|
|
383
|
+
question = askData.Question
|
|
384
|
+
options = askData.Options
|
|
385
|
+
} else {
|
|
386
|
+
question = messageText
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if question == "" {
|
|
390
|
+
return ""
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Build the body
|
|
394
|
+
var body strings.Builder
|
|
395
|
+
|
|
396
|
+
// Render the question
|
|
397
|
+
rendered := tr.renderMarkdown(question)
|
|
398
|
+
body.WriteString(rendered)
|
|
399
|
+
|
|
400
|
+
// Add options if available
|
|
401
|
+
if len(options) > 0 {
|
|
402
|
+
body.WriteString("\n\nOptions:\n")
|
|
403
|
+
for i, option := range options {
|
|
404
|
+
body.WriteString(fmt.Sprintf("%d. %s\n", i+1, option))
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return body.String()
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// GeneratePlanModeRespondHeader generates the header for plan mode responses
|
|
412
|
+
func (tr *ToolRenderer) GeneratePlanModeRespondHeader() string {
|
|
413
|
+
return "### Cline has a plan\n"
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// GeneratePlanModeRespondBody generates the body content for plan mode responses
|
|
417
|
+
func (tr *ToolRenderer) GeneratePlanModeRespondBody(messageText string) string {
|
|
418
|
+
var response string
|
|
419
|
+
var options []string
|
|
420
|
+
|
|
421
|
+
// Try to parse as JSON
|
|
422
|
+
type PlanModeResponse struct {
|
|
423
|
+
Response string `json:"response"`
|
|
424
|
+
Options []string `json:"options,omitempty"`
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
var planData PlanModeResponse
|
|
428
|
+
if err := json.Unmarshal([]byte(messageText), &planData); err == nil {
|
|
429
|
+
response = planData.Response
|
|
430
|
+
options = planData.Options
|
|
431
|
+
} else {
|
|
432
|
+
response = messageText
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if response == "" {
|
|
436
|
+
return ""
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Build the body
|
|
440
|
+
var body strings.Builder
|
|
441
|
+
|
|
442
|
+
// Render the response
|
|
443
|
+
rendered := tr.renderMarkdown(response)
|
|
444
|
+
body.WriteString(rendered)
|
|
445
|
+
|
|
446
|
+
// Add options if available
|
|
447
|
+
if len(options) > 0 {
|
|
448
|
+
body.WriteString("\n\nOptions:\n")
|
|
449
|
+
for i, option := range options {
|
|
450
|
+
body.WriteString(fmt.Sprintf("%d. %s\n", i+1, option))
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return body.String()
|
|
455
|
+
}
|