roast-ai 0.4.10 → 0.5.1

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 (175) 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/.ruby-version +1 -1
  8. data/CLAUDE.md +2 -2
  9. data/CONTRIBUTING.md +2 -0
  10. data/Gemfile +19 -18
  11. data/Gemfile.lock +35 -58
  12. data/README.md +118 -1432
  13. data/README_LEGACY.md +1464 -0
  14. data/Rakefile +39 -4
  15. data/dev.yml +29 -0
  16. data/dsl/agent_sessions.rb +20 -0
  17. data/dsl/async_cogs.rb +49 -0
  18. data/dsl/async_cogs_complex.rb +67 -0
  19. data/dsl/call.rb +44 -0
  20. data/dsl/collect_from.rb +72 -0
  21. data/dsl/json_output.rb +28 -0
  22. data/dsl/map.rb +55 -0
  23. data/dsl/map_reduce.rb +37 -0
  24. data/dsl/map_with_index.rb +49 -0
  25. data/dsl/next_break.rb +45 -0
  26. data/dsl/next_break_parallel.rb +44 -0
  27. data/dsl/outputs.rb +39 -0
  28. data/dsl/outputs_bang.rb +36 -0
  29. data/dsl/parallel_map.rb +37 -0
  30. data/dsl/prompts/simple_prompt.md.erb +3 -0
  31. data/dsl/prototype.rb +5 -7
  32. data/dsl/repeat_loop_results.rb +53 -0
  33. data/dsl/ruby_cog.rb +72 -0
  34. data/dsl/simple_agent.rb +18 -0
  35. data/dsl/simple_chat.rb +15 -1
  36. data/dsl/simple_repeat.rb +29 -0
  37. data/dsl/skip.rb +36 -0
  38. data/dsl/step_communication.rb +2 -3
  39. data/dsl/targets_and_params.rb +57 -0
  40. data/dsl/temperature.rb +17 -0
  41. data/dsl/temporary_directory.rb +22 -0
  42. data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
  43. data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
  44. data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
  45. data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
  46. data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
  47. data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
  48. data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
  49. data/dsl/tutorial/03_targets_and_params/README.md +230 -0
  50. data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
  51. data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
  52. data/dsl/tutorial/04_configuration_options/README.md +209 -0
  53. data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
  54. data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
  55. data/dsl/tutorial/05_control_flow/README.md +156 -0
  56. data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
  57. data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
  58. data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
  59. data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
  60. data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
  61. data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
  62. data/dsl/tutorial/07_processing_collections/README.md +152 -0
  63. data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
  64. data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
  65. data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
  66. data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
  67. data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
  68. data/dsl/tutorial/09_async_cogs/README.md +197 -0
  69. data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
  70. data/dsl/tutorial/README.md +222 -0
  71. data/dsl/working_directory.rb +16 -0
  72. data/exe/roast +1 -1
  73. data/internal/documentation/architectural-notes.md +115 -0
  74. data/internal/documentation/doc-comments-external.md +686 -0
  75. data/internal/documentation/doc-comments-internal.md +342 -0
  76. data/internal/documentation/doc-comments.md +211 -0
  77. data/lib/roast/dsl/cog/config.rb +274 -3
  78. data/lib/roast/dsl/cog/input.rb +53 -10
  79. data/lib/roast/dsl/cog/output.rb +297 -8
  80. data/lib/roast/dsl/cog/registry.rb +35 -3
  81. data/lib/roast/dsl/cog/stack.rb +1 -1
  82. data/lib/roast/dsl/cog/store.rb +5 -5
  83. data/lib/roast/dsl/cog.rb +70 -14
  84. data/lib/roast/dsl/cog_input_context.rb +36 -1
  85. data/lib/roast/dsl/cog_input_manager.rb +116 -7
  86. data/lib/roast/dsl/cogs/agent/config.rb +465 -0
  87. data/lib/roast/dsl/cogs/agent/input.rb +81 -0
  88. data/lib/roast/dsl/cogs/agent/output.rb +59 -0
  89. data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
  90. data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
  91. data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
  92. data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
  93. data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
  94. data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
  95. data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
  96. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
  97. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
  98. data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
  99. data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
  100. data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
  101. data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
  102. data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
  103. data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
  104. data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
  105. data/lib/roast/dsl/cogs/agent.rb +75 -0
  106. data/lib/roast/dsl/cogs/chat/config.rb +453 -0
  107. data/lib/roast/dsl/cogs/chat/input.rb +92 -0
  108. data/lib/roast/dsl/cogs/chat/output.rb +64 -0
  109. data/lib/roast/dsl/cogs/chat/session.rb +68 -0
  110. data/lib/roast/dsl/cogs/chat.rb +59 -56
  111. data/lib/roast/dsl/cogs/cmd.rb +251 -61
  112. data/lib/roast/dsl/cogs/ruby.rb +171 -0
  113. data/lib/roast/dsl/command_runner.rb +191 -0
  114. data/lib/roast/dsl/config_manager.rb +58 -11
  115. data/lib/roast/dsl/control_flow.rb +41 -0
  116. data/lib/roast/dsl/execution_manager.rb +162 -32
  117. data/lib/roast/dsl/nil_assertions.rb +23 -0
  118. data/lib/roast/dsl/system_cog/params.rb +32 -0
  119. data/lib/roast/dsl/system_cog.rb +36 -0
  120. data/lib/roast/dsl/system_cogs/call.rb +163 -0
  121. data/lib/roast/dsl/system_cogs/map.rb +454 -0
  122. data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
  123. data/lib/roast/dsl/workflow.rb +26 -16
  124. data/lib/roast/dsl/workflow_context.rb +20 -0
  125. data/lib/roast/dsl/workflow_params.rb +24 -0
  126. data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
  127. data/lib/roast/sorbet_runtime_stub.rb +154 -0
  128. data/lib/roast/tools/apply_diff.rb +1 -3
  129. data/lib/roast/tools/cmd.rb +4 -3
  130. data/lib/roast/tools/read_file.rb +1 -1
  131. data/lib/roast/tools/update_files.rb +1 -1
  132. data/lib/roast/tools/write_file.rb +1 -1
  133. data/lib/roast/version.rb +1 -1
  134. data/lib/roast/workflow/base_workflow.rb +4 -0
  135. data/lib/roast/workflow/step_loader.rb +14 -2
  136. data/lib/roast-ai.rb +4 -0
  137. data/lib/roast.rb +58 -21
  138. data/{roast.gemspec → roast-ai.gemspec} +9 -13
  139. data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
  140. data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
  141. data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
  142. data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
  143. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
  144. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
  145. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
  146. data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
  147. data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
  148. data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
  149. data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1185 -5
  150. data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +311 -5
  151. data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +486 -5
  152. data/sorbet/tapioca/config.yml +6 -0
  153. data/sorbet/tapioca/require.rb +2 -0
  154. metadata +157 -30
  155. data/dsl/less_simple.rb +0 -112
  156. data/dsl/scoped_executors.rb +0 -28
  157. data/dsl/simple.rb +0 -8
  158. data/lib/roast/dsl/cogs/execute.rb +0 -46
  159. data/lib/roast/dsl/cogs/graph.rb +0 -53
  160. data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
  161. data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
  162. data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
  163. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
  164. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
  165. data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
  166. data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
  167. data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
  168. data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
  169. data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
  170. data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
  171. data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
  172. data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
  173. data/sorbet/rbi/gems/rack@2.2.19.rbi +0 -5676
  174. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  175. data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
data/README.md CHANGED
@@ -2,1461 +2,147 @@
2
2
 
3
3
  # Roast
4
4
 
5
- A convention-oriented framework for creating structured AI workflows, maintained by the Augmented Engineering team at Shopify.
5
+ ### 🔥 _version 1.0 feature preview_ 🔥
6
+ A Ruby-based domain-specific language for creating structured AI workflows. Build complex AI-powered automation with simple, declarative Ruby syntax.
6
7
 
