roast-ai 0.4.10 → 0.5.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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/docs/write-comments.md +36 -0
  3. data/.github/CODEOWNERS +1 -1
  4. data/.github/workflows/ci.yaml +10 -6
  5. data/.gitignore +0 -1
  6. data/.rubocop.yml +7 -1
  7. data/CLAUDE.md +2 -2
  8. data/CONTRIBUTING.md +2 -0
  9. data/Gemfile +19 -18
  10. data/Gemfile.lock +35 -58
  11. data/README.md +118 -1432
  12. data/README_LEGACY.md +1464 -0
  13. data/Rakefile +39 -4
  14. data/dev.yml +29 -0
  15. data/dsl/agent_sessions.rb +20 -0
  16. data/dsl/async_cogs.rb +49 -0
  17. data/dsl/async_cogs_complex.rb +67 -0
  18. data/dsl/call.rb +44 -0
  19. data/dsl/collect_from.rb +72 -0
  20. data/dsl/json_output.rb +28 -0
  21. data/dsl/map.rb +55 -0
  22. data/dsl/map_reduce.rb +37 -0
  23. data/dsl/map_with_index.rb +49 -0
  24. data/dsl/next_break.rb +40 -0
  25. data/dsl/next_break_parallel.rb +44 -0
  26. data/dsl/outputs.rb +39 -0
  27. data/dsl/outputs_bang.rb +36 -0
  28. data/dsl/parallel_map.rb +37 -0
  29. data/dsl/prompts/simple_prompt.md.erb +3 -0
  30. data/dsl/prototype.rb +5 -7
  31. data/dsl/repeat_loop_results.rb +53 -0
  32. data/dsl/ruby_cog.rb +72 -0
  33. data/dsl/simple_agent.rb +18 -0
  34. data/dsl/simple_chat.rb +15 -1
  35. data/dsl/simple_repeat.rb +29 -0
  36. data/dsl/skip.rb +36 -0
  37. data/dsl/step_communication.rb +2 -3
  38. data/dsl/targets_and_params.rb +57 -0
  39. data/dsl/temperature.rb +17 -0
  40. data/dsl/temporary_directory.rb +22 -0
  41. data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
  42. data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
  43. data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
  44. data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
  45. data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
  46. data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
  47. data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
  48. data/dsl/tutorial/03_targets_and_params/README.md +230 -0
  49. data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
  50. data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
  51. data/dsl/tutorial/04_configuration_options/README.md +209 -0
  52. data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
  53. data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
  54. data/dsl/tutorial/05_control_flow/README.md +156 -0
  55. data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
  56. data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
  57. data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
  58. data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
  59. data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
  60. data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
  61. data/dsl/tutorial/07_processing_collections/README.md +152 -0
  62. data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
  63. data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
  64. data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
  65. data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
  66. data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
  67. data/dsl/tutorial/09_async_cogs/README.md +197 -0
  68. data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
  69. data/dsl/tutorial/README.md +222 -0
  70. data/dsl/working_directory.rb +16 -0
  71. data/exe/roast +1 -1
  72. data/internal/documentation/architectural-notes.md +115 -0
  73. data/internal/documentation/doc-comments-external.md +686 -0
  74. data/internal/documentation/doc-comments-internal.md +342 -0
  75. data/internal/documentation/doc-comments.md +211 -0
  76. data/lib/roast/dsl/cog/config.rb +274 -3
  77. data/lib/roast/dsl/cog/input.rb +53 -10
  78. data/lib/roast/dsl/cog/output.rb +297 -8
  79. data/lib/roast/dsl/cog/registry.rb +35 -3
  80. data/lib/roast/dsl/cog/stack.rb +1 -1
  81. data/lib/roast/dsl/cog/store.rb +5 -5
  82. data/lib/roast/dsl/cog.rb +70 -14
  83. data/lib/roast/dsl/cog_input_context.rb +36 -1
  84. data/lib/roast/dsl/cog_input_manager.rb +116 -7
  85. data/lib/roast/dsl/cogs/agent/config.rb +465 -0
  86. data/lib/roast/dsl/cogs/agent/input.rb +81 -0
  87. data/lib/roast/dsl/cogs/agent/output.rb +59 -0
  88. data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
  89. data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
  90. data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
  91. data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
  92. data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
  93. data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
  94. data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
  95. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
  96. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
  97. data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
  98. data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
  99. data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
  100. data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
  101. data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
  102. data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
  103. data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
  104. data/lib/roast/dsl/cogs/agent.rb +75 -0
  105. data/lib/roast/dsl/cogs/chat/config.rb +453 -0
  106. data/lib/roast/dsl/cogs/chat/input.rb +92 -0
  107. data/lib/roast/dsl/cogs/chat/output.rb +64 -0
  108. data/lib/roast/dsl/cogs/chat/session.rb +68 -0
  109. data/lib/roast/dsl/cogs/chat.rb +59 -56
  110. data/lib/roast/dsl/cogs/cmd.rb +248 -61
  111. data/lib/roast/dsl/cogs/ruby.rb +171 -0
  112. data/lib/roast/dsl/command_runner.rb +191 -0
  113. data/lib/roast/dsl/config_manager.rb +58 -11
  114. data/lib/roast/dsl/control_flow.rb +41 -0
  115. data/lib/roast/dsl/execution_manager.rb +162 -32
  116. data/lib/roast/dsl/nil_assertions.rb +23 -0
  117. data/lib/roast/dsl/system_cog/params.rb +32 -0
  118. data/lib/roast/dsl/system_cog.rb +36 -0
  119. data/lib/roast/dsl/system_cogs/call.rb +162 -0
  120. data/lib/roast/dsl/system_cogs/map.rb +448 -0
  121. data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
  122. data/lib/roast/dsl/workflow.rb +26 -16
  123. data/lib/roast/dsl/workflow_context.rb +20 -0
  124. data/lib/roast/dsl/workflow_params.rb +24 -0
  125. data/lib/roast/sorbet_runtime_stub.rb +154 -0
  126. data/lib/roast/tools/apply_diff.rb +1 -3
  127. data/lib/roast/tools/cmd.rb +4 -3
  128. data/lib/roast/tools/read_file.rb +1 -1
  129. data/lib/roast/tools/update_files.rb +1 -1
  130. data/lib/roast/tools/write_file.rb +1 -1
  131. data/lib/roast/version.rb +1 -1
  132. data/lib/roast/workflow/base_workflow.rb +4 -0
  133. data/lib/roast/workflow/step_loader.rb +14 -2
  134. data/lib/roast-ai.rb +4 -0
  135. data/lib/roast.rb +58 -21
  136. data/{roast.gemspec → roast-ai.gemspec} +9 -13
  137. data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
  138. data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
  139. data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
  140. data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
  141. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
  142. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
  143. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
  144. data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
  145. data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
  146. data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
  147. data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1185 -5
  148. data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +311 -5
  149. data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +486 -5
  150. data/sorbet/tapioca/config.yml +6 -0
  151. data/sorbet/tapioca/require.rb +2 -0
  152. metadata +157 -30
  153. data/dsl/less_simple.rb +0 -112
  154. data/dsl/scoped_executors.rb +0 -28
  155. data/dsl/simple.rb +0 -8
  156. data/lib/roast/dsl/cogs/execute.rb +0 -46
  157. data/lib/roast/dsl/cogs/graph.rb +0 -53
  158. data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
  159. data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
  160. data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
  161. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
  162. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
  163. data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
  164. data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
  165. data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
  166. data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
  167. data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
  168. data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
  169. data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
  170. data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
  171. data/sorbet/rbi/gems/rack@2.2.19.rbi +0 -5676
  172. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  173. data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
