shared_tools 0.3.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccef2b45ebf2ea78e16ba617d73d1b9617203343bd9b5aad139f3eb72140c3ee
4
- data.tar.gz: 67095ae417a67860aadd7b169233f3f40e93278b7d46d10feb4ffc0f8de27b71
3
+ metadata.gz: e5775624c9ae2d7f0fb1a47c515b2dbe7316faaee9de7bc62823e0d602274a0a
4
+ data.tar.gz: 480df1266dc12f1bd4104ef3f80a78b895ac22ebf149b9090f0bcf679bcb8a99
5
5
  SHA512:
6
- metadata.gz: d01003df48f64af2e9743264f213ba46b27002a2bf4aa29384d803d9a096c7fefe05a63a2479182f851b72acba842fb9cebac5398aa9c0b5e0ebbbd9895a72eb
7
- data.tar.gz: 7f6e1c51e2f174bb400cba67a999fbe422b483c0ea098b4031278c13d2766e65cb7799258e895ceebda6e9da02e8e7445f444d887667845964b59f1bd199c837
6
+ metadata.gz: 00a01f1113bb7e728b00aa196f998850c66246d4e5e26d7e1db16d0314bf3f2993f1632dafe964ea347690a6a03a9e9c04440ed47ef1f1870c858c1f2eda3aed
7
+ data.tar.gz: 613fbbae8478fa3b805d368102e31cf9c9709e16f954f87a8fea10aa8f148550147f884b68956714eb817cd6288b1f4213b3612ea887ad502e8be8d13973db74
data/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  ## Unreleased
4
4
 
5
5
  ## Released
6
+ ### [0.3.1] 2025-12-17
7
+
8
+ - added ClipboardTool for cross-platform clipboard read/write operations
9
+ - added CronTool for cron expression parsing and scheduling
10
+ - added CurrentDateTimeTool for date/time retrieval with timezone support
11
+ - added DnsTool for DNS lookups and resolution
12
+ - added SystemInfoTool for system information retrieval
13
+ - updated gem dependencies and Zeitwerk configuration
14
+ - improved BrowserTool, ComputerTool, and DatabaseTool implementations
15
+ - updated GitHub Pages deployment workflow
16
+ - fixed a problem with eager loading when used with the `aia` gem
17
+
6
18
  ### [0.3.0] 2025-11-08
7
19
  - changed focus of shared_tools to only support the ruby_llm and ruby_llm-mcp ecosystem
8
20
 
@@ -146,12 +146,15 @@ module SharedTools
146
146
 
147
147
 
148
148
  # @param logger [Logger] optional logger
149
- # @param driver [SharedTools::Tools::Browser::BaseDriver] optional, will attempt to create WatirDriver if not provided
149
+ # @param driver [SharedTools::Tools::Browser::BaseDriver] optional, will attempt to create WatirDriver when execute is called
150
150
  def initialize(logger: nil, driver: nil)
151
151
  @logger = logger || RubyLLM.logger
152
- @driver = driver || default_driver
152
+ @driver = driver # Defer default_driver to execute time to support RubyLLM tool discovery
153
153
  end
154
154
 
155
+ # Set driver after instantiation (useful when tool is discovered by RubyLLM)
156
+ attr_writer :driver
157
+
155
158
  def cleanup!
156
159
  @driver.close
157
160
  end
@@ -166,6 +169,9 @@ module SharedTools
166
169
  #
167
170
  # @return [String]
168
171
  def execute(action:, url: nil, selector: nil, value: nil, context_size: 2, full_html: false, text_content: nil)
172
+ # Lazily resolve driver at execute time
173
+ @driver ||= default_driver
174
+
169
175
  case action.to_s.downcase
170
176
  when Action::VISIT
