roast-ai 0.3.1 → 0.4.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.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +20 -0
  4. data/CLAUDE.md +52 -1
  5. data/Gemfile +3 -1
  6. data/Gemfile.lock +63 -16
  7. data/README.md +90 -5
  8. data/bin/roast +1 -1
  9. data/claude-swarm.yml +210 -0
  10. data/docs/AGENT_STEPS.md +264 -0
  11. data/examples/agent_workflow/README.md +75 -0
  12. data/examples/agent_workflow/apply_refactorings/prompt.md +22 -0
  13. data/examples/agent_workflow/identify_code_smells/prompt.md +15 -0
  14. data/examples/agent_workflow/summarize_improvements/prompt.md +18 -0
  15. data/examples/agent_workflow/workflow.yml +16 -0
  16. data/examples/available_tools_demo/README.md +42 -0
  17. data/examples/available_tools_demo/analyze_files/prompt.md +6 -0
  18. data/examples/available_tools_demo/explore_directory/prompt.md +6 -0
  19. data/examples/available_tools_demo/workflow.yml +32 -0
  20. data/examples/available_tools_demo/write_summary/prompt.md +6 -0
  21. data/examples/case_when/detect_language/prompt.md +2 -2
  22. data/examples/grading/run_coverage.rb +0 -2
  23. data/examples/iteration/analyze_complexity/prompt.md +2 -2
  24. data/examples/iteration/generate_recommendations/prompt.md +2 -2
  25. data/examples/iteration/implement_fix/prompt.md +2 -2
  26. data/examples/iteration/prioritize_issues/prompt.md +1 -1
  27. data/examples/iteration/prompts/analyze_file.md +2 -2
  28. data/examples/iteration/prompts/generate_summary.md +1 -1
  29. data/examples/iteration/prompts/update_report.md +3 -3
  30. data/examples/iteration/prompts/write_report.md +3 -3
  31. data/examples/iteration/read_file/prompt.md +2 -2
  32. data/examples/iteration/select_next_issue/prompt.md +2 -2
  33. data/examples/iteration/update_fix_count/prompt.md +4 -4
  34. data/examples/iteration/verify_fix/prompt.md +3 -3
  35. data/examples/mcp/README.md +3 -3
  36. data/examples/mcp/analyze_changes/prompt.md +1 -1
  37. data/examples/mcp/database_workflow.yml +1 -1
  38. data/examples/mcp/fetch_pr_context/prompt.md +1 -1
  39. data/examples/mcp/github_workflow.yml +1 -1
  40. data/examples/mcp/post_review/prompt.md +1 -1
  41. data/examples/pre_post_processing/analyze_test_file/prompt.md +1 -1
  42. data/examples/pre_post_processing/improve_test_coverage/prompt.md +1 -1
  43. data/examples/pre_post_processing/optimize_test_performance/prompt.md +1 -1
  44. data/examples/pre_post_processing/post_processing/aggregate_metrics/prompt.md +2 -2
  45. data/examples/pre_post_processing/post_processing/generate_summary_report/prompt.md +1 -1
  46. data/examples/pre_post_processing/pre_processing/setup_test_environment/prompt.md +1 -1
  47. data/examples/pre_post_processing/validate_changes/prompt.md +2 -2
  48. data/examples/user_input/README.md +90 -0
  49. data/examples/user_input/funny_name/create_backstory/prompt.md +10 -0
  50. data/examples/user_input/funny_name/workflow.yml +26 -0
  51. data/examples/user_input/generate_summary/prompt.md +11 -0
  52. data/examples/user_input/simple_input_demo/workflow.yml +35 -0
  53. data/examples/user_input/survey_workflow.yml +71 -0
  54. data/examples/user_input/welcome_message/prompt.md +3 -0
  55. data/examples/user_input/workflow.yml +73 -0
  56. data/examples/workflow_generator/create_workflow_files/prompt.md +1 -1
  57. data/lib/roast/errors.rb +6 -4
  58. data/lib/roast/helpers/function_caching_interceptor.rb +0 -2
  59. data/lib/roast/helpers/logger.rb +12 -35
  60. data/lib/roast/helpers/minitest_coverage_runner.rb +0 -1
  61. data/lib/roast/helpers/prompt_loader.rb +0 -2
  62. data/lib/roast/resources/api_resource.rb +0 -4
  63. data/lib/roast/resources/url_resource.rb +0 -3
  64. data/lib/roast/resources.rb +0 -8
  65. data/lib/roast/tools/ask_user.rb +0 -2
  66. data/lib/roast/tools/bash.rb +0 -3
  67. data/lib/roast/tools/cmd.rb +0 -3
  68. data/lib/roast/tools/coding_agent.rb +1 -8
  69. data/lib/roast/tools/grep.rb +0 -3
  70. data/lib/roast/tools/helpers/coding_agent_message_formatter.rb +1 -4
  71. data/lib/roast/tools/read_file.rb +0 -2
  72. data/lib/roast/tools/search_file.rb +0 -2
  73. data/lib/roast/tools/update_files.rb +0 -4
  74. data/lib/roast/tools/write_file.rb +0 -3
  75. data/lib/roast/tools.rb +0 -13
  76. data/lib/roast/value_objects/step_name.rb +14 -3
  77. data/lib/roast/value_objects/workflow_path.rb +0 -2
  78. data/lib/roast/value_objects.rb +4 -4
  79. data/lib/roast/version.rb +1 -1
  80. data/lib/roast/workflow/agent_step.rb +26 -0
  81. data/lib/roast/workflow/api_configuration.rb +0 -4
  82. data/lib/roast/workflow/base_iteration_step.rb +0 -4
  83. data/lib/roast/workflow/base_step.rb +54 -28
  84. data/lib/roast/workflow/base_workflow.rb +2 -21
  85. data/lib/roast/workflow/case_executor.rb +0 -1
  86. data/lib/roast/workflow/case_step.rb +0 -4
  87. data/lib/roast/workflow/command_executor.rb +0 -2
  88. data/lib/roast/workflow/conditional_executor.rb +0 -1
  89. data/lib/roast/workflow/conditional_step.rb +0 -4
  90. data/lib/roast/workflow/configuration.rb +3 -66
  91. data/lib/roast/workflow/configuration_loader.rb +0 -2
  92. data/lib/roast/workflow/configuration_parser.rb +1 -7
  93. data/lib/roast/workflow/dot_access_hash.rb +16 -1
  94. data/lib/roast/workflow/error_handler.rb +0 -3
  95. data/lib/roast/workflow/expression_evaluator.rb +0 -3
  96. data/lib/roast/workflow/file_state_repository.rb +0 -5
  97. data/lib/roast/workflow/input_executor.rb +41 -0
  98. data/lib/roast/workflow/input_step.rb +163 -0
  99. data/lib/roast/workflow/iteration_executor.rb +0 -2
  100. data/lib/roast/workflow/output_handler.rb +0 -2
  101. data/lib/roast/workflow/output_manager.rb +0 -2
  102. data/lib/roast/workflow/replay_handler.rb +0 -3
  103. data/lib/roast/workflow/resource_resolver.rb +0 -3
  104. data/lib/roast/workflow/session_manager.rb +0 -3
  105. data/lib/roast/workflow/state_manager.rb +0 -2
  106. data/lib/roast/workflow/step_executor_coordinator.rb +34 -11
  107. data/lib/roast/workflow/step_executor_factory.rb +0 -5
  108. data/lib/roast/workflow/step_executor_registry.rb +1 -4
  109. data/lib/roast/workflow/step_executors/hash_step_executor.rb +0 -3
  110. data/lib/roast/workflow/step_executors/parallel_step_executor.rb +0 -3
  111. data/lib/roast/workflow/step_executors/string_step_executor.rb +0 -2
  112. data/lib/roast/workflow/step_factory.rb +56 -0
  113. data/lib/roast/workflow/step_loader.rb +30 -16
  114. data/lib/roast/workflow/step_orchestrator.rb +3 -2
  115. data/lib/roast/workflow/step_type_resolver.rb +28 -1
  116. data/lib/roast/workflow/validator.rb +0 -4
  117. data/lib/roast/workflow/workflow_executor.rb +0 -16
  118. data/lib/roast/workflow/workflow_initializer.rb +1 -8
  119. data/lib/roast/workflow/workflow_runner.rb +0 -7
  120. data/lib/roast/workflow.rb +0 -15
  121. data/lib/roast.rb +55 -10
  122. data/roast.gemspec +2 -1
  123. data/schema/workflow.json +46 -0
  124. metadata +43 -6
  125. data/lib/roast/helpers.rb +0 -12