@@ -0,0 +1,242 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ module SystemCogs
7
+ # Repeat cog for executing a scope multiple times in a loop
8
+ #
9
+ # Executes a named execution scope (defined with `execute(:name)`) repeatedly until
10
+ # a `break!` is called. The output from each iteration becomes the input value for
11
+ # the next iteration, allowing for iterative transformations.
12
+ class Repeat < SystemCog
13
+ # Configuration for the `repeat` cog
14
+ #
15
+ # Currently has no configuration options.
16
+ class Config < Cog::Config; end
17
+
18
+ # Parameters for the `repeat` cog
19
+ class Params < SystemCog::Params
20
+ # The name of the execution scope to invoke for each iteration
21
+ #
22
+ #: Symbol
23
+ attr_accessor :run
24
+
25
+ # Initialize parameters with the cog name and execution scope
26
+ #
27
+ #: (?Symbol?, run: Symbol) -> void
28
+ def initialize(name = nil, run:)
29
+ super(name)
30
+ @run = run
31
+ end
32
+ end
33
+
34
+ # Input for the `repeat` cog
35
+ #
36
+ # Provides the initial value to pass to the first iteration. Each subsequent iteration
37
+ # receives the output from the previous iteration as its value.
38
+ class Input < Cog::Input
39
+ # The initial value to pass to the first iteration
40
+ #
41
+ # This value will be passed to the execution scope on the first iteration. Subsequent
42
+ # iterations receive the output from the previous iteration. Required.
43
+ #
44
+ #: untyped
45
+ attr_accessor :value
46
+
47
+ # The starting index for the first iteration
48
+ #
49
+ # Defaults to `0`. This affects the index value passed to each iteration.
50
+ #
51
+ # Integer
52
+ attr_accessor :index
53
+
54
+ # The maximum number of iterations for which the loop may run
55
+ #
56
+ # Defaults to `nil`, meaning that no maximum iteration limit is applied
57
+ #
58
+ #: Integer?
59
+ attr_accessor :max_iterations
60
+
61
+ # Initialize the input with default values
62
+ #
63
+ #: () -> void
64
+ def initialize
65
+ super
66
+ @index = 0
67
+ end
68
+
69
+ # Validate that required input values are present
70
+ #
71
+ #: () -> void
72
+ def validate!
73
+ raise Cog::Input::InvalidInputError, "'value' is required" if value.nil? && !coerce_ran?
74
+ raise Cog::Input::InvalidInputError, "'max_iterations' must be >= 1 if present" if (max_iterations || 1) < 1
75
+ end
76
+
77
+ # Coerce the input from the return value of the input block
78
+ #
79
+ # Sets the value from the input block's return value if not already set directly.
80
+ #
81
+ #: (untyped) -> void
82
+ def coerce(input_return_value)
83
+ super
84
+ @value = input_return_value unless @value.present?
85
+ end
86
+ end
87
+
88
+ # Output from running the `repeat` cog
89
+ #
90
+ # Contains results from all iterations that ran. Provides access to the final value
91
+ # (output from the last iteration) as well as individual iteration results.
92
+ #
93
+ # #### See Also
94
+ # - `Roast::DSL::CogInputContext#collect` - retrieves all iteration outputs as an array (via `results`)
95
+ # - `Roast::DSL::CogInputContext#reduce` - reduces iteration outputs to a single value (via `results`)
96
+ class Output < Cog::Output
97
+ # Initialize the output with results for all iterations
98
+ #
99
+ #: (Array[ExecutionManager]) -> void
100
+ def initialize(execution_managers)
101
+ super()
102
+ @execution_managers = execution_managers
103
+ end
104
+
105
+ # Get the final output value from the last iteration
106
+ #
107
+ # This is the output from the last iteration before `break!` was called.
108
+ # Returns `nil` if no iterations ran.
109
+ #
110
+ # #### Usage
111
+ # ```ruby
112
+ # # Get the final result directly
113
+ # final = repeat!(:process)
114
+ # ```
115
+ #
116
+ # #### See Also
117
+ # - `last`
118
+ # - `results`
119
+ #
120
+ #: () -> untyped
121
+ def value
122
+ @execution_managers.last&.final_output
123
+ end
124
+
125
+ # Get the output from a specific iteration
126
+ #
127
+ # Returns a `Roast::DSL::SystemCogs::Call::Output` object for the iteration at the given index.
128
+ # Supports negative indices to count from the end (e.g., `-1` for the last iteration).
129
+ #
130
+ # #### Usage
131
+ # ```ruby
132
+ # # Access a specific iteration
133
+ # result = from(repeat!(:process).iteration(2))
134
+ #
135
+ # # Access with negative index
136
+ # result = from(repeat!(:process).iteration(-1))
137
+ # ```
138
+ #
139
+ # #### See Also
140
+ # - `first`
141
+ # - `last`
142
+ # - `value`
143
+ # - `Roast::DSL::CogInputContext#from`
144
+ #
145
+ #: (Integer) -> Call::Output
146
+ def iteration(index)
147
+ Call::Output.new(@execution_managers.fetch(index))
148
+ end
149
+
150
+ # Get the output from the first iteration
151
+ #
152
+ # Convenience method equivalent to `iteration(0)`.
153
+ #
154
+ # #### See Also
155
+ # - `iteration`
156
+ # - `last`
157
+ #
158
+ #: () -> Call::Output
159
+ def first
160
+ iteration(0)
161
+ end
162
+
163
+ # Get the output from the last iteration
164
+ #
165
+ # Convenience method equivalent to `iteration(-1)`. Returns the same value as
166
+ # calling `value`, but wrapped in a `Roast::DSL::SystemCogs::Call::Output` for use with `from`.
167
+ #
168
+ # #### See Also
169
+ # - `iteration`
170
+ # - `first`
171
+ # - `value`
172
+ #
173
+ #: () -> Call::Output
174
+ def last
175
+ iteration(-1)
176
+ end
177
+
178
+ # Get all iteration results as a `Roast::DSL::SystemCogs::Map::Output` object
179
+ #
180
+ # Returns a `Roast::DSL::SystemCogs::Map::Output` containing all iterations, which can be used with
181
+ # `collect` or `reduce` to process all iteration outputs.
182
+ #
183
+ # #### Usage
184
+ # ```ruby
185
+ # # Collect all iteration outputs
186
+ # all_results = collect(repeat!(:process).results)
187
+ #
188
+ # # Reduce all iteration outputs
189
+ # sum = reduce(repeat!(:process).results, 0) { |acc, output| acc + output }
190
+ # ```
191
+ #
192
+ # #### See Also
193
+ # - `value`
194
+ # - `Roast::DSL::CogInputContext#collect`
195
+ # - `Roast::DSL::CogInputContext#reduce`
196
+ # - `Roast::DSL::SystemCogs::Map::Output`
197
+ #
198
+ #: () -> Map::Output
199
+ def results
200
+ Map::Output.new(@execution_managers)
201
+ end
202
+ end
203
+
204
+ # @requires_ancestor: Roast::DSL::ExecutionManager
205
+ module Manager
206
+ private
207
+
208
+ #: (Params, ^(Cog::Input) -> untyped) -> SystemCogs::Repeat
209
+ def create_repeat_system_cog(params, input_proc)
210
+ SystemCogs::Repeat.new(params.name, input_proc) do |input|
211
+ input = input #: as Input
212
+ raise ExecutionManager::ExecutionScopeNotSpecifiedError unless params.run.present?
213
+
214
+ ems = [] #: Array[ExecutionManager]
215
+ scope_value = input.value.deep_dup
216
+ max_iterations = input.max_iterations
217
+ loop do
218
+ ems << em = ExecutionManager.new(
219
+ @cog_registry,
220
+ @config_manager,
221
+ @all_execution_procs,
222
+ @workflow_context,
223
+ scope: params.run,
224
+ scope_value: scope_value,
225
+ scope_index: ems.length,
226
+ )
227
+ em.prepare!
228
+ em.run!
229
+ scope_value = em.final_output
230
+ break if max_iterations.present? && ems.length >= max_iterations
231
+ rescue ControlFlow::Break
232
+ # TODO: do something with the message passed to break!
233
+ break
234
+ end
235
+ Output.new(ems)
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -11,18 +11,22 @@ module Roast
11
11
  class InvalidCogReference < WorkflowError; end
