sublayer 0.2.2 → 0.2.4
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/bin/sublayer +5 -0
- data/lib/sublayer/cli.rb +191 -0
- data/lib/sublayer/components/output_adapters/formattable.rb +31 -12
- data/lib/sublayer/components/output_adapters/list_of_named_strings.rb +48 -0
- data/lib/sublayer/components/output_adapters/named_strings.rb +33 -0
- data/lib/sublayer/generators/base.rb +4 -1
- data/lib/sublayer/templates/cli/.gitignore +14 -0
- data/lib/sublayer/templates/cli/Gemfile +7 -0
- data/lib/sublayer/templates/cli/PROJECT_NAME.gemspec +35 -0
- data/lib/sublayer/templates/cli/README.md +22 -0
- data/lib/sublayer/templates/cli/bin/PROJECT_NAME +5 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/actions/example_action.rb +15 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/agents/example_agent.rb +21 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/cli.rb +13 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/commands/base_command.rb +21 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/commands/example_command.rb +13 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/config/.keep +0 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/config.rb +19 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/generators/example_generator.rb +25 -0
- data/lib/sublayer/templates/cli/lib/PROJECT_NAME/version.rb +5 -0
- data/lib/sublayer/templates/cli/lib/project_name.rb +21 -0
- data/lib/sublayer/templates/cli/spec/.keep +0 -0
- data/lib/sublayer/templates/quick_script/README.md +16 -0
- data/lib/sublayer/templates/quick_script/actions/example_action.rb +11 -0
- data/lib/sublayer/templates/quick_script/agents/example_agent.rb +17 -0
- data/lib/sublayer/templates/quick_script/generators/example_generator.rb +21 -0
- data/lib/sublayer/templates/quick_script/project_name.rb +7 -0
- data/lib/sublayer/version.rb +1 -1
- data/lib/sublayer.rb +1 -0
- data/sublayer.gemspec +7 -0
- metadata +99 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2238a9858f994058b23edf70f97255d6c3b20d648c7e73991c05eb7232a9ec6b
|
4
|
+
data.tar.gz: 935f154d71e8dab6ee3906f700d1f42092f33b9e15ce07c3b305817bcca8177e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae8d296c701a18a7e4dde9366f05e4ecf8a5afc964cdc336a563201049660d92345f92219bd1537517bc961e6baf74bab4b9f4652d79666cdf2041e510be2451
|
7
|
+
data.tar.gz: af8c3a33325de5e2354cc82edd73212f91e5f24bf8c70ae2fac31b58d480ea1c08c76e938e60be714a5f67871940d497b21611daf1e576d6333bb52772e1e4cd
|
data/bin/sublayer
ADDED
data/lib/sublayer/cli.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require "tty-prompt"
|
2
|
+
require "tty-progressbar"
|
3
|
+
require "tty-command"
|
4
|
+
require "tty-file"
|
5
|
+
require "fileutils"
|
6
|
+
require "yaml"
|
7
|
+
require "sublayer/version"
|
8
|
+
|
9
|
+
module Sublayer
|
10
|
+
class CLI
|
11
|
+
PLACEHOLDERS = {
|
12
|
+
"PROJECT_NAME" => { gsub: true, camelcase: false },
|
13
|
+
"ProjectName" => { gsub: true, camelcase: true },
|
14
|
+
"project_name" => { gsub: true, camelcase: false, underscore: true }
|
15
|
+
}
|
16
|
+
|
17
|
+
def self.start(args)
|
18
|
+
new.run(args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(args)
|
22
|
+
command = args.shift
|
23
|
+
|
24
|
+
case command
|
25
|
+
when "new"
|
26
|
+
create_new_project(args.first)
|
27
|
+
when "help", nil
|
28
|
+
display_help
|
29
|
+
else
|
30
|
+
puts "Unknown command: #{command}"
|
31
|
+
display_help
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def create_new_project(project_name)
|
38
|
+
prompt = TTY::Prompt.new
|
39
|
+
|
40
|
+
project_name ||= prompt.ask("What is the name of your project?")
|
41
|
+
|
42
|
+
project_type = prompt.select("Select a project template: ", ["CLI", "Quick Script"])
|
43
|
+
ai_provider = prompt.select("Select an AI provider:", ["OpenAI", "Claude", "Gemini"])
|
44
|
+
|
45
|
+
ai_model = if ai_provider == "OpenAI"
|
46
|
+
prompt.select("Which OpenAI model would you like to use?", ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo"])
|
47
|
+
elsif ai_provider == "Claude"
|
48
|
+
prompt.select("Which Anthropic model would you like to use?", ["claude-3-5-sonnet-20240620", "claude-3-opus-20240229", "claude-3-haiku-20240307"])
|
49
|
+
elsif ai_provider == "Gemini"
|
50
|
+
prompt.select("Which Gemini model would you like to use?", ["gemini-1.5-pro-latest", "gemini-1.5-flash-latest"])
|
51
|
+
end
|
52
|
+
|
53
|
+
create_project(project_name, project_type, ai_provider, ai_model)
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_project(project_name, project_type, ai_provider, ai_model)
|
57
|
+
project_path = File.join(Dir.pwd, project_name)
|
58
|
+
|
59
|
+
progress_bar = TTY::ProgressBar.new("Creating project [:bar] :percent", total: 8)
|
60
|
+
|
61
|
+
progress_bar.advance(1, log: "Creating project directory")
|
62
|
+
FileUtils.mkdir_p(project_path)
|
63
|
+
|
64
|
+
progress_bar.advance(1, log: "Copying template files")
|
65
|
+
copy_template_files(project_path, project_type)
|
66
|
+
|
67
|
+
progress_bar.advance(1, log: "Replacing placeholders")
|
68
|
+
replace_placeholders(project_path, project_name)
|
69
|
+
|
70
|
+
progress_bar.advance(1, log: "Generating configuration")
|
71
|
+
generate_config_file(project_path, project_name, project_type, ai_provider, ai_model)
|
72
|
+
|
73
|
+
progress_bar.advance(1, log: "Finalizing project")
|
74
|
+
finalize_project(project_path, project_type)
|
75
|
+
|
76
|
+
puts "\nSublayer project '#{project_name}' created successfully!"
|
77
|
+
end
|
78
|
+
|
79
|
+
def copy_template_files(project_path, project_type)
|
80
|
+
template_dir = project_type == "CLI" ? "cli" : "quick_script"
|
81
|
+
source_path = File.join(File.dirname(__FILE__), "templates", template_dir)
|
82
|
+
FileUtils.cp_r("#{source_path}/.", project_path)
|
83
|
+
|
84
|
+
FileUtils.mkdir_p(File.join(project_path, "log")) if should_add_log_folder(project_type)
|
85
|
+
end
|
86
|
+
|
87
|
+
def should_add_log_folder(project_type)
|
88
|
+
project_type == "CLI"
|
89
|
+
end
|
90
|
+
|
91
|
+
def generate_config_file(project_path, project_name, project_type, ai_provider, ai_model)
|
92
|
+
config = {
|
93
|
+
project_name: project_name,
|
94
|
+
project_type: project_type,
|
95
|
+
ai_provider: ai_provider,
|
96
|
+
ai_model: ai_model
|
97
|
+
}
|
98
|
+
|
99
|
+
TTY::File.create_file(File.join(project_path, "lib", project_name, "config", "sublayer.yml"), YAML.dump(config)) if project_type == "CLI"
|
100
|
+
|
101
|
+
if project_type == "Quick Script"
|
102
|
+
config_lines = <<~CONFIG
|
103
|
+
Sublayer.configuration.ai_provider = Sublayer::Providers::#{config[:ai_provider]}
|
104
|
+
Sublayer.configuration.ai_model = "#{config[:ai_model]}"
|
105
|
+
CONFIG
|
106
|
+
project_file = File.join(project_path, "#{project_name}.rb")
|
107
|
+
File.write(project_file, File.read(project_file) + config_lines)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def project_type_instructions(project_type)
|
112
|
+
if project_type == "CLI"
|
113
|
+
"Run your CLI application:\n ```\n ruby bin/#{File.basename(project_path)}\n ```"
|
114
|
+
else
|
115
|
+
"Start your web server:\n ```\n ruby app.rb\n ```\n Then visit http://localhost:4567 in your browser."
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def replace_placeholders(project_path, project_name)
|
120
|
+
# First rename the lib/PROJECT_NAME directory to lib/project_name
|
121
|
+
FileUtils.mv(File.join(project_path, "lib", "PROJECT_NAME"), File.join(project_path, "lib", project_name.gsub("-", "_").downcase)) if File.directory?(File.join(project_path, "lib", "PROJECT_NAME"))
|
122
|
+
|
123
|
+
# Then walk through each file in the project and replace the placeholder content and filenames
|
124
|
+
Dir.glob("#{project_path}/**/*", File::FNM_DOTMATCH).each do |file_path|
|
125
|
+
next if File.directory?(file_path)
|
126
|
+
next if file_path.include?(".git/")
|
127
|
+
|
128
|
+
content = File.read(file_path)
|
129
|
+
PLACEHOLDERS.each do |placeholder, options|
|
130
|
+
replacement = if options[:camelcase]
|
131
|
+
project_name.split(/[_-]/).map(&:capitalize).join
|
132
|
+
elsif options[:underscore]
|
133
|
+
project_name.gsub("-", "_").downcase
|
134
|
+
else
|
135
|
+
project_name
|
136
|
+
end
|
137
|
+
content.gsub!(placeholder, replacement) if options[:gsub]
|
138
|
+
end
|
139
|
+
|
140
|
+
File.write(file_path, content)
|
141
|
+
|
142
|
+
|
143
|
+
if file_path.include?('PROJECT_NAME')
|
144
|
+
new_path = file_path.gsub("PROJECT_NAME", project_name.gsub("-", "_").downcase)
|
145
|
+
|
146
|
+
FileUtils.mkdir_p(File.dirname(new_path))
|
147
|
+
FileUtils.mv(file_path, new_path)
|
148
|
+
end
|
149
|
+
|
150
|
+
if file_path.include?('project_name')
|
151
|
+
new_path = file_path.gsub("project_name", project_name.gsub("-", "_").downcase)
|
152
|
+
|
153
|
+
FileUtils.mkdir_p(File.dirname(new_path))
|
154
|
+
FileUtils.mv(file_path, new_path)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# replace the sublayer version in the gemspec file with Sublayer::VERSION
|
159
|
+
if File.exist?(File.join(project_path, "#{project_name}.gemspec"))
|
160
|
+
gemspec_path = File.join(project_path, "#{project_name}.gemspec")
|
161
|
+
gemspec_content = File.read(gemspec_path)
|
162
|
+
gemspec_content.gsub!("SUBLAYER_VERSION", Sublayer::VERSION)
|
163
|
+
File.write(gemspec_path, gemspec_content)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def finalize_project(project_path, project_type)
|
168
|
+
cmd = TTY::Command.new(printer: :null)
|
169
|
+
|
170
|
+
if TTY::Prompt.new.yes?("Initialize a git repository?")
|
171
|
+
cmd.run("git init", chdir: project_path)
|
172
|
+
end
|
173
|
+
|
174
|
+
if TTY::Prompt.new.yes?("Install dependencies now?")
|
175
|
+
cmd.run("bundle install", chdir: project_path)
|
176
|
+
end
|
177
|
+
|
178
|
+
puts "To get started, run:"
|
179
|
+
puts " cd #{File.basename(project_path)}"
|
180
|
+
puts " ./bin/#{File.basename(project_path)}" if project_type == "CLI"
|
181
|
+
puts " ruby #{File.basename(project_path)}.rb" if project_type == "Quick Script"
|
182
|
+
end
|
183
|
+
|
184
|
+
def display_help
|
185
|
+
puts "Usage: sublayer [command] [arguments]"
|
186
|
+
puts "Commands:"
|
187
|
+
puts " new PROJECT_NAME Create a new Sublayer project"
|
188
|
+
puts " help Display this help message"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -3,23 +3,42 @@ module Sublayer
|
|
3
3
|
module OutputAdapters
|
4
4
|
module Formattable
|
5
5
|
def format_properties
|
6
|
+
build_json_schema(self.properties)
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_json_schema(props)
|
6
10
|
formatted_properties = {}
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
}
|
12
|
-
|
13
|
-
property[:enum] = prop.enum if prop.respond_to?(:enum) && prop.enum
|
14
|
-
property[:default] = prop.default if prop.respond_to?(:default) && !prop.default.nil?
|
15
|
-
property[:minimum] = prop.minimum if prop.respond_to?(:minimum) && !prop.minimum.nil?
|
16
|
-
property[:maximum] = prop.maximum if prop.respond_to?(:maximum) && !prop.maximum.nil?
|
17
|
-
property[:items] = prop.items if prop.respond_to?(:items) && prop.items
|
18
|
-
formatted_properties[prop.name.to_sym] = property
|
11
|
+
|
12
|
+
props.map do |prop|
|
13
|
+
formatted_property = format_property(prop)
|
14
|
+
formatted_properties[prop.name.to_sym] = formatted_property
|
19
15
|
end
|
16
|
+
|
20
17
|
formatted_properties
|
21
18
|
end
|
22
19
|
|
20
|
+
def format_property(property)
|
21
|
+
result = {
|
22
|
+
type: property.type,
|
23
|
+
description: property.description
|
24
|
+
}
|
25
|
+
|
26
|
+
result[:enum] = property.enum if property.respond_to?(:enum) && property.enum
|
27
|
+
result[:default] = property.default if property.respond_to?(:default) && !property.default.nil?
|
28
|
+
result[:minimum] = property.minimum if property.respond_to?(:minimum) && !property.minimum.nil?
|
29
|
+
result[:maximum] = property.maximum if property.respond_to?(:maximum) && !property.maximum.nil?
|
30
|
+
|
31
|
+
case property.type
|
32
|
+
when 'array'
|
33
|
+
result[:items] = property.items.is_a?(OpenStruct) ? format_property(property.items) : property.items
|
34
|
+
when 'object'
|
35
|
+
result[:properties] = build_json_schema(property.properties) if property.properties
|
36
|
+
result[:required] = property.properties.select(&:required).map(&:name) if property.properties
|
37
|
+
end
|
38
|
+
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
23
42
|
def format_required
|
24
43
|
self.properties.select(&:required).map(&:name)
|
25
44
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Sublayer
|
2
|
+
module Components
|
3
|
+
module OutputAdapters
|
4
|
+
class ListOfNamedStrings
|
5
|
+
attr_reader :name, :description, :attributes, :item_name
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@name = options[:name]
|
9
|
+
@item_name = options[:item_name]
|
10
|
+
@description = options[:description]
|
11
|
+
@attributes = options[:attributes]
|
12
|
+
end
|
13
|
+
|
14
|
+
def properties
|
15
|
+
[
|
16
|
+
OpenStruct.new(
|
17
|
+
name: @name,
|
18
|
+
type: "array",
|
19
|
+
description: @description,
|
20
|
+
required: true,
|
21
|
+
items: OpenStruct.new(
|
22
|
+
type: "object",
|
23
|
+
description: "a single #{@item_name}",
|
24
|
+
name: @item_name,
|
25
|
+
properties: @attributes.map do |attribute|
|
26
|
+
OpenStruct.new(
|
27
|
+
type: "string",
|
28
|
+
name: attribute[:name],
|
29
|
+
description: attribute[:description],
|
30
|
+
required: true
|
31
|
+
)
|
32
|
+
end
|
33
|
+
)
|
34
|
+
)
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
def materialize_result(raw_results)
|
39
|
+
raw_results.map do |raw_result|
|
40
|
+
OpenStruct.new(
|
41
|
+
@attributes.map { |attribute| [attribute[:name], raw_result[attribute[:name]]] }.to_h
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sublayer
|
2
|
+
module Components
|
3
|
+
module OutputAdapters
|
4
|
+
class NamedStrings
|
5
|
+
attr_reader :name, :description, :attributes
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@name = options[:name]
|
9
|
+
@description = options[:description]
|
10
|
+
@attributes = options[:attributes]
|
11
|
+
end
|
12
|
+
|
13
|
+
def properties
|
14
|
+
[
|
15
|
+
OpenStruct.new(
|
16
|
+
name: @name,
|
17
|
+
type: "object",
|
18
|
+
description: @description,
|
19
|
+
required: true,
|
20
|
+
properties: @attributes.map { |attribute|
|
21
|
+
OpenStruct.new(type: "string", description: attribute[:description], required: true, name: attribute[:name])
|
22
|
+
}
|
23
|
+
)
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
def materialize_result(raw_result)
|
28
|
+
OpenStruct.new( @attributes.map { |attribute| [attribute[:name], raw_result[attribute[:name]]] }.to_h)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -10,7 +10,10 @@ module Sublayer
|
|
10
10
|
|
11
11
|
def generate
|
12
12
|
self.class::OUTPUT_ADAPTER.load_instance_data(self) if self.class::OUTPUT_ADAPTER.respond_to?(:load_instance_data)
|
13
|
-
|
13
|
+
|
14
|
+
raw_results = Sublayer.configuration.ai_provider.call(prompt: prompt, output_adapter: self.class::OUTPUT_ADAPTER)
|
15
|
+
|
16
|
+
@results = self.class::OUTPUT_ADAPTER.respond_to?(:materialize_result) ? self.class::OUTPUT_ADAPTER.materialize_result(raw_results) : raw_results
|
14
17
|
end
|
15
18
|
end
|
16
19
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "lib/PROJECT_NAME/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "PROJECT_NAME"
|
5
|
+
spec.version = ProjectName::VERSION
|
6
|
+
spec.authors = ["Your Name"]
|
7
|
+
spec.email = ["your.email@example.com"]
|
8
|
+
|
9
|
+
spec.summary = "Summary of your project"
|
10
|
+
spec.description = "Longer description of your project"
|
11
|
+
spec.homepage = "https://github.com/yourusername/PROJECT_NAME"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = ">= 2.6.0"
|
14
|
+
|
15
|
+
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/yourusername/PROJECT_NAME"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/yourusername/PROJECT_NAME/blob/master/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "bin"
|
29
|
+
spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
# Add dependencies here
|
33
|
+
spec.add_dependency "sublayer", "~> SUBLAYER_VERSION"
|
34
|
+
spec.add_dependency "thor", "~> 1.2"
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# ProjectName
|
2
|
+
|
3
|
+
Welcome to your new Sublayer CLI project!
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Execute:
|
8
|
+
|
9
|
+
$ bundle install
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
To run your CLI application:
|
14
|
+
|
15
|
+
```
|
16
|
+
$ bin/PROJECT_NAME
|
17
|
+
```
|
18
|
+
|
19
|
+
Available commands:
|
20
|
+
- `example`: Run the example generator
|
21
|
+
- `help`: Display the help message
|
22
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProjectName
|
4
|
+
module Actions
|
5
|
+
class ExampleAction < Sublayer::Actions::Base
|
6
|
+
def initialize(input:)
|
7
|
+
@input = input
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
puts "Performing action with input: #{@input}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProjectName
|
4
|
+
module Agents
|
5
|
+
class ExampleAgent < Sublayer::Agents::Base
|
6
|
+
trigger_on_files_changed { ["example_file.txt"] }
|
7
|
+
|
8
|
+
goal_condition { @goal_reached }
|
9
|
+
|
10
|
+
check_status do
|
11
|
+
@status_checked = true
|
12
|
+
end
|
13
|
+
|
14
|
+
step do
|
15
|
+
@step_taken = true
|
16
|
+
@goal_reached = true
|
17
|
+
puts "Example agent step executed"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProjectName
|
4
|
+
class CLI < Thor
|
5
|
+
ProjectName::Commands.constants.reject{ |command_class| command_class == :BaseCommand }.each do |command_class|
|
6
|
+
command = ProjectName::Commands.const_get(command_class)
|
7
|
+
desc command.command_name, command.description
|
8
|
+
define_method(command.command_name) do |*args|
|
9
|
+
command.new(options).execute(*args)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ProjectName
|
2
|
+
module Commands
|
3
|
+
class BaseCommand
|
4
|
+
def self.command_name
|
5
|
+
name.split("::").last.gsub(/Command$/, '').downcase
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.description
|
9
|
+
"Description for #{command_name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(*args)
|
17
|
+
raise NotImplementedError, "#{self.class} must implement #execute"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ProjectName
|
2
|
+
module Commands
|
3
|
+
class ExampleCommand < BaseCommand
|
4
|
+
def self.description
|
5
|
+
"An example command that generates a story based on the command line arguments."
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute(*args)
|
9
|
+
puts ProjectName::Generators::ExampleGenerator.new(input: args.join(" ")).generate
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ProjectName
|
2
|
+
module Config
|
3
|
+
def self.load
|
4
|
+
config_path = File.join(File.dirname(__FILE__), "config", "sublayer.yml")
|
5
|
+
|
6
|
+
if File.exist?(config_path)
|
7
|
+
config = YAML.load_file(config_path)
|
8
|
+
|
9
|
+
Sublayer.configure do |c|
|
10
|
+
c.ai_provider = Object.const_get("Sublayer::Providers::#{config[:ai_provider]}")
|
11
|
+
c.ai_model = config[:ai_model]
|
12
|
+
c.logger = Sublayer::Logging::JsonLogger.new(File.join(Dir.pwd, 'log', 'sublayer.log'))
|
13
|
+
end
|
14
|
+
else
|
15
|
+
puts "Warning: config/sublayer.yml not found. Using default configuration."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProjectName
|
4
|
+
module Generators
|
5
|
+
class ExampleGenerator < Sublayer::Generators::Base
|
6
|
+
llm_output_adapter type: :single_string,
|
7
|
+
name: "generated_text",
|
8
|
+
description: "A simple generated text"
|
9
|
+
|
10
|
+
def initialize(input:)
|
11
|
+
@input = input
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def prompt
|
19
|
+
<<-PROMPT
|
20
|
+
Generate a simple story based on this input: #{@input}
|
21
|
+
PROMPT
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "thor"
|
3
|
+
require "sublayer"
|
4
|
+
require_relative "PROJECT_NAME/version"
|
5
|
+
require_relative "PROJECT_NAME/config"
|
6
|
+
|
7
|
+
Dir[File.join(__dir__, "PROJECT_NAME", "commands", "*.rb")].each { |file| require file }
|
8
|
+
Dir[File.join(__dir__, "PROJECT_NAME", "generators", "*.rb")].each { |file| require file }
|
9
|
+
Dir[File.join(__dir__, "PROJECT_NAME", "actions", "*.rb")].each { |file| require file }
|
10
|
+
Dir[File.join(__dir__, "PROJECT_NAME", "agents", "*.rb")].each { |file| require file }
|
11
|
+
|
12
|
+
require_relative "PROJECT_NAME/cli"
|
13
|
+
|
14
|
+
module ProjectName
|
15
|
+
class Error < StandardError; end
|
16
|
+
Config.load
|
17
|
+
|
18
|
+
def self.root
|
19
|
+
File.dirname __dir__
|
20
|
+
end
|
21
|
+
end
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# ProjectName
|
2
|
+
|
3
|
+
Welcome to your new Sublayer quick script project!
|
4
|
+
|
5
|
+
There are example Agents, Generators, and Actions in the respective folders.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Create your own custom agents, generators, and actions and use them in
|
10
|
+
`project_name.rb`
|
11
|
+
|
12
|
+
Run your script:
|
13
|
+
|
14
|
+
```
|
15
|
+
$ ruby project_name.rb
|
16
|
+
```
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ExampleAgent < Sublayer::Agents::Base
|
4
|
+
trigger_on_files_changed { ["example_file.txt"] }
|
5
|
+
|
6
|
+
goal_condition { @goal_reached }
|
7
|
+
|
8
|
+
check_status do
|
9
|
+
@status_checked = true
|
10
|
+
end
|
11
|
+
|
12
|
+
step do
|
13
|
+
@step_taken = true
|
14
|
+
@goal_reached = true
|
15
|
+
puts "Example agent step executed"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ExampleGenerator < Sublayer::Generators::Base
|
4
|
+
llm_output_adapter type: :single_string,
|
5
|
+
name: "generated_text",
|
6
|
+
description: "A simple generated text"
|
7
|
+
|
8
|
+
def initialize(input:)
|
9
|
+
@input = input
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def prompt
|
17
|
+
<<-PROMPT
|
18
|
+
Generate a simple story based on this input: #{@input}
|
19
|
+
PROMPT
|
20
|
+
end
|
21
|
+
end
|
data/lib/sublayer/version.rb
CHANGED
data/lib/sublayer.rb
CHANGED
data/sublayer.gemspec
CHANGED
@@ -29,6 +29,8 @@ Gem::Specification.new do |spec|
|
|
29
29
|
end
|
30
30
|
|
31
31
|
spec.require_paths = ["lib"]
|
32
|
+
spec.bindir = "bin"
|
33
|
+
spec.executables = ["sublayer"]
|
32
34
|
|
33
35
|
spec.add_dependency "ruby-openai"
|
34
36
|
spec.add_dependency "colorize"
|
@@ -37,6 +39,11 @@ Gem::Specification.new do |spec|
|
|
37
39
|
spec.add_dependency "nokogiri", "~> 1.16.5"
|
38
40
|
spec.add_dependency "httparty"
|
39
41
|
spec.add_dependency "listen"
|
42
|
+
spec.add_dependency "tty-prompt", "~> 0.23"
|
43
|
+
spec.add_dependency "tty-progressbar", "~> 0.18"
|
44
|
+
spec.add_dependency "tty-command", "~> 0.10"
|
45
|
+
spec.add_dependency "tty-file", "~> 0.10"
|
46
|
+
spec.add_dependency "thor"
|
40
47
|
|
41
48
|
spec.add_development_dependency "rspec", "~> 3.12"
|
42
49
|
spec.add_development_dependency "pry", "~> 0.14"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sublayer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Werner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-openai
|
@@ -108,6 +108,76 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: tty-prompt
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.23'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.23'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: tty-progressbar
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.18'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.18'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: tty-command
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.10'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.10'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: tty-file
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0.10'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0.10'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: thor
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
111
181
|
- !ruby/object:Gem::Dependency
|
112
182
|
name: rspec
|
113
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -182,19 +252,24 @@ description: A DSL and framework for building AI powered applications through th
|
|
182
252
|
use of Generators, Actions, Tasks, and Agents
|
183
253
|
email:
|
184
254
|
- scott@sublayer.com
|
185
|
-
executables:
|
255
|
+
executables:
|
256
|
+
- sublayer
|
186
257
|
extensions: []
|
187
258
|
extra_rdoc_files: []
|
188
259
|
files:
|
189
260
|
- LICENSE
|
190
261
|
- README.md
|
191
262
|
- Rakefile
|
263
|
+
- bin/sublayer
|
192
264
|
- lib/sublayer.rb
|
193
265
|
- lib/sublayer/actions/base.rb
|
194
266
|
- lib/sublayer/agents/base.rb
|
267
|
+
- lib/sublayer/cli.rb
|
195
268
|
- lib/sublayer/components/output_adapters.rb
|
196
269
|
- lib/sublayer/components/output_adapters/formattable.rb
|
270
|
+
- lib/sublayer/components/output_adapters/list_of_named_strings.rb
|
197
271
|
- lib/sublayer/components/output_adapters/list_of_strings.rb
|
272
|
+
- lib/sublayer/components/output_adapters/named_strings.rb
|
198
273
|
- lib/sublayer/components/output_adapters/single_string.rb
|
199
274
|
- lib/sublayer/components/output_adapters/string_selection_from_list.rb
|
200
275
|
- lib/sublayer/generators/base.rb
|
@@ -206,6 +281,27 @@ files:
|
|
206
281
|
- lib/sublayer/providers/gemini.rb
|
207
282
|
- lib/sublayer/providers/open_ai.rb
|
208
283
|
- lib/sublayer/tasks/base.rb
|
284
|
+
- lib/sublayer/templates/cli/.gitignore
|
285
|
+
- lib/sublayer/templates/cli/Gemfile
|
286
|
+
- lib/sublayer/templates/cli/PROJECT_NAME.gemspec
|
287
|
+
- lib/sublayer/templates/cli/README.md
|
288
|
+
- lib/sublayer/templates/cli/bin/PROJECT_NAME
|
289
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/actions/example_action.rb
|
290
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/agents/example_agent.rb
|
291
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/cli.rb
|
292
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/commands/base_command.rb
|
293
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/commands/example_command.rb
|
294
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/config.rb
|
295
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/config/.keep
|
296
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/generators/example_generator.rb
|
297
|
+
- lib/sublayer/templates/cli/lib/PROJECT_NAME/version.rb
|
298
|
+
- lib/sublayer/templates/cli/lib/project_name.rb
|
299
|
+
- lib/sublayer/templates/cli/spec/.keep
|
300
|
+
- lib/sublayer/templates/quick_script/README.md
|
301
|
+
- lib/sublayer/templates/quick_script/actions/example_action.rb
|
302
|
+
- lib/sublayer/templates/quick_script/agents/example_agent.rb
|
303
|
+
- lib/sublayer/templates/quick_script/generators/example_generator.rb
|
304
|
+
- lib/sublayer/templates/quick_script/project_name.rb
|
209
305
|
- lib/sublayer/triggers/base.rb
|
210
306
|
- lib/sublayer/triggers/file_change.rb
|
211
307
|
- lib/sublayer/version.rb
|