@@ -1,13 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/resources/base_resource"
4
- require "roast/resources/file_resource"
5
- require "roast/resources/directory_resource"
6
- require "roast/resources/url_resource"
7
- require "roast/resources/api_resource"
8
- require "roast/resources/none_resource"
9
- require "uri"
10
-
11
3
  module Roast
12
4
  # The Resources module contains classes for handling different types of resources
13
5
  # that workflows can operate on. Each resource type implements a common interface.
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/helpers/logger"
4
-
5
3
  module Roast
6
4
  module Tools
7
5
  module AskUser
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "English"
4
- require "roast/helpers/logger"
5
-
6
3
  module Roast
7
4
  module Tools
8
5
  module Bash
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "English"
4
- require "roast/helpers/logger"
5
-
6
3
  module Roast
7
4
  module Tools
8
5
  module Cmd
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/helpers/logger"
4
- require "roast/tools/helpers/coding_agent_message_formatter"
5
- require "json"
6
- require "open3"
7
- require "tempfile"
8
- require "securerandom"
9
-
10
3
  module Roast
11
4
  module Tools
12
5
  module CodingAgent
@@ -110,7 +103,7 @@ module Roast
110
103
  def handle_intermediate_message(json)
111
104
  case json["type"]
112
105
  when "assistant", "user"