7
- ## Installation
8
-
9
- ```bash
10
- $ gem install roast-ai
11
- ```
12
-
13
- Or add to your Gemfile:
14
-
15
- ```ruby
16
- gem 'roast-ai'
17
- ```
18
-
19
- ## Why you should use Roast
20
-
21
- Roast provides a structured, declarative approach to building AI workflows with:
22
-
23
- - **Convention over configuration**: Define powerful workflows using simple YAML configuration files and prompts written in markdown (with ERB support)
24
- - **Built-in tools**: Ready-to-use tools for file operations, search, and AI interactions
25
- - **Ruby integration**: When prompts aren't enough, write custom steps in Ruby using a clean, extensible architecture
26
- - **Shared context**: Each step shares its conversation transcript with its parent workflow by default
27
- - **Step customization**: Steps can be fully configured with their own AI models and parameters.
28
- - **Session replay**: Rerun previous sessions starting at a specified step to speed up development time
29
- - **Parallel execution**: Run multiple steps concurrently to speed up workflow execution
30
- - **Function caching**: Flexibly cache the results of tool function calls to speed up workflows
31
- - **Extensive instrumentation**: Monitor and track workflow execution, AI calls, and tool usage ([see instrumentation documentation](docs/INSTRUMENTATION.md))
32
-
33
- ## What does it look like?
34
-
35
- Here's a simple workflow that analyzes test files:
36
-
37
- ```yaml
38
- name: analyze_tests
39
- # Default model for all steps
40
- model: gpt-4o-mini
41
- tools:
42
- - Roast::Tools::ReadFile
43
- - Roast::Tools::Grep
44
-
45
- steps:
46
- - read_test_file
47
- - analyze_coverage
48
- - generate_report
49
-
50
- # Step-specific model overrides the global model
51
- analyze_coverage:
52
- model: gpt-4-turbo
53
- json: true
54
-
55
- # Step-specific config that specifies a custom path, not in the current directory
56
- generate_report:
57
- path: ../reporting/generate_report
58
- ```
59
-
60
- Each step can have its own prompt file (e.g., `analyze_coverage/prompt.md`) and configuration. Steps can be run in parallel by nesting them in arrays:
61
-
62
- ```yaml
63
- steps:
64
- - prepare_data
65
- -
66
- - analyze_code_quality
67
- - check_test_coverage
68
- - verify_documentation
69
- - generate_final_report
70
- ```
71
-
72
- Workflows can include steps that run bash commands (wrap in `$()`), use interpolation with `{{}}` syntax, and even simple inlined prompts as a natural language string.
73
-
74
- ```yaml
75
- steps:
76
- - analyze_spec
77
- - create_minitest
78
- - run_and_improve
79
- - $(bundle exec rubocop -A {{file}})
80
- - Summarize the changes made to {{File.basename(file)}}.
81
- ```
82
-
83
- ## Try it
84
-
85
- If you don’t have one already, get an OpenAI key from [here](https://platform.openai.com/settings/organization/api-keys). You will need an account with a credit card and credits applied to the associated project. Make sure that a basic completion works:
86
-
87
- ```bash
88
- export OPENAI_API_KEY=sk-proj-....
89
-
90
- curl -H "Content-Type: application/json" \
91
- -H "Authorization: Bearer $OPENAI_API_KEY" \
92
- -d '{"model":"gpt-4.1-mini","messages":[{"role":"user","content":"What is 1+1?"}]}' \
93
- https://api.openai.com/v1/chat/completions
94
- ```
95
-
96
- The [test grading workflow](examples/grading/workflow.md) in this repository is a senior software engineer and testing expert that evaluates the quality of a test based on guidelines.
97
-
98
- Try the workflow.
99
-
100
- ```bash
101
- ./exe/roast execute examples/grading/workflow.yml test/roast/resources_test.rb
102
-
103
- 🔥🔥🔥 Everyone loves a good roast 🔥🔥🔥
104
- ...
105
- ```
106
-
107
- This will output a test grade.
108
-
109
- ```
110
- ========== TEST GRADE REPORT ==========
111
- Test file: test/roast/resources_test.rb
112
-
113
- FINAL GRADE:
114
- Score: 80/100
115
- Letter Grade: B
116
- ```
117
- Note that you may also need `shadowenv` and `rg`, on MacOS run `brew install shadowenv` and `brew install rg`.
118
-
119
- ## How to use Roast
120
-
121
- 1. Create a workflow YAML file defining your steps and tools
122
- 2. Create prompt files for each step (e.g., `step_name/prompt.md`)
123
- 3. Run the workflow:
124
-
125
- ```bash
126
- # With a target file
127
- roast execute workflow.yml target_file.rb
128
-
129
- # Or for a targetless workflow (API calls, data generation, etc.)
130
- roast execute workflow.yml
131
-
132
- # Roast will automatically search in `project_root/roast/workflow_name` if the path is incomplete.
133
- roast execute my_cool_workflow # Equivalent to `roast execute roast/my_cool_workflow/workflow.yml
134
- ```
135
-
136
- ### Understanding Workflows
137
-
138
- In Roast, workflows maintain a single conversation with the AI model throughout execution. Each step represents one or more user-assistant interactions within this conversation, with optional tool calls. Steps naturally build upon each other through the shared context.
139
-
140
- #### Step Types
141
-
142
- Roast supports several types of steps:
143
-
144
- 1. **Standard step**: References a directory containing at least a `prompt.md` and optional `output.txt` template. This is the most common type of step.
145
- ```yaml
146
- steps:
147
- - analyze_code
148
- ```
149
-
150
- As an alternative to a directory, you can also implement a custom step as a Ruby class, optionally extending `Roast::Workflow::BaseStep`.
151
-
152
- In the example given above, the script would live at `workflow/analyze_code.rb` and should contain a class named `AnalyzeCode` with an initializer that takes a workflow object as context, and a `call` method that will be invoked to run the step. The result of the `call` method will be stored in the `workflow.output` hash.
153
-
154
-
155
- 2. **Parallel steps**: Groups of steps executed concurrently
156
- ```yaml
157
- steps:
158
- -
159
- - analyze_code_quality
160
- - check_test_coverage
161
- ```
162
-
163
- 3. **Command execution step**: Executes shell commands directly, just wrap in `$(expr)`
164
- ```yaml
165
- steps:
166
- - $(command line expr)
167
- - rubocop: $(bundle exec rubocop -A)
168
- ```
169
- This will execute the command and store the result in the workflow output hash. Explicit key name is optional (`rubocop` in the second line of the example).
170
-
171
- By default, commands that exit with non-zero status will halt the workflow. You can configure steps to continue on error:
172
- ```yaml
173
- steps:
174
- - lint_check: $(rubocop {{file}})
175
- - fix_issues
176
-
177
- # Step configuration
178
- lint_check:
179
- exit_on_error: false # Continue workflow even if command fails
180
- ```
181
- When `exit_on_error: false`, the command output will include the exit status, allowing subsequent steps to process error information.
182
-
183
- 4. **Conditional steps**: Execute different steps based on conditions using `if/unless`
184
- ```yaml
185
- steps:
186
- - check_environment:
187
- if: "{{ENV['RAILS_ENV'] == 'production'}}"
188
- then:
189
- - run_production_checks
190
- - notify_team
191
- else:
192
- - run_development_setup
193
-
194
- - verify_dependencies:
195
- unless: "$(bundle check)"
196
- then:
197
- - bundle_install: "$(bundle install)"
198
- ```
199
-
200
- Conditions can be:
201
- - Ruby expressions: `if: "{{output['count'] > 5}}"`
202
- - Bash commands: `if: "$(test -f config.yml && echo true)"` (exit code 0 = true)
203
- - Step references: `if: "previous_step_name"` (uses the step's output)
204
- - Direct values: `if: "true"` or `if: "false"`
205
-
206
- 5. **Iteration steps**: Loop over collections or repeat steps with conditions
207
- ```yaml
208
- steps:
209
- # Loop over a collection
210
- - process_files:
211
- each: "{{Dir.glob('**/*.rb')}}"
212
- as: current_file
213
- steps:
214
- - analyze_file
215
- - Generate a report for {{current_file}}
216
-
217
- # Repeat until a condition is met
218
- - improve_code:
219
- repeat:
220
- until: "{{output['test_pass'] == true}}"
221
- max_iterations: 5
222
- steps:
223
- - run_tests
224
- - fix_issues
225
- ```
226
-
227
- Each loops support:
228
- - Collections from Ruby expressions: `each: "{{[1, 2, 3]}}"`
229
- - Command output: `each: "$(ls *.rb)"`
230
- - Step references: `each: "file_list"`
231
-
232
- Repeat loops support:
233
- - Until conditions: `until: "{{condition}}"`
234
- - Maximum iterations: `max_iterations: 10`
235
-
236
- 6. **Case/when/else steps**: Select different execution paths based on a value (similar to Ruby's case statement)
237
- ```yaml
238
- steps:
239
- - detect_language
240
-
241
- - case: "{{ workflow.output.detect_language }}"
242
- when:
243
- ruby:
244
- - lint_with_rubocop
245
- - test_with_rspec
246
- javascript:
247
- - lint_with_eslint
248
- - test_with_jest
249
- python:
250
- - lint_with_pylint
251
- - test_with_pytest
252
- else:
253
- - analyze_generic
254
- - generate_basic_report
255
- ```
256
-
257
- Case expressions can be:
258
- - Workflow outputs: `case: "{{ workflow.output.variable }}"`
259
- - Ruby expressions: `case: "{{ count > 10 ? 'high' : 'low' }}"`
260
- - Bash commands: `case: "$(echo $ENVIRONMENT)"`
261
- - Direct values: `case: "production"`
262
-
263
- The value is compared against each key in the `when` clause, and matching steps are executed.
264
- If no match is found, the `else` steps are executed (if provided).
265
-
266
- 7. **Raw prompt step**: Simple text prompts for the model without tools
267
- ```yaml
268
- steps:
269
- - Summarize the changes made to the codebase.
270
- ```
271
- This creates a simple prompt-response interaction without tool calls or looping. It's detected by the presence of spaces in the step name and is useful for summarization or simple questions at the end of a workflow.
272
-
273
- 8. **Agent step**: Direct pass-through to coding agents (e.g., Claude Code)
274
- ```yaml
275
- steps:
276
- - ^fix_linting_errors # File-based agent prompt
277
- - ^Review the code and identify any performance issues # Inline agent prompt
278
- - regular_analysis # Normal step through LLM
279
- ```
280
- Agent steps are prefixed with `^` and send the prompt content directly to the CodingAgent tool without LLM translation. This is useful when you want to give precise instructions to a coding agent without the intermediate interpretation layer. Agent steps support both file-based prompts (`fix_linting_errors/prompt.md`) and inline prompts (text with spaces).
281
-
282
- **Session continuity for agent steps:**
283
-
284
- Agent steps support two options for maintaining Claude context across steps:
285
-
286
- 1. **`continue: true`** - Continues from the immediately previous Claude Code session (note, if multiple Claude Code sessions are being run in parallel in the same working directory, this might not be the previous Claude Code session from this workflow)
287
- 2. **`resume: step_name`** - Resumes from a specific earlier step's Claude Code session
288
-
289
- **Continue option:**
290
-
291
- The `continue` option allows sequential agent steps to maintain a continuous conversation:
292
-
293
- ```yaml
294
- steps:
295
- - ^analyze_codebase
296
- - ^implement_feature
297
- - ^add_tests
298
-
299
- # Configuration
300
- analyze_codebase:
301
- continue: false # Start fresh (default)
302
-
303
- implement_feature:
304
- continue: true # Continue from immediately previous analyze_codebase step
305
-
306
- add_tests:
307
- continue: true # Continue from immediately previous implement_feature step
308
- ```
309
-
310
- **Resume functionality for agent steps:**
311
-
312
- Agent steps can resume from specific previous Claude Code sessions:
313
-
314
- ```yaml
315
- steps:
316
- - ^analyze_codebase
317
- - ^implement_feature
318
- - ^polish_implementation
319
-
320
- # Configuration
321
- analyze_codebase:
322
- continue: false # Start fresh
323
-
324
- implement_feature:
325
- continue: true # Continue from previous conversation
326
-
327
- polish_implementation:
328
- resume: analyze_codebase # Resume from a specific step's session not the immediately previous one
329
- ```
330
-
331
- Note: Session IDs are only available when the CodingAgent is configured to output JSON format (includes `--output-format stream-json` in the command). If you are using a custom CodingAgent command that does not produce JSON output, resume functionality will not be available.
332
-
333
- If `resume` is specified but the step name given does not have CodingAgent session to resume from, the CodingAgent will start Claude Code with a fresh session.
334
-
335
- 9. **Shell script step**: Execute shell scripts directly as workflow steps
336
- ```yaml
337
- steps:
338
- - setup_environment # Executes setup_environment.sh
339
- - run_tests # Executes run_tests.sh
340
- - cleanup
341
- ```
342
-
343
- Shell script steps allow you to execute `.sh` files directly as workflow steps alongside Ruby steps and AI prompts. Scripts are automatically discovered in the same locations as other step types.
344
-
345
- **Configuration options:**
346
- ```yaml
347
- # Step configuration
348
- my_script:
349
- json: true # Parse stdout as JSON
350
- exit_on_error: false # Don't fail workflow on non-zero exit
351
- env: # Custom environment variables
352
- CUSTOM_VAR: "value"
353
- ```
354
-
355
- **Environment integration:** Shell scripts automatically receive workflow context:
356
- - `ROAST_WORKFLOW_RESOURCE`: Current workflow resource
357
- - `ROAST_STEP_NAME`: Current step name
358
- - `ROAST_WORKFLOW_OUTPUT`: Previous step outputs as JSON
359
-
360
- **Example script (`setup_environment.sh`):**
361
- ```bash
362
- #!/bin/bash
363
- echo "Setting up environment for: $ROAST_WORKFLOW_RESOURCE"
364
-
365
- # Create a config file that subsequent steps can use
366
- mkdir -p tmp
367
- echo "DATABASE_URL=sqlite://test.db" > tmp/config.env
368
-
369
- # Output data for the workflow (available via ROAST_WORKFLOW_OUTPUT in later steps)
370
- echo '{"status": "configured", "database": "sqlite://test.db", "config_file": "tmp/config.env"}'
371
- ```
372
-
373
- 10. **Input step**: Interactive prompts for user input during workflow execution
374
- ```yaml
375
- steps:
376
- - analyze_code
377
- - input:
378
- name: get_user_feedback
379
- prompt: "Should we proceed with the refactoring? (yes/no)"
380
- type: confirm
381
- - input:
382
- name: review_changes
383
- prompt: "Enter your review comments"
384
- type: text
385
- - input:
386
- name: select_strategy
387
- prompt: "Choose optimization strategy"
388
- type: choice
389
- options:
390
- - "Performance optimization"
391
- - "Memory optimization"
392
- - "Code clarity"
393
- - input:
394
- name: api_configuration
395
- prompt: "Enter API key"
396
- type: password
397
- ```
398
-
399
- Input steps pause workflow execution to collect user input. They support several types:
400
- - `text`: Free-form text input (default if type not specified)
401
- - `confirm`: Yes/No confirmation prompts
402
- - `select`: Choice from a list of options
403
- - `password`: Masked input for sensitive data
404
-
405
- The user's input is stored in the workflow output using the step name as the key and can be accessed in subsequent steps via interpolation (e.g., `{{output.get_user_feedback}}`).
406
-
407
- #### Step Configuration
408
-
409
- Steps can be configured with various options to control their behavior:
410
-
411
- ```yaml
412
- steps:
413
- - analyze_code # Simple step reference
414
- - generate_report: # Step with configuration
415
- model: gpt-4o # Override the global model for this step
416
- print_response: true # Explicitly control output printing
417
- json: true # Request JSON-formatted response
418
- params: # Additional parameters for the API call
419
- temperature: 0.8
420
- ```
421
-
422
- **Configuration options:**
423
- - `model`: Override the workflow's default model for this specific step
424
- - `print_response`: Control whether the step's response is included in the final output (default: `false`, except for the last step which defaults to `true` as of v0.3.1)
425
- - `json`: Request a JSON-formatted response from the model
426
- - `params`: Additional parameters passed to the model API (temperature, max_tokens, etc.)
427
- - `path`: Custom directory path for the step's prompt files
428
- - `coerce_to`: Type coercion for the step result (`:boolean`, `:llm_boolean`, `:iterable`)
429
-
430
- **Automatic Last Step Output**: As of version 0.3.1, the last step in a workflow automatically has `print_response: true` unless explicitly configured otherwise. This ensures that newcomers to Roast see output from their workflows by default.
431
-
432
- #### Shared Configuration
433
-
434
- Roast supports sharing common configuration and steps across multiple workflows using a `shared.yml` file.
435
-
436
- 1. Place a `shared.yml` file one level above your workflow directory
437
- 2. Define YAML anchors for common configurations like tools, models or steps
438
- 3. Reference these anchors in your workflow files using YAML alias syntax
439
-
440
- **Example structure:**
441
- ```
442
- my_project/
443
- ├── shared.yml # Common configuration anchors
444
- └── workflows/
445
- ├── analyze_code.yml
446
- ├── generate_docs.yml
447
- └── test_suite.yml
448
- ```
449
-
450
- **Example `shared.yml`:**
451
- ```yaml
452
- # Define common tools
453
- standard_tools: &standard_tools
454
- - Roast::Tools::Grep
455
- - Roast::Tools::ReadFile
456
- - Roast::Tools::WriteFile
457
- - Roast::Tools::SearchFile
458
- ```
459
-
460
- **Using in workflows:**
461
- ```yaml
462
- name: Code Analysis Workflow
463
- tools: *standard_tools # Reference shared tools
464
-
465
- steps:
466
- ...
467
- ```
468
-
469
- #### Data Flow Between Steps
470
-
471
- Roast handles data flow between steps in three primary ways:
472
-
473
- 1. **Conversation Context (Implicit)**: The LLM naturally remembers the entire conversation history, including all previous prompts and responses. In most cases, this is all you need for a step to reference and build upon previous results. This is the preferred approach for most prompt-oriented workflows.
474
-
475
- 2. **Output Hash (Explicit)**: Each step's result is automatically stored in the `workflow.output` hash using the step name as the key. This programmatic access is mainly useful when:
476
- - You need to perform non-LLM transformations on data
477
- - You're writing custom output logic
478
- - You need to access specific values for presentation or logging
479
-
480
- 3. **Interpolation (Dynamic)**: You can use `{{expression}}` syntax to inject values from the workflow context directly into step names, commands, or prompt text. For example:
481
- ```yaml
482
- steps:
483
- - analyze_file
484
- - $(rubocop -A {{file}})
485
- - Generate a summary for {{file}}
486
- - result_for_{{file}}: store_results
487
- ```
488
-
489
- Interpolation supports:
490
- - Simple variable access: `{{file}}`, `{{resource.target}}`
491
- - Access to step outputs: `{{output['previous_step']}}`
492
- - Any valid Ruby expression evaluated in the workflow context: `{{File.basename(file)}}`
493
-
494
- For typical AI workflows, the continuous conversation history provides seamless data flow without requiring explicit access to the output hash. Steps can simply refer to previous information in their prompts, and the AI model will use its memory of the conversation to provide context-aware responses. For more dynamic requirements, the interpolation syntax provides a convenient way to inject context-specific values into steps.
495
-
496
- ### Command Line Options
497
-
498
- #### Basic Options
499
- - `-o, --output FILE`: Save results to a file instead of outputting to STDOUT
500
- - `-c, --concise`: Use concise output templates (exposed as a boolean flag on `workflow`)
501
- - `-v, --verbose`: Show output from all steps as they execute
502
- - `-r, --replay STEP_NAME`: Resume a workflow from a specific step, optionally with a specific session timestamp
503
- - `-f, --file-storage`: Use filesystem storage for sessions instead of SQLite (default: SQLite)
504
-
505
- #### Workflow Validation
506
-
507
- Roast provides a `validate` command to check workflow configuration files for errors and potential issues before execution:
508
-
509
- ```bash
510
- # Validate a specific workflow
511
- roast validate workflow.yml
512
-
513
- # Validate a workflow in a subdirectory
514
- roast validate my_workflow
515
-
516
- # Validate with strict mode (treats warnings as errors)
517
- roast validate workflow.yml --strict
518
- ```
519
-
520
- The validator checks for:
521
- - YAML syntax errors
522
- - Missing required fields
523
- - Invalid step references
524
- - Circular dependencies
525
- - Tool availability
526
- - Prompt file existence
527
- - Configuration consistency
528
-
529
- This helps catch configuration errors early and ensures workflows will run smoothly.
530
-
531
- #### Session Storage and Management
532
-
533
- Roast uses SQLite by default for session storage, providing better performance and advanced querying capabilities. Sessions are automatically saved during workflow execution, capturing each step's state including conversation transcripts and outputs.
534
-
535
- **Storage Options:**
536
-
537
- ```bash
538
- # Use default SQLite storage (recommended)
539
- roast execute workflow.yml
540
-
541
- # Use legacy filesystem storage
542
- roast execute workflow.yml --file-storage
543
-
544
- # Set storage type via environment variable
545
- ROAST_STATE_STORAGE=file roast execute workflow.yml
546
- ```
547
-
548
- **Session Management Commands:**
549
-
550
- ```bash
551
- # List all sessions
552
- roast sessions
553
-
554
- # Filter sessions by status
555
- roast sessions --status waiting
556
-
557
- # Filter sessions by workflow
558
- roast sessions --workflow my_workflow
559
-
560
- # Show sessions older than 7 days
561
- roast sessions --older-than 7d
562
-
563
- # Clean up old sessions
564
- roast sessions --cleanup --older-than 30d
565
-
566
- # View detailed session information
567
- roast session <session_id>
568
- ```
569
-
570
- #### Session Replay
571
-
572
- The session replay feature allows you to resume workflows from specific steps, saving time during development and debugging:
573
-
574
- ```bash
575
- # Resume from a specific step
576
- roast execute workflow.yml -r step_name
577
-
578
- # Resume from a specific step in a specific session
579
- roast execute workflow.yml -r 20250507_123456_789:step_name
580
- ```
581
-
582
- This feature is particularly useful when:
583
- - Debugging specific steps in a long workflow
584
- - Iterating on prompts without rerunning the entire workflow
585
- - Resuming after failures in long-running workflows
586
-
587
- **Storage Locations:**
588
- - SQLite: `~/.roast/sessions.db` (configurable via `ROAST_SESSIONS_DB`)
589
- - Filesystem: `.roast/sessions/` directory in your project
590
-
591
- #### Target Option (`-t, --target`)
592
-
593
- The target option is highly flexible and accepts several formats:
594
-
595
- **Single file path:**
596
- ```bash
597
- roast execute workflow.yml -t path/to/file.rb
598
-
599
- # is equivalent to
600
- roast execute workflow.yml path/to/file.rb
601
- ```
602
-
603
- **Directory path:**
604
- ```bash
605
- roast execute workflow.yml -t path/to/directory
606
-
607
- # Roast will run on the directory as a resource
608
- ```
609
-
610
- **Glob patterns:**
611
- ```bash
612
- roast execute workflow.yml -t "**/*_test.rb"
613
-
614
- # Roast will run the workflow on each matching file
615
- ```
616
-
617
- **URL as target:**
618
- ```bash
619
- roast execute workflow.yml -t "https://api.example.com/data"
620
-
621
- # Roast will run the workflow using the URL as a resource
622
- ```
623
-
624
- **API configuration (Fetch API-style):**
625
- ```bash
626
- roast execute workflow.yml -t '{
627
- "url": "https://api.example.com/resource",
628
- "options": {
629
- "method": "POST",
630
- "headers": {
631
- "Content-Type": "application/json",
632
- "Authorization": "Bearer ${API_TOKEN}"
633
- },
634
- "body": {
635
- "query": "search term",
636
- "limit": 10
637
- }
638
- }
639
- }'
640
-
641
- # Roast will recognize this as an API configuration with Fetch API-style format
642
- ```
643
-
644
- **Shell command execution with $(...):**
645
- ```bash
646
- roast execute workflow.yml -t "$(find . -name '*.rb' -mtime -1)"
647
-
648
- # Roast will run the workflow on each file returned (expects one per line)
649
- ```
650
-
651
- **Git integration examples:**
652
- ```bash
653
- # Process changed test files
654
- roast execute workflow.yml -t "$(git diff --name-only HEAD | grep _test.rb)"
655
-
656
- # Process staged files
657
- roast execute workflow.yml -t "$(git diff --cached --name-only)"
658
- ```
659
-
660
- #### Targetless Workflows
661
-
662
- Roast also supports workflows that don't operate on a specific pre-defined set of target files:
663
-
664
- **API-driven workflows:**
665
- ```yaml
666
- name: API Integration Workflow
667
- tools:
668
- - Roast::Tools::ReadFile
669
- - Roast::Tools::WriteFile
670
-
671
- # Dynamic API token using shell command
672
- api_token: $(cat ~/.my_token)
673
-
674
- # Option 1: Use a targetless workflow with API logic in steps
675
- steps:
676
- - fetch_api_data # Step will make API calls
677
- - transform_data
678
- - generate_report
679
-
680
- # Option 2: Specify an API target directly in the workflow
681
- target: '{
682
- "url": "https://api.example.com/resource",
683
- "options": {
684
- "method": "GET",
685
- "headers": {
686
- "Authorization": "Bearer ${API_TOKEN}"
687
- }
688
- }
689
- }'
690
-
691
- steps:
692
- - process_api_response
693
- - generate_report
694
- ```
695
-
696
- **Data generation workflows:**
697
- ```yaml
698
- name: Generate Documentation
699
- tools:
700
- - Roast::Tools::WriteFile
701
- steps:
702
- - generate_outline
703
- - write_documentation
704
- - create_examples
705
- ```
706
-
707
- These targetless workflows are ideal for:
708
- - API integrations
709
- - Content generation
710
- - Report creation
711
- - Interactive tools
712
- - Scheduled automation tasks
713
-
714
- #### Global Model Configuration
715
-
716
- You can set a default model for all steps in your workflow by specifying the `model` parameter at the top level:
717
-
718
- ```yaml
719
- name: My Workflow
720
- model: gpt-4o-mini # Will be used for all steps unless overridden
721
- ```
722
-
723
- Individual steps can override this setting with their own model parameter:
724
-
725
- ```yaml
726
- analyze_data:
727
- model: anthropic/claude-3-haiku # Takes precedence over the global model
728
- ```
729
-
730
- #### API Provider Configuration
731
-
732
- Roast supports both OpenAI and OpenRouter as API providers. By default, Roast uses OpenAI, but you can specify OpenRouter:
733
-
734
- ```yaml
735
- name: My Workflow
736
- api_provider: openrouter
737
- api_token: $(echo $OPENROUTER_API_KEY)
738
- model: anthropic/claude-3-opus-20240229
739
- ```
740
-
741
- Benefits of using OpenRouter:
742
- - Access to multiple model providers through a single API
743
- - Support for models from Anthropic, Meta, Mistral, and more
744
- - Consistent API interface across different model providers
745
-
746
- When using OpenRouter, specify fully qualified model names including the provider prefix (e.g., `anthropic/claude-3-opus-20240229`).
747
-
748
- #### Dynamic API Tokens and URIs
8
+ > #### ⚠️ Deprecation announcement ⚠️
9
+ > The YAML-based workflow syntax of Roast `v0.x` will be removed in `v1.0`.
10
+ > All `v0.x` functionality is still supported during the feature preview.
11
+ > For the existing documentation for Roast `v0.x` , see [README_LEGACY.md](README_LEGACY.md)
749
12
 
750
- Roast allows you to dynamically fetch attributes such as API token and URI base (to use with a proxy) via shell commands directly in your workflow configuration:
13
+ ## Overview
751
14
 
752
- ```yaml
753
- # This will execute the shell command and use the result as the API token
754
- api_token: $(print-token --key)
15
+ Roast lets you orchestrate AI workflows by combining "cogs" - building blocks that interact with LLMs, run code, execute commands, and process data. Write workflows that:
755
16
 
