openclacky 0.6.2 → 0.6.3
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/lib/clacky/agent.rb +542 -54
- data/lib/clacky/cli.rb +341 -2
- data/lib/clacky/default_skills/skill-add/SKILL.md +66 -0
- data/lib/clacky/skill.rb +236 -0
- data/lib/clacky/skill_loader.rb +320 -0
- data/lib/clacky/tools/file_reader.rb +112 -9
- data/lib/clacky/tools/grep.rb +9 -14
- data/lib/clacky/tools/safe_shell.rb +14 -8
- data/lib/clacky/tools/shell.rb +89 -52
- data/lib/clacky/tools/web_fetch.rb +81 -18
- data/lib/clacky/ui2/components/command_suggestions.rb +273 -0
- data/lib/clacky/ui2/components/inline_input.rb +34 -15
- data/lib/clacky/ui2/components/input_area.rb +105 -83
- data/lib/clacky/ui2/layout_manager.rb +89 -33
- data/lib/clacky/ui2/line_editor.rb +142 -2
- data/lib/clacky/ui2/themes/hacker_theme.rb +1 -1
- data/lib/clacky/ui2/themes/minimal_theme.rb +1 -1
- data/lib/clacky/ui2/ui_controller.rb +34 -43
- data/lib/clacky/utils/file_ignore_helper.rb +10 -12
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky.rb +2 -0
- metadata +5 -1
|
@@ -32,7 +32,7 @@ module Clacky
|
|
|
32
32
|
user: [:white, :white],
|
|
33
33
|
assistant: [:bright_green, :white],
|
|
34
34
|
tool_call: [:bright_cyan, :cyan],
|
|
35
|
-
tool_result: [:
|
|
35
|
+
tool_result: [:bright_cyan, :cyan],
|
|
36
36
|
tool_denied: [:bright_yellow, :yellow],
|
|
37
37
|
tool_planned: [:bright_cyan, :cyan],
|
|
38
38
|
tool_error: [:bright_red, :red],
|
|
@@ -29,7 +29,7 @@ module Clacky
|
|
|
29
29
|
user: [:white, :white],
|
|
30
30
|
assistant: [:green, :white],
|
|
31
31
|
tool_call: [:cyan, :cyan],
|
|
32
|
-
tool_result: [:
|
|
32
|
+
tool_result: [:cyan, :cyan],
|
|
33
33
|
tool_denied: [:yellow, :yellow],
|
|
34
34
|
tool_planned: [:cyan, :cyan],
|
|
35
35
|
tool_error: [:red, :red],
|
|
@@ -147,6 +147,12 @@ module Clacky
|
|
|
147
147
|
@mode_toggle_callback = block
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
+
# Set skill loader for command suggestions
|
|
151
|
+
# @param skill_loader [Clacky::SkillLoader] The skill loader instance
|
|
152
|
+
def set_skill_loader(skill_loader)
|
|
153
|
+
@input_area.set_skill_loader(skill_loader)
|
|
154
|
+
end
|
|
155
|
+
|
|
150
156
|
# Append output to the output area
|
|
151
157
|
# @param content [String] Content to append
|
|
152
158
|
def append_output(content)
|
|
@@ -214,7 +220,7 @@ module Clacky
|
|
|
214
220
|
|
|
215
221
|
# Delta tokens with color coding (green/yellow/red + dim)
|
|
216
222
|
delta_tokens = token_data[:delta_tokens]
|
|
217
|
-
delta_str = "+#{delta_tokens}"
|
|
223
|
+
delta_str = delta_tokens.negative? ? "#{delta_tokens}" : "+#{delta_tokens}"
|
|
218
224
|
color_style = if delta_tokens > 10000
|
|
219
225
|
:red
|
|
220
226
|
elsif delta_tokens > 5000
|
|
@@ -222,7 +228,11 @@ module Clacky
|
|
|
222
228
|
else
|
|
223
229
|
:green
|
|
224
230
|
end
|
|
225
|
-
colored_delta =
|
|
231
|
+
colored_delta = if delta_tokens.negative?
|
|
232
|
+
pastel.cyan(delta_str)
|
|
233
|
+
else
|
|
234
|
+
pastel.decorate(delta_str, color_style, :dim)
|
|
235
|
+
end
|
|
226
236
|
token_info << colored_delta
|
|
227
237
|
|
|
228
238
|
# Cache status indicator (using theme)
|
|
@@ -342,10 +352,13 @@ module Clacky
|
|
|
342
352
|
|
|
343
353
|
# Remove <think>...</think> blocks (multiline, case-insensitive)
|
|
344
354
|
# Also handles variations like <thinking>...</thinking>
|
|
345
|
-
filtered = content.gsub(%r{<think(?:ing)
|
|
355
|
+
filtered = content.gsub(%r{<think(?:ing)?>[\s\S]*?</think(?:ing)?>}mi, '')
|
|
356
|
+
|
|
357
|
+
# Clean up multiple empty lines left behind (max 2 consecutive newlines)
|
|
358
|
+
filtered.gsub!(/\n{3,}/, "\n\n")
|
|
346
359
|
|
|
347
|
-
#
|
|
348
|
-
filtered.
|
|
360
|
+
# Remove leading and trailing whitespace
|
|
361
|
+
filtered.strip
|
|
349
362
|
end
|
|
350
363
|
|
|
351
364
|
# Show tool call
|
|
@@ -556,8 +569,9 @@ module Clacky
|
|
|
556
569
|
# Collect input (blocks until user presses Enter)
|
|
557
570
|
result_text = inline_input.collect
|
|
558
571
|
|
|
559
|
-
# Clean up - remove the inline input
|
|
560
|
-
|
|
572
|
+
# Clean up - remove the inline input lines (handle wrapped lines)
|
|
573
|
+
line_count = inline_input.line_count
|
|
574
|
+
@layout.remove_last_line(line_count)
|
|
561
575
|
|
|
562
576
|
# Append the final response to output
|
|
563
577
|
if result_text.nil?
|
|
@@ -594,41 +608,15 @@ module Clacky
|
|
|
594
608
|
require 'diffy'
|
|
595
609
|
|
|
596
610
|
diff = Diffy::Diff.new(old_content, new_content, context: 3)
|
|
597
|
-
|
|
598
|
-
plain_lines = diff.to_s.lines
|
|
599
|
-
|
|
600
|
-
# Add line numbers to diff output
|
|
601
|
-
old_line_num = 0
|
|
602
|
-
new_line_num = 0
|
|
603
|
-
|
|
604
|
-
numbered_lines = all_lines.each_with_index.map do |line, index|
|
|
605
|
-
# Use plain text to detect line type (remove ANSI codes)
|
|
606
|
-
plain_line = plain_lines[index]&.chomp || line.gsub(/\e\[[0-9;]*m/, '').chomp
|
|
607
|
-
|
|
608
|
-
# Remove trailing newline from colored line to avoid double newlines
|
|
609
|
-
colored_line = line.chomp
|
|
610
|
-
|
|
611
|
-
# Determine line type and number (use single line number for simplicity)
|
|
612
|
-
if plain_line.start_with?('+') || plain_line.start_with?('-') || plain_line.start_with?(' ')
|
|
613
|
-
new_line_num += 1
|
|
614
|
-
sprintf("%4d | %s", new_line_num, colored_line)
|
|
615
|
-
elsif plain_line.start_with?('@@')
|
|
616
|
-
# Diff header: extract line numbers from @@ -old_start,old_count +new_start,new_count @@
|
|
617
|
-
if plain_line =~ /@@ -(\d+)(?:,\d+)? (\d+)(?:,\d+)? @@/
|
|
618
|
-
new_line_num = $2.to_i - 1
|
|
619
|
-
end
|
|
620
|
-
sprintf("%4s | %s", "", colored_line)
|
|
621
|
-
else
|
|
622
|
-
# Other lines (headers, etc.)
|
|
623
|
-
sprintf("%4s | %s", "", colored_line)
|
|
624
|
-
end
|
|
625
|
-
end
|
|
611
|
+
diff_lines = diff.to_s(:color).lines
|
|
626
612
|
|
|
627
|
-
|
|
628
|
-
|
|
613
|
+
# Show diff without line numbers
|
|
614
|
+
diff_lines.take(max_lines).each do |line|
|
|
615
|
+
append_output(line.chomp)
|
|
616
|
+
end
|
|
629
617
|
|
|
630
|
-
if
|
|
631
|
-
append_output("\n... (#{
|
|
618
|
+
if diff_lines.size > max_lines
|
|
619
|
+
append_output("\n... (#{diff_lines.size - max_lines} more lines, diff truncated)")
|
|
632
620
|
end
|
|
633
621
|
rescue LoadError
|
|
634
622
|
# Fallback if diffy is not available
|
|
@@ -788,16 +776,19 @@ module Clacky
|
|
|
788
776
|
|
|
789
777
|
# Handle key input for InlineInput
|
|
790
778
|
def handle_inline_input_key(key)
|
|
779
|
+
# Get old line count BEFORE modification
|
|
780
|
+
old_line_count = @inline_input.line_count
|
|
781
|
+
|
|
791
782
|
result = @inline_input.handle_key(key)
|
|
792
783
|
|
|
793
784
|
case result[:action]
|
|
794
785
|
when :update
|
|
795
|
-
# Update the
|
|
796
|
-
@layout.update_last_line(@inline_input.render)
|
|
786
|
+
# Update the output area with current input (considering wrapped lines)
|
|
787
|
+
@layout.update_last_line(@inline_input.render, old_line_count)
|
|
797
788
|
# Position cursor for inline input
|
|
798
789
|
@layout.position_inline_input_cursor(@inline_input)
|
|
799
790
|
when :submit, :cancel
|
|
800
|
-
# InlineInput is done, will be cleaned up by request_confirmation
|
|
791
|
+
# InlineInput is done, will be cleaned up by request_confirmation after collect returns
|
|
801
792
|
nil
|
|
802
793
|
when :toggle_mode
|
|
803
794
|
# Update mode and session bar info, but don't render yet
|
|
@@ -30,20 +30,18 @@ module Clacky
|
|
|
30
30
|
/\.ini$/,
|
|
31
31
|
/\.conf$/,
|
|
32
32
|
/\.config$/,
|
|
33
|
-
/config\//,
|
|
34
|
-
/\.config\//
|
|
35
33
|
].freeze
|
|
36
34
|
|
|
37
35
|
# Find .gitignore file in the search path or parent directories
|
|
38
36
|
# Only searches within the search path and up to the current working directory
|
|
39
37
|
def self.find_gitignore(path)
|
|
40
38
|
search_path = File.directory?(path) ? path : File.dirname(path)
|
|
41
|
-
|
|
39
|
+
|
|
42
40
|
# Look for .gitignore in current and parent directories
|
|
43
41
|
current = File.expand_path(search_path)
|
|
44
42
|
cwd = File.expand_path(Dir.pwd)
|
|
45
43
|
root = File.expand_path('/')
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
# Limit search: only go up to current working directory
|
|
48
46
|
# This prevents finding .gitignore files from unrelated parent directories
|
|
49
47
|
# when searching in temporary directories (like /tmp in tests)
|
|
@@ -52,16 +50,16 @@ module Clacky
|
|
|
52
50
|
else
|
|
53
51
|
current
|
|
54
52
|
end
|
|
55
|
-
|
|
53
|
+
|
|
56
54
|
loop do
|
|
57
55
|
gitignore = File.join(current, '.gitignore')
|
|
58
56
|
return gitignore if File.exist?(gitignore)
|
|
59
|
-
|
|
57
|
+
|
|
60
58
|
# Stop if we've reached the search limit or root
|
|
61
59
|
break if current == search_limit || current == root
|
|
62
60
|
current = File.dirname(current)
|
|
63
61
|
end
|
|
64
|
-
|
|
62
|
+
|
|
65
63
|
nil
|
|
66
64
|
end
|
|
67
65
|
|
|
@@ -71,10 +69,10 @@ module Clacky
|
|
|
71
69
|
# Expand both paths to handle symlinks and relative paths correctly
|
|
72
70
|
expanded_file = File.expand_path(file)
|
|
73
71
|
expanded_base = File.expand_path(base_path)
|
|
74
|
-
|
|
72
|
+
|
|
75
73
|
# For files, use the directory as base
|
|
76
74
|
expanded_base = File.dirname(expanded_base) if File.file?(expanded_base)
|
|
77
|
-
|
|
75
|
+
|
|
78
76
|
# Calculate relative path
|
|
79
77
|
if expanded_file.start_with?(expanded_base)
|
|
80
78
|
relative_path = expanded_file[(expanded_base.length + 1)..-1] || File.basename(expanded_file)
|
|
@@ -82,10 +80,10 @@ module Clacky
|
|
|
82
80
|
# File is outside base path - use just the filename
|
|
83
81
|
relative_path = File.basename(expanded_file)
|
|
84
82
|
end
|
|
85
|
-
|
|
83
|
+
|
|
86
84
|
# Clean up relative path
|
|
87
85
|
relative_path = relative_path.sub(/^\.\//, '') if relative_path
|
|
88
|
-
|
|
86
|
+
|
|
89
87
|
if gitignore
|
|
90
88
|
# Use .gitignore rules
|
|
91
89
|
gitignore.ignored?(relative_path)
|
|
@@ -96,7 +94,7 @@ module Clacky
|
|
|
96
94
|
File.fnmatch(pattern, relative_path, File::FNM_PATHNAME | File::FNM_DOTMATCH)
|
|
97
95
|
else
|
|
98
96
|
# Match pattern as a path component (not substring of absolute path)
|
|
99
|
-
relative_path.start_with?("#{pattern}/") ||
|
|
97
|
+
relative_path.start_with?("#{pattern}/") ||
|
|
100
98
|
relative_path.include?("/#{pattern}/") ||
|
|
101
99
|
relative_path == pattern ||
|
|
102
100
|
File.basename(relative_path) == pattern
|
data/lib/clacky/version.rb
CHANGED
data/lib/clacky.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openclacky
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- windy
|
|
@@ -167,11 +167,14 @@ files:
|
|
|
167
167
|
- lib/clacky/cli.rb
|
|
168
168
|
- lib/clacky/client.rb
|
|
169
169
|
- lib/clacky/config.rb
|
|
170
|
+
- lib/clacky/default_skills/skill-add/SKILL.md
|
|
170
171
|
- lib/clacky/gitignore_parser.rb
|
|
171
172
|
- lib/clacky/hook_manager.rb
|
|
172
173
|
- lib/clacky/model_pricing.rb
|
|
173
174
|
- lib/clacky/progress_indicator.rb
|
|
174
175
|
- lib/clacky/session_manager.rb
|
|
176
|
+
- lib/clacky/skill.rb
|
|
177
|
+
- lib/clacky/skill_loader.rb
|
|
175
178
|
- lib/clacky/thinking_verbs.rb
|
|
176
179
|
- lib/clacky/tool_registry.rb
|
|
177
180
|
- lib/clacky/tools/base.rb
|
|
@@ -191,6 +194,7 @@ files:
|
|
|
191
194
|
- lib/clacky/ui2.rb
|
|
192
195
|
- lib/clacky/ui2/README.md
|
|
193
196
|
- lib/clacky/ui2/components/base_component.rb
|
|
197
|
+
- lib/clacky/ui2/components/command_suggestions.rb
|
|
194
198
|
- lib/clacky/ui2/components/common_component.rb
|
|
195
199
|
- lib/clacky/ui2/components/inline_input.rb
|
|
196
200
|
- lib/clacky/ui2/components/input_area.rb
|