113
- CodingAgentMessageFormatter.format_messages(json).each(&method(:log_message))
106
+ Roast::Tools::Helpers::CodingAgentMessageFormatter.format_messages(json).each(&method(:log_message))
114
107
  when "result", "system"
115
108
  # Ignore these message types
116
109
  else
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/helpers/logger"
4
-
5
3
  module Roast
6
4
  module Tools
7
5
  module Grep
@@ -30,7 +28,6 @@ module Roast
30
28
  Roast::Helpers::Logger.info("🔍 Grepping for string: #{string}\n")
31
29
 
32
30
  # Use Open3 to safely pass the string as an argument, avoiding shell injection
33
- require "open3"
34
31
  cmd = ["rg", "-C", "4", "--trim", "--color=never", "--heading", "-F", "--", string, "."]
35
32
  stdout, _stderr, _status = Open3.capture3(*cmd)
36
33
 
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "yaml"
5
-
6
3
  module Roast
7
4
  module Tools
8
- module CodingAgent
5
+ module Helpers
9
6
  module CodingAgentMessageFormatter
10
7
  extend self
11
8
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/helpers/logger"
4
-
5
3
  module Roast
6
4
  module Tools
7
5
  module ReadFile
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/helpers/logger"
4
-
5
3
  module Roast
6
4
  module Tools
7
5
  module SearchFile
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
- require "roast/helpers/logger"
5
- require "diff/lcs"
6
-
7
3
  module Roast
8
4
  module Tools
9
5
  module UpdateFiles
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
- require "roast/helpers/logger"
5
-
6
3
  module Roast
7
4
  module Tools
8
5
  module WriteFile
data/lib/roast/tools.rb CHANGED
@@ -1,18 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "English"
4
- require "fileutils"
5
-
6
- require "roast/tools/ask_user"
7
- require "roast/tools/bash"
8
- require "roast/tools/cmd"
9
- require "roast/tools/coding_agent"
10
- require "roast/tools/grep"
11
- require "roast/tools/read_file"
12
- require "roast/tools/search_file"
13
- require "roast/tools/update_files"
14
- require "roast/tools/write_file"
15
-
16
3
  module Roast
17
4
  module Tools
18
5
  extend self
@@ -24,12 +24,23 @@ module Roast
24
24
  @value
25
25
  end
26
26
 