756
- # For OpenAI (default)
757
- api_token: $(echo $OPENAI_API_KEY)
17
+ - **Chain AI steps together** - Output from one cog flows seamlessly to the next
18
+ - **Run coding agents locally** - Full filesystem access with Claude Code or other providers
19
+ - **Process collections** - Map operations over arrays with serial or parallel execution
20
+ - **Control flow intelligently** - Conditional execution, iteration, and error handling
21
+ - **Reuse workflow components** - Create modular, parameterized scopes
758
22
 
759
- # For OpenRouter (requires api_provider setting)
760
- api_provider: openrouter
761
- api_token: $(echo $OPENROUTER_API_KEY)
762
-
763
- # Static Proxy URI
764
- uri_base: https://proxy.example.com/v1
765
-
766
- # Dynamic Proxy URI
767
- uri_base: $(echo $AI_PROXY_URI_BASE)
768
- ```
769
-
770
- This makes it easy to use environment-specific tokens without hardcoding credentials, especially useful in development environments or CI/CD pipelines. Alternatively, Roast will fall back to `OPENROUTER_API_KEY` or `OPENAI_API_KEY` environment variables based on the specified provider.
771
-
772
-
773
- ### Template Output with ERB
774
-
775
- Each step can have an `output.txt` file that uses ERB templating to format the final output. This allows you to customize how the AI's response is processed and displayed.
776
-
777
- Example `step_name/output.txt`:
778
- ```erb
779
- <% if workflow.verbose %>
780
- Detailed Analysis:
781
- <%= response %>
782
- <% else %>
783
- Summary: <%= response.lines.first %>
784
- <% end %>
785
-
786
- Files analyzed: <%= workflow.file %>
787
- Status: <%= workflow.output['status'] || 'completed' %>
788
- ```
789
-
790
- This is an example of where the `workflow.output` hash is useful - formatting output for display based on data from previous steps.
791
-
792
- Available in templates:
793
- - `response`: The AI's response for this step
794
- - `workflow`: Access to the workflow object
795
- - `workflow.output`: The shared hash containing results from all steps when you need programmatic access
796
- - `workflow.file`: Current file being processed (or `nil` for targetless workflows)
797
- - All workflow configuration options
798
-
799
- For most workflows, you'll mainly use `response` to access the current step's results. The `workflow.output` hash becomes valuable when you need to reference specific data points from previous steps in your templates or for conditional display logic.
800
-
801
- ## Advanced Features
802
-
803
- ### Workflow Metadata
804
-
805
- Roast workflows maintain a metadata store that allows steps to share structured data beyond the standard output hash. This is particularly useful for tracking state that needs to persist across steps but shouldn't be part of the conversation context.
806
-
807
- #### Setting Metadata
808
-
809
- Metadata can be set by custom Ruby steps that extend `BaseStep`:
23
+ ## Quick Example
810
24
 
811
25
  ```ruby