171
177
  require_param!(:url, url)
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby_llm/tool'
4
+
5
+ module SharedTools
6
+ module Tools
7
+ # A tool for reading from and writing to the system clipboard.
8
+ # Supports macOS (pbcopy/pbpaste), Linux (xclip/xsel), and Windows (clip).
9
+ #
10
+ # @example
11
+ # tool = SharedTools::Tools::ClipboardTool.new
12
+ # tool.execute(action: 'write', content: 'Hello, World!')
13
+ # result = tool.execute(action: 'read')
14
+ # puts result[:content] # "Hello, World!"
15
+ class ClipboardTool < RubyLLM::Tool
16
+ def self.name = 'clipboard'
17
+
18
+ description <<~'DESCRIPTION'
19
+ Read from or write to the system clipboard.
20
+
21
+ This tool provides cross-platform clipboard access:
22
+ - macOS: Uses pbcopy/pbpaste
23
+ - Linux: Uses xclip or xsel (must be installed)
24
+ - Windows: Uses clip/powershell
25
+
26
+ Actions:
27
+ - 'read': Get the current clipboard contents
28
+ - 'write': Set the clipboard contents
29
+ - 'clear': Clear the clipboard
30
+
31
+ Example usage:
32
+ tool = SharedTools::Tools::ClipboardTool.new
33
+
34
+ # Write to clipboard
35
+ tool.execute(action: 'write', content: 'Hello, World!')
36
+
37
+ # Read from clipboard
38
+ result = tool.execute(action: 'read')
39
+ puts result[:content]
40
+
41
+ # Clear clipboard
42
+ tool.execute(action: 'clear')
43
+ DESCRIPTION
44
+
45
+ params do
46
+ string :action, description: <<~DESC.strip
47
+ The clipboard action to perform:
48
+ - 'read': Get current clipboard contents
49
+ - 'write': Set clipboard contents (requires 'content' parameter)
50
+ - 'clear': Clear the clipboard
51
+ DESC
52
+
53
+ string :content, description: <<~DESC.strip, required: false
54
+ The text content to write to the clipboard.
55
+ Required when action is 'write'.
56
+ DESC
57
+ end
58
+
59
+ # @param logger [Logger] optional logger
60
+ def initialize(logger: nil)
61
+ @logger = logger || RubyLLM.logger
62
+ end
63
+
64
+ # Execute clipboard action
65
+ #
66
+ # @param action [String] 'read', 'write', or 'clear'
67
+ # @param content [String, nil] content to write (required for 'write' action)
68
+ # @return [Hash] result with success status and content/error
69
+ def execute(action:, content: nil)
70
+ @logger.info("ClipboardTool#execute action=#{action.inspect}")
71
+
72
+ case action.to_s.downcase
73
+ when 'read'
74
+ read_clipboard
75
+ when 'write'
76
+ write_clipboard(content)
77
+ when 'clear'
78
+ clear_clipboard
79
+ else
80
+ {
81
+ success: false,
82
+ error: "Unknown action: #{action}. Valid actions are: read, write, clear"
83
+ }
84
+ end
85
+ rescue => e
86
+ @logger.error("ClipboardTool error: #{e.message}")
87
+ {
88
+ success: false,
89
+ error: e.message
90
+ }
91
+ end
92
+
93
+ private
94
+
95
+ def read_clipboard
96
+ content = case platform
97
+ when :macos
98
+ `pbpaste 2>/dev/null`
99
+ when :linux
100
+ if command_exists?('xclip')
101
+ `xclip -selection clipboard -o 2>/dev/null`
102
+ elsif command_exists?('xsel')
103
+ `xsel --clipboard --output 2>/dev/null`
104
+ else
105
+ raise "No clipboard tool found. Install xclip or xsel."
106
+ end
107
+ when :windows
108
+ `powershell -command "Get-Clipboard" 2>nul`.chomp
109
+ else
110
+ raise "Unsupported platform: #{RUBY_PLATFORM}"
111
+ end
112
+
113
+ {
114
+ success: true,
115
+ content: content,
116
+ length: content.length
117
+ }
118
+ end
119
+
120
+ def write_clipboard(content)
121
+ if content.nil? || content.empty?
122
+ return {
123
+ success: false,
124
+ error: "Content is required for write action"
125
+ }
126
+ end
127
+
128
+ case platform
129
+ when :macos
130
+ IO.popen('pbcopy', 'w') { |io| io.print content }
131
+ when :linux
132
+ if command_exists?('xclip')
133
+ IO.popen('xclip -selection clipboard', 'w') { |io| io.print content }
134
+ elsif command_exists?('xsel')
135
+ IO.popen('xsel --clipboard --input', 'w') { |io| io.print content }
136
+ else
137
+ raise "No clipboard tool found. Install xclip or xsel."
138
+ end
139
+ when :windows
140
+ IO.popen('clip', 'w') { |io| io.print content }
141
+ else
142
+ raise "Unsupported platform: #{RUBY_PLATFORM}"
143
+ end
144
+
145
+ {
146
+ success: true,
147
+ message: "Content written to clipboard",
148
+ length: content.length
149
+ }
150
+ end
151
+
152
+ def clear_clipboard
153
+ case platform
154
+ when :macos
155
+ IO.popen('pbcopy', 'w') { |io| io.print '' }
156
+ when :linux
157
+ if command_exists?('xclip')
158
+ IO.popen('xclip -selection clipboard', 'w') { |io| io.print '' }
159
+ elsif command_exists?('xsel')
160
+ IO.popen('xsel --clipboard --input', 'w') { |io| io.print '' }
161
+ else
162
+ raise "No clipboard tool found. Install xclip or xsel."
163
+ end
164
+ when :windows
165
+ IO.popen('clip', 'w') { |io| io.print '' }
166
+ else
167
+ raise "Unsupported platform: #{RUBY_PLATFORM}"
168
+ end
169
+
170
+ {
171
+ success: true,
172
+ message: "Clipboard cleared"
173
+ }
174
+ end
175
+
176
+ def platform
177
+ case RUBY_PLATFORM
178
+ when /darwin/
179
+ :macos
180
+ when /linux/
181
+ :linux
182
+ when /mswin|mingw|cygwin/
183
+ :windows
184
+ else
185
+ :unknown
186
+ end
187
+ end
188
+
189
+ def command_exists?(cmd)
190
+ system("which #{cmd} > /dev/null 2>&1")
191
+ end
192
+ end
193
+ end
194
+ end
@@ -139,13 +139,16 @@ module SharedTools
139
139
  end
140
140
 
141
141
 
142
- # @param driver [Computer::BaseDriver] optional, will attempt to create platform-specific driver if not provided
142
+ # @param driver [Computer::BaseDriver] optional, will attempt to create platform-specific driver when execute is called
143
143
  # @param logger [Logger] optional logger
144
144
  def initialize(driver: nil, logger: nil)
145
145
  @logger = logger || RubyLLM.logger
146
- @driver = driver || default_driver
146
+ @driver = driver # Defer default_driver to execute time to support RubyLLM tool discovery
147
147
  end
148
148
 
149
+ # Set driver after instantiation (useful when tool is discovered by RubyLLM)
150
+ attr_writer :driver
151
+
149
152
  # @param action [String]
150
153
  # @param coordinate [Hash<{ width: Integer, height: Integer }>] the (x,y) coordinate
151
154
  # @param text [String]
@@ -162,6 +165,9 @@ module SharedTools
162
165
  scroll_direction: nil,
163
166
  scroll_amount: nil
164
167
  )
168
+ # Lazily resolve driver at execute time
169
+ @driver ||= default_driver
170
+
165
171
  @logger.info({
166
172
  action:,
167
173
  coordinate:,