27
+ # Enables implicit conversion to String
28
+ alias_method :to_str, :to_s
29
+
27
30
  def ==(other)
28
- return false unless other.is_a?(StepName)
31
+ case other
32
+ when StepName
33
+ value == other.value
34
+ when String
35
+ value == other
36
+ else
37
+ false
38
+ end
39
+ end
29
40
 
30
- value == other.value
41
+ def eql?(other)
42
+ other.is_a?(StepName) && value == other.value
31
43
  end
32
- alias_method :eql?, :==
33
44
 
34
45
  def hash
35
46
  [self.class, @value].hash
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pathname"
4
-
5
3
  module Roast
6
4
  module ValueObjects
7
5
  # Value object representing a workflow file path with validation and resolution
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/value_objects/api_token"
4
- require "roast/value_objects/step_name"
5
- require "roast/value_objects/workflow_path"
6
- require "roast/value_objects/uri_base"
3
+ module Roast
4
+ module ValueObjects
5
+ end
6
+ end
data/lib/roast/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Roast
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Roast
4
+ module Workflow
5
+ class AgentStep < BaseStep
6
+ def call
7
+ # For inline prompts (detected by plain text step names), use the name as the prompt
8
+ # For file-based steps, load from the prompt file
9
+ prompt_content = if name.plain_text?
10
+ name.to_s
11
+ else
12
+ read_sidecar_prompt
13
+ end
14
+
15
+ # Call CodingAgent directly with the prompt content
16
+ result = Roast::Tools::CodingAgent.call(prompt_content)
17
+
18
+ # Process output if print_response is enabled
19
+ process_output(result, print_response:)
20
+
21
+ # Apply coercion if configured
22
+ apply_coercion(result)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/factories/api_provider_factory"
4
- require "roast/workflow/resource_resolver"
5
- require "roast/value_objects/uri_base"
6
-
7
3
  module Roast
8
4
  module Workflow
9
5
  # Handles API-related configuration including tokens and providers
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/workflow/expression_utils"
4
- require "roast/workflow/llm_boolean_coercer"
5
- require "roast/workflow/workflow_executor"
6
-
7
3
  module Roast
8
4
  module Workflow
9
5
  # Base class for iteration steps (RepeatStep and EachStep)
@@ -1,37 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "erb"
4
- require "forwardable"
5
- require "roast/workflow/context_path_resolver"
6
-
7
3
  module Roast
8
4
  module Workflow
9
5
  class BaseStep
10
- extend Forwardable
11
-
12
- attr_accessor :model, :print_response, :json, :params, :resource, :coerce_to
6
+ attr_accessor :model, :print_response, :json, :params, :resource, :coerce_to, :available_tools
13
7
  attr_reader :workflow, :name, :context_path
14
8
 
15
- def_delegator :workflow, :append_to_final_output
16
- def_delegator :workflow, :chat_completion
17
- def_delegator :workflow, :transcript
9
+ delegate :append_to_final_output, :transcript, to: :workflow
10
+ delegate_missing_to :workflow
18
11
 
19
12
  # TODO: is this really the model we want to default to, and is this the right place to set it?
20
13
  def initialize(workflow, model: "anthropic:claude-opus-4", name: nil, context_path: nil)
21
14
  @workflow = workflow
22
15
  @model = model
23
- @name = name || self.class.name.underscore.split("/").last
16
+ @name = normalize_name(name)
24
17
  @context_path = context_path || ContextPathResolver.resolve(self.class)
25
18
  @print_response = false
26
19
  @json = false
27
20
  @params = {}
28
21
  @coerce_to = nil
22
+ @available_tools = nil
29
23
  @resource = workflow.resource if workflow.respond_to?(:resource)
30
24
  end
31
25
 
32
26
  def call
33
27
  prompt(read_sidecar_prompt)
34
- result = chat_completion(print_response:, json:, params:)
28
+ result = chat_completion(print_response:, json:, params:, available_tools:)
35
29
 
36
30
  # Apply coercion if configured
37
31
  apply_coercion(result)
@@ -39,25 +33,17 @@ module Roast
39
33
 
40
34
  protected