812
- # workflow/analyze_codebase.rb
813
- class AnalyzeCodebase < Roast::Workflow::BaseStep
814
- include Roast::Helpers::MetadataAccess
815
-
816
- def call
817
- # Perform analysis
818
- analysis_results = perform_deep_analysis
819
-
820
- # Store metadata for other steps to use
821
- workflow.metadata[name.to_s] ||= {}
822
- workflow.metadata[name.to_s]["total_files"] = analysis_results[:file_count]
823
- workflow.metadata[name.to_s]["complexity_score"] = analysis_results[:complexity]
824
- workflow.metadata[name.to_s]["analysis_id"] = SecureRandom.uuid
825
-
826
- # Return the normal output for the conversation
827
- "Analyzed #{analysis_results[:file_count]} files with average complexity of #{analysis_results[:complexity]}"
828
- end
829
-
830
- private
831
-
832
- def perform_deep_analysis
833
- # Your analysis logic here
834
- { file_count: 42, complexity: 7.5 }
26
+ # analyze_codebase.rb
27
+ execute do
28
+ # Get recent changes
29
+ cmd(:recent_changes) { "git diff --name-only HEAD~5..HEAD" }
30
+
31
+ # AI agent analyzes the code
32
+ agent(:review) do
33
+ files = cmd!(:recent_changes).lines
34
+ <<~PROMPT
35
+ Review these recently changed files for potential issues:
36
+ #{files.join("\n")}
37
+
38
+ Focus on security, performance, and maintainability.
39
+ PROMPT
835
40
  end