12
12
 
13
13
  class << self
14
- #: (String) -> void
15
- def from_file(workflow_path)
16
- workflow = new(workflow_path)
17
- workflow.prepare!
18
- workflow.start!
14
+ #: (String, WorkflowParams) -> void
15
+ def from_file(workflow_path, params)
16
+ Dir.mktmpdir("roast-") do |tmpdir|
17
+ workflow_context = WorkflowContext.new(params:, tmpdir:)
18
+ workflow = new(workflow_path, workflow_context)
19
+ workflow.prepare!
20
+ workflow.start!
21
+ end
19
22
  end
20
23
  end
21
24
 
22
- #: (String) -> void
23
- def initialize(workflow_path)
24
- @workflow_path = Pathname.new(workflow_path)
25
- @workflow_definition = File.read(workflow_path)
25
+ #: (String, WorkflowContext) -> void
26
+ def initialize(workflow_path, workflow_context)
27
+ @workflow_path = Pathname.new(workflow_path) #: Pathname
28
+ @workflow_context = workflow_context #: WorkflowContext
29
+ @workflow_definition = File.read(workflow_path) #: String
26
30
  @cog_registry = Cog::Registry.new #: Cog::Registry
27
31
  @config_procs = [] #: Array[^() -> void]
28
32
  @execution_procs = { nil: [] } #: Hash[Symbol?, Array[^() -> void]]
