shared_tools 0.4.4 → 0.6.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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +211 -7
  4. data/lib/shared_tools/apply_patch_tool.rb +4 -0
  5. data/lib/shared_tools/bash_tool.rb +4 -0
  6. data/lib/shared_tools/bundle_tool.rb +4 -0
  7. data/lib/shared_tools/command_guard.rb +35 -0
  8. data/lib/shared_tools/csv_read_tool.rb +4 -0
  9. data/lib/shared_tools/csv_write_tool.rb +4 -0
  10. data/lib/shared_tools/data_path.rb +52 -0
  11. data/lib/shared_tools/diff_tool.rb +4 -0
  12. data/lib/shared_tools/download_file_tool.rb +4 -0
  13. data/lib/shared_tools/gem_tool.rb +4 -0
  14. data/lib/shared_tools/git_tool.rb +5 -0
  15. data/lib/shared_tools/glob_tool.rb +4 -0
  16. data/lib/shared_tools/http_request_tool.rb +4 -0
  17. data/lib/shared_tools/json_query_tool.rb +4 -0
  18. data/lib/shared_tools/lint_tool.rb +4 -0
  19. data/lib/shared_tools/mcp/brave_search_client.rb +18 -16
  20. data/lib/shared_tools/mcp/chart_client.rb +18 -15
  21. data/lib/shared_tools/mcp/client_support.rb +103 -0
  22. data/lib/shared_tools/mcp/github_client.rb +23 -19
  23. data/lib/shared_tools/mcp/hugging_face_client.rb +22 -22
  24. data/lib/shared_tools/mcp/memory_client.rb +20 -15
  25. data/lib/shared_tools/mcp/notion_client.rb +21 -19
  26. data/lib/shared_tools/mcp/playwright_client.rb +19 -15
  27. data/lib/shared_tools/mcp/sequential_thinking_client.rb +17 -14
  28. data/lib/shared_tools/mcp/slack_client.rb +30 -25
  29. data/lib/shared_tools/mcp/tavily_client.rb +21 -21
  30. data/lib/shared_tools/mcp.rb +32 -41
  31. data/lib/shared_tools/multi_edit_tool.rb +4 -0
  32. data/lib/shared_tools/parse_ruby_tool.rb +4 -0
  33. data/lib/shared_tools/process_kill_tool.rb +4 -0
  34. data/lib/shared_tools/process_list_tool.rb +4 -0
  35. data/lib/shared_tools/process_output_tool.rb +4 -0
  36. data/lib/shared_tools/process_registry.rb +224 -0
  37. data/lib/shared_tools/process_runner.rb +69 -0
  38. data/lib/shared_tools/process_start_tool.rb +4 -0
  39. data/lib/shared_tools/python_tests_tool.rb +4 -0
  40. data/lib/shared_tools/replace_in_files_tool.rb +4 -0
  41. data/lib/shared_tools/ruby_outline.rb +207 -0
  42. data/lib/shared_tools/run_tests_tool.rb +4 -0
  43. data/lib/shared_tools/text_diff.rb +80 -0
  44. data/lib/shared_tools/todo_write_tool.rb +4 -0
  45. data/lib/shared_tools/toml_parser.rb +409 -0
  46. data/lib/shared_tools/toml_query_tool.rb +4 -0
  47. data/lib/shared_tools/tools/apply_patch_tool.rb +122 -0
  48. data/lib/shared_tools/tools/bash_tool.rb +102 -0
  49. data/lib/shared_tools/tools/browser/click_tool.rb +1 -1
  50. data/lib/shared_tools/tools/browser/inspect_tool.rb +1 -1
  51. data/lib/shared_tools/tools/browser/page_inspect_tool.rb +1 -1
  52. data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +1 -1
  53. data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +1 -1
  54. data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +1 -1
  55. data/lib/shared_tools/tools/browser/visit_tool.rb +1 -1
  56. data/lib/shared_tools/tools/browser_tool.rb +1 -1
  57. data/lib/shared_tools/tools/bundle_tool.rb +96 -0
  58. data/lib/shared_tools/tools/calculator_tool.rb +1 -1
  59. data/lib/shared_tools/tools/clipboard_tool.rb +1 -1
  60. data/lib/shared_tools/tools/composite_analysis_tool.rb +1 -1
  61. data/lib/shared_tools/tools/computer_tool.rb +1 -1
  62. data/lib/shared_tools/tools/cron_tool.rb +1 -1
  63. data/lib/shared_tools/tools/csv_read_tool.rb +82 -0
  64. data/lib/shared_tools/tools/csv_write_tool.rb +67 -0
  65. data/lib/shared_tools/tools/current_date_time_tool.rb +1 -1
  66. data/lib/shared_tools/tools/data_science_kit.rb +1 -1
  67. data/lib/shared_tools/tools/database_query_tool.rb +1 -1
  68. data/lib/shared_tools/tools/database_tool.rb +1 -1
  69. data/lib/shared_tools/tools/diff_tool.rb +48 -0
  70. data/lib/shared_tools/tools/disk/directory_create_tool.rb +1 -1
  71. data/lib/shared_tools/tools/disk/directory_delete_tool.rb +1 -1
  72. data/lib/shared_tools/tools/disk/directory_list_tool.rb +1 -1
  73. data/lib/shared_tools/tools/disk/directory_move_tool.rb +1 -1
  74. data/lib/shared_tools/tools/disk/file_create_tool.rb +1 -1
  75. data/lib/shared_tools/tools/disk/file_delete_tool.rb +1 -1
  76. data/lib/shared_tools/tools/disk/file_move_tool.rb +1 -1
  77. data/lib/shared_tools/tools/disk/file_read_tool.rb +1 -1
  78. data/lib/shared_tools/tools/disk/file_replace_tool.rb +1 -1
  79. data/lib/shared_tools/tools/disk/file_write_tool.rb +1 -1
  80. data/lib/shared_tools/tools/disk_tool.rb +1 -1
  81. data/lib/shared_tools/tools/dns_tool.rb +1 -1
  82. data/lib/shared_tools/tools/doc/docx_reader_tool.rb +1 -1
  83. data/lib/shared_tools/tools/doc/pdf_reader_tool.rb +1 -1
  84. data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +1 -1
  85. data/lib/shared_tools/tools/doc/text_reader_tool.rb +1 -1
  86. data/lib/shared_tools/tools/doc_tool.rb +1 -1
  87. data/lib/shared_tools/tools/docker/compose_run_tool.rb +1 -1
  88. data/lib/shared_tools/tools/download_file_tool.rb +87 -0
  89. data/lib/shared_tools/tools/enabler.rb +2 -2
  90. data/lib/shared_tools/tools/error_handling_tool.rb +1 -1
  91. data/lib/shared_tools/tools/eval/python_eval_tool.rb +1 -1
  92. data/lib/shared_tools/tools/eval/ruby_eval_tool.rb +1 -1
  93. data/lib/shared_tools/tools/eval/shell_eval_tool.rb +1 -1
  94. data/lib/shared_tools/tools/eval_tool.rb +1 -1
  95. data/lib/shared_tools/tools/gem_tool.rb +157 -0
  96. data/lib/shared_tools/tools/git/add_tool.rb +73 -0
  97. data/lib/shared_tools/tools/git/blame_tool.rb +77 -0
  98. data/lib/shared_tools/tools/git/branch_tool.rb +55 -0
  99. data/lib/shared_tools/tools/git/checkout_tool.rb +69 -0
  100. data/lib/shared_tools/tools/git/commit_tool.rb +73 -0
  101. data/lib/shared_tools/tools/git/diff_tool.rb +69 -0
  102. data/lib/shared_tools/tools/git/grep_tool.rb +82 -0
  103. data/lib/shared_tools/tools/git/helpers.rb +70 -0
  104. data/lib/shared_tools/tools/git/log_tool.rb +71 -0
  105. data/lib/shared_tools/tools/git/show_tool.rb +66 -0
  106. data/lib/shared_tools/tools/git/status_tool.rb +46 -0
  107. data/lib/shared_tools/tools/git.rb +18 -0
  108. data/lib/shared_tools/tools/git_tool.rb +148 -0
  109. data/lib/shared_tools/tools/glob_tool.rb +75 -0
  110. data/lib/shared_tools/tools/http_helpers.rb +119 -0
  111. data/lib/shared_tools/tools/http_request_tool.rb +91 -0
  112. data/lib/shared_tools/tools/json_query_tool.rb +81 -0
  113. data/lib/shared_tools/tools/lint_tool.rb +93 -0
  114. data/lib/shared_tools/tools/multi_edit_tool.rb +134 -0
  115. data/lib/shared_tools/tools/notification_tool.rb +1 -1
  116. data/lib/shared_tools/tools/parse_ruby_tool.rb +127 -0
  117. data/lib/shared_tools/tools/process_kill_tool.rb +55 -0
  118. data/lib/shared_tools/tools/process_list_tool.rb +41 -0
  119. data/lib/shared_tools/tools/process_output_tool.rb +69 -0
  120. data/lib/shared_tools/tools/process_start_tool.rb +0 -0
  121. data/lib/shared_tools/tools/python_tests_tool.rb +105 -0
  122. data/lib/shared_tools/tools/replace_in_files_tool.rb +166 -0
  123. data/lib/shared_tools/tools/run_tests_tool.rb +108 -0
  124. data/lib/shared_tools/tools/search_codebase_tool.rb +178 -47
  125. data/lib/shared_tools/tools/secure_tool_template.rb +1 -1
  126. data/lib/shared_tools/tools/system_info_tool.rb +1 -1
  127. data/lib/shared_tools/tools/todo_write_tool.rb +81 -0
  128. data/lib/shared_tools/tools/toml_query_tool.rb +81 -0
  129. data/lib/shared_tools/tools/toolchain_helpers.rb +64 -0
  130. data/lib/shared_tools/tools/tree_tool.rb +100 -0
  131. data/lib/shared_tools/tools/weather_tool.rb +1 -1
  132. data/lib/shared_tools/tools/web_fetch_tool.rb +85 -0
  133. data/lib/shared_tools/tools/workflow_manager_tool.rb +1 -1
  134. data/lib/shared_tools/tools/yaml_query_tool.rb +86 -0
  135. data/lib/shared_tools/tree_tool.rb +4 -0
  136. data/lib/shared_tools/url_guard.rb +106 -0
  137. data/lib/shared_tools/utilities.rb +2 -2
  138. data/lib/shared_tools/version.rb +1 -1
  139. data/lib/shared_tools/web_fetch_tool.rb +4 -0
  140. data/lib/shared_tools/yaml_query_tool.rb +4 -0
  141. data/lib/shared_tools.rb +1 -1
  142. metadata +516 -409
  143. data/lib/shared_tools/mcp/streamable_http_patch.rb +0 -42
  144. data/lib/shared_tools/tools/version.rb +0 -7
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../shared_tools"
4
+ require_relative "http_helpers"
5
+
6
+ module SharedTools
7
+ module Tools
8
+ # Fetches a URL over http/https and returns its readable text (HTML is
9
+ # stripped to text). Every request and redirect hop passes through
10
+ # UrlGuard, so it can't be pointed at internal/loopback/metadata
11
+ # addresses. Read-only — no authorization prompt required.
12
+ #
13
+ # @example
14
+ # tool = SharedTools::Tools::WebFetchTool.new
15
+ # tool.execute(url: "https://example.com")
16
+ class WebFetchTool < ::RubyLLM::Tool
17
+ include HttpHelpers
18
+
19
+ def self.name = 'web_fetch'
20
+
21
+ description "Fetch a web page or text/JSON resource over http/https and return its readable " \
22
+ "content (HTML is converted to text). Follows redirects safely. Cannot reach " \
23
+ "private, loopback, or cloud-metadata addresses."
24
+
25
+ parameters do
26
+ string :url, description: "The http/https URL to fetch."
27
+ end
28
+
29
+ # @param logger [Logger] optional logger
30
+ def initialize(logger: nil)
31
+ @logger = logger || RubyLLM.logger
32
+ end
33
+
34
+ # @param url [String]
35
+ #
36
+ # @return [String, Hash]
37
+ def execute(url:)
38
+ @logger.info("#{self.class.name}#execute url=#{url.inspect}")
39
+
40
+ response = guarded_get(url)
41
+ return { error: "HTTP #{response.status} from #{response.final_url}" } if response.status >= 400
42
+
43
+ content_type = content_type_of(response)
44
+ text = extract_text(response.body, content_type)
45
+
46
+ header = "GET #{response.final_url} -> #{response.status} (#{content_type})"
47
+ "#{header}\n\n#{text}".strip
48
+ rescue SharedTools::UrlGuard::Blocked => e
49
+ @logger.error("#{self.class.name} url blocked: #{e.message}")
50
+ { error: e.message }
51
+ rescue HttpHelpers::FetchError => e
52
+ @logger.error("#{self.class.name} fetch failed: #{e.message}")
53
+ { error: "fetch failed: #{e.message}" }
54
+ end
55
+
56
+ private
57
+
58
+ def content_type_of(response)
59
+ Array(response.headers["content-type"]).first.to_s.split(";").first.to_s.strip
60
+ end
61
+
62
+ def extract_text(body, content_type)
63
+ text = body.to_s.scrub
64
+ return collapse(text) unless content_type =~ /html|xml/i
65
+
66
+ text = text.dup
67
+ text.gsub!(%r{<script\b[^>]*>.*?</script>}mi, " ")
68
+ text.gsub!(%r{<style\b[^>]*>.*?</style>}mi, " ")
69
+ text.gsub!(/<!--.*?-->/m, " ")
70
+ text.gsub!(%r{</(?:p|div|br|li|h[1-6]|tr|section|article)\s*/?>}i, "\n")
71
+ text.gsub!(/<[^>]+>/, " ")
72
+ collapse(decode_entities(text))
73
+ end
74
+
75
+ def decode_entities(str)
76
+ str.gsub("&amp;", "&").gsub("&lt;", "<").gsub("&gt;", ">")
77
+ .gsub("&quot;", '"').gsub("&#39;", "'").gsub("&nbsp;", " ")
78
+ end
79
+
80
+ def collapse(str)
81
+ str.gsub(/[ \t]+/, " ").gsub(/\n[ \t]*/, "\n").gsub(/\n{3,}/, "\n\n").strip
82
+ end
83
+ end
84
+ end
85
+ end
@@ -27,7 +27,7 @@ module SharedTools
27
27
  All workflow state is persisted to disk and can survive process restarts.