836
- end
837
- ```
838
-
839
- #### Accessing Metadata
840
41
 
841
- Metadata from previous steps can be accessed in:
842
-
843
- 1. **Custom Ruby steps:**
844
- ```ruby
845
- class GenerateReport < Roast::Workflow::BaseStep
846
- def call
847
- # Access metadata from a previous step
848
- total_files = workflow.metadata.dig("analyze_codebase", "total_files")
849
- complexity = workflow.metadata.dig("analyze_codebase", "complexity_score")
850
-
851
- "Generated report for #{total_files} files with complexity score: #{complexity}"
42
+ # Summarize for stakeholders
43
+ chat(:summary) do
44
+ "Summarize this for non-technical stakeholders:\n\n#{agent!(:review).response}"
852
45
  end
853
46
  end
854
47
  ```
855
48
 
856
- 2. **Workflow configuration via interpolation:**
857
- ```yaml
858
- steps:
859
- - analyze_codebase
860
- - validate_threshold
861
- - generate_report
862
-
863
- # Use metadata in step configuration
864
- validate_threshold:
865
- if: "{{metadata.analyze_codebase.complexity_score > 8.0}}"
866
- then:
867
- - send_alert
868
- - create_ticket
869
- else:
870
- - mark_as_passed
871
-
872
- # Pass metadata to command steps
873
- send_alert:
874
- $(slack-notify "High complexity detected: {{metadata.analyze_codebase.complexity_score}}")
875
- ```
876
-
877
- 3. **Prompt templates (ERB):**
878
- ```erb
879
- # In analyze_codebase/output.txt
880
- Analysis Summary:
881
- Files analyzed: <%= workflow.metadata.dig(name.to_s, "total_files") %>
882
- Complexity score: <%= workflow.metadata.dig(name.to_s, "complexity_score") %>
883
- Analysis ID: <%= workflow.metadata.dig(name.to_s, "analysis_id") %>
884
- ```
885
-
886
- #### Metadata Best Practices
887
-
888
- - **Use metadata for data that shouldn't be in the conversation**
889
- - **Don't duplicate output data:** Metadata complements the output hash, it doesn't replace it
890
-
891
- The metadata system is particularly useful for:
892
- - Tracking session or transaction IDs across multiple steps
893
- - Storing configuration or state that tools need to access
894
- - Passing data between steps without cluttering the AI conversation
895
- - Implementing complex conditional logic based on computed values
896
-
897
- ### Instrumentation
898
-
899
- Roast provides extensive instrumentation capabilities using ActiveSupport::Notifications. You can monitor workflow execution, track AI model usage, measure performance, and integrate with external monitoring systems. [Read the full instrumentation documentation](docs/INSTRUMENTATION.md).
900
-
901
- ### Built-in Tools
902
-
903
- Roast provides several built-in tools that you can use in your workflows:
904
-
905
- #### Tool Configuration
906
-
907
- Tools can be configured using a hash format in your workflow YAML:
908
-
909
- ```yaml
910
- tools:
911
- - Roast::Tools::ReadFile # No configuration needed
912
- - Roast::Tools::Cmd: # With configuration
913
- allowed_commands:
914
- - git
915
- - npm
916
- - yarn
917
- - Roast::Tools::CodingAgent: # Optional configuration
918
- coding_agent_command: claude --model opus -p --allowedTools "Bash, Glob, Grep, LS, Read"
919
- model: opus # Model to use for all CodingAgent invocations
920
- retries: 3 # Number of automatic retries on failure (default: 0)
921
- ```
922
-
923
- Currently supported configurations:
924
- - `Roast::Tools::Cmd` via `allowed_commands`: restricts which commands can be executed (defaults to: `pwd`, `find`, `ls`, `rake`, `ruby`, `dev`, `mkdir`)
925
- - `Roast::Tools::CodingAgent` via:
926
- - `coding_agent_command`: customizes the Claude Code CLI command used by the agent
927
- - `model`: sets the model for all CodingAgent invocations (e.g., `opus`, `sonnet`)
928
- - `retries`: number of times to automatically retry if the agent encounters an error (default: 0, no retries)
929
-
930
- ##### Cmd Tool Configuration
931
-
932
- The `Cmd` tool's `allowed_commands` can be configured in two ways:
933
-
934
- **1. Simple String Format** (uses default descriptions):
935
- ```yaml
936
- tools:
937
- - Roast::Tools::Cmd:
938
- allowed_commands:
939
- - pwd
940
- - ls
941
- - git
942
- ```
943
-
944
- **2. Hash Format with Custom Descriptions**:
945
- ```yaml
946
- tools:
947
- - Roast::Tools::Cmd:
948
- allowed_commands:
949
- - pwd
950
- - name: git
951
- description: "git CLI - version control system with subcommands like status, commit, push"
952
- - name: npm
953
- description: "npm CLI - Node.js package manager with subcommands like install, run"
954
- - name: docker
955
- description: "Docker CLI - container platform with subcommands like build, run, ps"
956
- ```
957
-
958
- Custom descriptions help the LLM understand when and how to use each command, making your workflows more effective.
959
-
960
- ### Step-Level Tool Filtering
961
-
962
- You can restrict which tools are available to specific steps using the `available_tools` configuration:
963
-
964
- ```yaml
965
- # Define all tools globally
966
- tools:
967
- - Roast::Tools::Grep
968
- - Roast::Tools::ReadFile
969
- - Roast::Tools::WriteFile
970
- - Roast::Tools::Cmd:
971
- allowed_commands:
972
- - pwd
973
- - ls
974
- - echo
975
-
976
- # Configure steps with specific tool access
977
- explore_directory:
978
- available_tools:
979
- - pwd
980
- - ls
981
-
982
- analyze_files:
983
- available_tools:
984
- - grep
985
- - read_file
986
-
987
- write_summary:
988
- available_tools:
989
- - write_file
990
- - echo
991
- ```
992
-
993
- This feature provides:
994
- - **Security**: Each step only has access to the tools it needs
995
- - **Performance**: Reduces the tool list sent to the LLM
996
- - **Clarity**: Makes tool usage explicit for each step
997
-
998
- Key points:
999
- - Use snake_case tool names (e.g., `read_file` for `Roast::Tools::ReadFile`)
1000
- - For `Cmd` tool, use the specific command names (e.g., `pwd`, `ls`)
1001
- - When `available_tools` is not specified, all tools remain available (backward compatible)
1002
- - Empty array (`available_tools: []`) means no tools for that step
1003
-
1004
- See the [available_tools_demo](examples/available_tools_demo/) for a complete example.
1005
-
1006
- #### ReadFile
1007
-
1008
- Reads the contents of a file from the filesystem.
1009
-
1010
- ```ruby
1011
- # Basic usage
1012
- read_file(path: "path/to/file.txt")
1013
-
1014
- # Reading a specific portion of a file
1015
- read_file(path: "path/to/large_file.txt", offset: 100, limit: 50)
1016
- ```
1017
-
1018
- - The `path` can be absolute or relative to the current working directory
1019
- - Use `offset` and `limit` for large files to read specific sections (line numbers)
1020
- - Returns the file content as a string
1021
-
1022
- #### WriteFile
1023
-
1024
- Writes content to a file, creating the file if it doesn't exist or overwriting it if it does.
1025
-
1026
- ```ruby
1027
- # Basic usage
1028
- write_file(path: "output.txt", content: "This is the file content")
1029
-
1030
- # With path restriction for security
1031
- write_file(
1032
- path: "output.txt",
1033
- content: "Restricted content",
1034
- restrict: "/safe/directory" # Only allows writing to files under this path
1035
- )
1036
- ```
1037
-
1038
- - Creates missing directories automatically
1039
- - Can restrict file operations to specific directories for security
1040
- - Returns a success message with the number of lines written
1041
-
1042
- #### UpdateFiles
1043
-
1044
- Applies a unified diff/patch to one or more files. Changes are applied atomically when possible.
1045
-
1046
- ```ruby
1047
- update_files(
1048
- diff: <<~DIFF,
1049
- --- a/file1.txt
1050
- +++ b/file1.txt
1051
- @@ -1,3 +1,4 @@
1052
- line1
1053
- +new line
1054
- line2
1055
- line3
1056
-
1057
- --- a/file2.txt
1058
- +++ b/file2.txt
1059
- @@ -5,7 +5,7 @@
1060
- line5
1061
- line6
1062
- -old line7
1063
- +updated line7
1064
- line8
1065
- DIFF
1066
- base_path: "/path/to/project", # Optional, defaults to current working directory
1067
- restrict_path: "/path/to/allowed", # Optional, restricts where files can be modified
1068
- create_files: true, # Optional, defaults to true
1069
- )
1070
- ```
1071
-
1072
- - Accepts standard unified diff format from tools like `git diff`
1073
- - Supports multiple file changes in a single operation
1074
- - Handles file creation, deletion, and modification
1075
- - Performs atomic operations with rollback on failure
1076
- - Includes fuzzy matching to handle minor context differences
1077
- - This tool is especially useful for making targeted changes to multiple files at once
1078
-
1079
- #### Grep
1080
-
1081
- Searches file contents for a specific pattern using regular expressions.
1082
-
1083
- ```ruby
1084
- # Basic usage
1085
- grep(pattern: "function\\s+myFunction")
1086
-
1087
- # With file filtering
1088
- grep(pattern: "class\\s+User", include: "*.rb")
1089
-
1090
- # With directory scope
1091
- grep(pattern: "TODO:", path: "src/components")
1092
- ```
1093
-
1094
- - Uses regular expressions for powerful pattern matching
1095
- - Can filter by file types using the `include` parameter
1096
- - Can scope searches to specific directories with the `path` parameter
1097
- - Returns a list of files containing matches
1098
-
1099
- #### SearchFile
1100
-
1101
- Provides advanced file search capabilities beyond basic pattern matching.
1102
-
1103
- ```ruby
1104
- search_file(query: "class User", file_path: "app/models")
1105
- ```
1106
-
1107
- - Combines pattern matching with contextual search
1108
- - Useful for finding specific code structures or patterns
1109
- - Returns matched lines with context
1110
-
1111
- #### Cmd
1112
-
1113
- Executes shell commands with configurable restrictions. By default, only allows specific safe commands.
1114
-
1115
- ```ruby
1116
- # Execute allowed commands (pwd, find, ls, rake, ruby, dev, mkdir by default)
1117
- pwd(args: "-L")
1118
- ls(args: "-la")
1119
- ruby(args: "-e 'puts RUBY_VERSION'")
1120
-
1121
- # Or use the legacy cmd function with full command
1122
- cmd(command: "ls -la")
1123
- ```
1124
-
1125
- - Commands are registered as individual functions based on allowed_commands configuration
1126
- - Default allowed commands: pwd, find, ls, rake, ruby, dev, mkdir
1127
- - Each command has built-in descriptions to help the LLM understand usage
1128
- - Configurable via workflow YAML (see Tool Configuration section)
1129
-
1130
- #### Bash
1131
-
1132
- Executes shell commands without restrictions. **⚠️ WARNING: Use only in trusted environments!**
1133
-
1134
- ```ruby
1135
- # Execute any command - no restrictions
1136
- bash(command: "curl https://api.example.com | jq '.data'")
1137
-
1138
- # Complex operations with pipes and redirects
1139
- bash(command: "find . -name '*.log' -mtime +30 -delete")
1140
-
1141
- # System administration tasks
1142
- bash(command: "ps aux | grep ruby | awk '{print $2}'")
1143
- ```
1144
-
1145
- - **No command restrictions** - full shell access
1146
- - Designed for prototyping and development environments
1147
- - Logs warnings by default (disable with `ROAST_BASH_WARNINGS=false`)
1148
- - Should NOT be used in production or untrusted contexts
1149
- - See `examples/bash_prototyping/` for usage examples
1150
-
1151
- #### CodingAgent
1152
-
1153
- Creates a specialized agent for complex coding tasks or long-running operations.
1154
-
1155
- ```ruby
1156
- # Basic usage
1157
- coding_agent(
1158
- prompt: "Refactor the authentication module to use JWT tokens",
1159
- include_context_summary: true, # Include workflow context in the agent prompt
1160
- continue: true # Continue from previous agent session
1161
- )
1162
-
1163
- # With automatic retries on failure
1164
- coding_agent(
1165
- prompt: "Implement complex feature with error handling",
1166
- retries: 3 # Retry up to 3 times if the agent encounters errors
1167
- )
1168
- ```
1169
-
1170
- - Delegates complex tasks to a specialized coding agent (Claude Code)
1171
- - Useful for tasks that require deep code understanding or multi-step changes
1172
- - Can work across multiple files and languages
1173
- - Supports automatic retries on transient failures (network issues, API errors)
1174
- - Retries can be configured globally (see Tool Configuration) or per invocation
1175
-
1176
- ### MCP (Model Context Protocol) Tools
1177
-
1178
- Roast supports MCP tools, allowing you to integrate external services and tools through the Model Context Protocol standard. MCP enables seamless connections to databases, APIs, and specialized tools.
1179
-
1180
- #### Configuring MCP Tools
1181
-
1182
- MCP tools are configured in the `tools` section of your workflow YAML alongside traditional Roast tools:
1183
-
1184
- ```yaml
1185
- tools:
1186
- # Traditional Roast tools
1187
- - Roast::Tools::ReadFile
1188
-
1189
- # MCP tools with SSE (Server-Sent Events)
1190
- - Documentation:
1191
- url: https://gitmcp.io/myorg/myrepo/docs
1192
- env:
1193
- - "Authorization: Bearer {{ENV['API_TOKEN']}}"
1194
-
1195
- # MCP tools with stdio
1196
- - GitHub:
1197
- command: npx
1198
- args: ["-y", "@modelcontextprotocol/server-github"]
1199
- env:
1200
- GITHUB_PERSONAL_ACCESS_TOKEN: "{{ENV['GITHUB_TOKEN']}}"
1201
- only:
1202
- - search_repositories
1203
- - get_issue
1204
- - create_issue
49
+ Run with:
50
+ ```bash
51
+ bin/roast execute --executor=dsl analyze_codebase.rb
1205
52
  ```