@@ -37,9 +41,10 @@ module Roast
37
41
  @preparing = true
38
42
  extract_dsl_procs!
39
43
  @config_manager = ConfigManager.new(@cog_registry, @config_procs)
40
- @config_manager.prepare!
41
- @execution_manager = ExecutionManager.new(@cog_registry, @config_manager, @execution_procs)
42
- @execution_manager.prepare!
44
+ @config_manager.not_nil!.prepare!
45
+ # TODO: probably we should just not pass the params as the top-level scope value anymore
46
+ @execution_manager = ExecutionManager.new(@cog_registry, @config_manager.not_nil!, @execution_procs, @workflow_context, scope_value: @workflow_context.params)
47
+ @execution_manager.not_nil!.prepare!
43
48
 
44
49
  @prepared = true
45
50
  end
@@ -50,7 +55,12 @@ module Roast
50
55
  raise WorkflowAlreadyStartedError if started? || completed?
51
56
 
52
57
  @started = true
53
- @execution_manager.run!
58
+ begin
59
+ @execution_manager.run!
60
+ rescue ControlFlow::Break
61
+ # treat `break!` like `next!` in the top-level executor scope
62
+ # TODO: maybe do something with the message passed to break!
63
+ end
54
64
  @completed = true
55
65
  end
56
66
 
