shared_tools 0.4.4 → 0.7.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 +40 -0
- data/README.md +211 -7
- data/lib/shared_tools/apply_patch_tool.rb +4 -0
- data/lib/shared_tools/bash_tool.rb +4 -0
- data/lib/shared_tools/bundle_tool.rb +4 -0
- data/lib/shared_tools/command_guard.rb +35 -0
- data/lib/shared_tools/csv_read_tool.rb +4 -0
- data/lib/shared_tools/csv_write_tool.rb +4 -0
- data/lib/shared_tools/data_path.rb +52 -0
- data/lib/shared_tools/diff_tool.rb +4 -0
- data/lib/shared_tools/download_file_tool.rb +4 -0
- data/lib/shared_tools/gem_tool.rb +4 -0
- data/lib/shared_tools/git_tool.rb +5 -0
- data/lib/shared_tools/glob_tool.rb +4 -0
- data/lib/shared_tools/http_request_tool.rb +4 -0
- data/lib/shared_tools/json_query_tool.rb +4 -0
- data/lib/shared_tools/lint_tool.rb +4 -0
- data/lib/shared_tools/mcp/brave_search_client.rb +18 -16
- data/lib/shared_tools/mcp/chart_client.rb +18 -15
- data/lib/shared_tools/mcp/client_support.rb +103 -0
- data/lib/shared_tools/mcp/github_client.rb +23 -19
- data/lib/shared_tools/mcp/hugging_face_client.rb +22 -22
- data/lib/shared_tools/mcp/memory_client.rb +20 -15
- data/lib/shared_tools/mcp/notion_client.rb +21 -19
- data/lib/shared_tools/mcp/playwright_client.rb +19 -15
- data/lib/shared_tools/mcp/sequential_thinking_client.rb +17 -14
- data/lib/shared_tools/mcp/slack_client.rb +30 -25
- data/lib/shared_tools/mcp/tavily_client.rb +21 -21
- data/lib/shared_tools/mcp.rb +32 -41
- data/lib/shared_tools/multi_edit_tool.rb +4 -0
- data/lib/shared_tools/parse_ruby_tool.rb +4 -0
- data/lib/shared_tools/process_kill_tool.rb +4 -0
- data/lib/shared_tools/process_list_tool.rb +4 -0
- data/lib/shared_tools/process_output_tool.rb +4 -0
- data/lib/shared_tools/process_registry.rb +224 -0
- data/lib/shared_tools/process_runner.rb +69 -0
- data/lib/shared_tools/process_start_tool.rb +4 -0
- data/lib/shared_tools/python_tests_tool.rb +4 -0
- data/lib/shared_tools/replace_in_files_tool.rb +4 -0
- data/lib/shared_tools/ruby_outline.rb +207 -0
- data/lib/shared_tools/run_tests_tool.rb +4 -0
- data/lib/shared_tools/text_diff.rb +80 -0
- data/lib/shared_tools/todo_write_tool.rb +4 -0
- data/lib/shared_tools/toml_parser.rb +409 -0
- data/lib/shared_tools/toml_query_tool.rb +4 -0
- data/lib/shared_tools/tools/apply_patch_tool.rb +122 -0
- data/lib/shared_tools/tools/bash_tool.rb +102 -0
- data/lib/shared_tools/tools/browser/click_tool.rb +2 -2
- data/lib/shared_tools/tools/browser/inspect_tool.rb +2 -2
- data/lib/shared_tools/tools/browser/page_inspect_tool.rb +2 -2
- data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +2 -2
- data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +2 -2
- data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +2 -2
- data/lib/shared_tools/tools/browser/visit_tool.rb +2 -2
- data/lib/shared_tools/tools/browser_tool.rb +2 -2
- data/lib/shared_tools/tools/bundle_tool.rb +96 -0
- data/lib/shared_tools/tools/calculator_tool.rb +2 -2
- data/lib/shared_tools/tools/clipboard_tool.rb +2 -2
- data/lib/shared_tools/tools/composite_analysis_tool.rb +2 -2
- data/lib/shared_tools/tools/computer_tool.rb +2 -2
- data/lib/shared_tools/tools/cron_tool.rb +2 -2
- data/lib/shared_tools/tools/csv_read_tool.rb +82 -0
- data/lib/shared_tools/tools/csv_write_tool.rb +67 -0
- data/lib/shared_tools/tools/current_date_time_tool.rb +3 -3
- data/lib/shared_tools/tools/data_science_kit.rb +2 -2
- data/lib/shared_tools/tools/database_query_tool.rb +2 -2
- data/lib/shared_tools/tools/database_tool.rb +2 -2
- data/lib/shared_tools/tools/diff_tool.rb +48 -0
- data/lib/shared_tools/tools/disk/directory_create_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/directory_delete_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/directory_list_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/directory_move_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/file_create_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/file_delete_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/file_move_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/file_read_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/file_replace_tool.rb +2 -2
- data/lib/shared_tools/tools/disk/file_write_tool.rb +2 -2
- data/lib/shared_tools/tools/disk_tool.rb +2 -2
- data/lib/shared_tools/tools/dns_tool.rb +2 -2
- data/lib/shared_tools/tools/doc/docx_reader_tool.rb +2 -2
- data/lib/shared_tools/tools/doc/pdf_reader_tool.rb +2 -2
- data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +2 -2
- data/lib/shared_tools/tools/doc/text_reader_tool.rb +2 -2
- data/lib/shared_tools/tools/doc_tool.rb +2 -2
- data/lib/shared_tools/tools/docker/compose_run_tool.rb +2 -2
- data/lib/shared_tools/tools/download_file_tool.rb +87 -0
- data/lib/shared_tools/tools/enabler.rb +2 -2
- data/lib/shared_tools/tools/error_handling_tool.rb +2 -2
- data/lib/shared_tools/tools/eval/python_eval_tool.rb +2 -2
- data/lib/shared_tools/tools/eval/ruby_eval_tool.rb +2 -2
- data/lib/shared_tools/tools/eval/shell_eval_tool.rb +2 -2
- data/lib/shared_tools/tools/eval_tool.rb +2 -2
- data/lib/shared_tools/tools/gem_tool.rb +157 -0
- data/lib/shared_tools/tools/git/add_tool.rb +73 -0
- data/lib/shared_tools/tools/git/blame_tool.rb +77 -0
- data/lib/shared_tools/tools/git/branch_tool.rb +55 -0
- data/lib/shared_tools/tools/git/checkout_tool.rb +69 -0
- data/lib/shared_tools/tools/git/commit_tool.rb +73 -0
- data/lib/shared_tools/tools/git/diff_tool.rb +69 -0
- data/lib/shared_tools/tools/git/grep_tool.rb +82 -0
- data/lib/shared_tools/tools/git/helpers.rb +70 -0
- data/lib/shared_tools/tools/git/log_tool.rb +71 -0
- data/lib/shared_tools/tools/git/show_tool.rb +66 -0
- data/lib/shared_tools/tools/git/status_tool.rb +46 -0
- data/lib/shared_tools/tools/git.rb +18 -0
- data/lib/shared_tools/tools/git_tool.rb +148 -0
- data/lib/shared_tools/tools/glob_tool.rb +75 -0
- data/lib/shared_tools/tools/http_helpers.rb +119 -0
- data/lib/shared_tools/tools/http_request_tool.rb +91 -0
- data/lib/shared_tools/tools/json_query_tool.rb +81 -0
- data/lib/shared_tools/tools/lint_tool.rb +93 -0
- data/lib/shared_tools/tools/multi_edit_tool.rb +134 -0
- data/lib/shared_tools/tools/notification_tool.rb +2 -2
- data/lib/shared_tools/tools/parse_ruby_tool.rb +127 -0
- data/lib/shared_tools/tools/process_kill_tool.rb +55 -0
- data/lib/shared_tools/tools/process_list_tool.rb +41 -0
- data/lib/shared_tools/tools/process_output_tool.rb +69 -0
- data/lib/shared_tools/tools/process_start_tool.rb +0 -0
- data/lib/shared_tools/tools/python_tests_tool.rb +105 -0
- data/lib/shared_tools/tools/replace_in_files_tool.rb +166 -0
- data/lib/shared_tools/tools/run_tests_tool.rb +108 -0
- data/lib/shared_tools/tools/search_codebase_tool.rb +182 -51
- data/lib/shared_tools/tools/secure_tool_template.rb +2 -2
- data/lib/shared_tools/tools/system_info_tool.rb +2 -2
- data/lib/shared_tools/tools/todo_write_tool.rb +81 -0
- data/lib/shared_tools/tools/toml_query_tool.rb +81 -0
- data/lib/shared_tools/tools/toolchain_helpers.rb +64 -0
- data/lib/shared_tools/tools/tree_tool.rb +100 -0
- data/lib/shared_tools/tools/weather_tool.rb +2 -2
- data/lib/shared_tools/tools/web_fetch_tool.rb +85 -0
- data/lib/shared_tools/tools/workflow_manager_tool.rb +2 -2
- data/lib/shared_tools/tools/yaml_query_tool.rb +86 -0
- data/lib/shared_tools/tree_tool.rb +4 -0
- data/lib/shared_tools/url_guard.rb +106 -0
- data/lib/shared_tools/utilities.rb +2 -2
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools/web_fetch_tool.rb +4 -0
- data/lib/shared_tools/yaml_query_tool.rb +4 -0
- data/lib/shared_tools.rb +1 -1
- metadata +532 -409
- data/lib/shared_tools/mcp/streamable_http_patch.rb +0 -42
- data/lib/shared_tools/tools/version.rb +0 -7
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../shared_tools'
|
|
4
|
+
|
|
5
|
+
module SharedTools
|
|
6
|
+
module Tools
|
|
7
|
+
# Compares two blocks of text and returns a readable line diff. In-process,
|
|
8
|
+
# read-only — no filesystem or git access. For diffing files already on
|
|
9
|
+
# disk, see Git::DiffTool.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# tool = SharedTools::Tools::DiffTool.new
|
|
13
|
+
# tool.execute(old: "a\nb\n", new: "a\nc\n")
|
|
14
|
+
class DiffTool < ::RubyLLM::Tool
|
|
15
|
+
def self.tool_name = 'diff'
|
|
16
|
+
|
|
17
|
+
description "Compare two blocks of text and return a readable line-by-line diff " \
|
|
18
|
+
"(removed lines prefixed -, added lines prefixed +). In-process."
|
|
19
|
+
|
|
20
|
+
parameters do
|
|
21
|
+
string :old, description: "The original text."
|
|
22
|
+
string :new, description: "The changed text."
|
|
23
|
+
string :old_label, description: "Label for the original side. Default 'old'.", required: false
|
|
24
|
+
string :new_label, description: "Label for the changed side. Default 'new'.", required: false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param logger [Logger] optional logger
|
|
28
|
+
def initialize(logger: nil)
|
|
29
|
+
@logger = logger || RubyLLM.logger
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param old [String]
|
|
33
|
+
# @param new [String]
|
|
34
|
+
# @param old_label [String]
|
|
35
|
+
# @param new_label [String]
|
|
36
|
+
#
|
|
37
|
+
# @return [String]
|
|
38
|
+
def execute(old:, new:, old_label: "old", new_label: "new")
|
|
39
|
+
@logger.info("#{self.class.name}#execute old_label=#{old_label} new_label=#{new_label}")
|
|
40
|
+
|
|
41
|
+
SharedTools::TextDiff.unified(old.to_s, new.to_s, old_label: old_label.to_s, new_label: new_label.to_s)
|
|
42
|
+
rescue => e
|
|
43
|
+
@logger.error("#{self.class.name} failed: #{e.message}")
|
|
44
|
+
{ error: e.message }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::DirectoryCreateTool.new(root: "./project")
|
|
10
10
|
# tool.execute(path: "./foo/bar")
|
|
11
11
|
class DirectoryCreateTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_directory_create'
|
|
13
13
|
|
|
14
14
|
description "Creates a directory."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path to the directory (e.g. `./foo/bar`)"
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::DirectoryDeleteTool.new(root: "./project")
|
|
10
10
|
# tool.execute(path: "./foo/bar")
|
|
11
11
|
class DirectoryDeleteTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_directory_delete'
|
|
13
13
|
|
|
14
14
|
description "Deletes a directory."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path to the directory (e.g. `./foo/bar`)"
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::SummaryTool.new(root: "./project")
|
|
10
10
|
# tool.execute
|
|
11
11
|
class DirectoryListTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_directory_list'
|
|
13
13
|
|
|
14
14
|
description "Summarizes the contents (files and directories) of a directory."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path to the directory (e.g. `./foo/bar`)", required: false
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::DirectoryMoveTool.new(root: "./project")
|
|
10
10
|
# tool.execute(path: "./foo", destination: "./bar")
|
|
11
11
|
class DirectoryMoveTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_directory_move'
|
|
13
13
|
|
|
14
14
|
description "Moves a directory from one location to another."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path (e.g. `./old`)"
|
|
18
18
|
string :destination, description: "a path (e.g. `./new`)"
|
|
19
19
|
end
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::FileCreateTool.new(root: "./project")
|
|
10
10
|
# tool.execute(path: "./README.md")
|
|
11
11
|
class FileCreateTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_file_create'
|
|
13
13
|
|
|
14
14
|
description "Creates a file."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path to the file (e.g. `./README.md`)"
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::FileDeleteTool.new(root: "./project")
|
|
10
10
|
# tool.execute(path: "./README.md")
|
|
11
11
|
class FileDeleteTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_file_delete'
|
|
13
13
|
|
|
14
14
|
description "Deletes a file."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path to the file (e.g. `./README.md`)"
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -12,11 +12,11 @@ module SharedTools
|
|
|
12
12
|
# destination: "./README.md",
|
|
13
13
|
# )
|
|
14
14
|
class FileMoveTool < ::RubyLLM::Tool
|
|
15
|
-
def self.
|
|
15
|
+
def self.tool_name = 'disk_file_move'
|
|
16
16
|
|
|
17
17
|
description "Moves a file."
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
parameters do
|
|
20
20
|
string :path, description: "a path (e.g. `./old.rb`)"
|
|
21
21
|
string :destination, description: "a path (e.g. `./new.rb`)"
|
|
22
22
|
end
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::FileReadTool.new
|
|
10
10
|
# tool.execute(path: "./README.md") # => "..."
|
|
11
11
|
class FileReadTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_file_read'
|
|
13
13
|
|
|
14
14
|
description "Reads the contents of a file."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path (e.g. `./main.rb`)"
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -13,11 +13,11 @@ module SharedTools
|
|
|
13
13
|
# path: "README.md"
|
|
14
14
|
# )
|
|
15
15
|
class FileReplaceTool < ::RubyLLM::Tool
|
|
16
|
-
def self.
|
|
16
|
+
def self.tool_name = 'disk_file_replace'
|
|
17
17
|
|
|
18
18
|
description "Replaces a specific string in a file (old_text => new_text)."
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
parameters do
|
|
21
21
|
string :old_text, description: "the old text (e.g. `puts 'ABC'`)"
|
|
22
22
|
string :new_text, description: "the new text (e.g. `puts 'DEF'`)"
|
|
23
23
|
string :path, description: "a path (e.g. `./main.rb`)"
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Disk::FileWriteTool.new(root: "./project")
|
|
10
10
|
# tool.execute(path: "./README.md", text: "Hello World")
|
|
11
11
|
class FileWriteTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'disk_file_write'
|
|
13
13
|
|
|
14
14
|
description "Writes the contents of a file."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :path, description: "a path for the file (e.g. `./main.rb`)"
|
|
18
18
|
string :text, description: "the text to write to the file (e.g. `puts 'Hello World'`)"
|
|
19
19
|
end
|
|
@@ -13,7 +13,7 @@ module SharedTools
|
|
|
13
13
|
# disk.execute(action: SharedTools::Tools::DiskTool::Action::FILE_READ, path: "./demo.rb")
|
|
14
14
|
# disk.execute(action: SharedTools::Tools::DiskTool::Action::FILE_DELETE, path: "./demo.rb")
|
|
15
15
|
class DiskTool < ::RubyLLM::Tool
|
|
16
|
-
def self.
|
|
16
|
+
def self.tool_name = 'disk'
|
|
17
17
|
description <<~TEXT
|
|
18
18
|
A tool for interacting with a system. It is able to list, create, delete, move and modify directories and files.
|
|
19
19
|
TEXT
|
|
@@ -44,7 +44,7 @@ module SharedTools
|
|
|
44
44
|
Action::FILE_REPLACE,
|
|
45
45
|
].freeze
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
parameters do
|
|
48
48
|
string :action, description: <<~TEXT.strip
|
|
49
49
|
Options:
|
|
50
50
|
* `#{Action::DIRECTORY_CREATE}`: creates a directory at a specific `path`
|
|
@@ -20,7 +20,7 @@ module SharedTools
|
|
|
20
20
|
# tool.execute(action: 'whois', host: 'github.com')
|
|
21
21
|
# tool.execute(action: 'whois', host: '8.8.8.8')
|
|
22
22
|
class DnsTool < ::RubyLLM::Tool
|
|
23
|
-
def self.
|
|
23
|
+
def self.tool_name = 'dns'
|
|
24
24
|
|
|
25
25
|
description <<~DESC
|
|
26
26
|
Perform DNS lookups, reverse lookups, record queries, external IP detection,
|
|
@@ -40,7 +40,7 @@ module SharedTools
|
|
|
40
40
|
- 'whois' — Query the WHOIS database for a domain name or IP address
|
|
41
41
|
DESC
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
parameters do
|
|
44
44
|
string :action, description: <<~DESC.strip
|
|
45
45
|
The DNS or network operation to perform. One of:
|
|
46
46
|
- 'a' — Look up IPv4 A records for a hostname. Returns the IPv4 addresses
|
|
@@ -16,11 +16,11 @@ module SharedTools
|
|
|
16
16
|
# tool.execute(doc_path: "./report.docx")
|
|
17
17
|
# tool.execute(doc_path: "./report.docx", paragraph_range: "1-10")
|
|
18
18
|
class DocxReaderTool < ::RubyLLM::Tool
|
|
19
|
-
def self.
|
|
19
|
+
def self.tool_name = 'doc_docx_read'
|
|
20
20
|
|
|
21
21
|
description "Read the text content of a Microsoft Word (.docx) document."
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
parameters do
|
|
24
24
|
string :doc_path, description: "Path to the .docx file."
|
|
25
25
|
|
|
26
26
|
string :paragraph_range, description: <<~DESC.strip, required: false
|
|
@@ -14,11 +14,11 @@ module SharedTools
|
|
|
14
14
|
# tool = SharedTools::Tools::Doc::PdfReaderTool.new
|
|
15
15
|
# tool.execute(doc_path: "./document.pdf", page_numbers: "1, 5, 10")
|
|
16
16
|
class PdfReaderTool < ::RubyLLM::Tool
|
|
17
|
-
def self.
|
|
17
|
+
def self.tool_name = 'doc_pdf_read'
|
|
18
18
|
|
|
19
19
|
description "Read the text of any set of pages from a PDF document."
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
parameters do
|
|
22
22
|
string :page_numbers, description: 'Comma-separated page numbers (first page: 1). (e.g. "12, 14, 15")'
|
|
23
23
|
string :doc_path, description: "Path to the PDF document."
|
|
24
24
|
end
|
|
@@ -21,13 +21,13 @@ module SharedTools
|
|
|
21
21
|
# @example Read a row range from a worksheet
|
|
22
22
|
# tool.execute(doc_path: "./report.xlsx", sheet: "Summary", row_range: "2-50")
|
|
23
23
|
class SpreadsheetReaderTool < ::RubyLLM::Tool
|
|
24
|
-
def self.
|
|
24
|
+
def self.tool_name = 'doc_spreadsheet_read'
|
|
25
25
|
|
|
26
26
|
description "Read tabular data from spreadsheet files (CSV, XLSX, ODS, and other formats)."
|
|
27
27
|
|
|
28
28
|
SUPPORTED_FORMATS = %w[.csv .xlsx .ods .xlsm].freeze
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
parameters do
|
|
31
31
|
string :doc_path, description: <<~DESC.strip
|
|
32
32
|
Path to the spreadsheet file. Supported formats:
|
|
33
33
|
- .csv — Comma-separated values (plain text, no gem beyond roo required)
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Doc::TextReaderTool.new
|
|
10
10
|
# tool.execute(doc_path: "./guide.txt")
|
|
11
11
|
class TextReaderTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'doc_text_read'
|
|
13
13
|
|
|
14
14
|
description "Read the full contents of a plain text file."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :doc_path, description: "Path to the text file to read."
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -6,7 +6,7 @@ module SharedTools
|
|
|
6
6
|
module Tools
|
|
7
7
|
# A tool for reading and processing documents
|
|
8
8
|
class DocTool < ::RubyLLM::Tool
|
|
9
|
-
def self.
|
|
9
|
+
def self.tool_name = 'doc'
|
|
10
10
|
|
|
11
11
|
module Action
|
|
12
12
|
PDF_READ = "pdf_read"
|
|
@@ -85,7 +85,7 @@ module SharedTools
|
|
|
85
85
|
{"action": "#{Action::SPREADSHEET_READ}", "doc_path": "./report.xlsx", "row_range": "2-50", "headers": false}
|
|
86
86
|
TEXT
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
parameters do
|
|
89
89
|
string :action, description: <<~TEXT.strip
|
|
90
90
|
The document action to perform. Options:
|
|
91
91
|
* `#{Action::TEXT_READ}`: Read a plain text file
|
|
@@ -9,11 +9,11 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Docker::ComposeRunTool.new
|
|
10
10
|
# tool.execute(service: "app", command: "rspec", args: ["spec/main_spec.rb"])
|
|
11
11
|
class ComposeRunTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'docker_compose_run'
|
|
13
13
|
|
|
14
14
|
description "Runs a command via Docker with arguments on the project (e.g. `rspec spec/main_spec.rb`)."
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
parameters do
|
|
17
17
|
string :service, description: "The service to run the command on (e.g. `app`).", required: false
|
|
18
18
|
string :command, description: "The command to run (e.g. `rspec`)."
|
|
19
19
|
array :args, of: :string, description: "The arguments for the command.", required: false
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "pathname"
|
|
5
|
+
require_relative "../../shared_tools"
|
|
6
|
+
require_relative "http_helpers"
|
|
7
|
+
|
|
8
|
+
module SharedTools
|
|
9
|
+
module Tools
|
|
10
|
+
# Downloads a URL to a file within root. The request goes through
|
|
11
|
+
# UrlGuard (so it can't be pointed at internal/loopback/metadata
|
|
12
|
+
# addresses), follows redirects safely, and is capped at
|
|
13
|
+
# HttpHelpers::MAX_FETCH_BYTES. The destination path is confined to
|
|
14
|
+
# root. Mutating — requires user authorization (see
|
|
15
|
+
# SharedTools.execute?).
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# tool = SharedTools::Tools::DownloadFileTool.new(root: "./downloads")
|
|
19
|
+
# tool.execute(url: "https://example.com/file.zip", path: "file.zip")
|
|
20
|
+
class DownloadFileTool < ::RubyLLM::Tool
|
|
21
|
+
include HttpHelpers
|
|
22
|
+
|
|
23
|
+
def self.tool_name = 'download_file'
|
|
24
|
+
|
|
25
|
+
description "Download an http/https URL to a file within root. SSRF-protected and " \
|
|
26
|
+
"size-capped. Use for fetching an asset or release to disk (web_fetch returns text " \
|
|
27
|
+
"instead). Overwrites an existing file."
|
|
28
|
+
|
|
29
|
+
parameters do
|
|
30
|
+
string :url, description: "The http/https URL to download."
|
|
31
|
+
string :path, description: "Destination path, relative to root."
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param root [String] optional, defaults to the current directory
|
|
35
|
+
# @param logger [Logger] optional logger
|
|
36
|
+
def initialize(root: nil, logger: nil)
|
|
37
|
+
@root = root || Dir.pwd
|
|
38
|
+
@logger = logger || RubyLLM.logger
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param url [String]
|
|
42
|
+
# @param path [String]
|
|
43
|
+
#
|
|
44
|
+
# @return [String, Hash]
|
|
45
|
+
def execute(url:, path:)
|
|
46
|
+
@logger.info("#{self.class.name}#execute url=#{url.inspect} path=#{path.inspect}")
|
|
47
|
+
|
|
48
|
+
real = resolve!(path)
|
|
49
|
+
return { error: "path is a directory: #{path}" } if File.directory?(real)
|
|
50
|
+
|
|
51
|
+
allowed = SharedTools.execute?(tool: self.class.to_s, stuff: "Download #{url} to #{path}")
|
|
52
|
+
unless allowed
|
|
53
|
+
@logger.warn("User declined to download #{url}")
|
|
54
|
+
return { error: "User declined to download the file" }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
response = guarded_get(url)
|
|
58
|
+
return { error: "server returned HTTP #{response.status} for #{response.final_url}" } if response.status >= 400
|
|
59
|
+
|
|
60
|
+
body = response.body.to_s
|
|
61
|
+
FileUtils.mkdir_p(File.dirname(real))
|
|
62
|
+
File.binwrite(real, body)
|
|
63
|
+
|
|
64
|
+
"Downloaded #{body.bytesize} bytes from #{response.final_url} to #{path}"
|
|
65
|
+
rescue SecurityError => e
|
|
66
|
+
@logger.error("#{self.class.name} path denied: #{e.message}")
|
|
67
|
+
{ error: e.message }
|
|
68
|
+
rescue SharedTools::UrlGuard::Blocked => e
|
|
69
|
+
@logger.error("#{self.class.name} url blocked: #{e.message}")
|
|
70
|
+
{ error: e.message }
|
|
71
|
+
rescue HttpHelpers::FetchError => e
|
|
72
|
+
@logger.error("#{self.class.name} download failed: #{e.message}")
|
|
73
|
+
{ error: "download failed: #{e.message}" }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def resolve!(path)
|
|
79
|
+
root = Pathname.new(File.expand_path(@root))
|
|
80
|
+
resolved = (root + path).cleanpath
|
|
81
|
+
raise SecurityError, "path escapes root: #{path}" unless resolved.ascend.any? { |ancestor| ancestor == root }
|
|
82
|
+
|
|
83
|
+
resolved.to_s
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -24,8 +24,8 @@ module Tools
|
|
|
24
24
|
description <<~DESCRIPTION
|
|
25
25
|
Enables a tool based on the request.
|
|
26
26
|
DESCRIPTION
|
|
27
|
-
|
|
28
|
-
#
|
|
27
|
+
parameter :tool, description: 'Tool name to enable'
|
|
28
|
+
# parameter :params, description: 'JSON formatted parameters', required: false
|
|
29
29
|
|
|
30
30
|
def initialize(agent)
|
|
31
31
|
@agent = agent
|
|
@@ -22,7 +22,7 @@ module SharedTools
|
|
|
22
22
|
class ResourceNotFoundError < StandardError; end
|
|
23
23
|
|
|
24
24
|
class ErrorHandlingTool < RubyLLM::Tool
|
|
25
|
-
def self.
|
|
25
|
+
def self.tool_name = 'error_handling'
|
|
26
26
|
|
|
27
27
|
description <<~'DESCRIPTION'
|
|
28
28
|
Reference tool demonstrating comprehensive error handling patterns and resilience strategies
|
|
@@ -52,7 +52,7 @@ module SharedTools
|
|
|
52
52
|
)
|
|
53
53
|
DESCRIPTION
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
parameters do
|
|
56
56
|
string :operation, description: <<~DESC.strip
|
|
57
57
|
The operation to perform. Options:
|
|
58
58
|
- 'validate': Validate data structure and content
|
|
@@ -8,7 +8,7 @@ module SharedTools
|
|
|
8
8
|
# tool = SharedTools::Tools::Eval::PythonEvalTool.new
|
|
9
9
|
# tool.execute(code: "print('Hello'); result = 2 + 2")
|
|
10
10
|
class PythonEvalTool < ::RubyLLM::Tool
|
|
11
|
-
def self.
|
|
11
|
+
def self.tool_name = 'eval_python'
|
|
12
12
|
|
|
13
13
|
description <<~DESCRIPTION
|
|
14
14
|
Execute Python source code safely and return the result.
|
|
@@ -21,7 +21,7 @@ module SharedTools
|
|
|
21
21
|
NOTE: Requires python3 to be available in the system PATH.
|
|
22
22
|
DESCRIPTION
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
parameters do
|
|
25
25
|
string :code, description: "The Python code to execute"
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -9,7 +9,7 @@ module SharedTools
|
|
|
9
9
|
# tool = SharedTools::Tools::Eval::RubyEvalTool.new
|
|
10
10
|
# tool.execute(code: "puts 'Hello'; 2 + 2")
|
|
11
11
|
class RubyEvalTool < ::RubyLLM::Tool
|
|
12
|
-
def self.
|
|
12
|
+
def self.tool_name = 'eval_ruby'
|
|
13
13
|
|
|
14
14
|
description <<~DESCRIPTION
|
|
15
15
|
Execute Ruby source code safely and return the result.
|
|
@@ -20,7 +20,7 @@ module SharedTools
|
|
|
20
20
|
WARNING: This tool executes arbitrary Ruby code. Use with caution.
|
|
21
21
|
DESCRIPTION
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
parameters do
|
|
24
24
|
string :code, description: "The Ruby code to execute"
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -13,11 +13,11 @@ module SharedTools
|
|
|
13
13
|
STDOUT_MAX = 5000
|
|
14
14
|
STDERR_MAX = 2000
|
|
15
15
|
|
|
16
|
-
def self.
|
|
16
|
+
def self.tool_name = 'eval_shell'
|
|
17
17
|
|
|
18
18
|
description "Execute a shell command safely and return the result."
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
parameters do
|
|
21
21
|
string :command, description: "The shell command to execute"
|
|
22
22
|
string :workdir, description: "Working directory for the command (default: current directory)", required: false
|
|
23
23
|
end
|
|
@@ -6,7 +6,7 @@ module SharedTools
|
|
|
6
6
|
module Tools
|
|
7
7
|
# A tool for evaluating code in different programming languages
|
|
8
8
|
class EvalTool < ::RubyLLM::Tool
|
|
9
|
-
def self.
|
|
9
|
+
def self.tool_name = 'eval'
|
|
10
10
|
|
|
11
11
|
module Action
|
|
12
12
|
RUBY = "ruby"
|
|
@@ -58,7 +58,7 @@ module SharedTools
|
|
|
58
58
|
{"action": "#{Action::SHELL}", "command": "uname -a"}
|
|
59
59
|
TEXT
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
parameters do
|
|
62
62
|
string :action, description: <<~TEXT.strip
|
|
63
63
|
The evaluation action to perform. Options:
|
|
64
64
|
* `#{Action::RUBY}`: Execute Ruby code
|