28
28
  DESCRIPTION
29
29
 
30
- params do
30
+ parameters do
31
31
  string :action, description: <<~DESC.strip
32
32
  Workflow management action to perform:
33
33
  - 'start': Initialize a new workflow with initial data and return workflow ID
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'json'
5
+ require 'date'
6
+ require_relative '../../shared_tools'
7
+
8
+ module SharedTools
9
+ module Tools
10
+ # Parses YAML (from a file or an inline string) and either pretty-prints
11
+ # it (as JSON, for readability) or extracts a value with a dot/bracket
12
+ # path. Uses safe_load — no arbitrary Ruby objects are instantiated from
13
+ # the document.
14
+ #
15
+ # Path syntax matches JsonQueryTool: users[0].name, users[].email, config.port
16
+ #
17
+ # @example
18
+ # tool = SharedTools::Tools::YamlQueryTool.new
19
+ # tool.execute(path: "./docker-compose.yml", query: "services[0].image")
20
+ class YamlQueryTool < ::RubyLLM::Tool
21
+ MAX_BYTES = 5 * 1024 * 1024
22
+ PERMITTED = [Date, Time, Symbol].freeze
23
+
24
+ def self.name = 'yaml_query'
25
+
26
+ description "Query YAML with a path expression, or pretty-print it. Provide either a file " \
27
+ "path or an inline yaml string. Loaded safely (no object deserialization). Path " \
28
+ "syntax: keys separated by dots, [n] for array index, [] to map over an array."
29
+
30
+ parameters do
31
+ string :path, description: "YAML file to read. Provide this or yaml.", required: false
32
+ string :yaml, description: "Inline YAML string. Provide this or path.", required: false
33
+ string :query, description: "Path expression to extract, e.g. 'services[0].image'. Omit to pretty-print. Optional.", required: false
34
+ end
35
+
36
+ # @param logger [Logger] optional logger
37
+ def initialize(logger: nil)
38
+ @logger = logger || RubyLLM.logger
39
+ end
40
+
41
+ # @param path [String, nil]
42
+ # @param yaml [String, nil]
43
+ # @param query [String, nil]
44
+ #
45
+ # @return [String, Hash]
46
+ def execute(path: nil, yaml: nil, query: nil)
47
+ @logger.info("#{self.class.name}#execute path=#{path.inspect} query=#{query.inspect}")
48
+
49
+ source = load_source(path, yaml)
50
+ return source if source.is_a?(Hash) # error
51
+
52
+ data = YAML.safe_load(source, permitted_classes: PERMITTED, aliases: true)
53
+ result = query.to_s.strip.empty? ? data : SharedTools::DataPath.query(data, query)
54
+ JSON.pretty_generate(result)
55
+ rescue Psych::DisallowedClass => e
56
+ @logger.error("#{self.class.name} unsafe YAML: #{e.message}")
57
+ { error: "YAML contains a disallowed type: #{e.message}" }
58
+ rescue Psych::SyntaxError => e
59
+ @logger.error("#{self.class.name} invalid YAML: #{e.message}")
60
+ { error: "invalid YAML: #{e.message}" }
61
+ rescue SharedTools::DataPath::Error => e
62
+ @logger.error("#{self.class.name} bad query: #{e.message}")
63
+ { error: e.message }
64
+ rescue => e
65
+ @logger.error("#{self.class.name} failed: #{e.message}")
66
+ { error: e.message }
67
+ end
68
+
69
+ private
70
+
71
+ def load_source(path, yaml)
72
+ if path && !path.to_s.empty?
73
+ return { error: "file not found: #{path}" } unless File.exist?(path)
74
+ return { error: "not a file: #{path}" } unless File.file?(path)
75
+ return { error: "file too large (> #{MAX_BYTES} bytes)" } if File.size(path) > MAX_BYTES
76
+
77
+ File.read(path).scrub
78
+ elsif yaml && !yaml.to_s.empty?
79
+ yaml.to_s
80
+ else
81
+ { error: "provide either path or yaml" }
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/tree_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/tree_tool'
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "ipaddr"
5
+ require "resolv"
6
+
7
+ module SharedTools
8
+ # SSRF defense for tools that fetch arbitrary URLs (WebFetchTool,
9
+ # HttpRequestTool, DownloadFileTool). It:
10
+ # - allows only http/https,
11
+ # - rejects embedded credentials,
12
+ # - enforces optional domain allow/deny lists,
13
+ # - resolves the host and blocks the request if ANY resolved address is
14
+ # private, loopback, link-local (incl. the cloud metadata IP), CGNAT,
15
+ # unique-local IPv6, or otherwise reserved.
16
+ #
17
+ # resolve! also returns a vetted IP so the caller can pin the socket to it
18
+ # (Net::HTTP#ipaddr=), closing the DNS-rebinding window: the address that
19
+ # was vetted is exactly the one connected to, while TLS/SNI/cert checks
20
+ # still use the hostname. Re-run resolve! on every redirect hop (the http
21
+ # helper does).
22
+ class UrlGuard
23
+ class Blocked < StandardError; end
24
+
25
+ # A vetted result: the parsed URI plus a concrete IP that every
26
+ # connection should be pinned to, so a second DNS lookup at connect time
27
+ # can't swap in a private address (DNS rebinding).
28
+ Resolution = Struct.new(:uri, :address, keyword_init: true)
29
+
30
+ ALLOWED_SCHEMES = %w[http https].freeze
31
+
32
+ BLOCKED_RANGES = %w[
33
+ 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16
34
+ 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 192.168.0.0/16 198.18.0.0/15
35
+ 198.51.100.0/24 203.0.113.0/24 224.0.0.0/4 240.0.0.0/4 255.255.255.255/32
36
+ ::1/128 ::/128 fc00::/7 fe80::/10
37
+ ].map { |cidr| IPAddr.new(cidr) }.freeze
38
+
39
+ def initialize(allowlist: [], denylist: [])
40
+ @allowlist = Array(allowlist).map { |d| d.to_s.downcase }
41
+ @denylist = Array(denylist).map { |d| d.to_s.downcase }
42
+ end
43
+
44
+ # Returns a parsed URI if the URL is safe to fetch; raises Blocked
45
+ # otherwise.
46
+ def check!(url)
47
+ resolve!(url).uri
48
+ end
49
+
50
+ # Like check!, but also returns a vetted IP address to pin the connection
51
+ # to (see Resolution). Raises Blocked otherwise.
52
+ def resolve!(url)
53
+ uri = parse(url)
54
+ raise Blocked, "only http/https URLs are allowed" unless ALLOWED_SCHEMES.include?(uri.scheme)
55
+ raise Blocked, "URL must include a host" if uri.host.nil? || uri.host.empty?
56
+ raise Blocked, "URLs with embedded credentials are not allowed" if uri.userinfo
57
+
58
+ host = uri.host.downcase
59
+ enforce_domain_lists(host)
60
+ address = vetted_address(host)
61
+ Resolution.new(uri: uri, address: address)
62
+ end
63
+
64
+ private
65
+
66
+ def parse(url)
67
+ URI.parse(url.to_s)
68
+ rescue URI::InvalidURIError => e
69
+ raise Blocked, "invalid URL: #{e.message}"
70
+ end
71
+
72
+ def enforce_domain_lists(host)
73
+ raise Blocked, "host is denylisted: #{host}" if @denylist.any? { |d| host == d || host.end_with?(".#{d}") }
74
+ return if @allowlist.empty?
75
+
76
+ allowed = @allowlist.any? { |d| host == d || host.end_with?(".#{d}") }
77
+ raise Blocked, "host is not on the allowlist: #{host}" unless allowed
78
+ end
79
+
80
+ # Resolve the host, block the request if ANY resolved address is in a
81
+ # blocked range, and return the first address for the caller to pin to.
82
+ def vetted_address(host)
83
+ addresses = resolve(host)
84
+ raise Blocked, "could not resolve host: #{host}" if addresses.empty?
85
+
86
+ addresses.each do |addr|
87
+ raise Blocked, "host resolves to a blocked address (#{addr})" if blocked_ip?(addr)
88
+ end
89
+ addresses.first
90
+ end
91
+
92
+ def resolve(host)
93
+ Resolv.getaddresses(host)
94
+ rescue StandardError
95
+ []
96
+ end
97
+
98
+ def blocked_ip?(addr)
99
+ ip = IPAddr.new(addr.to_s)
100
+ ip = ip.native if ip.respond_to?(:ipv4_mapped?) && ip.ipv4_mapped?
101
+ BLOCKED_RANGES.any? { |range| range.include?(ip) }
102
+ rescue IPAddr::Error
103
+ true # if we can't parse it, don't fetch it
104
+ end
105
+ end
106
+ end
@@ -15,8 +15,8 @@ module SharedTools
15
15
  # MCP load tracking
16
16
  # ---------------------------------------------------------------------------
17
17
 
18
- # Record the outcome of loading a single MCP client.
19
- # Called by mcp.rb from within each loader thread.
18
+ # Record whether a single MCP server definition is usable (its required
19
+ # environment variables are set). Called by mcp.rb for each server class.
20
20
  #
21
21
  # SharedTools.record_mcp_result("tavily") # success
22
22
  # SharedTools.record_mcp_result("notion", error: e) # failure
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SharedTools
4
- VERSION = "0.4.4"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/web_fetch_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/web_fetch_tool'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/yaml_query_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/yaml_query_tool'
data/lib/shared_tools.rb CHANGED
@@ -17,8 +17,8 @@ SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/disk.rb")
17
17
  SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/doc.rb")
18
18
  SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/docker.rb")
19
19
  SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/eval.rb")
20
+ SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/git.rb")
20
21
  SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/notification.rb")
21
- SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/version.rb") # Defines VERSION constant, not Version class
22
22
  SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/incomplete") # Empty/incomplete tools directory
23
23
  SharedToolsLoader.ignore("#{__dir__}/shared_tools/tools/enabler.rb") # Experimental; defines Tools::Enabler not SharedTools::Tools::Enabler
24
24