omniai-tools 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 404c95beb54e2092a9a694887ba55a2e8ac8ec736bed56b755cfe34d0906cf9e
4
+ data.tar.gz: f07bf709204ea5a472733695e45870bbbf51b5f89a7d817d290e9e926cf4a57d
5
+ SHA512:
6
+ metadata.gz: 206fda9b71017456d3d4f6d2c590849b93bf302c298916fe64fdd9552429357b35c86a01fde359f0ff7e63fc6202429e9bf16e4c4563a61b7ddc065a77c404e8
7
+ data.tar.gz: c7a122e9c86e418825285c4503236eb921570980eba9c8586c509243d2bb9ee7d3bb2d0da2187f331c8c53647153c2109b258d44ed52f12b1929e8cf08d8d93e
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "factory_bot"
8
+ gem "irb"
9
+ gem "rake"
10
+ gem "rspec"
11
+ gem "rspec_junit_formatter"
12
+ gem "rubocop"
13
+ gem "rubocop-basic"
14
+ gem "rubocop-factory_bot"
15
+ gem "rubocop-rake"
16
+ gem "rubocop-rspec"
17
+ gem "simplecov"
18
+ gem "yard"
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # OmniAI::Tools
2
+
3
+ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ksylvest/omniai-tools/blob/main/LICENSE)
4
+ [![RubyGems](https://img.shields.io/gem/v/omniai-tools)](https://rubygems.org/gems/omniai-tools)
5
+ [![GitHub](https://img.shields.io/badge/github-repo-blue.svg)](https://github.com/ksylvest/omniai-tools)
6
+ [![Yard](https://img.shields.io/badge/docs-site-blue.svg)](https://omniai-tools.ksylvest.com)
7
+ [![CircleCI](https://img.shields.io/circleci/build/github/ksylvest/omniai-tools)](https://circleci.com/gh/ksylvest/omniai-tools)
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "omniai/tools"
6
+
7
+ require "irb"
8
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # class ExampleTool < OmniAI::Tools::Disk::BaseTool
8
+ # description "..."
9
+ # end
10
+ class BaseTool < OmniAI::Tool
11
+ # @param root [Pathname] The root path for which a tool is able to operate within.
12
+ # @param logger [IO] An optional logger for debugging executed commands.
13
+ def initialize(root:, logger: Logger.new(IO::NULL))
14
+ super()
15
+ @root = Pathname(root)
16
+ @logger = logger
17
+ end
18
+
19
+ protected
20
+
21
+ # @param directory [Pathname]
22
+ # @param path [String]
23
+ #
24
+ # @raise [SecurityError]
25
+ #
26
+ # @return Pathname
27
+ def resolve!(path:)
28
+ @root.join(path).tap do |resolved|
29
+ relative = resolved.ascend.any? { |ancestor| ancestor.eql?(@root) }
30
+ raise SecurityError, "unknown path=#{resolved}" unless relative
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::DirectoryCreateTool.new(root: "./project")
8
+ # tool.execute(path: "./foo/bar")
9
+ class DirectoryCreateTool < BaseTool
10
+ description "Creates a directory."
11
+
12
+ parameter :path, :string, description: "a path to the directory (e.g. `./foo/bar`)"
13
+
14
+ # @param path [String]
15
+ #
16
+ # @return [String]
17
+ def execute(path:)
18
+ @logger.info("#{self.class.name}#execute path=#{path.inspect}")
19
+
20
+ FileUtils.mkdir_p(resolve!(path:))
21
+ rescue SecurityError => e
22
+ @logger.error(e.message)
23
+ raise e
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::DirectoryDeleteTool.new(root: "./project")
8
+ # tool.execute(path: "./foo/bar")
9
+ class DirectoryDeleteTool < BaseTool
10
+ description "Deletes a directory."
11
+
12
+ parameter :path, :string, description: "a path to the directory (e.g. `./foo/bar`)"
13
+
14
+ # @param path [String]
15
+ #
16
+ # @return [String]
17
+ def execute(path:)
18
+ @logger.info("#{self.class.name}#execute path=#{path.inspect}")
19
+
20
+ FileUtils.rmdir(resolve!(path:))
21
+ rescue SecurityError => e
22
+ @logger.error(e.message)
23
+ raise e
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::FileCreateTool.new(root: "./project")
8
+ # tool.execute(path: "./README.md")
9
+ class FileCreateTool < BaseTool
10
+ description "Creates a file."
11
+
12
+ parameter :path, :string, description: "a path to the file (e.g. `./README.md`)"
13
+
14
+ # @param path [String]
15
+ #
16
+ # @return [String]
17
+ def execute(path:)
18
+ @logger.info("#{self.class.name}#execute path=#{path.inspect}")
19
+
20
+ resolved = resolve!(path:)
21
+ FileUtils.touch(resolved) unless File.exist?(resolved)
22
+ rescue SecurityError => e
23
+ @logger.error(e.message)
24
+ raise e
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::FileDeleteTool.new(root: "./project")
8
+ # tool.execute(path: "./README.md")
9
+ class FileDeleteTool < BaseTool
10
+ description "reads the contents of a file"
11
+
12
+ parameter :path, :string, description: "a path to the directory (e.g. `./foo/bar`)"
13
+
14
+ # @param path [String]
15
+ #
16
+ # @raise [SecurityError]
17
+ #
18
+ # @return [String]
19
+ def execute(path:)
20
+ @logger.info("#{self.class.name}#execute path=#{path.inspect}")
21
+
22
+ File.delete(resolve!(path:))
23
+ rescue SecurityError => e
24
+ @logger.error(e.message)
25
+ raise e
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::FileDeleteTool.new(root: "./project")
8
+ # tool.execute(
9
+ # old_path: "./README.txt",
10
+ # new_path: "./README.md",
11
+ # )
12
+ class FileMoveTool < BaseTool
13
+ description "moves a file"
14
+
15
+ parameter :old_path, :string, description: "a path (e.g. `./old.rb`)"
16
+ parameter :new_path, :string, description: "a path (e.g. `./new.rb`)"
17
+
18
+ # @param old_path [String]
19
+ # @param new_path [String]
20
+ #
21
+ # @return [String]
22
+ def execute(old_path:, new_path:)
23
+ @logger.info("#{self.class.name}#execute old_path=#{old_path.inspect} new_path=#{new_path.inspect}")
24
+
25
+ FileUtils.mv(
26
+ resolve!(path: old_path),
27
+ resolve!(path: new_path)
28
+ )
29
+ rescue SecurityError => e
30
+ @logger.info("ERROR: #{e.message}")
31
+ raise e
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::FileReadTool.new(root: "./project")
8
+ # tool.execute(path: "./README.md") # => "..."
9
+ class FileReadTool < BaseTool
10
+ description "reads the contents of a file"
11
+
12
+ parameter :path, :string, description: "a path (e.g. `./main.rb`)"
13
+
14
+ # @param path [String]
15
+ #
16
+ # @return [String]
17
+ def execute(path:)
18
+ @logger.info("#{self.class.name}#execute path=#{path}")
19
+ File.read(resolve!(path:))
20
+ rescue StandardError => e
21
+ @logger.error(e.message)
22
+ raise e
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::FileReadTool.new(root: "./project")
8
+ # tool.execute(
9
+ # old_text: 'puts "ABC"',
10
+ # new_text: 'puts "DEF"',
11
+ # path: "README.md"
12
+ # )
13
+ class FileReplaceTool < BaseTool
14
+ description "replace a specific string in a file (old_text => new_text)"
15
+
16
+ parameter :old_text, :string, description: "the old text (e.g. `puts 'ABC'`)"
17
+ parameter :new_text, :string, description: "the new text (e.g. `puts 'DEF'`)"
18
+ parameter :path, :string, description: "a path (e.g. `./main.rb`)"
19
+
20
+ # @param path [String]
21
+ # @param old_text [String]
22
+ # @param new_text [String]
23
+ def execute(old_text:, new_text:, path:)
24
+ @logger.info %(#{self.class.name}#execute old_text="#{old_text}" new_text="#{new_text}" path="#{path}")
25
+
26
+ resolved = resolve!(path:)
27
+ contents = File.read(resolved)
28
+ modified = contents.gsub(old_text, new_text)
29
+ File.write(resolved, modified)
30
+ rescue StandardError => e
31
+ @logger.error(e.message)
32
+ raise e
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::FileWriteTool.new(root: "./project")
8
+ # tool.execute(path: "./README.md", text: "Hello World")
9
+ class FileWriteTool < BaseTool
10
+ description "Write the contents of a file."
11
+
12
+ parameter :path, :string, description: "a path for the file (e.g. `./main.rb`)"
13
+ parameter :text, :string, description: "the text to write to the file (e.g. `puts 'Hello World'`)"
14
+
15
+ # @param path [String]
16
+ # @param text [String]
17
+ #
18
+ # @return [String]
19
+ def execute(path:, text:)
20
+ @logger.info("#{self.class.name}#execute path=#{path}")
21
+ File.write(resolve!(path:), text)
22
+ rescue StandardError => e
23
+ @logger.error("ERROR: #{e.message}")
24
+ raise e
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ module Disk
6
+ # @example
7
+ # tool = OmniAI::Tools::Disk::SummaryTool.new(root: "./project")
8
+ # tool.execute
9
+ class SummaryTool < BaseTool
10
+ description "Summarize the contents (files and directories) of the project."
11
+
12
+ # @return [String]
13
+ def execute
14
+ @logger.info("#{self.class.name}#execute")
15
+
16
+ Dir.chdir(@root) do
17
+ summary = Dir.glob("**/*").map { |path| summarize(path:) }.join("\n")
18
+ @logger.debug(summary)
19
+ summary
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # @param path [String]
26
+ def summarize(path:)
27
+ if File.directory?(path)
28
+ "📁 ./#{path}/"
29
+ else
30
+ "📄 ./#{path} (#{File.size(path)} bytes)"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module OmniAI
6
+ module Tools
7
+ module Docker
8
+ # @example
9
+ # class ExampleTool < OmniAI::Tools::Disk::BaseTool
10
+ # description "..."
11
+ # end
12
+ class BaseTool < OmniAI::Tool
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,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module OmniAI
6
+ module Tools
7
+ module Docker
8
+ # @example
9
+ # tool = OmniAI::Tools::Docker::ComposeRunTool.new(root: "./project")
10
+ # tool.execute(service: "app", command: "rspec" args: ["spec/main_spec.rb"])
11
+ class ComposeRunTool < BaseTool
12
+ description "Run a command via docker with arguments on the project (e.g. `rspec spec/main_spec.rb`)."
13
+
14
+ parameter :service, :string, description: "the service to run the command against (e.g. `app`)"
15
+ parameter :command, :string, description: "the command to run (e.g. `rspec`)"
16
+ parameter :args, :array, description: "the arguments for the command",
17
+ items: OmniAI::Tool::Property.string(description: "an argument for the command (e.g. `spec/main_spec.rb`)")
18
+
19
+ # @param service [String]
20
+ # @param command [String]
21
+ # @param args [Array<String>]
22
+ #
23
+ # @return [String]
24
+ def execute(command:, service: "app", args: [])
25
+ @logger.info(%(#{self.class.name}#execute service="#{service}" command="#{command}" args=#{args.inspect}))
26
+
27
+ Dir.chdir(@root) do
28
+ capture!("docker", "compose", "run", "--build", "--rm", service, command, *args)
29
+ rescue CaptureError => e
30
+ @logger.info("ERROR: #{e.message}")
31
+ return "ERROR: #{e.message}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAI
4
+ module Tools
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ require "omniai"
5
+
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.push_dir(__dir__, namespace: OmniAI)
8
+ loader.setup
9
+
10
+ module OmniAI
11
+ module Tools
12
+ class Error < StandardError; end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniai-tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Sylvestre
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-04-22 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: omniai
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: zeitwerk
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: These examples can be used for inspiration or directly within an app.
41
+ email:
42
+ - kevin@ksylvest.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - Gemfile
48
+ - README.md
49
+ - bin/console
50
+ - bin/setup
51
+ - lib/omniai/tools.rb
52
+ - lib/omniai/tools/disk/base_tool.rb
53
+ - lib/omniai/tools/disk/directory_create_tool.rb
54
+ - lib/omniai/tools/disk/directory_delete_tool.rb
55
+ - lib/omniai/tools/disk/file_create_tool.rb
56
+ - lib/omniai/tools/disk/file_delete_tool.rb
57
+ - lib/omniai/tools/disk/file_move_tool.rb
58
+ - lib/omniai/tools/disk/file_read_tool.rb
59
+ - lib/omniai/tools/disk/file_replace_tool.rb
60
+ - lib/omniai/tools/disk/file_write_tool.rb
61
+ - lib/omniai/tools/disk/summary_tool.rb
62
+ - lib/omniai/tools/docker/base_tool.rb
63
+ - lib/omniai/tools/docker/compose_run_tool.rb
64
+ - lib/omniai/tools/version.rb
65
+ homepage: https://github.com/ksylvest/omniai-tools
66
+ licenses:
67
+ - MIT
68
+ metadata:
69
+ homepage_uri: https://github.com/ksylvest/omniai-tools
70
+ changelog_uri: https://github.com/ksylvest/omniai-tools/releases
71
+ rubygems_mfa_required: 'true'
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 3.2.0
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubygems_version: 3.6.6
87
+ specification_version: 4
88
+ summary: A set of tools built for usage with OmniAI.
89
+ test_files: []