@@ -14,12 +14,12 @@ import (
14
14
15
15
// ClaudeOptions contains configuration for Claude API calls
16
16
type ClaudeOptions struct {
17
- Model string
18
- MaxTokens int
19
- Debug bool
20
- StrictMCPConfig bool
21
- OutputFormat string
22
- MaxConcurrent int
17
+ Model string
18
+ MaxTokens int
19
+ Debug bool
20
+ StrictMCPConfig bool
21
+ OutputFormat string
22
+ MaxConcurrent int
23
23
}
24
24
25
25
// ClaudeResponse represents the response from Claude CLI
@@ -49,7 +49,7 @@ func NewClaudeExecutor(options ClaudeOptions) *ClaudeExecutor {
49
49
if options .Debug {
50
50
tm .SetVerbose (true )
51
51
}
52
-
52
+
53
53
return & ClaudeExecutor {
54
54
options : options ,
55
55
taskManager : tm ,
@@ -63,40 +63,26 @@ func (ce *ClaudeExecutor) GetTaskManager() *clicky.TaskManager {
63
63
64
64
// ExecutePrompt executes a single Claude prompt with progress tracking
65
65
func (ce * ClaudeExecutor ) ExecutePrompt (ctx context.Context , name string , prompt string ) (* ClaudeResponse , error ) {
66
- task := ce . taskManager . Start (name , clicky . WithFunc ( func (ctx flanksourceContext.Context , t * clicky.Task ) error {
66
+ t := clicky . StartTask [ * ClaudeResponse ] (name , func (ctx flanksourceContext.Context , t * clicky.Task ) ( * ClaudeResponse , error ) {
67
67
t .Infof ("Starting Claude API call" )
68
68
t .SetProgress (10 , 100 )
69
-
69
+
70
70
response , err := ce .executeClaudeCLI (ctx , prompt , t )
71
71
if err != nil {
72
72
t .Errorf ("Claude API failed: %v" , err )
73
- return err
73
+ return nil , err
74
74
}
75
-
75
+
76
76
t .SetProgress (100 , 100 )
77
77
t .Infof ("Received response (%d tokens)" , response .GetTotalTokens ())
78
-
78
+
79
79
// Store response in task context
80
- t .SetStatus (fmt .Sprintf ("%s (completed)" , name ))
81
- return nil
82
- }))
83
-
84
- // Wait for this specific task
85
- for task .Status () == clicky .StatusPending || task .Status () == clicky .StatusRunning {
86
- select {
87
- case <- ctx .Done ():
88
- task .Cancel ()
89
- return nil , ctx .Err ()
90
- case <- time .After (100 * time .Millisecond ):
91
- }
92
- }
93
-
94
- if err := task .Error (); err != nil {
95
- return nil , err
96
- }
97
-
80
+ t .SetName (fmt .Sprintf ("%s (completed)" , name ))
81
+ return response , nil
82
+ })
83
+
98
84
// Execute again to get the actual response (since we can't store it in the task)
99
- return ce . executeClaudeCLI ( ctx , prompt , nil )
85
+ return t . GetResult ( )
100
86
}
101
87
102
88
// ExecutePromptBatch executes multiple prompts in parallel with concurrency control
@@ -107,20 +93,19 @@ func (ce *ClaudeExecutor) ExecutePromptBatch(ctx context.Context, prompts map[st
107
93
response * ClaudeResponse
108
94
err error
109
95
}, len (prompts ))
110
-
96
+
111
97
// Create tasks for all prompts
112
98
for name , prompt := range prompts {
113
99
taskName := name
114
- taskPrompt := prompt
115
-
116
- ce .taskManager .Start (taskName ,
100
+
101
+ ce .taskManager .Start (taskName ,
117
102
clicky .WithTimeout (5 * time .Minute ),
118
103
clicky .WithFunc (func (ctx flanksourceContext.Context , t * clicky.Task ) error {
119
104
t .Infof ("Processing prompt" )
120
105
t .SetProgress (0 , 100 )
121
-
122
- response , err := ce .executeClaudeCLI (t .Context (), taskPrompt , t )
123
-
106
+
107
+ response , err := ce .executeClaudeCLI (t .Context (), prompt , t )
108
+
124
109
resultsChan <- struct {
125
110
name string
126
111
response * ClaudeResponse
@@ -130,18 +115,18 @@ func (ce *ClaudeExecutor) ExecutePromptBatch(ctx context.Context, prompts map[st
130
115
response : response ,
131
116
err : err ,
132
117
}
133
-
118
+
134
119
if err != nil {
135
120
t .Errorf ("Failed: %v" , err )
136
121
return err
137
122
}
138
-
123
+
139
124
t .SetProgress (100 , 100 )
140
125
t .Infof ("Completed (%d tokens)" , response .GetTotalTokens ())
141
126
return nil
142
127
}))
143
128
}
144
-
129
+
145
130
// Collect results
146
131
go func () {
147
132
for i := 0 ; i < len (prompts ); i ++ {
@@ -156,55 +141,55 @@ func (ce *ClaudeExecutor) ExecutePromptBatch(ctx context.Context, prompts map[st
156
141
}
157
142
}
158
143
}()
159
-
144
+
160
145
// Wait for all tasks to complete
161
146
exitCode := ce .taskManager .Wait ()
162
147
if exitCode != 0 {
163
148
return results , fmt .Errorf ("some tasks failed (exit code %d)" , exitCode )
164
149
}
165
-
150
+
166
151
return results , nil
167
152
}
168
153
169
154
// executeClaudeCLI executes the Claude CLI command
170
155
func (ce * ClaudeExecutor ) executeClaudeCLI (ctx context.Context , prompt string , task * clicky.Task ) (* ClaudeResponse , error ) {
171
156
args := []string {"-p" }
172
-
157
+
173
158
if ce .options .Model != "" {
174
159
args = append (args , "--model" , ce .options .Model )
175
160
}
176
-
161
+
177
162
if ce .options .OutputFormat != "" {
178
163
args = append (args , "--output-format" , ce .options .OutputFormat )
179
164
} else {
180
165
args = append (args , "--output-format" , "json" )
181
166
}
182
-
167
+
183
168
if ce .options .StrictMCPConfig {
184
169
args = append (args , "--strict-mcp-config" )
185
170
}
186
-
171
+
187
172
args = append (args , prompt )
188
-
173
+
189
174
if task != nil {
190
175
task .Infof ("Executing: claude %s" , strings .Join (args [:2 ], " " ))
191
176
task .SetProgress (30 , 100 )
192
177
}
193
-
178
+
194
179
cmd := exec .CommandContext (ctx , "claude" , args ... )
195
180
output , err := cmd .CombinedOutput ()
196
-
181
+
197
182
if task != nil {
198
183
task .SetProgress (80 , 100 )
199
184
}
200
-
185
+
201
186
if err != nil {
202
187
if ctx .Err () != nil {
203
188
return nil , fmt .Errorf ("claude CLI cancelled: %w" , ctx .Err ())
204
189
}
205
190
return nil , fmt .Errorf ("claude CLI failed: %w\n Output: %s" , err , string (output ))
206
191
}
207
-
192
+
208
193
// Try to parse JSON response
209
194
var response ClaudeResponse
210
195
if err := json .Unmarshal (output , & response ); err != nil {
@@ -214,11 +199,11 @@ func (ce *ClaudeExecutor) executeClaudeCLI(ctx context.Context, prompt string, t
214
199
Result : string (output ),
215
200
}, nil
216
201
}
217
-
202
+
218
203
if response .IsError {
219
204
return nil , fmt .Errorf ("claude returned error: %s" , response .Result )
220
205
}
221
-
206
+
222
207
if task != nil && ce .options .Debug {
223
208
task .Infof ("Token usage: input=%d, cache_creation=%d, cache_read=%d, output=%d" ,
224
209
response .Usage .InputTokens ,
@@ -227,7 +212,7 @@ func (ce *ClaudeExecutor) executeClaudeCLI(ctx context.Context, prompt string, t
227
212
response .Usage .OutputTokens )
228
213
task .Infof ("Cost: $%.6f USD" , response .TotalCostUSD )
229
214
}
230
-
215
+
231
216
return & response , nil
232
217
}
233
218
@@ -241,4 +226,4 @@ func (r *ClaudeResponse) GetTotalTokens() int {
241
226
// GetAllTokens returns all tokens including cache reads
242
227
func (r * ClaudeResponse ) GetAllTokens () int {
243
228
return r .GetTotalTokens () + r .Usage .CacheReadInputTokens
244
- }
229
+ }
0 commit comments