shared_tools 0.2.1 → 0.3.0
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 +3 -0
- data/README.md +594 -42
- data/lib/shared_tools/{ruby_llm/mcp → mcp}/github_mcp_server.rb +31 -24
- data/lib/shared_tools/mcp/imcp.rb +28 -0
- data/lib/shared_tools/mcp/tavily_mcp_server.rb +44 -0
- data/lib/shared_tools/mcp.rb +24 -0
- data/lib/shared_tools/tools/browser/base_driver.rb +64 -0
- data/lib/shared_tools/tools/browser/base_tool.rb +50 -0
- data/lib/shared_tools/tools/browser/click_tool.rb +54 -0
- data/lib/shared_tools/tools/browser/elements/element_grouper.rb +73 -0
- data/lib/shared_tools/tools/browser/elements/nearby_element_detector.rb +109 -0
- data/lib/shared_tools/tools/browser/formatters/action_formatter.rb +37 -0
- data/lib/shared_tools/tools/browser/formatters/data_entry_formatter.rb +135 -0
- data/lib/shared_tools/tools/browser/formatters/element_formatter.rb +52 -0
- data/lib/shared_tools/tools/browser/formatters/input_formatter.rb +59 -0
- data/lib/shared_tools/tools/browser/inspect_tool.rb +87 -0
- data/lib/shared_tools/tools/browser/inspect_utils.rb +51 -0
- data/lib/shared_tools/tools/browser/page_inspect/button_summarizer.rb +140 -0
- data/lib/shared_tools/tools/browser/page_inspect/form_summarizer.rb +98 -0
- data/lib/shared_tools/tools/browser/page_inspect/html_summarizer.rb +37 -0
- data/lib/shared_tools/tools/browser/page_inspect/link_summarizer.rb +103 -0
- data/lib/shared_tools/tools/browser/page_inspect_tool.rb +55 -0
- data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +39 -0
- data/lib/shared_tools/tools/browser/selector_generator/base_selectors.rb +28 -0
- data/lib/shared_tools/tools/browser/selector_generator/contextual_selectors.rb +140 -0
- data/lib/shared_tools/tools/browser/selector_generator.rb +73 -0
- data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +67 -0
- data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +45 -0
- data/lib/shared_tools/tools/browser/visit_tool.rb +43 -0
- data/lib/shared_tools/tools/browser/watir_driver.rb +132 -0
- data/lib/shared_tools/tools/browser.rb +27 -0
- data/lib/shared_tools/tools/browser_tool.rb +255 -0
- data/lib/shared_tools/tools/calculator_tool.rb +169 -0
- data/lib/shared_tools/tools/composite_analysis_tool.rb +520 -0
- data/lib/shared_tools/tools/computer/base_driver.rb +177 -0
- data/lib/shared_tools/tools/computer/mac_driver.rb +103 -0
- data/lib/shared_tools/tools/computer.rb +21 -0
- data/lib/shared_tools/tools/computer_tool.rb +207 -0
- data/lib/shared_tools/tools/data_science_kit.rb +707 -0
- data/lib/shared_tools/tools/database/base_driver.rb +17 -0
- data/lib/shared_tools/tools/database/postgres_driver.rb +30 -0
- data/lib/shared_tools/tools/database/sqlite_driver.rb +29 -0
- data/lib/shared_tools/tools/database.rb +9 -0
- data/lib/shared_tools/tools/database_query_tool.rb +313 -0
- data/lib/shared_tools/tools/database_tool.rb +99 -0
- data/lib/shared_tools/tools/devops_toolkit.rb +420 -0
- data/lib/shared_tools/tools/disk/base_driver.rb +91 -0
- data/lib/shared_tools/tools/disk/base_tool.rb +20 -0
- data/lib/shared_tools/tools/disk/directory_create_tool.rb +39 -0
- data/lib/shared_tools/tools/disk/directory_delete_tool.rb +39 -0
- data/lib/shared_tools/tools/disk/directory_list_tool.rb +37 -0
- data/lib/shared_tools/tools/disk/directory_move_tool.rb +40 -0
- data/lib/shared_tools/tools/disk/file_create_tool.rb +38 -0
- data/lib/shared_tools/tools/disk/file_delete_tool.rb +40 -0
- data/lib/shared_tools/tools/disk/file_move_tool.rb +43 -0
- data/lib/shared_tools/tools/disk/file_read_tool.rb +40 -0
- data/lib/shared_tools/tools/disk/file_replace_tool.rb +44 -0
- data/lib/shared_tools/tools/disk/file_write_tool.rb +40 -0
- data/lib/shared_tools/tools/disk/local_driver.rb +91 -0
- data/lib/shared_tools/tools/disk.rb +17 -0
- data/lib/shared_tools/tools/disk_tool.rb +132 -0
- data/lib/shared_tools/tools/doc/pdf_reader_tool.rb +79 -0
- data/lib/shared_tools/tools/doc.rb +8 -0
- data/lib/shared_tools/tools/doc_tool.rb +109 -0
- data/lib/shared_tools/tools/docker/base_tool.rb +56 -0
- data/lib/shared_tools/tools/docker/compose_run_tool.rb +77 -0
- data/lib/shared_tools/tools/docker.rb +8 -0
- data/lib/shared_tools/tools/error_handling_tool.rb +403 -0
- data/lib/shared_tools/tools/eval/python_eval_tool.rb +209 -0
- data/lib/shared_tools/tools/eval/ruby_eval_tool.rb +93 -0
- data/lib/shared_tools/tools/eval/shell_eval_tool.rb +64 -0
- data/lib/shared_tools/tools/eval.rb +10 -0
- data/lib/shared_tools/tools/eval_tool.rb +139 -0
- data/lib/shared_tools/tools/secure_tool_template.rb +353 -0
- data/lib/shared_tools/tools/version.rb +7 -0
- data/lib/shared_tools/tools/weather_tool.rb +197 -0
- data/lib/shared_tools/tools/workflow_manager_tool.rb +312 -0
- data/lib/shared_tools/tools.rb +16 -0
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools.rb +9 -33
- metadata +189 -68
- data/lib/shared_tools/llm_rb/run_shell_command.rb +0 -23
- data/lib/shared_tools/llm_rb.rb +0 -9
- data/lib/shared_tools/omniai.rb +0 -9
- data/lib/shared_tools/raix/what_is_the_weather.rb +0 -18
- data/lib/shared_tools/raix.rb +0 -9
- data/lib/shared_tools/ruby_llm/edit_file.rb +0 -71
- data/lib/shared_tools/ruby_llm/incomplete/calculator_tool.rb +0 -70
- data/lib/shared_tools/ruby_llm/incomplete/composite_analysis_tool.rb +0 -89
- data/lib/shared_tools/ruby_llm/incomplete/data_science_kit.rb +0 -128
- data/lib/shared_tools/ruby_llm/incomplete/database_query_tool.rb +0 -100
- data/lib/shared_tools/ruby_llm/incomplete/devops_toolkit.rb +0 -112
- data/lib/shared_tools/ruby_llm/incomplete/error_handling_tool.rb +0 -109
- data/lib/shared_tools/ruby_llm/incomplete/secure_tool_template.rb +0 -117
- data/lib/shared_tools/ruby_llm/incomplete/weather_tool.rb +0 -110
- data/lib/shared_tools/ruby_llm/incomplete/workflow_manager_tool.rb +0 -145
- data/lib/shared_tools/ruby_llm/list_files.rb +0 -49
- data/lib/shared_tools/ruby_llm/mcp/imcp.rb +0 -33
- data/lib/shared_tools/ruby_llm/mcp.rb +0 -10
- data/lib/shared_tools/ruby_llm/pdf_page_reader.rb +0 -59
- data/lib/shared_tools/ruby_llm/python_eval.rb +0 -194
- data/lib/shared_tools/ruby_llm/read_file.rb +0 -40
- data/lib/shared_tools/ruby_llm/ruby_eval.rb +0 -77
- data/lib/shared_tools/ruby_llm/run_shell_command.rb +0 -49
- data/lib/shared_tools/ruby_llm.rb +0 -12
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# workflow_manager_tool.rb - Managing state across tool invocations
|
|
2
|
+
require 'ruby_llm/tool'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module SharedTools
|
|
8
|
+
module Tools
|
|
9
|
+
class WorkflowManagerTool < RubyLLM::Tool
|
|
10
|
+
def self.name = 'workflow_manager'
|
|
11
|
+
|
|
12
|
+
description <<~'DESCRIPTION'
|
|
13
|
+
Manage complex multi-step workflows with persistent state tracking across tool invocations.
|
|
14
|
+
This tool enables the creation and management of stateful workflows that can span multiple
|
|
15
|
+
AI interactions and tool calls. It provides workflow initialization, step-by-step execution,
|
|
16
|
+
status monitoring, and completion tracking. Each workflow maintains its state in persistent
|
|
17
|
+
storage, allowing for resumption of long-running processes and coordination between
|
|
18
|
+
multiple tools and AI interactions. Perfect for complex automation tasks that require
|
|
19
|
+
multiple stages and decision points.
|
|
20
|
+
|
|
21
|
+
Workflow lifecycle:
|
|
22
|
+
1. Start: Initialize a new workflow with initial data (returns workflow_id)
|
|
23
|
+
2. Step: Execute multiple workflow steps using the workflow_id
|
|
24
|
+
3. Status: Check workflow progress and state at any time
|
|
25
|
+
4. Complete: Finalize the workflow and clean up resources
|
|
26
|
+
|
|
27
|
+
All workflow state is persisted to disk and can survive process restarts.
|
|
28
|
+
DESCRIPTION
|
|
29
|
+
|
|
30
|
+
params do
|
|
31
|
+
string :action, description: <<~DESC.strip
|
|
32
|
+
Workflow management action to perform:
|
|
33
|
+
- 'start': Initialize a new workflow with initial data and return workflow ID
|
|
34
|
+
- 'step': Execute the next step in an existing workflow using provided step data
|
|
35
|
+
- 'status': Check the current status and progress of an existing workflow
|
|
36
|
+
- 'complete': Mark a workflow as finished and clean up associated resources
|
|
37
|
+
Each action requires different combinations of the other parameters.
|
|
38
|
+
DESC
|
|
39
|
+
|
|
40
|
+
string :workflow_id, description: <<~DESC.strip, required: false
|
|
41
|
+
Unique identifier for an existing workflow. Required for 'step', 'status', and 'complete'
|
|
42
|
+
actions. This ID is returned when starting a new workflow and should be used for all
|
|
43
|
+
subsequent operations on that workflow. The ID is a UUID string that ensures
|
|
44
|
+
uniqueness across all workflow instances.
|
|
45
|
+
DESC
|
|
46
|
+
|
|
47
|
+
object :step_data, description: <<~DESC.strip, required: false do
|
|
48
|
+
Data and parameters specific to the current workflow step. The structure is flexible
|
|
49
|
+
and depends on the workflow type and step requirements. Can contain any JSON-serializable
|
|
50
|
+
data types including nested objects and arrays.
|
|
51
|
+
DESC
|
|
52
|
+
# Note: This is a flexible object - actual properties vary by workflow type
|
|
53
|
+
# We define common optional fields but workflow steps can include any data
|
|
54
|
+
string :step_name, description: "Optional name or identifier for this workflow step", required: false
|
|
55
|
+
string :step_type, description: "Optional type or category of this workflow step", required: false
|
|
56
|
+
string :description, description: "Optional description of what this step does", required: false
|
|
57
|
+
object :parameters, description: "Optional nested parameters for this step", required: false do
|
|
58
|
+
# Flexible nested parameters - structure varies by workflow
|
|
59
|
+
end
|
|
60
|
+
object :metadata, description: "Optional metadata for this workflow step", required: false do
|
|
61
|
+
# Flexible metadata - structure varies by workflow
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize(logger: nil, storage_dir: nil)
|
|
67
|
+
@logger = logger || RubyLLM.logger
|
|
68
|
+
@storage_dir = storage_dir || ".workflows"
|
|
69
|
+
FileUtils.mkdir_p(@storage_dir) unless Dir.exist?(@storage_dir)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def execute(action:, workflow_id: nil, **step_data)
|
|
73
|
+
@logger.info("WorkflowManagerTool#execute action=#{action} workflow_id=#{workflow_id}")
|
|
74
|
+
|
|
75
|
+
case action
|
|
76
|
+
when "start"
|
|
77
|
+
start_workflow(step_data)
|
|
78
|
+
when "step"
|
|
79
|
+
return {success: false, error: "workflow_id required for 'step' action"} unless workflow_id
|
|
80
|
+
process_workflow_step(workflow_id, step_data)
|
|
81
|
+
when "status"
|
|
82
|
+
return {success: false, error: "workflow_id required for 'status' action"} unless workflow_id
|
|
83
|
+
get_workflow_status(workflow_id)
|
|
84
|
+
when "complete"
|
|
85
|
+
return {success: false, error: "workflow_id required for 'complete' action"} unless workflow_id
|
|
86
|
+
complete_workflow(workflow_id)
|
|
87
|
+
else
|
|
88
|
+
{success: false, error: "Unknown action: #{action}"}
|
|
89
|
+
end
|
|
90
|
+
rescue => e
|
|
91
|
+
@logger.error("Workflow operation failed: #{e.message}")
|
|
92
|
+
{
|
|
93
|
+
success: false,
|
|
94
|
+
error: "Workflow operation failed: #{e.message}",
|
|
95
|
+
error_type: e.class.name
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# Start a new workflow
|
|
102
|
+
def start_workflow(initial_data)
|
|
103
|
+
workflow_id = SecureRandom.uuid
|
|
104
|
+
workflow_state = {
|
|
105
|
+
id: workflow_id,
|
|
106
|
+
status: "active",
|
|
107
|
+
steps: [],
|
|
108
|
+
created_at: Time.now.iso8601,
|
|
109
|
+
updated_at: Time.now.iso8601,
|
|
110
|
+
data: initial_data,
|
|
111
|
+
metadata: {
|
|
112
|
+
step_count: 0,
|
|
113
|
+
last_step_at: nil
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
save_workflow_state(workflow_id, workflow_state)
|
|
118
|
+
@logger.info("Workflow started: #{workflow_id}")
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
success: true,
|
|
122
|
+
workflow_id: workflow_id,
|
|
123
|
+
status: "started",
|
|
124
|
+
created_at: workflow_state[:created_at],
|
|
125
|
+
next_actions: suggested_next_actions(workflow_state)
|
|
126
|
+
}
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Process a workflow step
|
|
130
|
+
def process_workflow_step(workflow_id, step_data)
|
|
131
|
+
workflow_state = load_workflow_state(workflow_id)
|
|
132
|
+
return {success: false, error: "Workflow not found: #{workflow_id}"} unless workflow_state
|
|
133
|
+
|
|
134
|
+
if workflow_state[:status] == "completed"
|
|
135
|
+
return {success: false, error: "Cannot add steps to completed workflow"}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
step_number = workflow_state[:steps].length + 1
|
|
139
|
+
step_result = process_step_logic(step_data, workflow_state)
|
|
140
|
+
|
|
141
|
+
step = {
|
|
142
|
+
step_number: step_number,
|
|
143
|
+
data: step_data,
|
|
144
|
+
result: step_result,
|
|
145
|
+
processed_at: Time.now.iso8601,
|
|
146
|
+
execution_time_seconds: 0.001 # Placeholder
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
workflow_state[:steps] << step
|
|
150
|
+
workflow_state[:updated_at] = Time.now.iso8601
|
|
151
|
+
workflow_state[:metadata][:step_count] = step_number
|
|
152
|
+
workflow_state[:metadata][:last_step_at] = step[:processed_at]
|
|
153
|
+
|
|
154
|
+
save_workflow_state(workflow_id, workflow_state)
|
|
155
|
+
@logger.info("Workflow step #{step_number} completed: #{workflow_id}")
|
|
156
|
+
|
|
157
|
+
{
|
|
158
|
+
success: true,
|
|
159
|
+
workflow_id: workflow_id,
|
|
160
|
+
step_number: step_number,
|
|
161
|
+
step_result: step_result,
|
|
162
|
+
workflow_status: workflow_state[:status],
|
|
163
|
+
total_steps: step_number,
|
|
164
|
+
next_actions: suggested_next_actions(workflow_state)
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Get workflow status
|
|
169
|
+
def get_workflow_status(workflow_id)
|
|
170
|
+
workflow_state = load_workflow_state(workflow_id)
|
|
171
|
+
return {success: false, error: "Workflow not found: #{workflow_id}"} unless workflow_state
|
|
172
|
+
|
|
173
|
+
@logger.debug("Workflow status retrieved: #{workflow_id}")
|
|
174
|
+
|
|
175
|
+
{
|
|
176
|
+
success: true,
|
|
177
|
+
workflow_id: workflow_id,
|
|
178
|
+
status: workflow_state[:status],
|
|
179
|
+
created_at: workflow_state[:created_at],
|
|
180
|
+
updated_at: workflow_state[:updated_at],
|
|
181
|
+
step_count: workflow_state[:steps].length,
|
|
182
|
+
steps: workflow_state[:steps].map { |step|
|
|
183
|
+
{
|
|
184
|
+
step_number: step[:step_number],
|
|
185
|
+
processed_at: step[:processed_at],
|
|
186
|
+
has_result: !step[:result].nil?
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
metadata: workflow_state[:metadata],
|
|
190
|
+
next_actions: suggested_next_actions(workflow_state)
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Complete a workflow
|
|
195
|
+
def complete_workflow(workflow_id)
|
|
196
|
+
workflow_state = load_workflow_state(workflow_id)
|
|
197
|
+
return {success: false, error: "Workflow not found: #{workflow_id}"} unless workflow_state
|
|
198
|
+
|
|
199
|
+
if workflow_state[:status] == "completed"
|
|
200
|
+
return {success: false, error: "Workflow already completed"}
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
workflow_state[:status] = "completed"
|
|
204
|
+
workflow_state[:completed_at] = Time.now.iso8601
|
|
205
|
+
workflow_state[:updated_at] = Time.now.iso8601
|
|
206
|
+
|
|
207
|
+
save_workflow_state(workflow_id, workflow_state)
|
|
208
|
+
@logger.info("Workflow completed: #{workflow_id}")
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
success: true,
|
|
212
|
+
workflow_id: workflow_id,
|
|
213
|
+
status: "completed",
|
|
214
|
+
completed_at: workflow_state[:completed_at],
|
|
215
|
+
total_steps: workflow_state[:steps].length,
|
|
216
|
+
summary: {
|
|
217
|
+
created_at: workflow_state[:created_at],
|
|
218
|
+
completed_at: workflow_state[:completed_at],
|
|
219
|
+
total_steps: workflow_state[:steps].length,
|
|
220
|
+
duration_seconds: calculate_duration(workflow_state)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Calculate workflow duration
|
|
226
|
+
def calculate_duration(workflow_state)
|
|
227
|
+
return 0 unless workflow_state[:completed_at] && workflow_state[:created_at]
|
|
228
|
+
|
|
229
|
+
completed = Time.parse(workflow_state[:completed_at])
|
|
230
|
+
created = Time.parse(workflow_state[:created_at])
|
|
231
|
+
(completed - created).round(2)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Suggest next actions based on workflow state
|
|
235
|
+
def suggested_next_actions(workflow_state)
|
|
236
|
+
return [] if workflow_state[:status] == "completed"
|
|
237
|
+
|
|
238
|
+
actions = []
|
|
239
|
+
|
|
240
|
+
# Always suggest adding a step
|
|
241
|
+
actions << {
|
|
242
|
+
action: "step",
|
|
243
|
+
description: "Add the next workflow step",
|
|
244
|
+
required_params: ["workflow_id", "step_data"]
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
# Suggest status check
|
|
248
|
+
actions << {
|
|
249
|
+
action: "status",
|
|
250
|
+
description: "Check workflow progress and status",
|
|
251
|
+
required_params: ["workflow_id"]
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
# Suggest completion if workflow has steps
|
|
255
|
+
if workflow_state[:steps].length > 0
|
|
256
|
+
actions << {
|
|
257
|
+
action: "complete",
|
|
258
|
+
description: "Mark workflow as complete",
|
|
259
|
+
required_params: ["workflow_id"]
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
actions
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Process individual step logic
|
|
267
|
+
def process_step_logic(step_data, workflow_state)
|
|
268
|
+
# This is a demonstration - in production, this would contain
|
|
269
|
+
# actual business logic for processing workflow steps
|
|
270
|
+
|
|
271
|
+
{
|
|
272
|
+
processed: true,
|
|
273
|
+
input_keys: step_data.keys,
|
|
274
|
+
workflow_context: {
|
|
275
|
+
current_step: workflow_state[:steps].length + 1,
|
|
276
|
+
total_steps_so_far: workflow_state[:steps].length
|
|
277
|
+
},
|
|
278
|
+
timestamp: Time.now.iso8601
|
|
279
|
+
}
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Save workflow state to disk
|
|
283
|
+
def save_workflow_state(workflow_id, state)
|
|
284
|
+
file_path = workflow_file_path(workflow_id)
|
|
285
|
+
File.write(file_path, JSON.pretty_generate(state))
|
|
286
|
+
@logger.debug("Workflow state saved: #{file_path}")
|
|
287
|
+
rescue => e
|
|
288
|
+
@logger.error("Failed to save workflow state: #{e.message}")
|
|
289
|
+
raise
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Load workflow state from disk
|
|
293
|
+
def load_workflow_state(workflow_id)
|
|
294
|
+
file_path = workflow_file_path(workflow_id)
|
|
295
|
+
return nil unless File.exist?(file_path)
|
|
296
|
+
|
|
297
|
+
JSON.parse(File.read(file_path), symbolize_names: true)
|
|
298
|
+
rescue => e
|
|
299
|
+
@logger.error("Failed to load workflow state: #{e.message}")
|
|
300
|
+
nil
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Get workflow file path
|
|
304
|
+
def workflow_file_path(workflow_id)
|
|
305
|
+
File.join(@storage_dir, "workflow_#{workflow_id}.json")
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Get storage directory (for testing)
|
|
309
|
+
attr_reader :storage_dir
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Loader file for all tools - loaded automatically by Zeitwerk
|
|
4
|
+
# Individual tool collections can be loaded with:
|
|
5
|
+
# require 'shared_tools/tools/disk' # Load all disk tools
|
|
6
|
+
# require 'shared_tools/tools/browser' # Load all browser tools
|
|
7
|
+
#
|
|
8
|
+
# Or load individual tools with Zeitwerk autoloading:
|
|
9
|
+
# SharedTools::Tools::Disk::FileReadTool
|
|
10
|
+
# SharedTools::Tools::Browser::VisitTool
|
|
11
|
+
|
|
12
|
+
module SharedTools
|
|
13
|
+
module Tools
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/shared_tools/version.rb
CHANGED
data/lib/shared_tools.rb
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'ruby_llm'
|
|
3
4
|
require 'io/console'
|
|
4
5
|
|
|
5
6
|
require "zeitwerk"
|
|
6
7
|
loader = Zeitwerk::Loader.for_gem
|
|
7
8
|
# Ignore aggregate loader files that don't define constants
|
|
8
9
|
loader.ignore("#{__dir__}/shared_tools/ruby_llm.rb")
|
|
9
|
-
loader.ignore("#{__dir__}/shared_tools/
|
|
10
|
-
loader.ignore("#{__dir__}/shared_tools/
|
|
11
|
-
loader.ignore("#{__dir__}/shared_tools/
|
|
10
|
+
loader.ignore("#{__dir__}/shared_tools/tools/browser.rb")
|
|
11
|
+
loader.ignore("#{__dir__}/shared_tools/tools/computer.rb")
|
|
12
|
+
loader.ignore("#{__dir__}/shared_tools/tools/database.rb")
|
|
13
|
+
loader.ignore("#{__dir__}/shared_tools/tools/disk.rb")
|
|
14
|
+
loader.ignore("#{__dir__}/shared_tools/tools/doc.rb")
|
|
15
|
+
loader.ignore("#{__dir__}/shared_tools/tools/docker.rb")
|
|
16
|
+
loader.ignore("#{__dir__}/shared_tools/tools/eval.rb")
|
|
12
17
|
loader.setup
|
|
13
18
|
|
|
14
19
|
module SharedTools
|
|
15
|
-
|
|
16
|
-
@auto_execute ||= false # Human in the loop
|
|
17
|
-
@mcp_servers ||= []
|
|
18
|
-
|
|
20
|
+
@auto_execute ||= true # Auto-execute by default, no human-in-the-loop
|
|
19
21
|
class << self
|
|
20
|
-
def mcp_servers
|
|
21
|
-
@mcp_servers
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def mcp_servers=(client)
|
|
25
|
-
@mcp_servers << client
|
|
26
|
-
end
|
|
27
22
|
|
|
28
23
|
def auto_execute(wildwest=true)
|
|
29
24
|
@auto_execute = wildwest
|
|
@@ -42,24 +37,5 @@ module SharedTools
|
|
|
42
37
|
print "\nIs it okay to proceed? (y/N"
|
|
43
38
|
STDIN.getch == "y"
|
|
44
39
|
end
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def detected_gem
|
|
48
|
-
return :ruby_llm if defined?(::RubyLLM::Tool)
|
|
49
|
-
return :llm_rb if defined?(::LLM) || defined?(::Llm)
|
|
50
|
-
return :omniai if defined?(::OmniAI) || defined?(::Omniai)
|
|
51
|
-
return :raix if defined?(::Raix::FunctionDispatch)
|
|
52
|
-
nil
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def verify_gem(a_symbol)
|
|
56
|
-
loaded = a_symbol == detected_gem
|
|
57
|
-
return true if loaded
|
|
58
|
-
raise "SharedTools: Please require '#{a_symbol}' gem before requiring 'shared_tools'."
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
if detected_gem.nil?
|
|
63
|
-
warn "⚠️ SharedTools: No supported gem detected. Supported gems are: #{SUPPORTED_GEMS.join(', ')}"
|
|
64
40
|
end
|
|
65
41
|
end
|