41
35
 
42
- def chat_completion(print_response: nil, json: nil, params: nil)
36
+ def chat_completion(print_response: nil, json: nil, params: nil, available_tools: nil)
43
37
  # Use instance variables as defaults if parameters are not provided
44
38
  print_response = @print_response if print_response.nil?
45
39
  json = @json if json.nil?
46
40
  params = @params if params.nil?
41
+ available_tools = @available_tools if available_tools.nil?
47
42
 
48
- workflow.chat_completion(openai: workflow.openai? && model, model: model, json:, params:).tap do |result|
49
- process_output(result, print_response:)
50
-
51
- begin
52
- if json
53
- return nil if result.strip.empty? # Explicitly handle empty string
43
+ result = workflow.chat_completion(openai: workflow.openai? && model, model: model, json:, params:, available_tools:)
44
+ process_output(result, print_response:)
54
45
 
55
- return JSON.parse(result)
56
- end
57
- rescue JSON::ParserError
58
- # If JSON parsing fails, leave it as a string
59
- end
60
- end
46
+ result
61
47
  end
62
48
 
63
49
  def prompt(text)
@@ -79,8 +65,20 @@ module Roast
79
65
  def process_output(response, print_response:)
80
66
  output_path = File.join(context_path, "output.txt")
81
67
  if File.exist?(output_path) && print_response
82
- # TODO: use the workflow binding or the step?
83
- append_to_final_output(ERB.new(File.read(output_path), trim_mode: "-").result(binding))
68
+ # Deep wrap the response for template access
69
+ template_response = deep_wrap_for_templates(response)
70
+
71
+ # Debug output
72
+ if template_response.is_a?(DotAccessHash) && template_response.recommendations&.is_a?(Array)
73
+ $stderr.puts "DEBUG: recommendations array has #{template_response.recommendations.size} items"
74
+ $stderr.puts "DEBUG: first item class: #{template_response.recommendations.first.class}" if template_response.recommendations.first
75
+ end
76
+
77
+ # Create a binding that includes the wrapped response
78
+ template_binding = binding
79
+ template_binding.local_variable_set(:response, template_response)
80
+
81
+ append_to_final_output(ERB.new(File.read(output_path), trim_mode: "-").result(template_binding))
84
82
  elsif print_response
85
83
  append_to_final_output(response)
86
84
  end
@@ -88,6 +86,35 @@ module Roast
88
86
 
89
87
  private
90
88
 
89
+ def normalize_name(name)
90
+ return name if name.is_a?(Roast::ValueObjects::StepName)
91
+
92
+ name_value = name || self.class.name.underscore.split("/").last
93
+ Roast::ValueObjects::StepName.new(name_value)
94
+ end
95
+
96
+ # Deep wrap response for ERB templates
97
+ # This creates a new structure where:
98
+ # - Hashes are wrapped in DotAccessHash
99
+ # - Arrays are cloned with their Hash elements wrapped
100
+ def deep_wrap_for_templates(obj)
101
+ case obj
102
+ when Hash
103
+ # Convert the hash to a new hash with wrapped values
104
+ wrapped_hash = {}
105
+ obj.each do |key, value|
106
+ wrapped_hash[key] = deep_wrap_for_templates(value)
107
+ end
108
+ DotAccessHash.new(wrapped_hash)
109
+ when Array
110
+ # Create a new array with wrapped elements
111
+ # This allows the template to use dot notation on array elements
112
+ obj.map { |item| deep_wrap_for_templates(item) }
113
+ else
114
+ obj
115
+ end
116
+ end
117
+
91
118
  def apply_coercion(result)
92
119
  case @coerce_to
93
120
  when :boolean
@@ -97,7 +124,6 @@ module Roast
97
124
  !!result
98
125
  when :llm_boolean
99
126
  # Use LLM boolean coercer for natural language responses
100
- require "roast/workflow/llm_boolean_coercer"
101
127
  LlmBooleanCoercer.coerce(result)
102
128
  when :iterable