@@ -74,12 +84,12 @@ module Roast
74
84
  @completed ||= false
75
85
  end
76
86
 
77
- #: { () [self: ConfigContext] -> void } -> void
87
+ #: { () [self: Roast::DSL::ConfigContext] -> void } -> void
78
88
  def config(&block)
79
89
  @config_procs << block
80
90
  end
81
91
 
82
- #: (?Symbol?) { () [self: ExecutionContext] -> void } -> void
92
+ #: (?Symbol?) { () [self: Roast::DSL::ExecutionContext] -> void } -> void
83
93
  def execute(scope = nil, &block)
84
94
  (@execution_procs[scope] ||= []) << block
85
95
  end
@@ -106,7 +116,7 @@ module Roast
106
116
  # but does not evaluate any of them individually yet.
107
117
  #: () -> void
108
118
  def extract_dsl_procs!
109
- instance_eval(@workflow_definition)
119
+ instance_eval(@workflow_definition, @workflow_path.realpath.to_s, 1)
110
120
  end
111
121
  end
112
122
  end
@@ -0,0 +1,20 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class WorkflowContext
7
+ #: WorkflowParams
8
+ attr_reader :params
9
+
10
+ #: String
11
+ attr_reader :tmpdir
12
+
13
+ #: (params: WorkflowParams, tmpdir: String) -> void
14
+ def initialize(params:, tmpdir:)
15
+ @params = params
16
+ @tmpdir = tmpdir
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class WorkflowParams
7
+ #: Array[String]
8
+ attr_reader :targets
9
+
10
+ #: Array[Symbol]
11
+ attr_reader :args
12
+
13
+ #: Hash[Symbol, String]
14
+ attr_reader :kwargs
15
+
16
+ #: (Array[String], Array[Symbol], Hash[Symbol, String]) -> void
17
+ def initialize(targets, args, kwargs)
18
+ @targets = targets
19
+ @args = args
20
+ @kwargs = kwargs
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,154 @@
1
+ # typed: ignore
2
+ # frozen_string_literal: true
3
+
4
+ module T
5
+ class << self
6
+ def absurd(value); end
7
+ def all(type_a, type_b, *types); end
8
+ def any(type_a, type_b, *types); end
9
+ def attached_class; end
10
+ def class_of(klass); end
11
+ def enum(values); end
12
+ def nilable(type); end
13
+ def noreturn; end
14
+ def self_type; end
15
+ def type_alias(type = nil, &_blk); end
16
+ def type_parameter(name); end
17
+ def untyped; end
18
+
19
+ def assert_type!(value, _type, _checked: true)
20
+ value
21
+ end
22
+
23
+ def cast(value, _type, _checked: true)
24
+ value
25
+ end
26
+
27
+ def let(value, _type, _checked: true)
28
+ value
29
+ end
30
+
31
+ def must(arg, _msg = nil)
32
+ arg
33
+ end
34
+
35
+ def proc
36
+ T::Proc.new
37
+ end
38
+
39
+ def reveal_type(value)
40
+ value
41
+ end
42
+
43
+ def unsafe(value)
44
+ value
45
+ end
46
+ end
47
+
48
+ module Sig
49
+ def sig(arg0 = nil, &blk); end
50
+ end
51
+
52
+ module Helpers
53
+ def abstract!; end
54
+ def interface!; end
55
+ def final!; end
56
+ def sealed!; end
57
+ def mixes_in_class_methods(mod); end
58
+ end
59
+
60
+ module Generic
61
+ include(T::Helpers)
62
+
63
+ def type_parameters(*params); end
64
+ def type_member(variance = :invariant, fixed: nil, lower: nil, upper: BasicObject); end
65
+ def type_template(variance = :invariant, fixed: nil, lower: nil, upper: BasicObject); end
66
+
67
+ def [](*types)
68
+ self
69
+ end
70
+ end
71
+
72
+ module Array
73
+ class << self
74
+ def [](type); end
75
+ end
76
+ end
77
+
78
+ Boolean = Object.new.freeze
79
+
80
+ module Configuration
81
+ class << self
82
+ def call_validation_error_handler(signature, opts); end
83
+ def call_validation_error_handler=(value); end
84
+ def default_checked_level=(default_checked_level); end
85
+ def enable_checking_for_sigs_marked_checked_tests; end
86
+ def enable_final_checks_on_hooks; end
87
+ def enable_legacy_t_enum_migration_mode; end
88
+ def reset_final_checks_on_hooks; end
89
+ def hard_assert_handler(str, extra); end
90
+ def hard_assert_handler=(value); end
91
+ def inline_type_error_handler(error); end
92
+ def inline_type_error_handler=(value); end
93
+ def log_info_handler(str, extra); end
94
+ def log_info_handler=(value); end
95
+ def scalar_types; end
96
+ def scalar_types=(values); end
97
+ def sealed_violation_whitelist; end
98
+ def sealed_violation_whitelist=(sealed_violation_whitelist); end
99
+ def sig_builder_error_handler=(value); end
100
+ def sig_validation_error_handler(error, opts); end
101
+ def sig_validation_error_handler=(value); end
102
+ def soft_assert_handler(str, extra); end
103
+ def soft_assert_handler=(value); end
104
+ end
105
+ end
106
+
107
+ module Enumerable
108
+ class << self
109
+ def [](type); end
110
+ end
111
+ end
112
+
113
+ module Enumerator
114
+ class << self
115
+ def [](type); end
116
+ end
117
+ end
118
+
119
+ module Hash
120
+ class << self
121
+ def [](keys, values); end
122
+ end
123
+ end
124
+
125
+ class Proc
126
+ def bind(*_)
127
+ self
128
+ end
129
+
130
+ def params(*_param)
131
+ self
132
+ end
133
+
134
+ def void
135
+ self
136
+ end
137
+
138
+ def returns(_type)
139
+ self
140
+ end
141
+ end
142
+
143
+ module Range
144
+ class << self
145
+ def [](type); end
146
+ end
147
+ end
148
+
149
+ module Set
150
+ class << self
151
+ def [](type); end
152
+ end
153
+ end
154
+ end
@@ -1,8 +1,6 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
- require "cli/ui"
5
-
6
4
  module Roast
