openclacky 0.5.3 → 0.5.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +2 -0
- data/Rakefile +1 -5
- data/lib/clacky/agent.rb +34 -16
- data/lib/clacky/cli.rb +54 -133
- data/lib/clacky/client.rb +53 -7
- data/lib/clacky/gitignore_parser.rb +114 -0
- data/lib/clacky/model_pricing.rb +280 -0
- data/lib/clacky/tools/grep.rb +268 -30
- data/lib/clacky/tools/safe_shell.rb +9 -4
- data/lib/clacky/tools/shell.rb +60 -22
- data/lib/clacky/ui/banner.rb +22 -11
- data/lib/clacky/ui/enhanced_prompt.rb +540 -0
- data/lib/clacky/ui/statusbar.rb +0 -2
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky.rb +3 -2
- metadata +4 -3
- data/lib/clacky/conversation.rb +0 -41
- data/lib/clacky/ui/prompt.rb +0 -72
data/lib/clacky/tools/shell.rb
CHANGED
|
@@ -22,6 +22,11 @@ module Clacky
|
|
|
22
22
|
hard_timeout: {
|
|
23
23
|
type: "integer",
|
|
24
24
|
description: "Hard timeout in seconds (force kill)"
|
|
25
|
+
},
|
|
26
|
+
max_output_lines: {
|
|
27
|
+
type: "integer",
|
|
28
|
+
description: "Maximum number of output lines to return (default: 1000)",
|
|
29
|
+
default: 1000
|
|
25
30
|
}
|
|
26
31
|
},
|
|
27
32
|
required: ["command"]
|
|
@@ -51,7 +56,7 @@ module Clacky
|
|
|
51
56
|
'go build'
|
|
52
57
|
].freeze
|
|
53
58
|
|
|
54
|
-
def execute(command:, soft_timeout: nil, hard_timeout: nil)
|
|
59
|
+
def execute(command:, soft_timeout: nil, hard_timeout: nil, max_output_lines: 1000)
|
|
55
60
|
require "open3"
|
|
56
61
|
require "stringio"
|
|
57
62
|
|
|
@@ -82,7 +87,8 @@ module Clacky
|
|
|
82
87
|
stderr_buffer.string,
|
|
83
88
|
elapsed,
|
|
84
89
|
:hard_timeout,
|
|
85
|
-
hard_timeout
|
|
90
|
+
hard_timeout,
|
|
91
|
+
max_output_lines
|
|
86
92
|
)
|
|
87
93
|
end
|
|
88
94
|
|
|
@@ -97,7 +103,8 @@ module Clacky
|
|
|
97
103
|
command,
|
|
98
104
|
stdout_buffer.string,
|
|
99
105
|
stderr_buffer.string,
|
|
100
|
-
interaction
|
|
106
|
+
interaction,
|
|
107
|
+
max_output_lines
|
|
101
108
|
)
|
|
102
109
|
end
|
|
103
110
|
|
|
@@ -114,7 +121,8 @@ module Clacky
|
|
|
114
121
|
command,
|
|
115
122
|
stdout_buffer.string,
|
|
116
123
|
stderr_buffer.string,
|
|
117
|
-
elapsed
|
|
124
|
+
elapsed,
|
|
125
|
+
max_output_lines
|
|
118
126
|
)
|
|
119
127
|
end
|
|
120
128
|
end
|
|
@@ -151,22 +159,30 @@ module Clacky
|
|
|
151
159
|
rescue StandardError
|
|
152
160
|
end
|
|
153
161
|
|
|
162
|
+
stdout_output = stdout_buffer.string
|
|
163
|
+
stderr_output = stderr_buffer.string
|
|
164
|
+
|
|
154
165
|
{
|
|
155
166
|
command: command,
|
|
156
|
-
stdout:
|
|
157
|
-
stderr:
|
|
167
|
+
stdout: truncate_output(stdout_output, max_output_lines),
|
|
168
|
+
stderr: truncate_output(stderr_output, max_output_lines),
|
|
158
169
|
exit_code: wait_thr.value.exitstatus,
|
|
159
170
|
success: wait_thr.value.success?,
|
|
160
|
-
elapsed: Time.now - start_time
|
|
171
|
+
elapsed: Time.now - start_time,
|
|
172
|
+
output_truncated: output_truncated?(stdout_output, stderr_output, max_output_lines)
|
|
161
173
|
}
|
|
162
174
|
end
|
|
163
175
|
rescue StandardError => e
|
|
176
|
+
stdout_output = stdout_buffer.string
|
|
177
|
+
stderr_output = "Error executing command: #{e.message}\n#{e.backtrace.first(3).join("\n")}"
|
|
178
|
+
|
|
164
179
|
{
|
|
165
180
|
command: command,
|
|
166
|
-
stdout:
|
|
167
|
-
stderr:
|
|
181
|
+
stdout: truncate_output(stdout_output, max_output_lines),
|
|
182
|
+
stderr: truncate_output(stderr_output, max_output_lines),
|
|
168
183
|
exit_code: -1,
|
|
169
|
-
success: false
|
|
184
|
+
success: false,
|
|
185
|
+
output_truncated: output_truncated?(stdout_output, stderr_output, max_output_lines)
|
|
170
186
|
}
|
|
171
187
|
end
|
|
172
188
|
end
|
|
@@ -206,16 +222,17 @@ module Clacky
|
|
|
206
222
|
nil
|
|
207
223
|
end
|
|
208
224
|
|
|
209
|
-
def format_waiting_input_result(command, stdout, stderr, interaction)
|
|
225
|
+
def format_waiting_input_result(command, stdout, stderr, interaction, max_output_lines)
|
|
210
226
|
{
|
|
211
227
|
command: command,
|
|
212
|
-
stdout: stdout,
|
|
213
|
-
stderr: stderr,
|
|
228
|
+
stdout: truncate_output(stdout, max_output_lines),
|
|
229
|
+
stderr: truncate_output(stderr, max_output_lines),
|
|
214
230
|
exit_code: -2,
|
|
215
231
|
success: false,
|
|
216
232
|
state: 'WAITING_INPUT',
|
|
217
233
|
interaction_type: interaction[:type],
|
|
218
|
-
message: format_waiting_message(stdout, interaction)
|
|
234
|
+
message: format_waiting_message(truncate_output(stdout, max_output_lines), interaction),
|
|
235
|
+
output_truncated: output_truncated?(stdout, stderr, max_output_lines)
|
|
219
236
|
}
|
|
220
237
|
end
|
|
221
238
|
|
|
@@ -238,16 +255,17 @@ module Clacky
|
|
|
238
255
|
MSG
|
|
239
256
|
end
|
|
240
257
|
|
|
241
|
-
def format_stuck_result(command, stdout, stderr, elapsed)
|
|
258
|
+
def format_stuck_result(command, stdout, stderr, elapsed, max_output_lines)
|
|
242
259
|
{
|
|
243
260
|
command: command,
|
|
244
|
-
stdout: stdout,
|
|
245
|
-
stderr: stderr,
|
|
261
|
+
stdout: truncate_output(stdout, max_output_lines),
|
|
262
|
+
stderr: truncate_output(stderr, max_output_lines),
|
|
246
263
|
exit_code: -3,
|
|
247
264
|
success: false,
|
|
248
265
|
state: 'STUCK',
|
|
249
266
|
elapsed: elapsed,
|
|
250
|
-
message: format_stuck_message(stdout, elapsed)
|
|
267
|
+
message: format_stuck_message(truncate_output(stdout, max_output_lines), elapsed),
|
|
268
|
+
output_truncated: output_truncated?(stdout, stderr, max_output_lines)
|
|
251
269
|
}
|
|
252
270
|
end
|
|
253
271
|
|
|
@@ -267,18 +285,38 @@ module Clacky
|
|
|
267
285
|
MSG
|
|
268
286
|
end
|
|
269
287
|
|
|
270
|
-
def format_timeout_result(command, stdout, stderr, elapsed, type, timeout)
|
|
288
|
+
def format_timeout_result(command, stdout, stderr, elapsed, type, timeout, max_output_lines)
|
|
271
289
|
{
|
|
272
290
|
command: command,
|
|
273
|
-
stdout: stdout,
|
|
274
|
-
stderr: stderr.empty? ? "Command timed out after #{elapsed.round(1)} seconds (#{type}=#{timeout}s)" : stderr,
|
|
291
|
+
stdout: truncate_output(stdout, max_output_lines),
|
|
292
|
+
stderr: truncate_output(stderr.empty? ? "Command timed out after #{elapsed.round(1)} seconds (#{type}=#{timeout}s)" : stderr, max_output_lines),
|
|
275
293
|
exit_code: -1,
|
|
276
294
|
success: false,
|
|
277
295
|
state: 'TIMEOUT',
|
|
278
|
-
timeout_type: type
|
|
296
|
+
timeout_type: type,
|
|
297
|
+
output_truncated: output_truncated?(stdout, stderr, max_output_lines)
|
|
279
298
|
}
|
|
280
299
|
end
|
|
281
300
|
|
|
301
|
+
# Truncate output to max_lines, adding a truncation notice if needed
|
|
302
|
+
def truncate_output(output, max_lines)
|
|
303
|
+
return output if output.nil? || output.empty?
|
|
304
|
+
|
|
305
|
+
lines = output.lines
|
|
306
|
+
return output if lines.length <= max_lines
|
|
307
|
+
|
|
308
|
+
truncated_lines = lines.first(max_lines)
|
|
309
|
+
truncation_notice = "\n\n... [Output truncated: showing #{max_lines} of #{lines.length} lines] ...\n"
|
|
310
|
+
truncated_lines.join + truncation_notice
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Check if output was truncated
|
|
314
|
+
def output_truncated?(stdout, stderr, max_lines)
|
|
315
|
+
stdout_lines = stdout&.lines&.length || 0
|
|
316
|
+
stderr_lines = stderr&.lines&.length || 0
|
|
317
|
+
stdout_lines > max_lines || stderr_lines > max_lines
|
|
318
|
+
end
|
|
319
|
+
|
|
282
320
|
def format_call(args)
|
|
283
321
|
cmd = args[:command] || args['command'] || ''
|
|
284
322
|
cmd_parts = cmd.split
|
data/lib/clacky/ui/banner.rb
CHANGED
|
@@ -9,10 +9,10 @@ module Clacky
|
|
|
9
9
|
LOGO = <<~'LOGO'
|
|
10
10
|
██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗██╗ █████╗ ██████╗██╗ ██╗██╗ ██╗
|
|
11
11
|
██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██║ ██╔══██╗██╔════╝██║ ██╔╝╚██╗ ██╔╝
|
|
12
|
-
██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ███████║██║ █████╔╝ ╚████╔╝
|
|
13
|
-
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██╔══██║██║ ██╔═██╗ ╚██╔╝
|
|
14
|
-
╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗███████╗██║ ██║╚██████╗██║ ██╗ ██║
|
|
15
|
-
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝
|
|
12
|
+
██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ███████║██║ █████╔╝ ╚████╔╝
|
|
13
|
+
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██╔══██║██║ ██╔═██╗ ╚██╔╝
|
|
14
|
+
╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗███████╗██║ ██║╚██████╗██║ ██╗ ██║
|
|
15
|
+
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝
|
|
16
16
|
LOGO
|
|
17
17
|
|
|
18
18
|
TAGLINE = "[>] AI Coding Assistant & Technical Co-founder"
|
|
@@ -72,25 +72,36 @@ module Clacky
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
# Display task completion summary
|
|
75
|
-
def display_task_complete(iterations:, cost:, total_tasks:, total_cost:, cache_stats: {})
|
|
75
|
+
def display_task_complete(iterations:, cost:, total_tasks:, total_cost:, cost_source:, cache_stats: {})
|
|
76
76
|
puts
|
|
77
77
|
puts separator("-")
|
|
78
78
|
puts @pastel.bright_green("[✓] TASK COMPLETED")
|
|
79
79
|
puts info_line("Iterations", iterations)
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
# Add cost source indicator
|
|
82
|
+
cost_suffix = case cost_source
|
|
83
|
+
when :api
|
|
84
|
+
" (by API)"
|
|
85
|
+
when :price
|
|
86
|
+
" (by PRICE)"
|
|
87
|
+
when :default
|
|
88
|
+
" (by DEFAULT)"
|
|
89
|
+
else
|
|
90
|
+
" (estimated)"
|
|
91
|
+
end
|
|
92
|
+
puts info_line("Cost", "$#{cost}#{cost_suffix}")
|
|
81
93
|
puts info_line("Session Total", "#{total_tasks} tasks, $#{total_cost}")
|
|
82
|
-
|
|
94
|
+
|
|
83
95
|
# Display cache statistics if available
|
|
84
96
|
if cache_stats[:total_requests] && cache_stats[:total_requests] > 0
|
|
85
97
|
puts
|
|
86
98
|
puts @pastel.cyan(" [Prompt Caching]")
|
|
87
|
-
puts info_line(" Cache Writes", "#{cache_stats[:cache_creation_input_tokens]} tokens")
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
puts info_line(" Cache Reads/Writes", "#{cache_stats[:cache_read_input_tokens]}/#{cache_stats[:cache_creation_input_tokens]} tokens")
|
|
100
|
+
|
|
90
101
|
hit_rate = (cache_stats[:cache_hit_requests].to_f / cache_stats[:total_requests] * 100).round(1)
|
|
91
102
|
puts info_line(" Cache Hit Rate", "#{hit_rate}% (#{cache_stats[:cache_hit_requests]}/#{cache_stats[:total_requests]} requests)")
|
|
92
103
|
end
|
|
93
|
-
|
|
104
|
+
|
|
94
105
|
puts separator("-")
|
|
95
106
|
puts
|
|
96
107
|
end
|