103
129
  # Ensure result is iterable
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "raix/chat_completion"
4
- require "raix/function_dispatch"
5
-
6
- require "roast/workflow/context_path_resolver"
7
- require "roast/workflow/dot_access_hash"
8
- require "roast/workflow/output_manager"
9
-
10
3
  module Roast
11
4
  module Workflow
12
5
  class BaseWorkflow
@@ -29,6 +22,7 @@ module Roast
29
22
 
30
23
  delegate :api_provider, :openai?, to: :workflow_configuration, allow_nil: true
31
24
  delegate :output, :output=, :append_to_final_output, :final_output, to: :output_manager
25
+ delegate_missing_to :output
32
26
 
33
27
  def initialize(file = nil, name: nil, context_path: nil, resource: nil, session_name: nil, workflow_configuration: nil, pre_processing_data: nil)
34
28
  @file = file
@@ -81,7 +75,7 @@ module Roast
81
75
  rescue Faraday::ResourceNotFound => e
82
76
  execution_time = Time.now - start_time
83
77
  message = e.response.dig(:body, "error", "message") || e.message
84
- error = Roast::ResourceNotFoundError.new(message)
78
+ error = Roast::Errors::ResourceNotFoundError.new(message)
85
79
  error.set_backtrace(e.backtrace)
86
80
  log_and_raise_error(error, message, step_model || model, kwargs, execution_time)
87
81
  rescue => e
@@ -104,19 +98,6 @@ module Roast
104
98
  # Expose output manager for state management
105
99
  attr_reader :output_manager
106
100
 
107
- # Allow direct access to output values without 'output.' prefix
108
- def method_missing(method_name, *args, &block)
109
- if output.respond_to?(method_name)
110
- output.send(method_name, *args, &block)
111
- else
112
- super
113
- end
114
- end
115
-
116
- def respond_to_missing?(method_name, include_private = false)
117
- output.respond_to?(method_name) || super
118
- end
119
-
120
101
  private
121
102
 
122
103
  def log_and_raise_error(error, message, model, params, execution_time)
@@ -24,7 +24,6 @@ module Roast
24
24
  raise WorkflowExecutor::ConfigurationError, "Missing 'when' clauses in case configuration" unless when_clauses
25
25
 
26
26
  # Create and execute a CaseStep