7
5
  module Tools
8
6
  module ApplyDiff
@@ -64,7 +62,7 @@ module Roast
64
62
  Roast::Helpers::Logger.info(cancel_msg + "\n")
65
63
  cancel_msg
66
64
  end
67
- rescue Roast::Error => e
65
+ rescue Roast::Error, Errno::EACCES, Errno::ENOENT => e
68
66
  error_message = "Error applying diff: #{e.message}"
69
67
  Roast::Helpers::Logger.error(error_message + "\n")
70
68
  Roast::Helpers::Logger.debug(e.backtrace.join("\n") + "\n") if ENV["DEBUG"]
@@ -153,9 +153,10 @@ module Roast
153
153
  )
154
154
 
155
155
  format_output(command, result, status.exitstatus)
156
- rescue Timeout::Error => e
157
- Roast::Helpers::Logger.error(e.message + "\n")
158
- e.message
156
+ rescue Timeout::Error, Errno::ENOENT => e
157
+ error_message = "Error running command: #{e.message}"
158
+ Roast::Helpers::Logger.error(error_message + "\n")
159
+ error_message
159
160
  end
160
161
 
161
162
  def format_output(command, result, exit_status)
@@ -39,7 +39,7 @@ module Roast
39
39
  else
40
40
  File.read(path)
