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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -8
  3. data/CHANGELOG.md +14 -0
  4. data/CI_STATUS.md +56 -0
  5. data/Gemfile.lock +2 -2
  6. data/Makefile +22 -6
  7. data/lib/language_operator/agent/base.rb +10 -6
  8. data/lib/language_operator/agent/executor.rb +19 -97
  9. data/lib/language_operator/agent/safety/ast_validator.rb +62 -43
  10. data/lib/language_operator/agent/safety/safe_executor.rb +27 -2
  11. data/lib/language_operator/agent/scheduler.rb +60 -0
  12. data/lib/language_operator/agent/task_executor.rb +548 -0
  13. data/lib/language_operator/agent.rb +90 -27
  14. data/lib/language_operator/cli/base_command.rb +117 -0
  15. data/lib/language_operator/cli/commands/agent.rb +339 -407
  16. data/lib/language_operator/cli/commands/cluster.rb +274 -290
  17. data/lib/language_operator/cli/commands/install.rb +110 -119
  18. data/lib/language_operator/cli/commands/model.rb +284 -184
  19. data/lib/language_operator/cli/commands/persona.rb +218 -284
  20. data/lib/language_operator/cli/commands/quickstart.rb +4 -5
  21. data/lib/language_operator/cli/commands/status.rb +31 -35
  22. data/lib/language_operator/cli/commands/system.rb +221 -233
  23. data/lib/language_operator/cli/commands/tool.rb +356 -422
  24. data/lib/language_operator/cli/commands/use.rb +19 -22
  25. data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
  26. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -1
  27. data/lib/language_operator/client/config.rb +20 -21
  28. data/lib/language_operator/config.rb +115 -3
  29. data/lib/language_operator/constants.rb +54 -0
  30. data/lib/language_operator/dsl/agent_context.rb +7 -7
  31. data/lib/language_operator/dsl/agent_definition.rb +111 -26
  32. data/lib/language_operator/dsl/config.rb +30 -66
  33. data/lib/language_operator/dsl/main_definition.rb +114 -0
  34. data/lib/language_operator/dsl/schema.rb +84 -43
  35. data/lib/language_operator/dsl/task_definition.rb +315 -0
  36. data/lib/language_operator/dsl.rb +0 -1
  37. data/lib/language_operator/instrumentation/task_tracer.rb +285 -0
  38. data/lib/language_operator/logger.rb +4 -4
  39. data/lib/language_operator/synthesis_test_harness.rb +324 -0
  40. data/lib/language_operator/templates/examples/agent_synthesis.tmpl +26 -8
  41. data/lib/language_operator/templates/schema/CHANGELOG.md +26 -0
  42. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
  43. data/lib/language_operator/templates/schema/agent_dsl_schema.json +84 -42
  44. data/lib/language_operator/type_coercion.rb +250 -0
  45. data/lib/language_operator/ux/base.rb +81 -0
  46. data/lib/language_operator/ux/concerns/README.md +155 -0
  47. data/lib/language_operator/ux/concerns/headings.rb +90 -0
  48. data/lib/language_operator/ux/concerns/input_validation.rb +146 -0
  49. data/lib/language_operator/ux/concerns/provider_helpers.rb +167 -0
  50. data/lib/language_operator/ux/create_agent.rb +252 -0
  51. data/lib/language_operator/ux/create_model.rb +267 -0
  52. data/lib/language_operator/ux/quickstart.rb +594 -0
  53. data/lib/language_operator/version.rb +1 -1
  54. data/lib/language_operator.rb +2 -0
  55. data/requirements/ARCHITECTURE.md +1 -0
  56. data/requirements/SCRATCH.md +153 -0
  57. data/requirements/dsl.md +0 -0
  58. data/requirements/features +1 -0
  59. data/requirements/personas +1 -0
  60. data/requirements/proposals +1 -0
  61. data/requirements/tasks/iterate.md +14 -15
  62. data/requirements/tasks/optimize.md +13 -4
  63. data/synth/001/Makefile +90 -0
  64. data/synth/001/agent.rb +26 -0
  65. data/synth/001/agent.yaml +7 -0
  66. data/synth/001/output.log +44 -0
  67. data/synth/Makefile +39 -0
  68. data/synth/README.md +342 -0
  69. metadata +37 -10
  70. data/lib/language_operator/dsl/workflow_definition.rb +0 -259
  71. data/test_agent_dsl.rb +0 -108
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
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 < Thor
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
- current_cluster = Config::ClusterConfig.current_cluster
22
- clusters = Config::ClusterConfig.list_clusters
23
-
24
- # Current cluster context
25
- if current_cluster
26
- cluster_config = Config::ClusterConfig.get_cluster(current_cluster)
27
-
28
- puts "\nCluster Details"
29
- puts '----------------'
30
- puts "Name: #{pastel.bold.white(current_cluster)}"
31
- puts "Namespace: #{pastel.bold.white(cluster_config[:namespace])}"
32
- puts
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
- # Check cluster health
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
- rescue StandardError => e
100
- Formatters::ProgressFormatter.error("Connection failed: #{e.message}")
99
+ else
100
+ Formatters::ProgressFormatter.warn('No cluster selected')
101
101
  puts
