aigency 0.1.0 → 0.2.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 +11 -0
- data/lib/aigency/aigent.rb +12 -8
- data/lib/aigency/version.rb +1 -1
- data/lib/aigency/workspace/directory_deleter.rb +8 -9
- data/lib/aigency/workspace/directory_reader.rb +7 -8
- data/lib/aigency/workspace/file_deleter.rb +6 -8
- data/lib/aigency/workspace/file_reader.rb +11 -17
- data/lib/aigency/workspace/file_writer.rb +6 -5
- data/lib/aigency/workspace/helpers.rb +24 -0
- data/lib/aigency/workspace.rb +4 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b9b5b24d49d87f69a17cec9420b4815db4646b37d8759951e4ced8431765800
|
|
4
|
+
data.tar.gz: 04ecc1ddb1433cfcd2c18b7c3af420df7dd52a417ef6ab90a2a3de35adb2b99b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ab68e574725ebb40bad60ff6e807e480733c91f406d75c8e45b6678e16952e0cd67dde124df13ef9036ef82437bcdae97a4182169cbc615b30116f29c89667c
|
|
7
|
+
data.tar.gz: 6a4e0866fc61258b3fee11e76baeadc72ade5d5d66b87f4d8d41ad2127ac27a50a5f7ef394d0d940e03d7cc389e567f4d7e814ebb1792ef56c125904a75c4dcc
|
data/CHANGELOG.md
ADDED
data/lib/aigency/aigent.rb
CHANGED
|
@@ -38,26 +38,30 @@ module Aigency
|
|
|
38
38
|
# Initialize a RubyLLM::Chat.
|
|
39
39
|
#
|
|
40
40
|
# @param extra [Array<String>] Additional instructions
|
|
41
|
+
# @param tools [Array<Class<Tool>>] Available tools (default is all registered tools)
|
|
41
42
|
# @return [RubyLLM::Chat]
|
|
42
|
-
def brief(*extra)
|
|
43
|
+
def brief(*extra, tools: Tools.registry)
|
|
43
44
|
chat = rubyllm_context.chat(provider: :openai)
|
|
44
|
-
|
|
45
|
+
tools.each { |klass| chat.with_tool(klass.new(self)) }
|
|
45
46
|
chat.with_instructions(build_instructions(extra), append: true)
|
|
46
|
-
# @todo This should be configurable
|
|
47
47
|
chat.with_params(max_tokens: max_tokens)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def load filename, *extra, out: $stdout
|
|
51
|
-
stream File.read(filename), *extra, out: out
|
|
50
|
+
def load filename, *extra, tools: Tools.registry, out: $stdout
|
|
51
|
+
stream File.read(filename), *extra, tools: tools, out: out
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def stream text, *extra, with: [], out: $stdout
|
|
54
|
+
def stream text, *extra, with: [], tools: Tools.registry, out: $stdout
|
|
55
55
|
original_log = @log
|
|
56
56
|
@log = out
|
|
57
|
-
brief(*extra).ask text, with: with do |chunk|
|
|
57
|
+
response = brief(*extra, tools: tools).ask text, with: with do |chunk|
|
|
58
58
|
@log.print chunk.thinking&.text
|
|
59
59
|
@log.print chunk.content
|
|
60
|
-
end
|
|
60
|
+
end
|
|
61
|
+
@log.puts "Input tokens: #{response.tokens.input}"
|
|
62
|
+
@log.puts "Output tokens: #{response.tokens.output}"
|
|
63
|
+
@log = original_log
|
|
64
|
+
response
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
# @param name [String]
|
data/lib/aigency/version.rb
CHANGED
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
module Aigency
|
|
4
4
|
module Workspace
|
|
5
5
|
class DirectoryDeleter < Tool
|
|
6
|
-
|
|
6
|
+
include Helpers
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
puts "Deleting directory #{dirname}"
|
|
8
|
+
desc "Delete a directory and all of its contents from your private workspace"
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return { dirname: dirname, success: false, error: "Permission denied" } if resolved.include?('/.git/') || resolved.end_with?('/.git')
|
|
14
|
-
return { dirname: dirname, success: false, error: "Not a directory" } if File.file?(resolved)
|
|
15
|
-
return { dirname: dirname, sucess: false, error: "Directory not found" } unless File.directory?(resolved)
|
|
10
|
+
def execute(directory_name:)
|
|
11
|
+
aigent.log.puts "[Workspace::DirectoryDeleter] #{directory_name}"
|
|
16
12
|
|
|
13
|
+
resolved = expand_to_workspace(directory_name)
|
|
17
14
|
FileUtils.rm_rf(resolved)
|
|
18
|
-
{ dirname:
|
|
15
|
+
{ dirname: directory_name, success: true }
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
{ success: false, error: "[#{e.class}] #{e.message}"}
|
|
19
18
|
end
|
|
20
19
|
end
|
|
21
20
|
end
|
|
@@ -3,21 +3,18 @@
|
|
|
3
3
|
module Aigency
|
|
4
4
|
module Workspace
|
|
5
5
|
class DirectoryReader < Tool
|
|
6
|
-
|
|
6
|
+
include Helpers
|
|
7
|
+
|
|
8
|
+
desc "Get the contents of a directory from your private workspace"
|
|
7
9
|
|
|
8
10
|
def execute(directory_name:)
|
|
9
11
|
aigent.log.puts "[Workspace::DirectoryReader] Reading #{directory_name}"
|
|
10
|
-
|
|
11
|
-
resolved = File.absolute_path(directory_name, directory)
|
|
12
|
-
return { directory: directory_name,
|
|
13
|
-
success: false,
|
|
14
|
-
error: "Permission denied. Path must be local to #{directory}" } unless resolved.start_with?(directory)
|
|
15
|
-
|
|
12
|
+
resolved = expand_to_workspace(directory_name)
|
|
16
13
|
if File.directory?(resolved)
|
|
17
14
|
{
|
|
18
15
|
directory: directory_name,
|
|
19
16
|
success: true,
|
|
20
|
-
entries: Dir
|
|
17
|
+
entries: Dir.entries(resolved)[2..]
|
|
21
18
|
}
|
|
22
19
|
else
|
|
23
20
|
{
|
|
@@ -26,6 +23,8 @@ module Aigency
|
|
|
26
23
|
error: "Not a directory"
|
|
27
24
|
}
|
|
28
25
|
end
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
{ success: false, error: "[#{e.class}] #{e.message}" }
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
30
|
end
|
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
module Aigency
|
|
4
4
|
module Workspace
|
|
5
5
|
class FileDeleter < Tool
|
|
6
|
-
|
|
6
|
+
include Helpers
|
|
7
|
+
|
|
8
|
+
desc "Delete a file from your private workspace"
|
|
7
9
|
|
|
8
10
|
def execute(filename:)
|
|
9
11
|
aigent.log.puts "[Workspace::FileDeleter] Deleting file #{filename}"
|
|
10
|
-
|
|
11
|
-
directory = aigent.data('directory')
|
|
12
|
-
resolved = File.absolute_path(filename, directory)
|
|
13
|
-
return { filename: filename, error: "Permission denied" } unless resolved.start_with?(directory)
|
|
14
|
-
return { filename: filename, error: "Permission denied" } if resolved.include?('/.git/')
|
|
15
|
-
return { filename: filename, error: "File does not exist" } unless File.file?(resolved)
|
|
16
|
-
|
|
12
|
+
resolved = expand_to_workspace(filename)
|
|
17
13
|
File.delete resolved
|
|
18
14
|
{ filename: filename, success: true }
|
|
15
|
+
rescue StandardError => e
|
|
16
|
+
{ success: false, error: "[#{e.class}] #{e.message}" }
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
end
|
|
@@ -3,28 +3,22 @@
|
|
|
3
3
|
module Aigency
|
|
4
4
|
module Workspace
|
|
5
5
|
class FileReader < Tool
|
|
6
|
-
|
|
6
|
+
include Helpers
|
|
7
|
+
|
|
8
|
+
desc "Read a file from your private workspace"
|
|
7
9
|
|
|
8
10
|
def execute(filename:)
|
|
9
11
|
aigent.log.puts "[Workspace::FileReader] Reading #{filename}"
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
resolved = File.absolute_path(filename, directory)
|
|
13
|
-
return { filename: filename, success: false, error: "Permission denied" } unless resolved.start_with?(directory)
|
|
13
|
+
resolved = expand_to_workspace(filename)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
filename: filename,
|
|
24
|
-
success: false,
|
|
25
|
-
error: "File not found"
|
|
26
|
-
}
|
|
27
|
-
end
|
|
15
|
+
{
|
|
16
|
+
success: true,
|
|
17
|
+
filename: filename,
|
|
18
|
+
content: File.read(resolved)
|
|
19
|
+
}
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
{ success: false, error: "[#{e.class}] #{e.message}"}
|
|
28
22
|
end
|
|
29
23
|
end
|
|
30
24
|
end
|
|
@@ -5,18 +5,19 @@ require 'fileutils'
|
|
|
5
5
|
module Aigency
|
|
6
6
|
module Workspace
|
|
7
7
|
class FileWriter < Tool
|
|
8
|
-
|
|
8
|
+
include Helpers
|
|
9
|
+
|
|
10
|
+
desc "Write a file to your private workspace"
|
|
9
11
|
|
|
10
12
|
def execute(filename:, content:)
|
|
11
13
|
aigent.log.puts "[Workspace::FileWriter] Writing #{filename}"
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
resolved = File.absolute_path(filename, directory)
|
|
15
|
-
return { filename: filename, success: false, error: "Permission denied" } unless resolved.start_with?(directory)
|
|
16
|
-
|
|
15
|
+
resolved = expand_to_workspace(filename)
|
|
17
16
|
FileUtils.mkdir_p File.dirname(resolved)
|
|
18
17
|
File.write resolved, content
|
|
19
18
|
return { filename: filename, success: true }
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
{ success: false, error: "[#{e.class}] #{e.message}" }
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aigency
|
|
4
|
+
module Workspace
|
|
5
|
+
module Helpers
|
|
6
|
+
# @!method aigent
|
|
7
|
+
# @abstract
|
|
8
|
+
# @return [Aigency::Aigent]
|
|
9
|
+
|
|
10
|
+
# Expand a path relative to the aigent's workspace directory.
|
|
11
|
+
#
|
|
12
|
+
# @raise [PathError] if the expanded path is not inside the workspace
|
|
13
|
+
# @param path [String]
|
|
14
|
+
# @return [String]
|
|
15
|
+
def expand_to_workspace(path)
|
|
16
|
+
workspace = aigent.data('workspace')
|
|
17
|
+
resolved = File.expand_path(path, workspace)
|
|
18
|
+
return resolved if resolved == workspace || resolved.start_with?("#{workspace}/")
|
|
19
|
+
|
|
20
|
+
raise PathError, "Permission denied: `#{path}` is not relative to your root workspace"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/aigency/workspace.rb
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Aigency
|
|
4
4
|
module Workspace
|
|
5
|
+
require 'aigency/workspace/helpers'
|
|
6
|
+
require 'aigency/workspace/directory_deleter'
|
|
5
7
|
require 'aigency/workspace/directory_reader'
|
|
6
8
|
require 'aigency/workspace/file_reader'
|
|
7
9
|
require 'aigency/workspace/file_writer'
|
|
10
|
+
|
|
11
|
+
class PathError < RuntimeError; end
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
Tools.mkdir_p 'data/workspace'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aigency
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fred Snyder
|
|
@@ -58,6 +58,7 @@ executables:
|
|
|
58
58
|
extensions: []
|
|
59
59
|
extra_rdoc_files: []
|
|
60
60
|
files:
|
|
61
|
+
- CHANGELOG.md
|
|
61
62
|
- README.md
|
|
62
63
|
- Rakefile
|
|
63
64
|
- exe/aigency
|
|
@@ -74,6 +75,7 @@ files:
|
|
|
74
75
|
- lib/aigency/workspace/file_deleter.rb
|
|
75
76
|
- lib/aigency/workspace/file_reader.rb
|
|
76
77
|
- lib/aigency/workspace/file_writer.rb
|
|
78
|
+
- lib/aigency/workspace/helpers.rb
|
|
77
79
|
- scaffold/.gitignore
|
|
78
80
|
- scaffold/Gemfile
|
|
79
81
|
- scaffold/IDENTITY.md
|