41
41
  end
42
- rescue Roast::Error => e
42
+ rescue Roast::Error, Errno::ENOENT, Errno::EACCES => e
43
43
  "Error reading file: #{e.message}".tap do |error_message|
44
44
  Roast::Helpers::Logger.error(error_message + "\n")
45
45
  Roast::Helpers::Logger.debug(e.backtrace.join("\n") + "\n") if ENV["DEBUG"]
@@ -306,7 +306,7 @@ module Roast
306
306
  if new_content_lines
307
307
  content_lines = new_content_lines
308
308
  else
309
- raise "Hunk could not be applied cleanly: #{hunk[:header]}"
309
+ raise Roast::Error, "Hunk could not be applied cleanly: #{hunk[:header]}"
310
310
  end
311
311
  end
312
312
 
@@ -49,7 +49,7 @@ module Roast
49
49
  Roast::Helpers::Logger.error(restriction_message)
50
50
  "Error: Path must start with '#{restrict_path}' to use the write_file tool, try again."
51
51
  end
52
- rescue Roast::Error => e
52
+ rescue Roast::Error, Errno::EACCES, Errno::ENOENT => e
53
53
  "Error writing file: #{e.message}".tap do |error_message|
54
54
  Roast::Helpers::Logger.error(error_message + "\n")
55
55
  Roast::Helpers::Logger.debug(e.backtrace.join("\n") + "\n") if ENV["DEBUG"]
data/lib/roast/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Roast
5
- VERSION = "0.4.10"
5
+ VERSION = "0.5.0"
6
6
  end
@@ -175,7 +175,11 @@ module Roast
175
175
 
176
176
  if error_detail && !error_detail.empty? && !message.include?(error_detail)
177
177
  " (#{error_detail})"
178
+ else
179
+ ""
178
180
  end
181
+ else
182
+ ""
179
183
  end
180
184
 
181
185
  message
@@ -169,7 +169,14 @@ module Roast
169
169
  raise StepExecutionError.new("Syntax error in step file: #{e.message}", step_name: step_name, original_error: e)
170
170
  end
171
171
 
172
- step_class = step_name.classify.constantize
172
+ step_class = if Object.const_defined?(step_name.camelize, false)
173
+ step_name.camelize.constantize
174
+ elsif Object.const_defined?(step_name.classify, false)
175
+ step_name.classify.constantize
176
+ else
177
+ raise StepExecutionError.new("No class named #{step_name.camelize} found in step file #{file_path}", step_name: step_name)
178
+ end
179
+
173
180
  context = File.dirname(file_path)
174
181
  # For Ruby steps, we instantiate the specific class directly
175
182
  # Convert step_name to StepName value object
@@ -208,7 +215,12 @@ module Roast
208
215
  def configure_step(step, step_name, is_last_step: nil)
209
216
  step_config = config_hash[step_name]
210
217
 
211
- # Only set the model if explicitly specified for this step
218
+ # Apply global model if no step-specific model is configured
219
+ if config_hash.key?("model") && !step_config&.key?("model")
220
+ step.model = config_hash["model"]
221
+ end
222
+
223
+ # Apply step-specific model (overrides global model)
212
224
  step.model = step_config["model"] if step_config&.key?("model")
213
225
 
214
226
  # Pass resource to step if supported
data/lib/roast-ai.rb ADDED
@@ -0,0 +1,4 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "roast"