102
- puts 'Check your cluster connection and try again'
103
- end
104
- else
105
- Formatters::ProgressFormatter.warn('No cluster selected')
106
- puts
107
- if clusters.any?
108
- puts 'Available clusters:'
109
- clusters.each do |cluster|
110
- puts " - #{cluster[:name]}"
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
- puts
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 < Thor
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
- # Handle version flag
43
- if options[:version]
44
- puts Dsl::Schema.version
45
- return
46
- end
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
- # Generate schema based on format
49
- format = options[:format].downcase
50
- case format
51
- when 'json'
52
- output_json_schema
53
- when 'yaml'
54
- output_yaml_schema
55
- when 'openapi'
56
- output_openapi_schema
57
- else
58
- Formatters::ProgressFormatter.error("Invalid format: #{format}")
59
- puts
60
- puts 'Supported formats: json, yaml, openapi'
61
- exit 1
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
- # 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
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
- 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}")
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 'Supported types: agent, persona'
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
- # 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')
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
- puts "Found #{code_examples.size} code example(s)"
156
- puts
157
-
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
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
- result = validate_code_against_schema(example[:code])
184
+ puts
185
+ end
165
186
 
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
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('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
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
- # Load synthesis template
238
- template_content = load_bundled_template('agent')
239
-
240
- # Detect temporal intent from instructions
241
- temporal_intent = detect_temporal_intent(options[:instructions])
242
-
243
- # Prepare template data
244
- template_data = {
245
- 'Instructions' => options[:instructions],
246
- 'AgentName' => options[:agent_name],
247
- 'ToolsList' => format_tools_list(options[:tools]),
248
- 'ModelsList' => format_models_list(options[:models]),
249
- 'TemporalIntent' => temporal_intent,
250
- 'PersonaSection' => '',
251
- 'ScheduleSection' => temporal_intent == 'scheduled' ? ' schedule "0 */1 * * *" # Example hourly schedule' : '',
252
- 'ScheduleRules' => temporal_intent == 'scheduled' ? "\n2. Include schedule with cron expression\n3. Set mode to :scheduled\n4. " : "\n2. ",
253
- 'ConstraintsSection' => '',
254
- 'ErrorContext' => nil
255
- }
256
-
257
- # Render template (Go-style template syntax)
258
- rendered_prompt = render_go_template(template_content, template_data)
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
- if options[:dry_run]
261
- # Show the prompt that would be sent
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
- # Call LLM to generate code
273
- puts 'Generating agent code from instructions...'
274
- puts
274
+ llm_response = call_llm_for_synthesis(rendered_prompt)
275
275
 
276
- llm_response = call_llm_for_synthesis(rendered_prompt)
276
+ # Extract Ruby code from response
277
+ generated_code = extract_ruby_code(llm_response)
277
278
 
278
- # Extract Ruby code from response
279
- generated_code = extract_ruby_code(llm_response)
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
- if generated_code.nil?
282
- Formatters::ProgressFormatter.error('Failed to extract Ruby code from LLM response')
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
- # Display generated code
290
- puts 'Generated Code:'
291
- puts '=' * 80
292
- puts generated_code
293
- puts '=' * 80
294
- puts
295
-
296
- # Validate generated code
297
- puts 'Validating generated code...'
298
- validation_result = validate_code_against_schema(generated_code)
299
-
300
- if validation_result[:valid] && validation_result[:warnings].empty?
301
- Formatters::ProgressFormatter.success('✅ Code is valid - No issues found')
302
- elsif validation_result[:valid]
303
- Formatters::ProgressFormatter.success('✅ Code is valid - With warnings')
304
- puts
305
- validation_result[:warnings].each do |warn|
306
- puts " ⚠ #{warn[:message]}"
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
- else
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
- # Validate type
356
- template_type = options[:type].downcase
357
- unless %w[agent persona].include?(template_type)
358
- Formatters::ProgressFormatter.error("Invalid template type: #{template_type}")
359
- puts
360
- puts 'Supported types: agent, persona'
361
- exit 1
362
- end
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
- # Load template
365
- template_content = load_template(template_type)
360
+ # Load template
361
+ template_content = load_template(template_type)
366
362
 
367
- # Validate if requested
368
- if options[:validate]
369
- validation_result = validate_template_content(template_content, template_type)
363
+ # Validate if requested
364
+ if options[:validate]
365
+ validation_result = validate_template_content(template_content, template_type)
370
366
 
371
- # Display warnings if any
372
- unless validation_result[:warnings].empty?
373
- Formatters::ProgressFormatter.warn('Template validation warnings:')
374
- validation_result[:warnings].each do |warning|
375
- puts " ⚠ #{warning}"
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
- # Display errors and exit if validation failed
381
- if validation_result[:valid]
382
- Formatters::ProgressFormatter.success('Template validation passed')
383
- return
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('Template validation failed:')
386
- validation_result[:errors].each do |error|
387
- puts " ✗ #{error}"
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 'parser/current'
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
- buffer = Parser::Source::Buffer.new('(template)')
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(ast, method_calls)
666
- rescue Parser::SyntaxError
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.is_a?(Parser::AST::Node)
667
+ return unless node
677
668
 
678
- if node.type == :send
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.children.each do |child|
671
+ node.compact_child_nodes.each do |child|
684
672
  extract_methods_from_ast(child, methods)
685
673
  end
686
674
  end