language-operator 0.1.31 → 0.1.35
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/.rubocop.yml +7 -8
- data/CHANGELOG.md +14 -0
- data/CI_STATUS.md +56 -0
- data/Gemfile.lock +2 -2
- data/Makefile +22 -6
- data/lib/language_operator/agent/base.rb +10 -6
- data/lib/language_operator/agent/executor.rb +19 -97
- data/lib/language_operator/agent/safety/ast_validator.rb +62 -43
- data/lib/language_operator/agent/safety/safe_executor.rb +27 -2
- data/lib/language_operator/agent/scheduler.rb +60 -0
- data/lib/language_operator/agent/task_executor.rb +548 -0
- data/lib/language_operator/agent.rb +90 -27
- data/lib/language_operator/cli/base_command.rb +117 -0
- data/lib/language_operator/cli/commands/agent.rb +339 -407
- data/lib/language_operator/cli/commands/cluster.rb +274 -290
- data/lib/language_operator/cli/commands/install.rb +110 -119
- data/lib/language_operator/cli/commands/model.rb +284 -184
- data/lib/language_operator/cli/commands/persona.rb +218 -284
- data/lib/language_operator/cli/commands/quickstart.rb +4 -5
- data/lib/language_operator/cli/commands/status.rb +31 -35
- data/lib/language_operator/cli/commands/system.rb +221 -233
- data/lib/language_operator/cli/commands/tool.rb +356 -422
- data/lib/language_operator/cli/commands/use.rb +19 -22
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -1
- data/lib/language_operator/client/config.rb +20 -21
- data/lib/language_operator/config.rb +115 -3
- data/lib/language_operator/constants.rb +54 -0
- data/lib/language_operator/dsl/agent_context.rb +7 -7
- data/lib/language_operator/dsl/agent_definition.rb +111 -26
- data/lib/language_operator/dsl/config.rb +30 -66
- data/lib/language_operator/dsl/main_definition.rb +114 -0
- data/lib/language_operator/dsl/schema.rb +84 -43
- data/lib/language_operator/dsl/task_definition.rb +315 -0
- data/lib/language_operator/dsl.rb +0 -1
- data/lib/language_operator/instrumentation/task_tracer.rb +285 -0
- data/lib/language_operator/logger.rb +4 -4
- data/lib/language_operator/synthesis_test_harness.rb +324 -0
- data/lib/language_operator/templates/examples/agent_synthesis.tmpl +26 -8
- data/lib/language_operator/templates/schema/CHANGELOG.md +26 -0
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +84 -42
- data/lib/language_operator/type_coercion.rb +250 -0
- data/lib/language_operator/ux/base.rb +81 -0
- data/lib/language_operator/ux/concerns/README.md +155 -0
- data/lib/language_operator/ux/concerns/headings.rb +90 -0
- data/lib/language_operator/ux/concerns/input_validation.rb +146 -0
- data/lib/language_operator/ux/concerns/provider_helpers.rb +167 -0
- data/lib/language_operator/ux/create_agent.rb +252 -0
- data/lib/language_operator/ux/create_model.rb +267 -0
- data/lib/language_operator/ux/quickstart.rb +594 -0
- data/lib/language_operator/version.rb +1 -1
- data/lib/language_operator.rb +2 -0
- data/requirements/ARCHITECTURE.md +1 -0
- data/requirements/SCRATCH.md +153 -0
- data/requirements/dsl.md +0 -0
- data/requirements/features +1 -0
- data/requirements/personas +1 -0
- data/requirements/proposals +1 -0
- data/requirements/tasks/iterate.md +14 -15
- data/requirements/tasks/optimize.md +13 -4
- data/synth/001/Makefile +90 -0
- data/synth/001/agent.rb +26 -0
- data/synth/001/agent.yaml +7 -0
- data/synth/001/output.log +44 -0
- data/synth/Makefile +39 -0
- data/synth/README.md +342 -0
- metadata +37 -10
- data/lib/language_operator/dsl/workflow_definition.rb +0 -259
- data/test_agent_dsl.rb +0 -108
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative '../base_command'
|
|
4
4
|
require_relative '../formatters/progress_formatter'
|
|
5
5
|
require_relative '../formatters/table_formatter'
|
|
6
6
|
require_relative '../formatters/status_formatter'
|
|
@@ -13,26 +13,26 @@ module LanguageOperator
|
|
|
13
13
|
module CLI
|
|
14
14
|
module Commands
|
|
15
15
|
# System status and overview command
|
|
16
|
-
class Status <
|
|
16
|
+
class Status < BaseCommand
|
|
17
17
|
include Helpers::PastelHelper
|
|
18
18
|
|
|
19
19
|
desc 'overview', 'Show system status and overview'
|
|
20
20
|
def overview
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
handle_command_error('retrieve cluster status') do
|
|
22
|
+
current_cluster = Config::ClusterConfig.current_cluster
|
|
23
|
+
clusters = Config::ClusterConfig.list_clusters
|
|
24
|
+
|
|
25
|
+
# Current cluster context
|
|
26
|
+
if current_cluster
|
|
27
|
+
cluster_config = Config::ClusterConfig.get_cluster(current_cluster)
|
|
28
|
+
|
|
29
|
+
puts "\nCluster Details"
|
|
30
|
+
puts '----------------'
|
|
31
|
+
puts "Name: #{pastel.bold.white(current_cluster)}"
|
|
32
|
+
puts "Namespace: #{pastel.bold.white(cluster_config[:namespace])}"
|
|
33
|
+
puts
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
begin
|
|
35
|
+
# Check cluster health
|
|
36
36
|
k8s = Helpers::ClusterValidator.kubernetes_client(current_cluster)
|
|
37
37
|
|
|
38
38
|
# Operator status
|
|
@@ -96,29 +96,25 @@ module LanguageOperator
|
|
|
96
96
|
else
|
|
97
97
|
puts ' (none)'
|
|
98
98
|
end
|
|
99
|
-
|
|
100
|
-
Formatters::ProgressFormatter.
|
|
99
|
+
else
|
|
100
|
+
Formatters::ProgressFormatter.warn('No cluster selected')
|
|
101
101
|
puts
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
if clusters.any?
|
|
103
|
+
puts 'Available clusters:'
|
|
104
|
+
clusters.each do |cluster|
|
|
105
|
+
puts " - #{cluster[:name]}"
|
|
106
|
+
end
|
|
107
|
+
puts
|
|
108
|
+
puts 'Select a cluster with:'
|
|
109
|
+
puts ' aictl use <cluster>'
|
|
110
|
+
else
|
|
111
|
+
puts 'No clusters found. Create one with:'
|
|
112
|
+
puts ' aictl cluster create <name>'
|
|
111
113
|
end
|
|
112
|
-
puts
|
|
113
|
-
puts 'Select a cluster with:'
|
|
114
|
-
puts ' aictl use <cluster>'
|
|
115
|
-
else
|
|
116
|
-
puts 'No clusters found. Create one with:'
|
|
117
|
-
puts ' aictl cluster create <name>'
|
|
118
114
|
end
|
|
119
|
-
end
|
|
120
115
|
|
|
121
|
-
|
|
116
|
+
puts
|
|
117
|
+
end
|
|
122
118
|
end
|
|
123
119
|
|
|
124
120
|
default_task :overview
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'thor'
|
|
4
3
|
require 'json'
|
|
5
4
|
require 'yaml'
|
|
5
|
+
require_relative '../base_command'
|
|
6
6
|
require_relative '../formatters/progress_formatter'
|
|
7
7
|
require_relative '../../dsl/schema'
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ module LanguageOperator
|
|
|
10
10
|
module CLI
|
|
11
11
|
module Commands
|
|
12
12
|
# System commands for schema introspection and metadata
|
|
13
|
-
class System <
|
|
13
|
+
class System < BaseCommand
|
|
14
14
|
desc 'schema', 'Export the DSL schema in various formats'
|
|
15
15
|
long_desc <<-DESC
|
|
16
16
|
Export the Language Operator Agent DSL schema in various formats.
|
|
@@ -39,30 +39,29 @@ module LanguageOperator
|
|
|
39
39
|
option :format, type: :string, default: 'json', desc: 'Output format (json, yaml, openapi)'
|
|
40
40
|
option :version, type: :boolean, default: false, desc: 'Show schema version only'
|
|
41
41
|
def schema
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
handle_command_error('generate schema') do
|
|
43
|
+
# Handle version flag
|
|
44
|
+
if options[:version]
|
|
45
|
+
puts Dsl::Schema.version
|
|
46
|
+
return
|
|
47
|
+
end
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
# Generate schema based on format
|
|
50
|
+
format = options[:format].downcase
|
|
51
|
+
case format
|
|
52
|
+
when 'json'
|
|
53
|
+
output_json_schema
|
|
54
|
+
when 'yaml'
|
|
55
|
+
output_yaml_schema
|
|
56
|
+
when 'openapi'
|
|
57
|
+
output_openapi_schema
|
|
58
|
+
else
|
|
59
|
+
Formatters::ProgressFormatter.error("Invalid format: #{format}")
|
|
60
|
+
puts
|
|
61
|
+
puts 'Supported formats: json, yaml, openapi'
|
|
62
|
+
exit 1
|
|
63
|
+
end
|
|
62
64
|
end
|
|
63
|
-
rescue StandardError => e
|
|
64
|
-
Formatters::ProgressFormatter.error("Failed to generate schema: #{e.message}")
|
|
65
|
-
exit 1
|
|
66
65
|
end
|
|
67
66
|
|
|
68
67
|
no_commands do
|
|
@@ -110,95 +109,93 @@ module LanguageOperator
|
|
|
110
109
|
option :type, type: :string, default: 'agent', desc: 'Template type if using bundled template (agent, persona)'
|
|
111
110
|
option :verbose, type: :boolean, default: false, desc: 'Show detailed violation information'
|
|
112
111
|
def validate_template
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
handle_command_error('validate template') do
|
|
113
|
+
# Determine template source
|
|
114
|
+
if options[:template]
|
|
115
|
+
# Load custom template from file
|
|
116
|
+
unless File.exist?(options[:template])
|
|
117
|
+
Formatters::ProgressFormatter.error("Template file not found: #{options[:template]}")
|
|
118
|
+
exit 1
|
|
119
|
+
end
|
|
120
|
+
template_content = File.read(options[:template])
|
|
121
|
+
template_name = File.basename(options[:template])
|
|
122
|
+
else
|
|
123
|
+
# Load bundled template
|
|
124
|
+
template_type = options[:type].downcase
|
|
125
|
+
unless %w[agent persona].include?(template_type)
|
|
126
|
+
Formatters::ProgressFormatter.error("Invalid template type: #{template_type}")
|
|
127
|
+
puts
|
|
128
|
+
puts 'Supported types: agent, persona'
|
|
129
|
+
exit 1
|
|
130
|
+
end
|
|
131
|
+
template_content = load_bundled_template(template_type)
|
|
132
|
+
template_name = "bundled #{template_type} template"
|
|
119
133
|
end
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
|
|
135
|
+
# Display header
|
|
136
|
+
puts "Validating template: #{template_name}"
|
|
137
|
+
puts '=' * 60
|
|
138
|
+
puts
|
|
139
|
+
|
|
140
|
+
# Extract code examples
|
|
141
|
+
code_examples = extract_code_examples(template_content)
|
|
142
|
+
|
|
143
|
+
if code_examples.empty?
|
|
144
|
+
Formatters::ProgressFormatter.warn('No Ruby code examples found in template')
|
|
127
145
|
puts
|
|
128
|
-
puts '
|
|
146
|
+
puts 'Templates should contain Ruby code blocks like:'
|
|
147
|
+
puts '```ruby'
|
|
148
|
+
puts 'agent "my-agent" do'
|
|
149
|
+
puts ' # ...'
|
|
150
|
+
puts 'end'
|
|
151
|
+
puts '```'
|
|
129
152
|
exit 1
|
|
130
153
|
end
|
|
131
|
-
template_content = load_bundled_template(template_type)
|
|
132
|
-
template_name = "bundled #{template_type} template"
|
|
133
|
-
end
|
|
134
154
|
|
|
135
|
-
|
|
136
|
-
puts "Validating template: #{template_name}"
|
|
137
|
-
puts '=' * 60
|
|
138
|
-
puts
|
|
139
|
-
|
|
140
|
-
# Extract code examples
|
|
141
|
-
code_examples = extract_code_examples(template_content)
|
|
142
|
-
|
|
143
|
-
if code_examples.empty?
|
|
144
|
-
Formatters::ProgressFormatter.warn('No Ruby code examples found in template')
|
|
155
|
+
puts "Found #{code_examples.size} code example(s)"
|
|
145
156
|
puts
|
|
146
|
-
puts 'Templates should contain Ruby code blocks like:'
|
|
147
|
-
puts '```ruby'
|
|
148
|
-
puts 'agent "my-agent" do'
|
|
149
|
-
puts ' # ...'
|
|
150
|
-
puts 'end'
|
|
151
|
-
puts '```'
|
|
152
|
-
exit 1
|
|
153
|
-
end
|
|
154
157
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
# Validate each example
|
|
159
|
+
all_valid = true
|
|
160
|
+
code_examples.each_with_index do |example, idx|
|
|
161
|
+
puts "Example #{idx + 1} (starting at line #{example[:start_line]}):"
|
|
162
|
+
puts '-' * 40
|
|
163
|
+
|
|
164
|
+
result = validate_code_against_schema(example[:code])
|
|
165
|
+
|
|
166
|
+
if result[:valid] && result[:warnings].empty?
|
|
167
|
+
Formatters::ProgressFormatter.success('Valid - No issues found')
|
|
168
|
+
elsif result[:valid]
|
|
169
|
+
Formatters::ProgressFormatter.success('Valid - With warnings')
|
|
170
|
+
result[:warnings].each do |warn|
|
|
171
|
+
line = example[:start_line] + (warn[:location] || 0)
|
|
172
|
+
puts " ⚠ Line #{line}: #{warn[:message]}"
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
Formatters::ProgressFormatter.error('Invalid - Violations detected')
|
|
176
|
+
result[:errors].each do |err|
|
|
177
|
+
line = example[:start_line] + (err[:location] || 0)
|
|
178
|
+
puts " ✗ Line #{line}: #{err[:message]}"
|
|
179
|
+
puts " Type: #{err[:type]}" if options[:verbose]
|
|
180
|
+
end
|
|
181
|
+
all_valid = false
|
|
182
|
+
end
|
|
163
183
|
|
|
164
|
-
|
|
184
|
+
puts
|
|
185
|
+
end
|
|
165
186
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Formatters::ProgressFormatter.success('
|
|
170
|
-
|
|
171
|
-
line = example[:start_line] + (warn[:location] || 0)
|
|
172
|
-
puts " ⚠ Line #{line}: #{warn[:message]}"
|
|
173
|
-
end
|
|
187
|
+
# Final summary
|
|
188
|
+
puts '=' * 60
|
|
189
|
+
if all_valid
|
|
190
|
+
Formatters::ProgressFormatter.success('All examples are valid')
|
|
191
|
+
exit 0
|
|
174
192
|
else
|
|
175
|
-
Formatters::ProgressFormatter.error('
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
puts " Type: #{err[:type]}" if options[:verbose]
|
|
180
|
-
end
|
|
181
|
-
all_valid = false
|
|
193
|
+
Formatters::ProgressFormatter.error('Validation failed')
|
|
194
|
+
puts
|
|
195
|
+
puts 'Fix the violations above and run validation again.'
|
|
196
|
+
exit 1
|
|
182
197
|
end
|
|
183
|
-
|
|
184
|
-
puts
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Final summary
|
|
188
|
-
puts '=' * 60
|
|
189
|
-
if all_valid
|
|
190
|
-
Formatters::ProgressFormatter.success('All examples are valid')
|
|
191
|
-
exit 0
|
|
192
|
-
else
|
|
193
|
-
Formatters::ProgressFormatter.error('Validation failed')
|
|
194
|
-
puts
|
|
195
|
-
puts 'Fix the violations above and run validation again.'
|
|
196
|
-
exit 1
|
|
197
198
|
end
|
|
198
|
-
rescue StandardError => e
|
|
199
|
-
Formatters::ProgressFormatter.error("Validation error: #{e.message}")
|
|
200
|
-
puts e.backtrace.first(5).join("\n") if options[:verbose]
|
|
201
|
-
exit 1
|
|
202
199
|
end
|
|
203
200
|
|
|
204
201
|
desc 'test-synthesis', 'Test agent synthesis from natural language instructions'
|
|
@@ -234,90 +231,88 @@ module LanguageOperator
|
|
|
234
231
|
option :models, type: :string, desc: 'Comma-separated list of available models'
|
|
235
232
|
option :dry_run, type: :boolean, default: false, desc: 'Show prompt without calling LLM'
|
|
236
233
|
def test_synthesis
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
234
|
+
handle_command_error('test synthesis') do
|
|
235
|
+
# Load synthesis template
|
|
236
|
+
template_content = load_bundled_template('agent')
|
|
237
|
+
|
|
238
|
+
# Detect temporal intent from instructions
|
|
239
|
+
temporal_intent = detect_temporal_intent(options[:instructions])
|
|
240
|
+
|
|
241
|
+
# Prepare template data
|
|
242
|
+
template_data = {
|
|
243
|
+
'Instructions' => options[:instructions],
|
|
244
|
+
'AgentName' => options[:agent_name],
|
|
245
|
+
'ToolsList' => format_tools_list(options[:tools]),
|
|
246
|
+
'ModelsList' => format_models_list(options[:models]),
|
|
247
|
+
'TemporalIntent' => temporal_intent,
|
|
248
|
+
'PersonaSection' => '',
|
|
249
|
+
'ScheduleSection' => temporal_intent == 'scheduled' ? ' schedule "0 */1 * * *" # Example hourly schedule' : '',
|
|
250
|
+
'ScheduleRules' => temporal_intent == 'scheduled' ? "\n2. Include schedule with cron expression\n3. Set mode to :scheduled\n4. " : "\n2. ",
|
|
251
|
+
'ConstraintsSection' => '',
|
|
252
|
+
'ErrorContext' => nil
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# Render template (Go-style template syntax)
|
|
256
|
+
rendered_prompt = render_go_template(template_content, template_data)
|
|
257
|
+
|
|
258
|
+
if options[:dry_run]
|
|
259
|
+
# Show the prompt that would be sent
|
|
260
|
+
puts 'Synthesis Prompt Preview'
|
|
261
|
+
puts '=' * 80
|
|
262
|
+
puts
|
|
263
|
+
puts rendered_prompt
|
|
264
|
+
puts
|
|
265
|
+
puts '=' * 80
|
|
266
|
+
Formatters::ProgressFormatter.success('Dry-run complete - prompt displayed above')
|
|
267
|
+
return
|
|
268
|
+
end
|
|
259
269
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
puts 'Synthesis Prompt Preview'
|
|
263
|
-
puts '=' * 80
|
|
270
|
+
# Call LLM to generate code
|
|
271
|
+
puts 'Generating agent code from instructions...'
|
|
264
272
|
puts
|
|
265
|
-
puts rendered_prompt
|
|
266
|
-
puts
|
|
267
|
-
puts '=' * 80
|
|
268
|
-
Formatters::ProgressFormatter.success('Dry-run complete - prompt displayed above')
|
|
269
|
-
return
|
|
270
|
-
end
|
|
271
273
|
|
|
272
|
-
|
|
273
|
-
puts 'Generating agent code from instructions...'
|
|
274
|
-
puts
|
|
274
|
+
llm_response = call_llm_for_synthesis(rendered_prompt)
|
|
275
275
|
|
|
276
|
-
|
|
276
|
+
# Extract Ruby code from response
|
|
277
|
+
generated_code = extract_ruby_code(llm_response)
|
|
277
278
|
|
|
278
|
-
|
|
279
|
-
|
|
279
|
+
if generated_code.nil?
|
|
280
|
+
Formatters::ProgressFormatter.error('Failed to extract Ruby code from LLM response')
|
|
281
|
+
puts
|
|
282
|
+
puts 'LLM Response:'
|
|
283
|
+
puts llm_response
|
|
284
|
+
exit 1
|
|
285
|
+
end
|
|
280
286
|
|
|
281
|
-
|
|
282
|
-
|
|
287
|
+
# Display generated code
|
|
288
|
+
puts 'Generated Code:'
|
|
289
|
+
puts '=' * 80
|
|
290
|
+
puts generated_code
|
|
291
|
+
puts '=' * 80
|
|
283
292
|
puts
|
|
284
|
-
puts 'LLM Response:'
|
|
285
|
-
puts llm_response
|
|
286
|
-
exit 1
|
|
287
|
-
end
|
|
288
293
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
294
|
+
# Validate generated code
|
|
295
|
+
puts 'Validating generated code...'
|
|
296
|
+
validation_result = validate_code_against_schema(generated_code)
|
|
297
|
+
|
|
298
|
+
if validation_result[:valid] && validation_result[:warnings].empty?
|
|
299
|
+
Formatters::ProgressFormatter.success('✅ Code is valid - No issues found')
|
|
300
|
+
elsif validation_result[:valid]
|
|
301
|
+
Formatters::ProgressFormatter.success('✅ Code is valid - With warnings')
|
|
302
|
+
puts
|
|
303
|
+
validation_result[:warnings].each do |warn|
|
|
304
|
+
puts " ⚠ #{warn[:message]}"
|
|
305
|
+
end
|
|
306
|
+
else
|
|
307
|
+
Formatters::ProgressFormatter.error('❌ Code validation failed')
|
|
308
|
+
puts
|
|
309
|
+
validation_result[:errors].each do |err|
|
|
310
|
+
puts " ✗ #{err[:message]}"
|
|
311
|
+
end
|
|
307
312
|
end
|
|
308
|
-
|
|
309
|
-
Formatters::ProgressFormatter.error('❌ Code validation failed')
|
|
313
|
+
|
|
310
314
|
puts
|
|
311
|
-
validation_result[:errors].each do |err|
|
|
312
|
-
puts " ✗ #{err[:message]}"
|
|
313
|
-
end
|
|
314
315
|
end
|
|
315
|
-
|
|
316
|
-
puts
|
|
317
|
-
rescue StandardError => e
|
|
318
|
-
Formatters::ProgressFormatter.error("Test synthesis failed: #{e.message}")
|
|
319
|
-
puts e.backtrace.first(5).join("\n") if options[:verbose]
|
|
320
|
-
exit 1
|
|
321
316
|
end
|
|
322
317
|
|
|
323
318
|
desc 'synthesis-template', 'Export synthesis templates for agent code generation'
|
|
@@ -352,62 +347,61 @@ module LanguageOperator
|
|
|
352
347
|
option :with_schema, type: :boolean, default: false, desc: 'Include DSL schema in output'
|
|
353
348
|
option :validate, type: :boolean, default: false, desc: 'Validate template syntax'
|
|
354
349
|
def synthesis_template
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
350
|
+
handle_command_error('load template') do
|
|
351
|
+
# Validate type
|
|
352
|
+
template_type = options[:type].downcase
|
|
353
|
+
unless %w[agent persona].include?(template_type)
|
|
354
|
+
Formatters::ProgressFormatter.error("Invalid template type: #{template_type}")
|
|
355
|
+
puts
|
|
356
|
+
puts 'Supported types: agent, persona'
|
|
357
|
+
exit 1
|
|
358
|
+
end
|
|
363
359
|
|
|
364
|
-
|
|
365
|
-
|
|
360
|
+
# Load template
|
|
361
|
+
template_content = load_template(template_type)
|
|
366
362
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
363
|
+
# Validate if requested
|
|
364
|
+
if options[:validate]
|
|
365
|
+
validation_result = validate_template_content(template_content, template_type)
|
|
370
366
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
367
|
+
# Display warnings if any
|
|
368
|
+
unless validation_result[:warnings].empty?
|
|
369
|
+
Formatters::ProgressFormatter.warn('Template validation warnings:')
|
|
370
|
+
validation_result[:warnings].each do |warning|
|
|
371
|
+
puts " ⚠ #{warning}"
|
|
372
|
+
end
|
|
373
|
+
puts
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Display errors and exit if validation failed
|
|
377
|
+
if validation_result[:valid]
|
|
378
|
+
Formatters::ProgressFormatter.success('Template validation passed')
|
|
379
|
+
return
|
|
380
|
+
else
|
|
381
|
+
Formatters::ProgressFormatter.error('Template validation failed:')
|
|
382
|
+
validation_result[:errors].each do |error|
|
|
383
|
+
puts " ✗ #{error}"
|
|
384
|
+
end
|
|
385
|
+
exit 1
|
|
376
386
|
end
|
|
377
|
-
puts
|
|
378
387
|
end
|
|
379
388
|
|
|
380
|
-
#
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
389
|
+
# Generate output based on format
|
|
390
|
+
format = options[:format].downcase
|
|
391
|
+
case format
|
|
392
|
+
when 'template'
|
|
393
|
+
output_template_format(template_content)
|
|
394
|
+
when 'json'
|
|
395
|
+
output_json_format(template_content, template_type)
|
|
396
|
+
when 'yaml'
|
|
397
|
+
output_yaml_format(template_content, template_type)
|
|
384
398
|
else
|
|
385
|
-
Formatters::ProgressFormatter.error(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
end
|
|
399
|
+
Formatters::ProgressFormatter.error("Invalid format: #{format}")
|
|
400
|
+
puts
|
|
401
|
+
puts 'Supported formats: template, json, yaml'
|
|
389
402
|
exit 1
|
|
390
403
|
end
|
|
391
404
|
end
|
|
392
|
-
|
|
393
|
-
# Generate output based on format
|
|
394
|
-
format = options[:format].downcase
|
|
395
|
-
case format
|
|
396
|
-
when 'template'
|
|
397
|
-
output_template_format(template_content)
|
|
398
|
-
when 'json'
|
|
399
|
-
output_json_format(template_content, template_type)
|
|
400
|
-
when 'yaml'
|
|
401
|
-
output_yaml_format(template_content, template_type)
|
|
402
|
-
else
|
|
403
|
-
Formatters::ProgressFormatter.error("Invalid format: #{format}")
|
|
404
|
-
puts
|
|
405
|
-
puts 'Supported formats: template, json, yaml'
|
|
406
|
-
exit 1
|
|
407
|
-
end
|
|
408
|
-
rescue StandardError => e
|
|
409
|
-
Formatters::ProgressFormatter.error("Failed to load template: #{e.message}")
|
|
410
|
-
exit 1
|
|
411
405
|
end
|
|
412
406
|
|
|
413
407
|
private
|
|
@@ -649,21 +643,18 @@ module LanguageOperator
|
|
|
649
643
|
# Extract method calls from template code
|
|
650
644
|
# Returns array of method name strings
|
|
651
645
|
def extract_method_calls(template)
|
|
652
|
-
require '
|
|
646
|
+
require 'prism'
|
|
653
647
|
|
|
654
648
|
method_calls = []
|
|
655
649
|
code_examples = extract_code_examples(template)
|
|
656
650
|
|
|
657
651
|
code_examples.each do |example|
|
|
658
652
|
# Parse the code to find method calls
|
|
659
|
-
|
|
660
|
-
buffer.source = example[:code]
|
|
661
|
-
parser = Parser::CurrentRuby.new
|
|
662
|
-
ast = parser.parse(buffer)
|
|
653
|
+
result = Prism.parse(example[:code])
|
|
663
654
|
|
|
664
655
|
# Walk the AST to find method calls
|
|
665
|
-
extract_methods_from_ast(
|
|
666
|
-
rescue
|
|
656
|
+
extract_methods_from_ast(result.value, method_calls) if result.success?
|
|
657
|
+
rescue Prism::ParseError
|
|
667
658
|
# Skip code with syntax errors - they'll be caught by validate_code_against_schema
|
|
668
659
|
next
|
|
669
660
|
end
|
|
@@ -673,14 +664,11 @@ module LanguageOperator
|
|
|
673
664
|
|
|
674
665
|
# Recursively extract method names from AST
|
|
675
666
|
def extract_methods_from_ast(node, methods)
|
|
676
|
-
return unless node
|
|
667
|
+
return unless node
|
|
677
668
|
|
|
678
|
-
|
|
679
|
-
_, method_name, * = node.children
|
|
680
|
-
methods << method_name.to_s if method_name
|
|
681
|
-
end
|
|
669
|
+
methods << node.name.to_s if node.is_a?(Prism::CallNode)
|
|
682
670
|
|
|
683
|
-
node.
|
|
671
|
+
node.compact_child_nodes.each do |child|
|
|
684
672
|
extract_methods_from_ast(child, methods)
|
|
685
673
|
end
|
|
686
674
|
end
|