1206
53
 
1207
- #### SSE MCP Tools
1208
-
1209
- Connect to HTTP endpoints implementing the MCP protocol:
1210
-
1211
- ```yaml
1212
- - Tool Name:
1213
- url: https://example.com/mcp-endpoint
1214
- env:
1215
- - "Authorization: Bearer {{resource.api_token}}"
1216
- only: [function1, function2] # Optional whitelist
1217
- except: [function3] # Optional blacklist
1218
- ```
54
+ ## Core Cogs
1219
55
 
1220
- #### Stdio MCP Tools
56
+ - **`chat`** - Send prompts to cloud-based LLMs (OpenAI, Anthropic, Gemini, etc.)
57
+ - **`agent`** - Run local coding agents with filesystem access (Claude Code CLI, etc.)
58
+ - **`ruby`** - Execute custom Ruby code within workflows
59
+ - **`cmd`** - Run shell commands and capture output
60
+ - **`map`** - Process collections in serial or parallel
61
+ - **`repeat`** - Iterate until conditions are met
62
+ - **`call`** - Invoke reusable workflow scopes
1221
63
 
1222
- Connect to local processes implementing the MCP protocol:
64
+ ## Installation
1223
65
 
1224
- ```yaml
1225
- - Tool Name:
1226
- command: docker
1227
- args: ["run", "-i", "--rm", "ghcr.io/example/mcp-server"]
1228
- env:
1229
- API_KEY: "{{ENV['API_KEY']}}"
66
+ ```bash
67
+ gem install roast-ai
1230
68
  ```
1231
69
 
1232
- See the [MCP tools example](examples/mcp/) for complete documentation and more examples.
1233
-
1234
- ### Custom Tools
1235
-
1236
- You can create your own tools using the [Raix function dispatch pattern](https://github.com/OlympiaAI/raix-rails?tab=readme-ov-file#use-of-toolsfunctions). Custom tools should be placed in `.roast/initializers/` (subdirectories are supported):
1237
-
70
+ Or add to your Gemfile:
1238
71
  ```ruby
