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,57 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ chat { no_display! }
8
+ end
9
+
10
+ execute(:refine_text) do
11
+ chat(:improve) do |_, text|
12
+ <<~PROMPT
13
+ Improve this text by making it more concise and clear.
14
+ Keep the same meaning but use fewer words.
15
+
16
+ Text: #{text}
17
+
18
+ Return only the improved text, nothing else.
19
+ PROMPT
20
+ end
21
+
22
+ ruby do |_, _, index|
23
+ puts "\n--- Iteration #{index} ---"
24
+ puts chat!(:improve).response
25
+ break! if index >= 3
26
+ end
27
+
28
+ outputs { chat!(:improve).text }
29
+ end
30
+
31
+ execute do
32
+ initial_text = <<~TEXT
33
+ It is widely known and commonly accepted by many people that
34
+ regular physical exercise and activity is generally beneficial
35
+ and helpful for maintaining good health and overall wellness.
36
+ TEXT
37
+
38
+ puts "=== Original Text ==="
39
+ puts initial_text
40
+
41
+ repeat(:refinement, run: :refine_text) { initial_text }
42
+
43
+ ruby do
44
+ puts "\n=== Final Result ==="
45
+ puts repeat!(:refinement).value
46
+
47
+ puts "\n=== All Iterations ==="
48
+ all_versions = collect(repeat!(:refinement).results) do
49
+ chat!(:improve).response
50
+ end
51
+
52
+ all_versions.each_with_index do |version, i|
53
+ puts "\nIteration #{i}:"
54
+ puts version
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ chat { display! }
8
+ end
9
+
10
+ execute(:guess_number) do
11
+ ruby { |_, _, index| puts "\n--- Iteration #{index} ---" }
12
+
13
+ chat(:make_initial_guess) do |_, _, index|
14
+ skip! unless index == 0
15
+
16
+ <<~PROMPT
17
+ I'm thinking of a number between 1 and 100.
18
+ Take a guess! Respond with just the number.
19
+ PROMPT
20
+ end
21
+
22
+ chat(:make_guess) do |my, state, index|
23
+ skip! if index == 0
24
+
25
+ guess, session, target = state.values_at(:guess, :session, :target)
26
+ break! if guess == target
27
+
28
+ my.session = session
29
+ my.prompt = "Too #{guess > target ? "high" : "low"}. Guess again"
30
+ end
31
+
32
+ outputs do |_, target|
33
+ result = chat(:make_guess) || chat!(:make_initial_guess)
34
+ { guess: result.integer, session: result.session, target: }
35
+ end
36
+ end
37
+
38
+ execute do
39
+ ruby do
40
+ fail! "Target number out of range" if target!.to_i < 1 || target!.to_i > 100
41
+
42
+ puts "=== Number Guessing Game ==="
43
+ puts "Target number: #{target!} (hidden from the LLM)\n"
44
+ end
45
+
46
+ repeat(:guessing, run: :guess_number) do |my|
47
+ my.value = target!.to_i
48
+ my.max_iterations = 7
49
+ end
50
+
51
+ ruby do
52
+ iteration_count = collect(repeat!(:guessing).results).length
53
+
54
+ puts "\n=== Game Complete ==="
55
+ puts "Found the number in #{iteration_count} guesses!"
56
+ end
57
+ end
@@ -0,0 +1,197 @@
1
+ # Chapter 9: Asynchronous Cogs
2
+
3
+ Learn how to run cogs asynchronously in the background while other work continues. This is different from `map` with
4
+ parallel execution, which can run multiple invocations of an execution scope in parallel, each processing a different
5
+ input. Async cogs let you run all sorts of independent tasks concurrently, __within the same execution scope__ without
6
+ waiting for one task to complete before starting another.
7
+
8
+ ## What Are Async Cogs?
9
+
10
+ By default, cogs run synchronously: each cog must complete before the next one starts. With async cogs, you can kick off
11
+ long-running tasks in the background and continue with other work immediately.
12
+
13
+ ```ruby
14
+ config do
15
+ agent(:analyze_code) { async! }
16
+ end
17
+
18
+ execute do
19
+ # This agent starts running in the background
20
+ agent(:analyze_code) do
21
+ "Analyze the Ruby files in src/ for code quality issues. List the top three issues."
22
+ end
23
+
24
+ # This starts immediately without waiting for the agent to finish
25
+ chat(:draft_email) do
26
+ "Draft a brief status update email about the current sprint"
27
+ end
28
+
29
+ # This blocks until the agent completes, then uses its output
30
+ chat(:finish_email) do |my|
31
+ my.session = chat!(:draft_email).session
32
+ my.prompt = <<~PROMPT
33
+ "Update the status email with a summary of the issues we'll tackle next week:
34
+ #{agent!(:analyze_code).text}"
35
+ PROMPT
36
+ end
37
+ end
38
+ ```
39
+
40
+ ## Key Differences from Parallel Map
41
+
42
+ - **Async cogs**: Run different, independent tasks concurrently in the same scope (e.g., analyze code + draft email)
43
+ - **Parallel map**: Apply the same operation to multiple items concurrently (e.g., process 10 files in parallel), each in their own scope
44
+
45
+ Use async cogs when you have separate tasks that can happen at the same time. Use parallel map when you're processing a
46
+ collection.
47
+
48
+ ## Configuring Async Cogs
49
+
50
+ Configure cogs to run asynchronously in the `config` block:
51
+
52
+ ```ruby
53
+ config do
54
+ # Make specific cog async
55
+ agent(:background_task) { async! }
56
+
57
+ # Make all agent cogs async
58
+ agent { async! }
59
+
60
+ # Pattern-based configuration
61
+ agent(/analyze_/) { async! }
62
+
63
+ # Override a more general config to make a cog explicitly synchronous (this is the default)
64
+ chat(:critical_step) { no_async! }
65
+ end
66
+ ```
67
+
68
+ ## How Async Execution Works
69
+
70
+ When an async cog is invoked:
71
+
72
+ 1. It starts running in the background
73
+ 2. The next cog starts immediately (doesn't wait)
74
+ 3. If you try access the async cog's output from a later cog, that will block until the async cog completes
75
+ 4. The workflow doesn't exit until all async cogs have finished
76
+ 5. A named execute scope doesn't complete until all async cogs within it have finished
77
+
78
+ ```ruby
79
+ config do
80
+ agent { async! }
81
+ end
82
+
83
+ execute do
84
+ agent(:task1) { "Read and summarize README.md" }
85
+ agent(:task2) { "Count lines of code in src/" }
86
+ agent(:task3) { "List all TODO comments" }
87
+
88
+ # Accessing outputs blocks until each completes
89
+ ruby do
90
+ puts "\nTask 1: #{agent!(:task1).response}"
91
+ puts "\nTask 2: #{agent!(:task2).response}"
92
+ puts "\nTask 3: #{agent!(:task3).response}"
93
+ end
94
+ end
95
+ ```
96
+
97
+ ## Accessing Async Cog Outputs
98
+
99
+ All three accessor patterns (the `!`, `?` and normal cog methods) work with async cogs and all will block if needed:
100
+
101
+ ```ruby
102
+ config do
103
+ agent { async! }
104
+ end
105
+
106
+ execute do
107
+ agent(:background) { "Long running task" }
108
+
109
+ ruby do
110
+ # All of these block until the async cog completes:
111
+
112
+ # Check if it ran (blocks)
113
+ if agent?(:background)
114
+ puts "Completed!"
115
+ end
116
+
117
+ # Get output or nil (blocks)
118
+ result = agent(:background)
119
+
120
+ # Get output or raise error (blocks)
121
+ result = agent!(:background)
122
+ end
123
+ end
124
+ ```
125
+
126
+ ## Real-World Example: Parallel Code Analysis
127
+
128
+ Run multiple independent analyses concurrently:
129
+
130
+ ```ruby
131
+ config do
132
+ agent { async! }
133
+ end
134
+
135
+ execute do
136
+ # All three start immediately
137
+ agent(:security) do
138
+ "Review files in src/ for security vulnerabilities"
139
+ end
140
+
141
+ agent(:performance) do
142
+ "Identify performance bottlenecks in src/"
143
+ end
144
+
145
+ agent(:style) do
146
+ "Check code style and formatting in src/"
147
+ end
148
+
149
+ # Collect results as they complete
150
+ ruby do
151
+ puts "\n=== Security Issues ==="
152
+ puts agent!(:security).response
153
+
154
+ puts "\n=== Performance Issues ==="
155
+ puts agent!(:performance).response
156
+
157
+ puts "\n=== Style Issues ==="
158
+ puts agent!(:style).response
159
+ end
160
+ end
161
+ ```
162
+
163
+ ## When to Use Async Cogs
164
+
165
+ **Good use cases:**
166
+
167
+ - Running multiple independent agent tasks that don't depend on each other
168
+ - Starting a long-running background task while doing other work
169
+ - Parallelizing unrelated API calls or file operations
170
+
171
+ **Not needed when:**
172
+
173
+ - Cogs already run fast enough sequentially
174
+ - Cogs depend on each other's outputs (they'll block anyway)
175
+ - You're processing a collection (use `map` with `parallel!` instead)
176
+
177
+ ## Important Notes
178
+
179
+ - Async cogs use Ruby's fiber-based concurrency (efficient and lightweight)
180
+ - The workflow waits for all async cogs to complete before exiting
181
+ - Accessing an async cog's output blocks until that cog finishes
182
+ - All cog types (`agent`, `chat`, `cmd`, `ruby`, and even `call`, `map`, and `repeat`) can be async
183
+
184
+ ## Running the Workflows
185
+
186
+ Try these examples to see async cogs in action:
187
+
188
+ ```bash
189
+ # Basic async execution
190
+ bin/roast execute --executor=dsl dsl/tutorial/09_async_cogs/basic_async.rb
191
+
192
+ # Parallel analysis with multiple agents
193
+ bin/roast execute --executor=dsl dsl/tutorial/09_async_cogs/parallel_analysis.rb
194
+
195
+ # Synchronous barriers controlling execution order
196
+ bin/roast execute --executor=dsl dsl/tutorial/09_async_cogs/sync_barriers.rb
197
+ ```
@@ -0,0 +1,38 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ agent do
8
+ async!
9
+ display!
10
+ end
11
+ end
12
+
13
+ execute do
14
+ # All three agents start immediately and run in the background
15
+ agent(:readme) do
16
+ "Read the README.md file and give me a one-sentence summary of what this project does."
17
+ end
18
+
19
+ agent(:gemfile) do
20
+ "Look at the Gemfile and list the 3 most important dependencies (just their names)."
21
+ end
22
+
23
+ agent(:structure) do
24
+ "Look at the directory structure and tell me what programming language this project uses."
25
+ end
26
+
27
+ # When we access outputs, we block until that agent completes
28
+ ruby do
29
+ puts "=== Project Summary ==="
30
+ puts agent!(:readme).response
31
+
32
+ puts "=== Key Dependencies ==="
33
+ puts agent!(:gemfile).response
34
+
35
+ puts "=== Language ==="
36
+ puts agent!(:structure).response
37
+ end
38
+ end
@@ -0,0 +1,222 @@
1
+ ![roast-horiz-logo](https://github.com/user-attachments/assets/f9b1ace2-5478-4f4a-ac8e-5945ed75c5b4)
2
+
3
+ # Roast Tutorial
4
+
5
+ 🔥 _version 1.0 feature preview_ 🔥
6
+
7
+ Welcome to the Roast tutorial! This guide will teach you how to build AI workflows using the Roast DSL.
8
+
9
+ ## What is Roast?
10
+
11
+ Roast is a Ruby-based domain-specific language for creating structured AI workflows
12
+ and building complex AI-powered automation. You write workflows in a simple Ruby syntax to orchestrate LLM calls,
13
+ run coding agents, process data, and so much more.
14
+
15
+ ## Prerequisites
16
+
17
+ - Ruby installed (3.4.2+)
18
+ - Roast gem installed
19
+ - API keys for your AI providers of choice (to use LLM chat)
20
+ - Claude Code CLI installed and configured (to use coding agents)
21
+
22
+ ## How to Use This Tutorial
23
+
24
+ Each chapter is a self-contained lesson with:
25
+
26
+ - **README.md** - Lesson content with explanations and code snippets
27
+ - **Workflow files** (`.rb`) - Complete, runnable examples
28
+ - **data/** folder - Sample data files (when needed)
29
+
30
+ To run any example:
31
+
32
+ ```bash
33
+ bin/roast execute --executor=dsl dsl/tutorial/CHAPTER_NAME/workflow_name.rb
34
+ ```
35
+
36
+ ## Tutorial Chapters
37
+
38
+ ### Chapter 1: Your First Workflow
39
+
40
+ Quickly learn the basics: how to create and run a simple workflow with a single chat cog.
41
+
42
+ **You'll learn:**
43
+
44
+ - Basic workflow file structure
45
+ - Using the `chat` cog
46
+ - Running workflows
47
+ - Simple configuration
48
+
49
+ **Files:**
50
+
51
+ - `01_your_first_workflow/hello.rb` - Simplest possible workflow
52
+ - `01_your_first_workflow/configured_chat.rb` - Adding configuration
53
+
54
+ ---
55
+
56
+ ### Chapter 2: Chaining Cogs Together
57
+
58
+ Learn how to build multi-step workflows by chaining cogs together, where the output of one step becomes the input to the
59
+ next.
60
+
61
+ **You'll learn:**
62
+
63
+ - How to name cogs and reference their outputs
64
+ - Using the `!` suffix to access previous results
65
+ - The difference between `agent` and `chat` cogs
66
+ - Using the `ruby` cog for custom logic
67
+ - How data flows through a workflow
68
+ - Resuming conversations with session management
69
+
70
+ **Files:**
71
+
72
+ - `02_chaining_cogs/simple_chain.rb` - Basic chaining with `chat` cogs
73
+ - `02_chaining_cogs/code_review.rb` - Realistic workflow with `agent`, `chat`, and `ruby`
74
+ - `02_chaining_cogs/session_resumption.rb` - Multi-turn conversations with session resumption
75
+
76
+ ---
77
+
78
+ ### Chapter 3: Targets and Parameters
79
+
80
+ Learn how to make workflows flexible by accepting targets and custom parameters from the command line.
81
+
82
+ **You'll learn:**
83
+
84
+ - Using `target!` for single-target workflows (file, url, etc.)
85
+ - Using `targets` for a workflow that can accept multiple targets in one shot
86
+ - Passing custom arguments with `args` and `kwargs`
87
+ - Checking for argument presence
88
+ - Combining targets with arguments
89
+
90
+ **Files:**
91
+
92
+ - `03_targets_and_params/single_target.rb` - Processing a single URL with `target!`
93
+ - `03_targets_and_params/multiple_targets.rb` - Processing multiple files with arguments
94
+
95
+ ---
96
+
97
+ ### Chapter 4: Configuration Options
98
+
99
+ Learn how to configure cogs to control their behavior, use different models for different steps, and manage what
100
+ gets displayed during execution.
101
+
102
+ **You'll learn:**
103
+
104
+ - Global vs per-step configuration
105
+ - Pattern-based configuration with regex
106
+ - Display options (`show_prompt!`, `no_show_response!`)
107
+ - Using different models for different steps
108
+
109
+ **Files:**
110
+
111
+ - `04_configuration_options/simple_config.rb` - Basic configuration and overrides
112
+ - `04_configuration_options/control_display_and_temperature.rb` - Configuring multiple parameters for multiple steps
113
+
114
+ ---
115
+
116
+ ### Chapter 5: Control Flow
117
+
118
+ Learn how to create dynamic workflows that adapt based on conditions: skipping steps, handling failures, and checking
119
+ whether steps actually ran.
120
+
121
+ **You'll learn:**
122
+
123
+ - Conditional execution with `skip!`
124
+ - Early termination with `fail!`
125
+ - Checking if cogs ran with `?` accessor
126
+ - The difference between `!`, `?`, and non-bang accessors
127
+
128
+ **Files:**
129
+
130
+ - `05_control_flow/conditional_execution.rb` - Using `skip!` and `?` accessor
131
+ - `05_control_flow/handling_failures.rb` - Command failures with `no_abort_on_failure!`, `no_fail_on_error!`, and
132
+ explicit `fail!`
133
+
134
+ ---
135
+
136
+ ### Chapter 6: Reusable Scopes
137
+
138
+ Learn how to create reusable `execute` scopes that can be called multiple times with different inputs, making your
139
+ workflows more modular and maintainable.
140
+
141
+ **You'll learn:**
142
+
143
+ - Defining named execute scopes
144
+ - Calling scopes with the `call` cog
145
+ - Passing values to scopes
146
+ - Returning values with `outputs`
147
+ - Extracting outputs with `from()`
148
+
149
+ **Files:**
150
+
151
+ - `06_reusable_scopes/basic_scope.rb` - Defining and calling named scopes
152
+ - `06_reusable_scopes/parameterized_scope.rb` - Passing values to scopes
153
+ - `06_reusable_scopes/accessing_scope_outputs.rb` - Using `from()` with a block to access specific cog outputs
154
+
155
+ ---
156
+
157
+ ### Chapter 7: Processing Collections
158
+
159
+ Learn how to process arrays and collections by applying a named execute scope to each item with the `map` cog.
160
+
161
+ **You'll learn:**
162
+
163
+ - Using the `map` cog to process collections
164
+ - Collecting and reducing results with `collect` and `reduce`
165
+ - Parallel execution with `parallel` configuration
166
+ - Accessing specific iteration outputs
167
+ - Working with iteration indices
168
+
169
+ **Files:**
170
+
171
+ - `07_processing_collections/basic_map.rb` - Basic map usage with collect, reduce, and accessing specific iterations
172
+ - `07_processing_collections/parallel_map.rb` - Serial, limited parallel, and unlimited parallel execution
173
+
174
+ ---
175
+
176
+ ### Chapter 8: Iterative Workflows
177
+
178
+ Learn how to create iterative workflows with the `repeat` cog, which executes a scope repeatedly until a condition is
179
+ met, with each iteration's output becoming the next iteration's input.
180
+
181
+ **You'll learn:**
182
+
183
+ - Using the `repeat` cog for iterative transformations
184
+ - How output from one iteration becomes input to the next
185
+ - Breaking out of loops with `break!`
186
+ - Skipping to the next iteration with `next!`
187
+ - Accessing specific iterations and the final result with `from`
188
+ - Processing all iterations with `collect` and `reduce`
189
+ - Using the `outputs` block for controlling iteration values
190
+
191
+ **Files:**
192
+
193
+ - `08_iterative_workflows/basic_repeat.rb` - Iterative text refinement showing basic repeat patterns
194
+ - `08_iterative_workflows/conditional_break.rb` - Number guessing game with conditional termination
195
+ - `08_iterative_workflows/skip_with_next.rb` - Processing numbers while skipping even values
196
+
197
+ ---
198
+
199
+ ### Chapter 9: Asynchronous Cogs
200
+
201
+ Learn how to run cogs asynchronously to improve workflow performance when you have independent tasks
202
+ that can run concurrently.
203
+
204
+ **You'll learn:**
205
+
206
+ - How async cogs differ from parallel map execution
207
+ - Configuring cogs to run asynchronously with `async!`
208
+ - How async execution works and when cogs block
209
+ - Using synchronous cogs as synchronization barriers
210
+ - Real-world patterns for parallel code analysis
211
+ - When to use async cogs vs other parallelization methods
212
+
213
+ **Files:**
214
+
215
+ - `09_async_cogs/basic_async.rb` - Simple async execution with multiple independent agent tasks
216
+ - `09_async_cogs/parallel_analysis.rb` - Parallel code analysis using multiple agents
217
+ - `09_async_cogs/sync_barriers.rb` - Using synchronous cogs to control execution phases
218
+
219
+ ---
220
+
221
+ Let's get started with
222
+ [Chapter 1](https://github.com/Shopify/roast/blob/edge/dsl/tutorial/01_your_first_workflow/README.md)!
@@ -0,0 +1,16 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ cmd { display! }
8
+ cmd(:alt) { working_directory "/tmp" }
9
+ cmd(:orig) { use_current_working_directory! }
10
+ end
11
+
12
+ execute do
13
+ cmd(:cwd) { "echo Current working directory: `pwd`" }
14
+ cmd(:alt) { "echo Alternate working directory: `pwd`" }
15
+ cmd(:orig) { "echo Back to original working directory: `pwd`" }
16
+ end
data/exe/roast CHANGED
@@ -13,5 +13,5 @@ unshift_path.call("lib")
13
13
  require "bundler/setup"
14
14
  require "roast"
15
15
 
16
- puts "🔥🔥🔥 Everyone loves a good roast 🔥🔥🔥\n\n"
16
+ $stderr.puts "🔥🔥🔥 Everyone loves a good roast 🔥🔥🔥\n\n"
17
17
  Roast::CLI.start(ARGV)