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 +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/shared_tools/tools/browser_tool.rb +8 -2
- data/lib/shared_tools/tools/clipboard_tool.rb +194 -0
- data/lib/shared_tools/tools/computer_tool.rb +8 -2
- data/lib/shared_tools/tools/cron_tool.rb +474 -0
- data/lib/shared_tools/tools/current_date_time_tool.rb +154 -0
- data/lib/shared_tools/tools/database_tool.rb +8 -3
- data/lib/shared_tools/tools/dns_tool.rb +356 -0
- data/lib/shared_tools/tools/system_info_tool.rb +417 -0
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools.rb +54 -13
- metadata +7 -2
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ruby_llm/tool'
|
|
4
|
+
|
|
5
|
+
module SharedTools
|
|
6
|
+
module Tools
|
|
7
|
+
# A tool for retrieving system information including OS, CPU, memory, and disk details.
|
|
8
|
+
# Provides cross-platform support for macOS, Linux, and Windows.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# tool = SharedTools::Tools::SystemInfoTool.new
|
|
12
|
+
# result = tool.execute(category: 'all')
|
|
13
|
+
# puts result[:os][:name] # "macOS"
|
|
14
|
+
# puts result[:memory][:total] # "32 GB"
|
|
15
|
+
class SystemInfoTool < RubyLLM::Tool
|
|
16
|
+
def self.name = 'system_info'
|
|
17
|
+
|
|
18
|
+
description <<~'DESCRIPTION'
|
|
19
|
+
Retrieve system information including operating system, CPU, memory, and disk details.
|
|
20
|
+
|
|
21
|
+
This tool provides cross-platform system information:
|
|
22
|
+
- macOS: Uses system_profiler, sysctl, and df commands
|
|
23
|
+
- Linux: Uses /proc filesystem and df command
|
|
24
|
+
- Windows: Uses wmic and powershell commands
|
|
25
|
+
|
|
26
|
+
Categories:
|
|
27
|
+
- 'all': Returns all system information (default)
|
|
28
|
+
- 'os': Operating system information only
|
|
29
|
+
- 'cpu': CPU information only
|
|
30
|
+
- 'memory': Memory information only
|
|
31
|
+
- 'disk': Disk space information only
|
|
32
|
+
- 'network': Network interface information only
|
|
33
|
+
|
|
34
|
+
Example usage:
|
|
35
|
+
tool = SharedTools::Tools::SystemInfoTool.new
|
|
36
|
+
|
|
37
|
+
# Get all system info
|
|
38
|
+
result = tool.execute(category: 'all')
|
|
39
|
+
|
|
40
|
+
# Get specific category
|
|
41
|
+
result = tool.execute(category: 'memory')
|
|
42
|
+
puts result[:total] # Total RAM
|
|
43
|
+
DESCRIPTION
|
|
44
|
+
|
|
45
|
+
params do
|
|
46
|
+
string :category, description: <<~DESC.strip, required: false
|
|
47
|
+
The category of system information to retrieve:
|
|
48
|
+
- 'all' (default): All system information
|
|
49
|
+
- 'os': Operating system details
|
|
50
|
+
- 'cpu': CPU details
|
|
51
|
+
- 'memory': Memory/RAM details
|
|
52
|
+
- 'disk': Disk space details
|
|
53
|
+
- 'network': Network interface details
|
|
54
|
+
DESC
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @param logger [Logger] optional logger
|
|
58
|
+
def initialize(logger: nil)
|
|
59
|
+
@logger = logger || RubyLLM.logger
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Execute system info retrieval
|
|
63
|
+
#
|
|
64
|
+
# @param category [String] category of info to retrieve
|
|
65
|
+
# @return [Hash] system information
|
|
66
|
+
def execute(category: 'all')
|
|
67
|
+
@logger.info("SystemInfoTool#execute category=#{category.inspect}")
|
|
68
|
+
|
|
69
|
+
case category.to_s.downcase
|
|
70
|
+
when 'all'
|
|
71
|
+
get_all_info
|
|
72
|
+
when 'os'
|
|
73
|
+
{ success: true, os: get_os_info }
|
|
74
|
+
when 'cpu'
|
|
75
|
+
{ success: true, cpu: get_cpu_info }
|
|
76
|
+
when 'memory'
|
|
77
|
+
{ success: true, memory: get_memory_info }
|
|
78
|
+
when 'disk'
|
|
79
|
+
{ success: true, disk: get_disk_info }
|
|
80
|
+
when 'network'
|
|
81
|
+
{ success: true, network: get_network_info }
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
success: false,
|
|
85
|
+
error: "Unknown category: #{category}. Valid categories are: all, os, cpu, memory, disk, network"
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
rescue => e
|
|
89
|
+
@logger.error("SystemInfoTool error: #{e.message}")
|
|
90
|
+
{
|
|
91
|
+
success: false,
|
|
92
|
+
error: e.message
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def get_all_info
|
|
99
|
+
{
|
|
100
|
+
success: true,
|
|
101
|
+
os: get_os_info,
|
|
102
|
+
cpu: get_cpu_info,
|
|
103
|
+
memory: get_memory_info,
|
|
104
|
+
disk: get_disk_info,
|
|
105
|
+
network: get_network_info,
|
|
106
|
+
ruby: get_ruby_info
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def get_os_info
|
|
111
|
+
case platform
|
|
112
|
+
when :macos
|
|
113
|
+
{
|
|
114
|
+
name: 'macOS',
|
|
115
|
+
version: `sw_vers -productVersion 2>/dev/null`.strip,
|
|
116
|
+
build: `sw_vers -buildVersion 2>/dev/null`.strip,
|
|
117
|
+
hostname: `hostname 2>/dev/null`.strip,
|
|
118
|
+
kernel: `uname -r 2>/dev/null`.strip,
|
|
119
|
+
architecture: `uname -m 2>/dev/null`.strip,
|
|
120
|
+
uptime: parse_uptime(`uptime 2>/dev/null`.strip)
|
|
121
|
+
}
|
|
122
|
+
when :linux
|
|
123
|
+
{
|
|
124
|
+
name: get_linux_distro,
|
|
125
|
+
version: get_linux_version,
|
|
126
|
+
hostname: `hostname 2>/dev/null`.strip,
|
|
127
|
+
kernel: `uname -r 2>/dev/null`.strip,
|
|
128
|
+
architecture: `uname -m 2>/dev/null`.strip,
|
|
129
|
+
uptime: parse_uptime(`uptime 2>/dev/null`.strip)
|
|
130
|
+
}
|
|
131
|
+
when :windows
|
|
132
|
+
{
|
|
133
|
+
name: 'Windows',
|
|
134
|
+
version: `ver 2>nul`.strip,
|
|
135
|
+
hostname: `hostname 2>nul`.strip,
|
|
136
|
+
architecture: ENV['PROCESSOR_ARCHITECTURE'] || 'unknown'
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
{ name: 'unknown', platform: RUBY_PLATFORM }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def get_cpu_info
|
|
144
|
+
case platform
|
|
145
|
+
when :macos
|
|
146
|
+
{
|
|
147
|
+
model: `sysctl -n machdep.cpu.brand_string 2>/dev/null`.strip,
|
|
148
|
+
cores: `sysctl -n hw.ncpu 2>/dev/null`.strip.to_i,
|
|
149
|
+
physical_cores: `sysctl -n hw.physicalcpu 2>/dev/null`.strip.to_i,
|
|
150
|
+
architecture: `uname -m 2>/dev/null`.strip,
|
|
151
|
+
load_average: get_load_average
|
|
152
|
+
}
|
|
153
|
+
when :linux
|
|
154
|
+
cpu_info = File.read('/proc/cpuinfo') rescue ''
|
|
155
|
+
model = cpu_info[/model name\s*:\s*(.+)/, 1] || 'unknown'
|
|
156
|
+
cores = cpu_info.scan(/^processor/i).count
|
|
157
|
+
{
|
|
158
|
+
model: model,
|
|
159
|
+
cores: cores,
|
|
160
|
+
architecture: `uname -m 2>/dev/null`.strip,
|
|
161
|
+
load_average: get_load_average
|
|
162
|
+
}
|
|
163
|
+
when :windows
|
|
164
|
+
{
|
|
165
|
+
model: `wmic cpu get name 2>nul`.lines[1]&.strip || 'unknown',
|
|
166
|
+
cores: ENV['NUMBER_OF_PROCESSORS']&.to_i || 0,
|
|
167
|
+
architecture: ENV['PROCESSOR_ARCHITECTURE'] || 'unknown'
|
|
168
|
+
}
|
|
169
|
+
else
|
|
170
|
+
{ cores: 0, model: 'unknown' }
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def get_memory_info
|
|
175
|
+
case platform
|
|
176
|
+
when :macos
|
|
177
|
+
total_bytes = `sysctl -n hw.memsize 2>/dev/null`.strip.to_i
|
|
178
|
+
# Get page size and memory statistics
|
|
179
|
+
vm_stat = `vm_stat 2>/dev/null`
|
|
180
|
+
page_size = vm_stat[/page size of (\d+)/, 1]&.to_i || 4096
|
|
181
|
+
pages_free = vm_stat[/Pages free:\s+(\d+)/, 1]&.to_i || 0
|
|
182
|
+
pages_inactive = vm_stat[/Pages inactive:\s+(\d+)/, 1]&.to_i || 0
|
|
183
|
+
|
|
184
|
+
available_bytes = (pages_free + pages_inactive) * page_size
|
|
185
|
+
used_bytes = total_bytes - available_bytes
|
|
186
|
+
|
|
187
|
+
{
|
|
188
|
+
total: format_bytes(total_bytes),
|
|
189
|
+
total_bytes: total_bytes,
|
|
190
|
+
available: format_bytes(available_bytes),
|
|
191
|
+
available_bytes: available_bytes,
|
|
192
|
+
used: format_bytes(used_bytes),
|
|
193
|
+
used_bytes: used_bytes,
|
|
194
|
+
percent_used: ((used_bytes.to_f / total_bytes) * 100).round(1)
|
|
195
|
+
}
|
|
196
|
+
when :linux
|
|
197
|
+
meminfo = File.read('/proc/meminfo') rescue ''
|
|
198
|
+
total_kb = meminfo[/MemTotal:\s+(\d+)/, 1]&.to_i || 0
|
|
199
|
+
available_kb = meminfo[/MemAvailable:\s+(\d+)/, 1]&.to_i || 0
|
|
200
|
+
used_kb = total_kb - available_kb
|
|
201
|
+
|
|
202
|
+
{
|
|
203
|
+
total: format_bytes(total_kb * 1024),
|
|
204
|
+
total_bytes: total_kb * 1024,
|
|
205
|
+
available: format_bytes(available_kb * 1024),
|
|
206
|
+
available_bytes: available_kb * 1024,
|
|
207
|
+
used: format_bytes(used_kb * 1024),
|
|
208
|
+
used_bytes: used_kb * 1024,
|
|
209
|
+
percent_used: total_kb > 0 ? ((used_kb.to_f / total_kb) * 100).round(1) : 0
|
|
210
|
+
}
|
|
211
|
+
when :windows
|
|
212
|
+
# Using powershell for more reliable output
|
|
213
|
+
output = `powershell -command "Get-CimInstance Win32_OperatingSystem | Select-Object TotalVisibleMemorySize,FreePhysicalMemory" 2>nul`
|
|
214
|
+
total_kb = output[/TotalVisibleMemorySize\s*:\s*(\d+)/, 1]&.to_i || 0
|
|
215
|
+
free_kb = output[/FreePhysicalMemory\s*:\s*(\d+)/, 1]&.to_i || 0
|
|
216
|
+
used_kb = total_kb - free_kb
|
|
217
|
+
|
|
218
|
+
{
|
|
219
|
+
total: format_bytes(total_kb * 1024),
|
|
220
|
+
total_bytes: total_kb * 1024,
|
|
221
|
+
available: format_bytes(free_kb * 1024),
|
|
222
|
+
available_bytes: free_kb * 1024,
|
|
223
|
+
used: format_bytes(used_kb * 1024),
|
|
224
|
+
used_bytes: used_kb * 1024,
|
|
225
|
+
percent_used: total_kb > 0 ? ((used_kb.to_f / total_kb) * 100).round(1) : 0
|
|
226
|
+
}
|
|
227
|
+
else
|
|
228
|
+
{ total: 'unknown', available: 'unknown' }
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def get_disk_info
|
|
233
|
+
disks = []
|
|
234
|
+
|
|
235
|
+
case platform
|
|
236
|
+
when :macos, :linux
|
|
237
|
+
df_output = `df -h 2>/dev/null`.lines[1..]
|
|
238
|
+
df_output&.each do |line|
|
|
239
|
+
parts = line.split
|
|
240
|
+
next if parts.length < 6
|
|
241
|
+
next unless parts[0].start_with?('/') || parts[5]&.start_with?('/')
|
|
242
|
+
|
|
243
|
+
mount_point = parts[5] || parts[0]
|
|
244
|
+
disks << {
|
|
245
|
+
filesystem: parts[0],
|
|
246
|
+
size: parts[1],
|
|
247
|
+
used: parts[2],
|
|
248
|
+
available: parts[3],
|
|
249
|
+
percent_used: parts[4],
|
|
250
|
+
mount_point: mount_point
|
|
251
|
+
}
|
|
252
|
+
end
|
|
253
|
+
when :windows
|
|
254
|
+
output = `wmic logicaldisk get size,freespace,caption 2>nul`
|
|
255
|
+
output.lines[1..].each do |line|
|
|
256
|
+
parts = line.split
|
|
257
|
+
next if parts.length < 3
|
|
258
|
+
|
|
259
|
+
caption, free_space, size = parts
|
|
260
|
+
next unless size.to_i > 0
|
|
261
|
+
|
|
262
|
+
disks << {
|
|
263
|
+
filesystem: caption,
|
|
264
|
+
size: format_bytes(size.to_i),
|
|
265
|
+
available: format_bytes(free_space.to_i),
|
|
266
|
+
used: format_bytes(size.to_i - free_space.to_i),
|
|
267
|
+
percent_used: "#{((1 - free_space.to_f / size.to_i) * 100).round}%"
|
|
268
|
+
}
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
disks
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def get_network_info
|
|
276
|
+
interfaces = []
|
|
277
|
+
|
|
278
|
+
case platform
|
|
279
|
+
when :macos
|
|
280
|
+
ifconfig = `ifconfig 2>/dev/null`
|
|
281
|
+
current_interface = nil
|
|
282
|
+
|
|
283
|
+
ifconfig.each_line do |line|
|
|
284
|
+
if line =~ /^(\w+):/
|
|
285
|
+
current_interface = { name: $1, addresses: [] }
|
|
286
|
+
interfaces << current_interface
|
|
287
|
+
elsif current_interface && line =~ /inet (\d+\.\d+\.\d+\.\d+)/
|
|
288
|
+
current_interface[:addresses] << { type: 'IPv4', address: $1 }
|
|
289
|
+
elsif current_interface && line =~ /inet6 ([a-f0-9:]+)/
|
|
290
|
+
current_interface[:addresses] << { type: 'IPv6', address: $1 }
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
when :linux
|
|
294
|
+
# Try ip command first, fall back to ifconfig
|
|
295
|
+
output = `ip addr 2>/dev/null`
|
|
296
|
+
if output.empty?
|
|
297
|
+
output = `ifconfig 2>/dev/null`
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
current_interface = nil
|
|
301
|
+
output.each_line do |line|
|
|
302
|
+
if line =~ /^\d+:\s+(\w+):/
|
|
303
|
+
current_interface = { name: $1, addresses: [] }
|
|
304
|
+
interfaces << current_interface
|
|
305
|
+
elsif line =~ /^(\w+):/
|
|
306
|
+
current_interface = { name: $1, addresses: [] }
|
|
307
|
+
interfaces << current_interface
|
|
308
|
+
elsif current_interface && line =~ /inet (\d+\.\d+\.\d+\.\d+)/
|
|
309
|
+
current_interface[:addresses] << { type: 'IPv4', address: $1 }
|
|
310
|
+
elsif current_interface && line =~ /inet6 ([a-f0-9:]+)/
|
|
311
|
+
current_interface[:addresses] << { type: 'IPv6', address: $1 }
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
when :windows
|
|
315
|
+
output = `ipconfig 2>nul`
|
|
316
|
+
current_interface = nil
|
|
317
|
+
|
|
318
|
+
output.each_line do |line|
|
|
319
|
+
if line =~ /adapter (.+):/i
|
|
320
|
+
current_interface = { name: $1.strip, addresses: [] }
|
|
321
|
+
interfaces << current_interface
|
|
322
|
+
elsif current_interface && line =~ /IPv4.*:\s*(\d+\.\d+\.\d+\.\d+)/
|
|
323
|
+
current_interface[:addresses] << { type: 'IPv4', address: $1 }
|
|
324
|
+
elsif current_interface && line =~ /IPv6.*:\s*([a-f0-9:]+)/i
|
|
325
|
+
current_interface[:addresses] << { type: 'IPv6', address: $1 }
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Filter out interfaces with no addresses
|
|
331
|
+
interfaces.select { |i| !i[:addresses].empty? }
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def get_ruby_info
|
|
335
|
+
{
|
|
336
|
+
version: RUBY_VERSION,
|
|
337
|
+
platform: RUBY_PLATFORM,
|
|
338
|
+
engine: RUBY_ENGINE,
|
|
339
|
+
engine_version: RUBY_ENGINE_VERSION,
|
|
340
|
+
patchlevel: RUBY_PATCHLEVEL
|
|
341
|
+
}
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def platform
|
|
345
|
+
case RUBY_PLATFORM
|
|
346
|
+
when /darwin/
|
|
347
|
+
:macos
|
|
348
|
+
when /linux/
|
|
349
|
+
:linux
|
|
350
|
+
when /mswin|mingw|cygwin/
|
|
351
|
+
:windows
|
|
352
|
+
else
|
|
353
|
+
:unknown
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def format_bytes(bytes)
|
|
358
|
+
return '0 B' if bytes.nil? || bytes == 0
|
|
359
|
+
|
|
360
|
+
units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
|
361
|
+
exp = (Math.log(bytes) / Math.log(1024)).to_i
|
|
362
|
+
exp = units.length - 1 if exp >= units.length
|
|
363
|
+
|
|
364
|
+
"#{(bytes.to_f / (1024**exp)).round(2)} #{units[exp]}"
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def get_load_average
|
|
368
|
+
case platform
|
|
369
|
+
when :macos, :linux
|
|
370
|
+
uptime_output = `uptime 2>/dev/null`
|
|
371
|
+
if uptime_output =~ /load average[s]?:\s*([\d.]+),?\s*([\d.]+),?\s*([\d.]+)/
|
|
372
|
+
{ '1min' => $1.to_f, '5min' => $2.to_f, '15min' => $3.to_f }
|
|
373
|
+
else
|
|
374
|
+
{}
|
|
375
|
+
end
|
|
376
|
+
else
|
|
377
|
+
{}
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def parse_uptime(uptime_str)
|
|
382
|
+
# Extract uptime portion from the uptime command output
|
|
383
|
+
if uptime_str =~ /up\s+(.+?),\s+\d+\s+user/
|
|
384
|
+
$1.strip
|
|
385
|
+
elsif uptime_str =~ /up\s+(.+)/
|
|
386
|
+
$1.split(',').first.strip
|
|
387
|
+
else
|
|
388
|
+
uptime_str
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def get_linux_distro
|
|
393
|
+
if File.exist?('/etc/os-release')
|
|
394
|
+
content = File.read('/etc/os-release')
|
|
395
|
+
content[/^NAME="?([^"\n]+)"?/, 1] || 'Linux'
|
|
396
|
+
elsif File.exist?('/etc/lsb-release')
|
|
397
|
+
content = File.read('/etc/lsb-release')
|
|
398
|
+
content[/DISTRIB_ID=(.+)/, 1] || 'Linux'
|
|
399
|
+
else
|
|
400
|
+
'Linux'
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def get_linux_version
|
|
405
|
+
if File.exist?('/etc/os-release')
|
|
406
|
+
content = File.read('/etc/os-release')
|
|
407
|
+
content[/^VERSION="?([^"\n]+)"?/, 1] || ''
|
|
408
|
+
elsif File.exist?('/etc/lsb-release')
|
|
409
|
+
content = File.read('/etc/lsb-release')
|
|
410
|
+
content[/DISTRIB_RELEASE=(.+)/, 1] || ''
|
|
411
|
+
else
|
|
412
|
+
''
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
end
|
data/lib/shared_tools/version.rb
CHANGED
data/lib/shared_tools.rb
CHANGED
|
@@ -4,26 +4,67 @@ require 'ruby_llm'
|
|
|
4
4
|
require 'io/console'
|
|
5
5
|
|
|
6
6
|
require "zeitwerk"
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
|
|
8
|
+
# Set up Zeitwerk loader outside module, then pass reference in
|
|
9
|
+
SharedToolsLoader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
|
10
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/ruby_llm.rb")
|
|
11
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/mcp.rb") # Documentation/loader file only
|
|
12
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/mcp") # Entire mcp directory (naming issues)
|
|
13
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/browser.rb")
|
|
14
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/computer.rb")
|
|
15
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/database.rb")
|
|
16
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/disk.rb")
|
|
17
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/doc.rb")
|
|
18
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/docker.rb")
|
|
19
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/eval.rb")
|
|
20
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/version.rb") # Defines VERSION constant, not Version class
|
|
21
|
+
SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/incomplete") # Empty/incomplete tools directory
|
|
22
|
+
SharedToolsLoader.setup
|
|
18
23
|
|
|
19
24
|
module SharedTools
|
|
20
|
-
@auto_execute
|
|
21
|
-
class << self
|
|
25
|
+
@auto_execute ||= true # Auto-execute by default, no human-in-the-loop
|
|
22
26
|
|
|
27
|
+
class << self
|
|
23
28
|
def auto_execute(wildwest=true)
|
|
24
29
|
@auto_execute = wildwest
|
|
25
30
|
end
|
|
26
31
|
|
|
32
|
+
# Load all tool classes so they're available via ObjectSpace
|
|
33
|
+
# Call this when using AIA with --rq shared_tools
|
|
34
|
+
# Uses manual loading to gracefully handle missing dependencies
|
|
35
|
+
def load_all_tools
|
|
36
|
+
tools_dir = File.join(__dir__, 'shared_tools', 'tools')
|
|
37
|
+
Dir.glob(File.join(tools_dir, '*_tool.rb')).each do |tool_file|
|
|
38
|
+
begin
|
|
39
|
+
require tool_file
|
|
40
|
+
rescue LoadError => e
|
|
41
|
+
# Skip tools with missing dependencies
|
|
42
|
+
warn "SharedTools: Skipping #{File.basename(tool_file)} - #{e.message}" if ENV['DEBUG']
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Get all available tool classes (those that inherit from RubyLLM::Tool)
|
|
48
|
+
# Only returns tools that can be successfully instantiated without arguments (RubyLLM requirement)
|
|
49
|
+
def tools
|
|
50
|
+
load_all_tools
|
|
51
|
+
ObjectSpace.each_object(Class).select do |klass|
|
|
52
|
+
next false unless klass < RubyLLM::Tool
|
|
53
|
+
next false unless klass.to_s.start_with?('SharedTools::')
|
|
54
|
+
|
|
55
|
+
# Actually try to instantiate the tool to verify it works
|
|
56
|
+
# RubyLLM calls tool.new without args, so tools must be instantiable this way
|
|
57
|
+
begin
|
|
58
|
+
klass.new
|
|
59
|
+
true
|
|
60
|
+
rescue ArgumentError, LoadError, StandardError => e
|
|
61
|
+
# Skip tools that can't be instantiated (missing args, missing platform drivers, etc.)
|
|
62
|
+
warn "SharedTools: Excluding #{klass} - #{e.message}" if ENV['DEBUG']
|
|
63
|
+
false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
27
68
|
def execute?(tool: 'unknown', stuff: '')
|
|
28
69
|
# Return true if auto_execute is explicitly enabled
|
|
29
70
|
return true if @auto_execute == true
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shared_tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -278,11 +278,14 @@ files:
|
|
|
278
278
|
- lib/shared_tools/tools/browser/watir_driver.rb
|
|
279
279
|
- lib/shared_tools/tools/browser_tool.rb
|
|
280
280
|
- lib/shared_tools/tools/calculator_tool.rb
|
|
281
|
+
- lib/shared_tools/tools/clipboard_tool.rb
|
|
281
282
|
- lib/shared_tools/tools/composite_analysis_tool.rb
|
|
282
283
|
- lib/shared_tools/tools/computer.rb
|
|
283
284
|
- lib/shared_tools/tools/computer/base_driver.rb
|
|
284
285
|
- lib/shared_tools/tools/computer/mac_driver.rb
|
|
285
286
|
- lib/shared_tools/tools/computer_tool.rb
|
|
287
|
+
- lib/shared_tools/tools/cron_tool.rb
|
|
288
|
+
- lib/shared_tools/tools/current_date_time_tool.rb
|
|
286
289
|
- lib/shared_tools/tools/data_science_kit.rb
|
|
287
290
|
- lib/shared_tools/tools/database.rb
|
|
288
291
|
- lib/shared_tools/tools/database/base_driver.rb
|
|
@@ -306,6 +309,7 @@ files:
|
|
|
306
309
|
- lib/shared_tools/tools/disk/file_write_tool.rb
|
|
307
310
|
- lib/shared_tools/tools/disk/local_driver.rb
|
|
308
311
|
- lib/shared_tools/tools/disk_tool.rb
|
|
312
|
+
- lib/shared_tools/tools/dns_tool.rb
|
|
309
313
|
- lib/shared_tools/tools/doc.rb
|
|
310
314
|
- lib/shared_tools/tools/doc/pdf_reader_tool.rb
|
|
311
315
|
- lib/shared_tools/tools/doc_tool.rb
|
|
@@ -319,6 +323,7 @@ files:
|
|
|
319
323
|
- lib/shared_tools/tools/eval/shell_eval_tool.rb
|
|
320
324
|
- lib/shared_tools/tools/eval_tool.rb
|
|
321
325
|
- lib/shared_tools/tools/secure_tool_template.rb
|
|
326
|
+
- lib/shared_tools/tools/system_info_tool.rb
|
|
322
327
|
- lib/shared_tools/tools/version.rb
|
|
323
328
|
- lib/shared_tools/tools/weather_tool.rb
|
|
324
329
|
- lib/shared_tools/tools/workflow_manager_tool.rb
|
|
@@ -344,7 +349,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
344
349
|
- !ruby/object:Gem::Version
|
|
345
350
|
version: '0'
|
|
346
351
|
requirements: []
|
|
347
|
-
rubygems_version:
|
|
352
|
+
rubygems_version: 4.0.1
|
|
348
353
|
specification_version: 4
|
|
349
354
|
summary: Shared utilities and AI tools for Ruby applications with configurable logging
|
|
350
355
|
test_files: []
|