1239
- # .roast/initializers/tools/git_analyzer.rb
1240
- module MyProject
1241
- module Tools
1242
- module GitAnalyzer
1243
- extend self
1244
-
1245
- def self.included(base)
1246
- base.class_eval do
1247
- function(
1248
- :analyze_commit,
1249
- "Analyze a git commit for code quality and changes",
1250
- commit_sha: { type: "string", description: "The SHA of the commit to analyze" },
1251
- include_diff: { type: "boolean", description: "Include the full diff in the analysis", default: false }
1252
- ) do |params|
1253
- GitAnalyzer.call(params[:commit_sha], params[:include_diff])
1254
- end
1255
- end
1256
- end
1257
-
1258
- def call(commit_sha, include_diff = false)
1259
- Roast::Helpers::Logger.info("🔍 Analyzing commit: #{commit_sha}\n")
1260
-
1261
- # Your implementation here
1262
- commit_info = `git show #{commit_sha} --stat`
1263
- commit_info += "\n\n" + `git show #{commit_sha}` if include_diff
1264
-
1265
- commit_info
1266
- rescue StandardError => e
1267
- "Error analyzing commit: #{e.message}".tap do |error_message|
1268
- Roast::Helpers::Logger.error(error_message + "\n")
1269
- end
1270
- end
1271
- end
1272
- end
1273
- end
1274
- ```
1275
-
1276
- Then include your tool in the workflow:
1277
-
1278
- ```yaml
1279
- tools:
1280
- - MyProject::Tools::GitAnalyzer
1281
- ```
1282
-
1283
- The tool will be available to the AI model during workflow execution, and it can call `analyze_commit` with the appropriate parameters.
1284
-
1285
- ### Project-specific Configuration
1286
-
1287
- You can extend Roast with project-specific configuration by creating initializers in `.roast/initializers/`. These are automatically loaded when workflows run, allowing you to:
1288
-
1289
- - Add custom instrumentation
1290
- - Configure monitoring and metrics
1291
- - Set up project-specific tools
1292
- - Customize workflow behavior
1293
-
1294
- Example structure:
1295
- ```
1296
- your-project/
1297
- ├── .roast/
1298
- │ └── initializers/
1299
- │ ├── metrics.rb
1300
- │ ├── logging.rb
1301
- │ └── custom_tools.rb
1302
- └── ...
1303
- ```
1304
-
1305
- ### Pre/Post Processing Framework
1306
-
1307
- Roast supports pre-processing and post-processing phases for workflows. This enables powerful workflows that need setup/teardown or result aggregation across all processed files.
1308
-
1309
- #### Overview
1310
-
1311
- - **Pre-processing**: Steps executed once before any targets are processed
1312
- - **Post-processing**: Steps executed once after all targets have been processed
1313
- - **Shared state**: Pre-processing results are available to all subsequent steps
1314
- - **Result aggregation**: Post-processing has access to all workflow execution results
1315
- - **Single-target support**: Pre/post processing works with single-target workflows too
1316
- - **Output templates**: Post-processing supports `output.txt` templates for custom formatting
1317
-
1318
- #### Configuration
1319
-
1320
- ```yaml
1321
- name: optimize_tests
1322
- model: gpt-4o
1323
- target: "test/**/*_test.rb"
1324
-
1325
- # Pre-processing steps run once before any test files
1326
- pre_processing:
1327
- - gather_baseline_metrics
1328
- - setup_test_environment
1329
-
1330
- # Main workflow steps run for each test file
1331
- steps:
1332
- - analyze_test
1333
- - improve_coverage
1334
- - optimize_performance
1335
-
1336
- # Post-processing steps run once after all test files
1337
- post_processing:
1338
- - aggregate_results
1339
- - generate_report
1340
- - cleanup_environment
1341
- ```
1342
-
1343
- #### Directory Structure
1344
-
1345
- Pre and post-processing steps follow the same conventions as regular steps but are organized in their own directories:
1346
-
1347
- ```
1348
- workflow.yml
1349
- pre_processing/
1350
- ├── gather_baseline_metrics/
1351
- │ └── prompt.md
1352
- └── setup_test_environment/
1353
- └── prompt.md
1354
- analyze_test/
1355
- └── prompt.md
1356
- improve_coverage/
1357
- └── prompt.md
1358
- optimize_performance/
1359
- └── prompt.md
1360
- post_processing/
1361
- ├── output.txt
1362
- ├── aggregate_results/
1363
- │ └── prompt.md
1364
- ├── generate_report/
1365
- │ └── prompt.md
1366
- └── cleanup_environment/
1367
- └── prompt.md
1368
- ```
1369
-
1370
- #### Data Access
1371
-
1372
- **Pre-processing results in target workflows:**
1373
-
1374
- Target workflows have access to pre-processing results through the `pre_processing_data` variable with dot notation:
1375
-
1376
- ```erb
1377
- # In a target workflow step prompt
1378
- The baseline metrics from pre-processing:
1379
- <%= pre_processing_data.gather_baseline_metrics %>
1380
-
1381
- Environment setup details:
1382
- <%= pre_processing_data.setup_test_environment %>
1383
- ```
1384
-
1385
- **Post-processing data access:**
1386
-
1387
- Post-processing steps have access to:
1388
-
1389
- - `pre_processing`: Direct access to pre-processing results with dot notation
1390
- - `targets`: Hash of all target workflow results, keyed by file paths
1391
-
1392
- Example post-processing prompt:
1393
- ```markdown
1394
- # Generate Summary Report
1395
-
1396
- Based on the baseline metrics:
1397
- <%= pre_processing.gather_baseline_metrics %>
1398
-
1399
- Environment configuration:
1400
- <%= pre_processing.setup_test_environment %>
1401
-
1402
- And the results from processing all files:
1403
- <% targets.each do |file, target| %>
1404
- File: <%= file %>
1405
- Analysis results: <%= target.output.analyze_test %>
1406
- Coverage improvements: <%= target.output.improve_coverage %>
1407
- Performance optimizations: <%= target.output.optimize_performance %>
1408
- <% end %>
1409
-
1410
- Please generate a comprehensive summary report showing:
1411
- 1. Overall improvements achieved
1412
- 2. Files with the most significant changes
1413
- 3. Recommendations for further optimization
1414
- ```
1415
-
1416
- #### Output Templates
1417
-
1418
- Post-processing supports custom output formatting using ERB templates. Create an `output.txt` file in your `post_processing` directory to format the final workflow output:
1419
-
1420
- ```erb
1421
- # post_processing/output.txt
1422
- === Workflow Summary Report ===
1423
- Generated at: <%= Time.now.strftime("%Y-%m-%d %H:%M:%S") %>
1424
-
1425
- Environment: <%= pre_processing.setup_test_environment %>
1426
-
1427
- Files Processed: <%= targets.size %>
1428
-
1429
- <% targets.each do |file, target| %>
1430
- - <%= file %>: <%= target.output.analyze_test %>
1431
- <% end %>
1432
-
1433
- <%= output.generate_report %>
1434
- ===============================
72
+ gem 'roast-ai'
1435
73
  ```
1436
74
 
1437
- The template has access to:
1438
- - `pre_processing`: All pre-processing step outputs with dot notation
1439
- - `targets`: Hash of all target workflow results with dot notation (each target has `.output` and `.final_output`)
1440
- - `output`: Post-processing step outputs with dot notation
1441
-
1442
- #### Use Cases
1443
-
1444
- This pattern is ideal for:
1445
-
1446
- - **Code migrations**: Setup migration tools, process files, generate migration report
1447
- - **Test optimization**: Baseline metrics, optimize tests, aggregate improvements
1448
- - **Documentation generation**: Analyze codebase, generate docs per module, create index
1449
- - **Dependency updates**: Check versions, update files, verify compatibility
1450
- - **Security audits**: Setup scanners, check each file, generate security report
1451
- - **Performance analysis**: Establish baselines, analyze components, summarize findings
1452
-
1453
- See the [pre/post processing example](examples/pre_post_processing) for a complete working demonstration.
1454
-
1455
-
1456
- ## Development
1457
-
1458
- After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake` to run the tests and linter. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
75
+ ## Requirements
76
+
77
+ - Ruby 3.0+
78
+ - API keys for your AI provider (OpenAI/Anthropic)
79
+ - Claude Code CLI installed (for agent cog)
80
+
81
+ ## Getting Started
82
+
83
+ The best way to learn Roast is through the interactive tutorial:
84
+
85
+ 📚 **[Start the Tutorial](https://github.com/Shopify/roast/blob/main/dsl/tutorial/README.md)**
86
+
87
+ The tutorial covers:
88
+ 1. Your first workflow
89
+ 2. Chaining cogs together
90
+ 3. Accepting targets and parameters
91
+ 4. Configuration options
92
+ 5. Control flow
93
+ 6. Reusable scopes
94
+ 7. Processing collections
95
+ 8. Iterative workflows
96
+ 9. Async execution
97
+
98
+ ## Documentation
99
+
100
+ - [Tutorial](https://github.com/Shopify/roast/blob/main/dsl/tutorial/README.md) - Step-by-step guide with examples
101
+ - [Functional Tests](https://github.com/Shopify/roast/tree/main/dsl) - Toy workflows that demonstrate all functional patterns, use for end-to-end test
102
+ - [API Reference](https://github.com/Shopify/roast/tree/main/sorbet/rbi/shims/lib/roast/dsl) - Complete cog documentation
103
+
104
+ ## Documentation & References
105
+
106
+ > ⚠️ Roast stills supports the legacy version `0.x`.
107
+ > Examples of workflows using the new version `1.0-preview` system
108
+ > are namespaced within the `dsl` hierarchy.
109
+
110
+ * __v1.0 source code root__: https://github.com/Shopify/roast/tree/main/lib/roast/dsl
111
+
112
+ The public interfaces of the new Roast are extensively documented in
113
+ class and method comments on the relevant classes.
114
+
115
+ * __Tutorial and Examples__
116
+ * [Tutorial -- Table of Contents](https://github.com/Shopify/roast/tree/main/dsl/tutorial) (contains step-by-step guides and runnable examples showing real-world usage)
117
+ * [Additional Example Workflows](https://github.com/Shopify/roast/tree/main/dsl) (these comprise the Roast end-to-end test suite)
118
+ * __Configuation__
119
+ * [General configuration block: `config-context.rbi`](https://github.com/Shopify/roast/blob/main/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi)
120
+ * [Global cog configuration: `cog/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cog/config.rb)
121
+ * [Agent cog configuration: `agent/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/agent/config.rb)
122
+ * [Chat cog configuration: `chat/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/chat/config.rb)
123
+ * [Cmd cog configuration: `cmd/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/cmd.rb)
124
+ * [Map cog configuration: `map.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/system_cogs/map.rb)
125
+ * __Execution__
126
+ * [General Execution block: `execution-context.rbi`](https://github.com/Shopify/roast/blob/main/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi)
127
+ * __Input and Output__
128
+ * [General cog input block: `cog-input-context.rbi`](https://github.com/Shopify/roast/blob/main/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi)
129
+ * [Global cog output: `cog/output.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cog/output.rb)
130
+ * [Agent cog input `agent/input.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/agent/input.rb)
131
+ * [Agent cog output `agent/output.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/agent/output.rb)
132
+ * [Call cog input: `call.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/system_cogs/call.rb)
133
+ * [Chat cog input: `chat.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/chat/input.rb)
134
+ * [Chat cog output: `chat.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/chat/output.rb)
135
+ * [Cmd cog input: `cmd.rb:159`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/cmd.rb#L159) (scroll down)
136
+ * [Cmd cog output: `cmd.rb:214`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/cmd.rb#L214) (scroll down)
137
+ * [Map cog input: `map.rb:116`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/system_cogs/map.rb#L116) (scroll down)
138
+ * [Repeat cog input: `repeat.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/system_cogs/repeat.rb)
139
+ * [Ruby cog input: `ruby.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/ruby.rb)
140
+ * [Ruby cog output: `ruby.rb:63`](https://github.com/Shopify/roast/blob/main/lib/roast/dsl/cogs/ruby.rb#L63) (scroll down)
141
+
142
+ ## Contributing
143
+
144
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
1459
145
 
1460
146
  ## License
1461
147
 
1462
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
148
+ [MIT License](LICENSE)