27
- require "roast/workflow/case_step" unless defined?(Roast::Workflow::CaseStep)
28
27
  case_step = CaseStep.new(
29
28
  @workflow,
30
29
  config: case_config,
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/workflow/base_step"
4
- require "roast/workflow/expression_evaluator"
5
- require "roast/workflow/interpolator"
6
-
7
3
  module Roast
8
4
  module Workflow
9
5
  class CaseStep < BaseStep
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "English"
4
-
5
3
  module Roast
6
4
  module Workflow
7
5
  class CommandExecutor
@@ -24,7 +24,6 @@ module Roast
24
24
  raise WorkflowExecutor::ConfigurationError, "Missing 'then' steps in conditional configuration" unless then_steps
25
25
 
26
26
  # Create and execute a ConditionalStep
27
- require "roast/workflow/conditional_step" unless defined?(Roast::Workflow::ConditionalStep)
28
27
  conditional_step = ConditionalStep.new(
29
28
  @workflow,
30
29
  config: conditional_config,
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/workflow/base_step"
4
- require "roast/workflow/expression_evaluator"
5
- require "roast/workflow/interpolator"
6
-
7
3
  module Roast
8
4
  module Workflow
9
5
  class ConditionalStep < BaseStep
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/workflow/api_configuration"
4
- require "roast/workflow/configuration_loader"
5
- require "roast/workflow/resource_resolver"
6
- require "roast/workflow/step_finder"
7
-
8
3
  module Roast
9
4
  module Workflow
10
5
  # Encapsulates workflow configuration data and provides structured access
@@ -45,8 +40,6 @@ module Roast
45
40
  # Process target and resource
46
41
  @target = ConfigurationLoader.extract_target(@config_hash, options)
47
42
  process_resource
48
-
49
- mark_last_step_for_output
50
43
  end
51
44
 
52
45
  def context_path
@@ -98,65 +91,9 @@ module Roast
98
91
  attr_reader :api_configuration
99
92
 
100
93
  def process_resource
101
- if defined?(Roast::Resources)
102
- @resource = ResourceResolver.resolve(@target, context_path)
103
- # Update target with processed value for backward compatibility
104
- @target = @resource.value if has_target?
105
- end
106
- end
107
-
108
- def mark_last_step_for_output
109
- return if @steps.empty?
110
-
111
- last_step = find_last_executable_step(@steps.last)
112
- return unless last_step
113
-
114
- # Get the step name/key
115
- step_key = extract_step_key(last_step)
116
- return unless step_key
117
-
118
- # Ensure config exists for this step
119
- @config_hash[step_key] ||= {}
120
-
121
- # Only set print_response if not already explicitly configured
122
- @config_hash[step_key]["print_response"] = true unless @config_hash[step_key].key?("print_response")
123
- end
124
-
125
- def find_last_executable_step(step)
126
- case step
127
- when String
128
- step
129
- when Hash
130
- # Check if it's a special step type (if, unless, each, repeat, case)
131
- if step.key?("if") || step.key?("unless")
132
- # For conditional steps, try to find the last step in the "then" branch
133
- then_steps = step["then"] || step["steps"]
134
- find_last_executable_step(then_steps.last) if then_steps&.any?
135
- elsif step.key?("each") || step.key?("repeat")
136
- # For iteration steps, we can't reliably determine the last step
137
- nil
138
- elsif step.key?("case")
139
- # For case steps, we can't reliably determine the last step
140
- nil
141
- elsif step.size == 1
142
- # Regular hash step with variable assignment
143
- step
144
- end
145
- when Array
146
- # For parallel steps, we can't determine a single "last" step
147
- nil
148
- else
149
- step
150
- end
151
- end
152
-
153
- def extract_step_key(step)
154
- case step
155
- when String
156
- step
157
- when Hash
158
- step.keys.first
159
- end
94
+ @resource = ResourceResolver.resolve(@target, context_path)
95
+ # Update target with processed value for backward compatibility
96
+ @target = @resource.value if has_target?
160
97
  end
161
98
  end
162
99
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "yaml"
4
-
5
3
  module Roast
6
4
  module Workflow
7
5
  # Handles loading and parsing of workflow configuration files
@@ -1,17 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/workflow/configuration"
4
- require "roast/workflow/workflow_initializer"
5
- require "roast/workflow/workflow_runner"
6
-
7
3
  module Roast
8
4
  module Workflow
9
5
  class ConfigurationParser
10
- extend Forwardable
11
-
12
6
  attr_reader :configuration, :options, :files, :current_workflow
13
7
 
14
- def_delegator :current_workflow, :output
8
+ delegate :output, to: :current_workflow
15
9
 
16
10
  def initialize(workflow_path, files = [], options = {})
17
11
  @configuration = Configuration.new(workflow_path, options)
@@ -9,7 +9,7 @@ module Roast
9
9
 
10
10
  def [](key)
11
11
  value = @hash[key.to_sym] || @hash[key.to_s]
12
- value.is_a?(Hash) ? DotAccessHash.new(value) : value
12
+ wrap_value(value)
13
13
  end
14
14
 
15
15
  def []=(key, value)
@@ -193,6 +193,21 @@ module Roast
193
193
  end
194
194
 
195
195
  alias_method :member?, :has_key?
196
+
197
+ private
198
+
199
+ def wrap_value(value)
200
+ case value
201
+ when Hash
202
+ DotAccessHash.new(value)
203
+ when Array
204
+ # Don't create a new array - return the original array
205
+ # Only wrap Hash elements within the array when needed
206
+ value
207
+ else
208
+ value
209
+ end
210
+ end
196
211
  end
197
212
  end
198
213
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/helpers/logger"
4
- require "roast/workflow/command_executor"
5
-
6
3
  module Roast
7
4
  module Workflow
8
5
  # Handles error logging and instrumentation for workflow execution
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "roast/workflow/command_executor"
4
- require "roast/workflow/expression_utils"
5
-
6
3
  module Roast
7
4
  module Workflow
8
5
  # Shared module for evaluating expressions in workflow steps