shared_tools 0.2.3 → 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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +594 -42
  4. data/lib/shared_tools/{ruby_llm/mcp → mcp}/github_mcp_server.rb +20 -3
  5. data/lib/shared_tools/mcp/imcp.rb +28 -0
  6. data/lib/shared_tools/mcp/tavily_mcp_server.rb +44 -0
  7. data/lib/shared_tools/mcp.rb +24 -0
  8. data/lib/shared_tools/tools/browser/base_driver.rb +64 -0
  9. data/lib/shared_tools/tools/browser/base_tool.rb +50 -0
  10. data/lib/shared_tools/tools/browser/click_tool.rb +54 -0
  11. data/lib/shared_tools/tools/browser/elements/element_grouper.rb +73 -0
  12. data/lib/shared_tools/tools/browser/elements/nearby_element_detector.rb +109 -0
  13. data/lib/shared_tools/tools/browser/formatters/action_formatter.rb +37 -0
  14. data/lib/shared_tools/tools/browser/formatters/data_entry_formatter.rb +135 -0
  15. data/lib/shared_tools/tools/browser/formatters/element_formatter.rb +52 -0
  16. data/lib/shared_tools/tools/browser/formatters/input_formatter.rb +59 -0
  17. data/lib/shared_tools/tools/browser/inspect_tool.rb +87 -0
  18. data/lib/shared_tools/tools/browser/inspect_utils.rb +51 -0
  19. data/lib/shared_tools/tools/browser/page_inspect/button_summarizer.rb +140 -0
  20. data/lib/shared_tools/tools/browser/page_inspect/form_summarizer.rb +98 -0
  21. data/lib/shared_tools/tools/browser/page_inspect/html_summarizer.rb +37 -0
  22. data/lib/shared_tools/tools/browser/page_inspect/link_summarizer.rb +103 -0
  23. data/lib/shared_tools/tools/browser/page_inspect_tool.rb +55 -0
  24. data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +39 -0
  25. data/lib/shared_tools/tools/browser/selector_generator/base_selectors.rb +28 -0
  26. data/lib/shared_tools/tools/browser/selector_generator/contextual_selectors.rb +140 -0
  27. data/lib/shared_tools/tools/browser/selector_generator.rb +73 -0
  28. data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +67 -0
  29. data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +45 -0
  30. data/lib/shared_tools/tools/browser/visit_tool.rb +43 -0
  31. data/lib/shared_tools/tools/browser/watir_driver.rb +132 -0
  32. data/lib/shared_tools/tools/browser.rb +27 -0
  33. data/lib/shared_tools/tools/browser_tool.rb +255 -0
  34. data/lib/shared_tools/tools/calculator_tool.rb +169 -0
  35. data/lib/shared_tools/tools/composite_analysis_tool.rb +520 -0
  36. data/lib/shared_tools/tools/computer/base_driver.rb +177 -0
  37. data/lib/shared_tools/tools/computer/mac_driver.rb +103 -0
  38. data/lib/shared_tools/tools/computer.rb +21 -0
  39. data/lib/shared_tools/tools/computer_tool.rb +207 -0
  40. data/lib/shared_tools/tools/data_science_kit.rb +707 -0
  41. data/lib/shared_tools/tools/database/base_driver.rb +17 -0
  42. data/lib/shared_tools/tools/database/postgres_driver.rb +30 -0
  43. data/lib/shared_tools/tools/database/sqlite_driver.rb +29 -0
  44. data/lib/shared_tools/tools/database.rb +9 -0
  45. data/lib/shared_tools/tools/database_query_tool.rb +313 -0
  46. data/lib/shared_tools/tools/database_tool.rb +99 -0
  47. data/lib/shared_tools/tools/devops_toolkit.rb +420 -0
  48. data/lib/shared_tools/tools/disk/base_driver.rb +91 -0
  49. data/lib/shared_tools/tools/disk/base_tool.rb +20 -0
  50. data/lib/shared_tools/tools/disk/directory_create_tool.rb +39 -0
  51. data/lib/shared_tools/tools/disk/directory_delete_tool.rb +39 -0
  52. data/lib/shared_tools/tools/disk/directory_list_tool.rb +37 -0
  53. data/lib/shared_tools/tools/disk/directory_move_tool.rb +40 -0
  54. data/lib/shared_tools/tools/disk/file_create_tool.rb +38 -0
  55. data/lib/shared_tools/tools/disk/file_delete_tool.rb +40 -0
  56. data/lib/shared_tools/tools/disk/file_move_tool.rb +43 -0
  57. data/lib/shared_tools/tools/disk/file_read_tool.rb +40 -0
  58. data/lib/shared_tools/tools/disk/file_replace_tool.rb +44 -0
  59. data/lib/shared_tools/tools/disk/file_write_tool.rb +40 -0
  60. data/lib/shared_tools/tools/disk/local_driver.rb +91 -0
  61. data/lib/shared_tools/tools/disk.rb +17 -0
  62. data/lib/shared_tools/tools/disk_tool.rb +132 -0
  63. data/lib/shared_tools/tools/doc/pdf_reader_tool.rb +79 -0
  64. data/lib/shared_tools/tools/doc.rb +8 -0
  65. data/lib/shared_tools/tools/doc_tool.rb +109 -0
  66. data/lib/shared_tools/tools/docker/base_tool.rb +56 -0
  67. data/lib/shared_tools/tools/docker/compose_run_tool.rb +77 -0
  68. data/lib/shared_tools/tools/docker.rb +8 -0
  69. data/lib/shared_tools/tools/error_handling_tool.rb +403 -0
  70. data/lib/shared_tools/tools/eval/python_eval_tool.rb +209 -0
  71. data/lib/shared_tools/tools/eval/ruby_eval_tool.rb +93 -0
  72. data/lib/shared_tools/tools/eval/shell_eval_tool.rb +64 -0
  73. data/lib/shared_tools/tools/eval.rb +10 -0
  74. data/lib/shared_tools/tools/eval_tool.rb +139 -0
  75. data/lib/shared_tools/tools/secure_tool_template.rb +353 -0
  76. data/lib/shared_tools/tools/version.rb +7 -0
  77. data/lib/shared_tools/tools/weather_tool.rb +197 -0
  78. data/lib/shared_tools/tools/workflow_manager_tool.rb +312 -0
  79. data/lib/shared_tools/tools.rb +16 -0
  80. data/lib/shared_tools/version.rb +1 -1
  81. data/lib/shared_tools.rb +9 -24
  82. metadata +189 -68
  83. data/lib/shared_tools/llm_rb/run_shell_command.rb +0 -23
  84. data/lib/shared_tools/llm_rb.rb +0 -9
  85. data/lib/shared_tools/omniai.rb +0 -9
  86. data/lib/shared_tools/raix/what_is_the_weather.rb +0 -18
  87. data/lib/shared_tools/raix.rb +0 -9
  88. data/lib/shared_tools/ruby_llm/edit_file.rb +0 -71
  89. data/lib/shared_tools/ruby_llm/incomplete/calculator_tool.rb +0 -70
  90. data/lib/shared_tools/ruby_llm/incomplete/composite_analysis_tool.rb +0 -89
  91. data/lib/shared_tools/ruby_llm/incomplete/data_science_kit.rb +0 -128
  92. data/lib/shared_tools/ruby_llm/incomplete/database_query_tool.rb +0 -100
  93. data/lib/shared_tools/ruby_llm/incomplete/devops_toolkit.rb +0 -112
  94. data/lib/shared_tools/ruby_llm/incomplete/error_handling_tool.rb +0 -109
  95. data/lib/shared_tools/ruby_llm/incomplete/secure_tool_template.rb +0 -117
  96. data/lib/shared_tools/ruby_llm/incomplete/weather_tool.rb +0 -110
  97. data/lib/shared_tools/ruby_llm/incomplete/workflow_manager_tool.rb +0 -145
  98. data/lib/shared_tools/ruby_llm/list_files.rb +0 -49
  99. data/lib/shared_tools/ruby_llm/mcp/imcp.rb +0 -15
  100. data/lib/shared_tools/ruby_llm/mcp.rb +0 -12
  101. data/lib/shared_tools/ruby_llm/pdf_page_reader.rb +0 -59
  102. data/lib/shared_tools/ruby_llm/python_eval.rb +0 -194
  103. data/lib/shared_tools/ruby_llm/read_file.rb +0 -40
  104. data/lib/shared_tools/ruby_llm/ruby_eval.rb +0 -77
  105. data/lib/shared_tools/ruby_llm/run_shell_command.rb +0 -49
  106. data/lib/shared_tools/ruby_llm.rb +0 -12
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module SharedTools
6
+ module Tools
7
+ module Docker
8
+ # @example
9
+ # class ExampleTool s::Disk::BaseTool
10
+ # description "..."
11
+ # end
12
+ class BaseTool
13
+ # @example
14
+ # raise CaptureError.new(text: "an unknown error occurred", status: Process::Status.new(0))
15
+ class CaptureError < StandardError
16
+ # !@attribute [rw] text
17
+ # @return [String]
18
+ attr_accessor :text
19
+
20
+ # @!attribute [rw] status
21
+ # @return [Process::Status]
22
+ attr_accessor :status
23
+
24
+ # @param text [String]
25
+ # @param status [Process::Status]
26
+ def initialize(text:, status:)
27
+ super("[STATUS=#{status.exitstatus}] #{text}")
28
+ @text = text
29
+ @status = status
30
+ end
31
+ end
32
+
33
+ # @param root [Pathname]
34
+ # @param logger [IO] optional
35
+ def initialize(root:, logger: Logger.new(IO::NULL))
36
+ super()
37
+ @root = root
38
+ @logger = logger
39
+ end
40
+
41
+ protected
42
+
43
+ # @raise [CaptureError]
44
+ #
45
+ # @return [String]
46
+ def capture!(...)
47
+ text, status = Open3.capture2e(...)
48
+
49
+ raise CaptureError.new(text:, status:) unless status.success?
50
+
51
+ text
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module SharedTools
6
+ module Tools
7
+ module Docker
8
+ # @example
9
+ # tool = SharedTools::Tools::Docker::ComposeRunTool.new
10
+ # tool.execute(service: "app", command: "rspec", args: ["spec/main_spec.rb"])
11
+ class ComposeRunTool < ::RubyLLM::Tool
12
+ def self.name = 'docker_compose_run'
13
+
14
+ description "Runs a command via Docker with arguments on the project (e.g. `rspec spec/main_spec.rb`)."
15
+
16
+ params do
17
+ string :service, description: "The service to run the command on (e.g. `app`).", required: false
18
+ string :command, description: "The command to run (e.g. `rspec`)."
19
+ array :args, description: "The arguments for the command.", required: false
20
+ end
21
+
22
+ # @example
23
+ # class ExampleTool < ::RubyLLM::Tool
24
+ # # ...
25
+ # end
26
+ class CaptureError < StandardError
27
+ attr_accessor :text
28
+ attr_accessor :status
29
+
30
+ # @param text [String]
31
+ # @param status [Process::Status]
32
+ def initialize(text:, status:)
33
+ super("[STATUS=#{status.exitstatus}] #{text}")
34
+ @text = text
35
+ @status = status
36
+ end
37
+ end
38
+
39
+ # @param root [String, Pathname] optional, defaults to current directory
40
+ # @param logger [Logger] optional logger
41
+ def initialize(root: nil, logger: nil)
42
+ @root = root || Dir.pwd
43
+ @logger = logger || RubyLLM.logger
44
+ end
45
+
46
+ # @param service [String]
47
+ # @param command [String]
48
+ # @param args [Array<String>]
49
+ #
50
+ # @return [String]
51
+ def execute(command:, service: "app", args: [])
52
+ @logger.info(%(#{self.class.name}#execute service="#{service}" command="#{command}" args=#{args.inspect}))
53
+
54
+ Dir.chdir(@root) do
55
+ capture!("docker", "compose", "run", "--build", "--rm", service, command, *args)
56
+ rescue CaptureError => e
57
+ @logger.info("ERROR: #{e.message}")
58
+ return "ERROR: #{e.message}"
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ # @raise [CaptureError]
65
+ #
66
+ # @return [String]
67
+ def capture!(...)
68
+ text, status = Open3.capture2e(...)
69
+
70
+ raise CaptureError.new(text:, status:) unless status.success?
71
+
72
+ text
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Collection loader for all docker tools
4
+ # Usage: require 'shared_tools/tools/docker'
5
+
6
+ require 'shared_tools'
7
+
8
+ require_relative 'docker/compose_run_tool'
@@ -0,0 +1,403 @@
1
+ # error_handling_tool.rb - Comprehensive error handling demonstration
2
+ require 'ruby_llm/tool'
3
+ require 'securerandom'
4
+ require 'net/http'
5
+ require 'json'
6
+
7
+ module SharedTools
8
+ module Tools
9
+ # Custom error classes for different error scenarios
10
+ class ValidationError < StandardError
11
+ attr_reader :suggestions
12
+
13
+ def initialize(message, suggestions: [])
14
+ super(message)
15
+ @suggestions = suggestions
16
+ end
17
+ end
18
+
19
+ class NetworkError < StandardError; end
20
+ class AuthorizationError < StandardError; end
21
+ class RetryableError < StandardError; end
22
+ class ResourceNotFoundError < StandardError; end
23
+
24
+ class ErrorHandlingTool < RubyLLM::Tool
25
+ def self.name = 'error_handling_tool'
26
+
27
+ description <<~'DESCRIPTION'
28
+ Reference tool demonstrating comprehensive error handling patterns and resilience strategies
29
+ for robust tool development. This tool showcases best practices for handling different
30
+ types of errors including validation errors, network failures, authorization issues,
31
+ and general exceptions. It implements retry mechanisms with exponential backoff,
32
+ proper resource cleanup, detailed error categorization, and user-friendly error messages.
33
+
34
+ This tool performs a demonstration operation (data validation and processing) that can
35
+ encounter various error scenarios to showcase the error handling patterns.
36
+
37
+ Error handling features:
38
+ - Input validation with helpful suggestions
39
+ - Network retry with exponential backoff
40
+ - Authorization checks
41
+ - Resource cleanup in ensure blocks
42
+ - Detailed error categorization
43
+ - Operation metadata tracking
44
+ - Support reference IDs for debugging
45
+
46
+ Example usage:
47
+ tool = SharedTools::Tools::ErrorHandlingTool.new
48
+ result = tool.execute(
49
+ operation: "validate",
50
+ data: {name: "test", value: 42},
51
+ simulate_error: nil
52
+ )
53
+ DESCRIPTION
54
+
55
+ params do
56
+ string :operation, description: <<~DESC.strip
57
+ The operation to perform. Options:
58
+ - 'validate': Validate data structure and content
59
+ - 'process': Process data with simulated network operation
60
+ - 'authorize': Check authorization (always succeeds unless simulating error)
61
+
62
+ This parameter demonstrates how to validate enum-like inputs.
63
+ DESC
64
+
65
+ object :data, description: <<~DESC.strip, required: false do
66
+ Data object to process. Contains the data to be validated or processed.
67
+ Example: {name: "example", value: 100, optional_field: "extra info"}
68
+ DESC
69
+ string :name, description: "Name or identifier of the data item. Should be at least 2 characters long.", required: false
70
+ number :value, description: "Numeric value to process. Must be a valid number. Negative values will generate a warning.", required: false
71
+ string :optional_field, description: "Any optional string field for additional data or context.", required: false
72
+ end
73
+
74
+ string :simulate_error, description: <<~DESC.strip, required: false
75
+ Simulate a specific error type for testing error handling. Options:
76
+ - 'validation': Trigger validation error
77
+ - 'network': Trigger network error
78
+ - 'authorization': Trigger authorization error
79
+ - 'retryable': Trigger retryable error
80
+ - 'resource_not_found': Trigger resource not found error
81
+ - null/empty: Normal operation (default)
82
+
83
+ This is useful for testing error handling in client applications.
84
+ DESC
85
+
86
+ integer :max_retries, description: <<~DESC.strip, required: false
87
+ Maximum number of retries for retryable operations. Default: 3.
88
+ Valid range: 0-10. Set to 0 to disable retries.
89
+ DESC
90
+ end
91
+
92
+ # @param logger [Logger] optional logger
93
+ def initialize(logger: nil)
94
+ @logger = logger || RubyLLM.logger
95
+ @resources_allocated = []
96
+ @operation_start_time = nil
97
+ end
98
+
99
+ # Execute operation with comprehensive error handling
100
+ #
101
+ # @param operation [String] Operation to perform
102
+ # @param data [Hash] Data to process
103
+ # @param simulate_error [String, nil] Error type to simulate
104
+ # @param max_retries [Integer] Maximum retry attempts
105
+ #
106
+ # @return [Hash] Operation result with success status
107
+ def execute(operation:, simulate_error: nil, max_retries: 3, **data)
108
+ @operation_start_time = Time.now
109
+ @logger.info("ErrorHandlingTool#execute operation=#{operation} simulate_error=#{simulate_error}")
110
+
111
+ begin
112
+ # Validate inputs
113
+ validate_preconditions(operation, data, max_retries)
114
+
115
+ # Allocate resources (demonstration)
116
+ allocate_resources
117
+
118
+ # Perform main operation
119
+ result = perform_operation(operation, data, simulate_error, max_retries)
120
+
121
+ # Validate outputs
122
+ validate_postconditions(result)
123
+
124
+ @logger.info("Operation completed successfully")
125
+
126
+ {
127
+ success: true,
128
+ result: result,
129
+ metadata: operation_metadata
130
+ }
131
+ rescue ValidationError => e
132
+ @logger.error("Validation error: #{e.message}")
133
+ handle_validation_error(e, operation)
134
+ rescue NetworkError => e
135
+ @logger.error("Network error: #{e.message}")
136
+ handle_network_error(e, operation)
137
+ rescue AuthorizationError => e
138
+ @logger.error("Authorization error: #{e.message}")
139
+ handle_authorization_error(e, operation)
140
+ rescue ResourceNotFoundError => e
141
+ @logger.error("Resource not found: #{e.message}")
142
+ handle_resource_not_found_error(e, operation)
143
+ rescue StandardError => e
144
+ @logger.error("General error: #{e.class} - #{e.message}")
145
+ handle_general_error(e, operation)
146
+ ensure
147
+ cleanup_resources
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ # Validate operation parameters before execution
154
+ def validate_preconditions(operation, data, max_retries)
155
+ valid_operations = %w[validate process authorize]
156
+ unless valid_operations.include?(operation)
157
+ raise ValidationError.new(
158
+ "Invalid operation: #{operation}",
159
+ suggestions: ["Use one of: #{valid_operations.join(', ')}"]
160
+ )
161
+ end
162
+
163
+ if max_retries < 0 || max_retries > 10
164
+ raise ValidationError.new(
165
+ "max_retries must be between 0 and 10, got #{max_retries}",
166
+ suggestions: ["Use a value between 0 and 10"]
167
+ )
168
+ end
169
+
170
+ if operation == 'process' && data.empty?
171
+ raise ValidationError.new(
172
+ "Data is required for 'process' operation",
173
+ suggestions: ["Provide a data object with 'name' and 'value' fields"]
174
+ )
175
+ end
176
+
177
+ @logger.debug("Preconditions validated successfully")
178
+ end
179
+
180
+ # Main operation with retry logic
181
+ def perform_operation(operation, data, simulate_error, max_retries)
182
+ retry_count = 0
183
+
184
+ begin
185
+ # Simulate specific error if requested
186
+ simulate_error_if_requested(simulate_error, retry_count)
187
+
188
+ case operation
189
+ when 'validate'
190
+ perform_validation(data)
191
+ when 'process'
192
+ perform_processing(data)
193
+ when 'authorize'
194
+ perform_authorization(data)
195
+ else
196
+ raise ArgumentError, "Unknown operation: #{operation}"
197
+ end
198
+
199
+ rescue RetryableError => e
200
+ retry_count += 1
201
+ @logger.warn("Retryable error occurred, attempt #{retry_count}/#{max_retries}")
202
+
203
+ if retry_count <= max_retries
204
+ sleep_duration = 2 ** retry_count # Exponential backoff
205
+ @logger.debug("Sleeping for #{sleep_duration} seconds before retry")
206
+ sleep(sleep_duration)
207
+ retry
208
+ else
209
+ @logger.error("Max retries (#{max_retries}) exceeded")
210
+ raise NetworkError, "Operation failed after #{max_retries} retries: #{e.message}"
211
+ end
212
+ end
213
+ end
214
+
215
+ # Simulate errors for testing purposes
216
+ def simulate_error_if_requested(error_type, retry_count)
217
+ return if error_type.nil? || error_type.empty?
218
+
219
+ case error_type
220
+ when 'validation'
221
+ raise ValidationError.new(
222
+ "Simulated validation error",
223
+ suggestions: ["Fix the data format", "Check required fields"]
224
+ )
225
+ when 'network'
226
+ raise NetworkError, "Simulated network connection failure"
227
+ when 'authorization'
228
+ raise AuthorizationError, "Simulated authorization failure"
229
+ when 'retryable'
230
+ # Only fail on first two attempts to test retry logic
231
+ if retry_count < 2
232
+ raise RetryableError, "Simulated temporary failure (will retry)"
233
+ end
234
+ when 'resource_not_found'
235
+ raise ResourceNotFoundError, "Simulated resource not found"
236
+ end
237
+ end
238
+
239
+ # Perform data validation
240
+ def perform_validation(data)
241
+ @logger.debug("Performing validation")
242
+
243
+ errors = []
244
+ warnings = []
245
+
246
+ if data[:name].nil? || data[:name].to_s.empty?
247
+ errors << "name is required"
248
+ elsif data[:name].to_s.length < 2
249
+ warnings << "name is very short"
250
+ end
251
+
252
+ if data[:value].nil?
253
+ errors << "value is required"
254
+ elsif !data[:value].is_a?(Numeric)
255
+ errors << "value must be a number"
256
+ elsif data[:value] < 0
257
+ warnings << "value is negative"
258
+ end
259
+
260
+ if errors.any?
261
+ raise ValidationError.new(
262
+ "Data validation failed: #{errors.join(', ')}",
263
+ suggestions: ["Ensure all required fields are present and valid"]
264
+ )
265
+ end
266
+
267
+ {
268
+ validated: true,
269
+ data: data,
270
+ warnings: warnings,
271
+ validated_at: Time.now.iso8601
272
+ }
273
+ end
274
+
275
+ # Perform data processing (simulates network operation)
276
+ def perform_processing(data)
277
+ @logger.debug("Performing processing")
278
+
279
+ # Simulate processing work
280
+ sleep(0.1)
281
+
282
+ processed_value = data[:value].to_f * 1.5
283
+ {
284
+ processed: true,
285
+ original_value: data[:value],
286
+ processed_value: processed_value.round(2),
287
+ name: data[:name],
288
+ processed_at: Time.now.iso8601
289
+ }
290
+ end
291
+
292
+ # Perform authorization check
293
+ def perform_authorization(data)
294
+ @logger.debug("Performing authorization")
295
+
296
+ {
297
+ authorized: true,
298
+ operation: "authorize",
299
+ authorized_at: Time.now.iso8601
300
+ }
301
+ end
302
+
303
+ # Validate operation results
304
+ def validate_postconditions(result)
305
+ unless result.is_a?(Hash)
306
+ raise StandardError, "Operation result must be a Hash"
307
+ end
308
+
309
+ @logger.debug("Postconditions validated successfully")
310
+ end
311
+
312
+ # Allocate resources (demonstration)
313
+ def allocate_resources
314
+ resource_id = SecureRandom.uuid
315
+ @resources_allocated << resource_id
316
+ @logger.debug("Allocated resource: #{resource_id}")
317
+ end
318
+
319
+ # Handle validation errors
320
+ def handle_validation_error(error, operation)
321
+ {
322
+ success: false,
323
+ error_type: "validation",
324
+ error: error.message,
325
+ suggestions: error.suggestions,
326
+ operation: operation,
327
+ support_reference: SecureRandom.uuid
328
+ }
329
+ end
330
+
331
+ # Handle network errors
332
+ def handle_network_error(error, operation)
333
+ {
334
+ success: false,
335
+ error_type: "network",
336
+ error: "Network operation failed: #{error.message}",
337
+ retry_suggested: true,
338
+ retry_after: 30,
339
+ operation: operation,
340
+ support_reference: SecureRandom.uuid
341
+ }
342
+ end
343
+
344
+ # Handle authorization errors
345
+ def handle_authorization_error(error, operation)
346
+ {
347
+ success: false,
348
+ error_type: "authorization",
349
+ error: "Access denied: #{error.message}",
350
+ documentation_url: "https://github.com/madbomber/shared_tools",
351
+ operation: operation,
352
+ support_reference: SecureRandom.uuid
353
+ }
354
+ end
355
+
356
+ # Handle resource not found errors
357
+ def handle_resource_not_found_error(error, operation)
358
+ {
359
+ success: false,
360
+ error_type: "resource_not_found",
361
+ error: "Resource not found: #{error.message}",
362
+ operation: operation,
363
+ support_reference: SecureRandom.uuid
364
+ }
365
+ end
366
+
367
+ # Handle general errors
368
+ def handle_general_error(error, operation)
369
+ {
370
+ success: false,
371
+ error_type: "general",
372
+ error: "An unexpected error occurred: #{error.message}",
373
+ error_class: error.class.name,
374
+ operation: operation,
375
+ support_reference: SecureRandom.uuid
376
+ }
377
+ end
378
+
379
+ # Collect operation metadata
380
+ def operation_metadata
381
+ execution_time = @operation_start_time ? (Time.now - @operation_start_time).round(3) : 0
382
+
383
+ {
384
+ execution_time_seconds: execution_time,
385
+ resources_allocated: @resources_allocated.length,
386
+ timestamp: Time.now.iso8601,
387
+ tool_version: "1.0.0"
388
+ }
389
+ end
390
+
391
+ # Clean up allocated resources
392
+ def cleanup_resources
393
+ if @resources_allocated.any?
394
+ @logger.debug("Cleaning up #{@resources_allocated.length} resources")
395
+ @resources_allocated.each do |resource_id|
396
+ @logger.debug("Released resource: #{resource_id}")
397
+ end
398
+ @resources_allocated.clear
399
+ end
400
+ end
401
+ end
402
+ end
403
+ end