roast-ai 0.1.7 → 0.2.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +1 -1
  3. data/CHANGELOG.md +40 -1
  4. data/CLAUDE.md +20 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +9 -6
  7. data/README.md +81 -14
  8. data/bin/roast +27 -0
  9. data/docs/ITERATION_SYNTAX.md +119 -0
  10. data/examples/conditional/README.md +161 -0
  11. data/examples/conditional/check_condition/prompt.md +1 -0
  12. data/examples/conditional/simple_workflow.yml +15 -0
  13. data/examples/conditional/workflow.yml +23 -0
  14. data/examples/dot_notation/README.md +37 -0
  15. data/examples/dot_notation/workflow.yml +44 -0
  16. data/examples/exit_on_error/README.md +50 -0
  17. data/examples/exit_on_error/analyze_lint_output/prompt.md +9 -0
  18. data/examples/exit_on_error/apply_fixes/prompt.md +2 -0
  19. data/examples/exit_on_error/workflow.yml +19 -0
  20. data/examples/grading/workflow.yml +5 -1
  21. data/examples/iteration/IMPLEMENTATION.md +88 -0
  22. data/examples/iteration/README.md +68 -0
  23. data/examples/iteration/analyze_complexity/prompt.md +22 -0
  24. data/examples/iteration/generate_recommendations/prompt.md +21 -0
  25. data/examples/iteration/generate_report/prompt.md +129 -0
  26. data/examples/iteration/implement_fix/prompt.md +25 -0
  27. data/examples/iteration/prioritize_issues/prompt.md +24 -0
  28. data/examples/iteration/prompts/analyze_file.md +28 -0
  29. data/examples/iteration/prompts/generate_summary.md +24 -0
  30. data/examples/iteration/prompts/update_report.md +29 -0
  31. data/examples/iteration/prompts/write_report.md +22 -0
  32. data/examples/iteration/read_file/prompt.md +9 -0
  33. data/examples/iteration/select_next_issue/prompt.md +25 -0
  34. data/examples/iteration/simple_workflow.md +39 -0
  35. data/examples/iteration/simple_workflow.yml +58 -0
  36. data/examples/iteration/update_fix_count/prompt.md +26 -0
  37. data/examples/iteration/verify_fix/prompt.md +29 -0
  38. data/examples/iteration/workflow.yml +42 -0
  39. data/examples/openrouter_example/workflow.yml +2 -2
  40. data/examples/workflow_generator/README.md +27 -0
  41. data/examples/workflow_generator/analyze_user_request/prompt.md +34 -0
  42. data/examples/workflow_generator/create_workflow_files/prompt.md +32 -0
  43. data/examples/workflow_generator/get_user_input/prompt.md +14 -0
  44. data/examples/workflow_generator/info_from_roast.rb +22 -0
  45. data/examples/workflow_generator/workflow.yml +35 -0
  46. data/lib/roast/errors.rb +9 -0
  47. data/lib/roast/factories/api_provider_factory.rb +61 -0
  48. data/lib/roast/helpers/function_caching_interceptor.rb +1 -1
  49. data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
  50. data/lib/roast/helpers/prompt_loader.rb +50 -1
  51. data/lib/roast/resources/base_resource.rb +7 -0
  52. data/lib/roast/resources.rb +6 -6
  53. data/lib/roast/tools/ask_user.rb +40 -0
  54. data/lib/roast/tools/cmd.rb +1 -1
  55. data/lib/roast/tools/search_file.rb +1 -1
  56. data/lib/roast/tools.rb +11 -1
  57. data/lib/roast/value_objects/api_token.rb +49 -0
  58. data/lib/roast/value_objects/step_name.rb +39 -0
  59. data/lib/roast/value_objects/workflow_path.rb +77 -0
  60. data/lib/roast/value_objects.rb +5 -0
  61. data/lib/roast/version.rb +1 -1
  62. data/lib/roast/workflow/api_configuration.rb +61 -0
  63. data/lib/roast/workflow/base_iteration_step.rb +165 -0
  64. data/lib/roast/workflow/base_step.rb +4 -24
  65. data/lib/roast/workflow/base_workflow.rb +76 -73
  66. data/lib/roast/workflow/command_executor.rb +88 -0
  67. data/lib/roast/workflow/conditional_executor.rb +50 -0
  68. data/lib/roast/workflow/conditional_step.rb +96 -0
  69. data/lib/roast/workflow/configuration.rb +35 -158
  70. data/lib/roast/workflow/configuration_loader.rb +78 -0
  71. data/lib/roast/workflow/configuration_parser.rb +13 -248
  72. data/lib/roast/workflow/context_path_resolver.rb +43 -0
  73. data/lib/roast/workflow/dot_access_hash.rb +198 -0
  74. data/lib/roast/workflow/each_step.rb +86 -0
  75. data/lib/roast/workflow/error_handler.rb +97 -0
  76. data/lib/roast/workflow/expression_utils.rb +36 -0
  77. data/lib/roast/workflow/file_state_repository.rb +3 -2
  78. data/lib/roast/workflow/interpolator.rb +34 -0
  79. data/lib/roast/workflow/iteration_executor.rb +85 -0
  80. data/lib/roast/workflow/llm_boolean_coercer.rb +55 -0
  81. data/lib/roast/workflow/output_handler.rb +35 -0
  82. data/lib/roast/workflow/output_manager.rb +77 -0
  83. data/lib/roast/workflow/parallel_executor.rb +49 -0
  84. data/lib/roast/workflow/repeat_step.rb +75 -0
  85. data/lib/roast/workflow/replay_handler.rb +123 -0
  86. data/lib/roast/workflow/resource_resolver.rb +77 -0
  87. data/lib/roast/workflow/session_manager.rb +6 -2
  88. data/lib/roast/workflow/state_manager.rb +97 -0
  89. data/lib/roast/workflow/step_executor_coordinator.rb +205 -0
  90. data/lib/roast/workflow/step_executor_factory.rb +47 -0
  91. data/lib/roast/workflow/step_executor_registry.rb +79 -0
  92. data/lib/roast/workflow/step_executors/base_step_executor.rb +23 -0
  93. data/lib/roast/workflow/step_executors/hash_step_executor.rb +43 -0
  94. data/lib/roast/workflow/step_executors/parallel_step_executor.rb +54 -0
  95. data/lib/roast/workflow/step_executors/string_step_executor.rb +29 -0
  96. data/lib/roast/workflow/step_finder.rb +97 -0
  97. data/lib/roast/workflow/step_loader.rb +154 -0
  98. data/lib/roast/workflow/step_orchestrator.rb +45 -0
  99. data/lib/roast/workflow/step_runner.rb +23 -0
  100. data/lib/roast/workflow/step_type_resolver.rb +117 -0
  101. data/lib/roast/workflow/workflow_context.rb +60 -0
  102. data/lib/roast/workflow/workflow_executor.rb +90 -209
  103. data/lib/roast/workflow/workflow_initializer.rb +112 -0
  104. data/lib/roast/workflow/workflow_runner.rb +87 -0
  105. data/lib/roast/workflow.rb +3 -0
  106. data/lib/roast.rb +96 -3
  107. data/roast.gemspec +2 -1
  108. data/schema/workflow.json +85 -0
  109. metadata +97 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40bb03c596dcaa3dd49685c9b5f804ca6c6db00cf239d5e52748e6fa485f9c3b
4
- data.tar.gz: c56399cca493aa9d925bb4d0550dc7ad0c0ed03b74b4f7894a3205ba72e66d1e
3
+ metadata.gz: 8ab27704514fc6ddfa09003464d2aa324b852379b0548619e2f0023bebe793ae
4
+ data.tar.gz: 18c0936f2b5dc2b54ef420be2ac089b8252f64a02227ab33d98db9d2e6249136
5
5
  SHA512:
6
- metadata.gz: 2560cd52f21eb2aa0c954d1759ac762c12f2840a1533cc41017003a022f002212e9fb8d6e6b3d11fc1245ff413a86bd68d3aad7a142de9a30d4c534277a80f51
7
- data.tar.gz: 371d573330873f1e76aec168e72ed8e2c53d0a127af83683205fae7d060dfa758aeb2352133b4f8b263803b5384b77be76b91a564d945cfd8966a682959a0d13
6
+ metadata.gz: 50552385cdb280592a1e331c4a3ec5de2924fa05636af5deb742b96f2fde229e3448e61e6516e570c02a8dc0f416a3b9a3c3d884a800eb89987c90c856a0258d
7
+ data.tar.gz: 8feeb510d87e6e6256c46cbca3e13fbc65701ceb28b049452e5bea2f44815f7770e688f8422487b431a85c5f459beffadcce590e7b860cde8a1ab4ee939ff50e
@@ -16,7 +16,7 @@ jobs:
16
16
  fail-fast: false
17
17
  matrix:
18
18
  os: [ubuntu]
19
- ruby: ['3.4']
19
+ ruby: ['3.2', '3.3', '3.4']
20
20
  runs-on: ${{ matrix.os }}-latest
21
21
  continue-on-error: ${{ matrix.ruby == 'ruby-head' }}
22
22
  steps:
data/CHANGELOG.md CHANGED
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2025-05-26
9
+
10
+ ### Added
11
+ - Conditional execution support with `if` and `unless` clauses for workflow steps
12
+ - Iteration mechanisms for workflows with `repeat` and `each` constructs (resolving issue #48)
13
+ - Support for conditional repetition with `until` condition and safety limits
14
+ - Collection iteration with variable binding for processing lists of items
15
+ - State persistence for loop iterations to enable resumption after failure
16
+ - Standardized evaluation of Ruby expressions in iteration constructs using `{{}}` syntax
17
+ - Support for using bash commands, step names, and Ruby expressions in iteration conditions and collections
18
+ - Intelligent LLM response to boolean conversion with pattern-based recognition for natural language responses
19
+ - `exit_on_error` configuration option for command steps to continue workflow on failure (resolving issue #71)
20
+ - Dot notation access for workflow outputs (e.g., `workflow.output.step.field`)
21
+ - `--pause` flag for stepping through workflow execution interactively
22
+
23
+ ### Fixed
24
+ - Automatically add `.gitignore` file to cache directory when created (completing issue #22)
25
+ - Load initializers before trying to load tools in case custom tools are defined in initializers (thanks @palkan)
26
+ - Fix loading of targetless workflows (thanks @palkan)
27
+ - Fix OpenRouter support (thanks @xrendan)
28
+ - API authentication error handling and model access issues
29
+ - Conditional step transcript replay regression
30
+ - DotAccessHash serialization for AI prompts
31
+
32
+ ### Improved
33
+ - Enhanced search file tool logging to show full expanded paths instead of relative paths
34
+ - Major refactoring to eliminate circular dependencies and improve architecture
35
+ - Extracted command execution logic into dedicated CommandExecutor class
36
+ - Separated conditional execution from iteration logic for better SOLID compliance
37
+ - Enhanced error messages for API authentication failures
38
+ - Replaced all `require_relative` with `require` statements for consistency
39
+
40
+ ### Changed
41
+ - Refactored god objects to improve code organization and maintainability
42
+ - Improved separation of concerns between workflow components
43
+
44
+ [0.2.0]: https://github.com/Shopify/roast/compare/v0.1.7...v0.2.0
45
+
8
46
  ## [0.1.7] - 2024-05-16
9
47
 
10
48
  ### Added
@@ -13,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
51
  - Comprehensive documentation for all built-in tools
14
52
  - Enhanced README with detailed tool usage examples
15
53
 
54
+ ``[0.1.7]: https://github.com/Shopify/roast/compare/v0.1.6...v0.1.7
55
+
16
56
  ## [0.1.6] - 2024-05-15
17
57
 
18
58
  ### Added
@@ -33,7 +73,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
33
73
  - Improved initializer loading and error handling
34
74
  - Fixed tests for nested .roast folders
35
75
 
36
- [0.1.7]: https://github.com/Shopify/roast/compare/v0.1.6...v0.1.7
37
76
  [0.1.6]: https://github.com/Shopify/roast/compare/v0.1.5...v0.1.6
38
77
 
39
78
  ## [0.1.5] - 2024-05-13
data/CLAUDE.md CHANGED
@@ -12,10 +12,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
12
12
  - Run single test: `bundle exec ruby -Itest test/path/to/test_file.rb`
13
13
  - Lint: `bundle exec rubocop`
14
14
  - Lint (with autocorrect, preferred): `bundle exec rubocop -A`
15
+ - Whenever you want to run the whole test suite just run `bundle exec rake` to also run linting, and note the linting errors too (most will auto correct but not all)
16
+ - **Run roast locally**: Use `bin/roast` (not `bundle exec roast` which may use the installed gem)
17
+ - Alternative: `bundle exec exe/roast`
15
18
 
16
19
  ## Tech stack
17
20
  - `thor` and `cli-ui` for the CLI tool
18
21
  - Testing: Use Minitest, VCR for HTTP mocking, test files named with `_test.rb` suffix
22
+ - Prefer using the more literate `test "this is a test description" do` type of testing that we get from extending ActiveSupport::TestCase over the primitive XUnit-style def test_description headings for tests
19
23
 
20
24
  ## Code Style Guidelines
21
25
 
@@ -30,6 +34,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
30
34
  - Add runtime dependencies to `roast.gemspec`.
31
35
  - Add development dependencies to `Gemfile`.
32
36
  - Don't ever test private methods directly. Specs should test behavior, not implementation.
37
+ - I do not like test-specific code embedded in production code, don't ever do that
38
+ - **Do not use require_relative**
39
+ - Require statements should always be in alphabetical order
40
+ - Always leave a blank line after module includes and before the rest of the class
41
+
42
+ ## Architecture Guidelines
43
+
44
+ - **Maintain proper separation of concerns**: Don't mix unrelated concepts in the same class or module
45
+ - Example: Conditional execution (if/unless) should NOT be mixed with iteration execution (each/repeat)
46
+ - Each concept should have its own executor class and be handled separately
47
+ - **Use appropriate inheritance**: Only inherit from a base class if the child truly "is-a" type of the parent
48
+ - Don't inherit just to reuse some methods - use composition instead
49
+ - **Follow Single Responsibility Principle**: Each class should have one reason to change
50
+ - IterationExecutor handles iterations (each, repeat)
51
+ - ConditionalExecutor handles conditionals (if, unless)
52
+ - Don't combine different responsibilities in one class
33
53
 
34
54
  ## Git Workflow Practices
35
55
 
data/Gemfile CHANGED
@@ -8,6 +8,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
8
  gemspec
9
9
 
10
10
  gem "cgi"
11
+ gem "cli-ui"
11
12
  gem "dotenv"
12
13
  gem "guard"
13
14
  gem "guard-minitest"
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (0.1.7)
4
+ roast-ai (0.2.0)
5
5
  activesupport (~> 8.0)
6
+ cli-ui
6
7
  diff-lcs (~> 1.5)
7
8
  faraday-retry
8
9
  json-schema
9
- raix (~> 0.8.4)
10
+ raix (~> 0.8.6)
10
11
  thor (~> 1.3)
11
12
 
12
13
  GEM
@@ -32,9 +33,10 @@ GEM
32
33
  benchmark (0.4.0)
33
34
  bigdecimal (3.1.9)
34
35
  cgi (0.4.2)
36
+ cli-ui (2.3.1)
35
37
  coderay (1.1.3)
36
38
  concurrent-ruby (1.3.5)
37
- connection_pool (2.5.1)
39
+ connection_pool (2.5.3)
38
40
  crack (1.0.0)
39
41
  bigdecimal
40
42
  rexml
@@ -42,7 +44,7 @@ GEM
42
44
  dotenv (3.1.8)
43
45
  drb (2.2.1)
44
46
  event_stream_parser (1.0.0)
45
- faraday (2.13.0)
47
+ faraday (2.13.1)
46
48
  faraday-net_http (>= 2.0, < 3.5)
47
49
  json
48
50
  logger
@@ -74,7 +76,7 @@ GEM
74
76
  hashdiff (1.1.2)
75
77
  i18n (1.14.7)
76
78
  concurrent-ruby (~> 1.0)
77
- json (2.10.2)
79
+ json (2.12.0)
78
80
  json-schema (5.1.1)
79
81
  addressable (~> 2.8)
80
82
  bigdecimal (~> 3.1)
@@ -113,7 +115,7 @@ GEM
113
115
  public_suffix (6.0.1)
114
116
  racc (1.8.1)
115
117
  rainbow (3.1.1)
116
- raix (0.8.4)
118
+ raix (0.8.6)
117
119
  activesupport (>= 6.0)
118
120
  faraday-retry (~> 2.0)
119
121
  open_router (~> 0.2)
@@ -168,6 +170,7 @@ PLATFORMS
168
170
 
169
171
  DEPENDENCIES
170
172
  cgi
173
+ cli-ui
171
174
  dotenv
172
175
  guard
173
176
  guard-minitest
data/README.md CHANGED
@@ -46,7 +46,7 @@ Each step can have its own prompt file (e.g., `analyze_coverage/prompt.md`) and
46
46
  ```yaml
47
47
  steps:
48
48
  - prepare_data
49
- -
49
+ -
50
50
  - analyze_code_quality
51
51
  - check_test_coverage
52
52
  - verify_documentation
@@ -86,21 +86,21 @@ In Roast, workflows maintain a single conversation with the AI model throughout
86
86
 
87
87
  Roast supports several types of steps:
88
88
 
89
- 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.
89
+ 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.
90
90
  ```yaml
91
91
  steps:
92
92
  - analyze_code
93
93
  ```
94
94
 
95
95
  As an alternative to a directory, you can also implement a custom step as a Ruby class, optionally extending `Roast::Workflow::BaseStep`.
96
-
96
+
97
97
  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.
98
98
 
99
99
 
100
100
  2. **Parallel steps**: Groups of steps executed concurrently
101
101
  ```yaml
102
102
  steps:
103
- -
103
+ -
104
104
  - analyze_code_quality
105
105
  - check_test_coverage
106
106
  ```
@@ -113,7 +113,72 @@ Roast supports several types of steps:
113
113
  ```
114
114
  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).
115
115
 
116
- 4. **Raw prompt step**: Simple text prompts for the model without tools
116
+ By default, commands that exit with non-zero status will halt the workflow. You can configure steps to continue on error:
117
+ ```yaml
118
+ steps:
119
+ - lint_check: $(rubocop {{file}})
120
+ - fix_issues
121
+
122
+ # Step configuration
123
+ lint_check:
124
+ exit_on_error: false # Continue workflow even if command fails
125
+ ```
126
+ When `exit_on_error: false`, the command output will include the exit status, allowing subsequent steps to process error information.
127
+
128
+ 4. **Conditional steps**: Execute different steps based on conditions using `if/unless`
129
+ ```yaml
130
+ steps:
131
+ - check_environment:
132
+ if: "{{ENV['RAILS_ENV'] == 'production'}}"
133
+ then:
134
+ - run_production_checks
135
+ - notify_team
136
+ else:
137
+ - run_development_setup
138
+
139
+ - verify_dependencies:
140
+ unless: "$(bundle check)"
141
+ then:
142
+ - bundle_install: "$(bundle install)"
143
+ ```
144
+
145
+ Conditions can be:
146
+ - Ruby expressions: `if: "{{output['count'] > 5}}"`
147
+ - Bash commands: `if: "$(test -f config.yml && echo true)"` (exit code 0 = true)
148
+ - Step references: `if: "previous_step_name"` (uses the step's output)
149
+ - Direct values: `if: "true"` or `if: "false"`
150
+
151
+ 5. **Iteration steps**: Loop over collections or repeat steps with conditions
152
+ ```yaml
153
+ steps:
154
+ # Loop over a collection
155
+ - process_files:
156
+ each: "{{Dir.glob('**/*.rb')}}"
157
+ as: current_file
158
+ steps:
159
+ - analyze_file
160
+ - Generate a report for {{current_file}}
161
+
162
+ # Repeat until a condition is met
163
+ - improve_code:
164
+ repeat:
165
+ until: "{{output['test_pass'] == true}}"
166
+ max_iterations: 5
167
+ steps:
168
+ - run_tests
169
+ - fix_issues
170
+ ```
171
+
172
+ Each loops support:
173
+ - Collections from Ruby expressions: `each: "{{[1, 2, 3]}}"`
174
+ - Command output: `each: "$(ls *.rb)"`
175
+ - Step references: `each: "file_list"`
176
+
177
+ Repeat loops support:
178
+ - Until conditions: `until: "{{condition}}"`
179
+ - Maximum iterations: `max_iterations: 10`
180
+
181
+ 6. **Raw prompt step**: Simple text prompts for the model without tools
117
182
  ```yaml
118
183
  steps:
119
184
  - Summarize the changes made to the codebase.
@@ -139,7 +204,7 @@ Roast handles data flow between steps in three primary ways:
139
204
  - Generate a summary for {{file}}
140
205
  - result_for_{{file}}: store_results
141
206
  ```
142
-
207
+
143
208
  Interpolation supports:
144
209
  - Simple variable access: `{{file}}`, `{{resource.target}}`
145
210
  - Access to step outputs: `{{output['previous_step']}}`
@@ -150,7 +215,7 @@ For typical AI workflows, the continuous conversation history provides seamless
150
215
  ### Command Line Options
151
216
 
152
217
  #### Basic Options
153
- - `-o, --output FILE`: Save results to a file instead of outputting to STDOUT
218
+ - `-o, --output FILE`: Save results to a file instead of outputting to STDOUT
154
219
  - `-c, --concise`: Use concise output templates (exposed as a boolean flag on `workflow`)
155
220
  - `-v, --verbose`: Show output from all steps as they execute
156
221
  - `-r, --replay STEP_NAME`: Resume a workflow from a specific step, optionally with a specific session timestamp
@@ -241,7 +306,7 @@ roast execute workflow.yml -t "$(find . -name '*.rb' -mtime -1)"
241
306
  # Process changed test files
242
307
  roast execute workflow.yml -t "$(git diff --name-only HEAD | grep _test.rb)"
243
308
 
244
- # Process staged files
309
+ # Process staged files
245
310
  roast execute workflow.yml -t "$(git diff --cached --name-only)"
246
311
  ```
247
312
 
@@ -255,7 +320,7 @@ name: API Integration Workflow
255
320
  tools:
256
321
  - Roast::Tools::ReadFile
257
322
  - Roast::Tools::WriteFile
258
-
323
+
259
324
  # Dynamic API token using shell command
260
325
  api_token: $(cat ~/.my_token)
261
326
 
@@ -349,7 +414,9 @@ api_provider: openrouter
349
414
  api_token: $(echo $OPENROUTER_API_KEY)
350
415
  ```
351
416
 
352
- This makes it easy to use environment-specific tokens without hardcoding credentials, especially useful in development environments or CI/CD pipelines.
417
+
418
+ 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.
419
+
353
420
 
354
421
  ### Template Output with ERB
355
422
 
@@ -372,7 +439,7 @@ This is an example of where the `workflow.output` hash is useful - formatting ou
372
439
 
373
440
  Available in templates:
374
441
  - `response`: The AI's response for this step
375
- - `workflow`: Access to the workflow object
442
+ - `workflow`: Access to the workflow object
376
443
  - `workflow.output`: The shared hash containing results from all steps when you need programmatic access
377
444
  - `workflow.file`: Current file being processed (or `nil` for targetless workflows)
378
445
  - All workflow configuration options
@@ -439,7 +506,7 @@ update_files(
439
506
  +new line
440
507
  line2
441
508
  line3
442
-
509
+
443
510
  --- a/file2.txt
444
511
  +++ b/file2.txt
445
512
  @@ -5,7 +5,7 @@
@@ -556,11 +623,11 @@ module MyProject
556
623
 
557
624
  def call(commit_sha, include_diff = false)
558
625
  Roast::Helpers::Logger.info("🔍 Analyzing commit: #{commit_sha}\n")
559
-
626
+
560
627
  # Your implementation here
561
628
  commit_info = `git show #{commit_sha} --stat`
562
629
  commit_info += "\n\n" + `git show #{commit_sha}` if include_diff
563
-
630
+
564
631
  commit_info
565
632
  rescue StandardError => e
566
633
  "Error analyzing commit: #{e.message}".tap do |error_message|
data/bin/roast ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'roast' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../.ruby-lsp/Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("roast-ai", "roast")
@@ -0,0 +1,119 @@
1
+ # Using Iteration with Standardized Syntax
2
+
3
+ ## Overview
4
+
5
+ Roast supports powerful iteration constructs with the `repeat` and `each` workflow steps. These features now support a standardized approach to evaluating expressions using the double-curly braces syntax (`{{...}}`).
6
+
7
+ ## Syntax Options for Iteration Inputs
8
+
9
+ Both `until` conditions (in `repeat`) and collection expressions (in `each`) accept the following formats:
10
+
11
+ ### 1. Ruby Expressions with `{{...}}` Syntax
12
+
13
+ For evaluating Ruby code in the workflow context:
14
+
15
+ ```yaml
16
+ # Repeat until a condition is met
17
+ - repeat:
18
+ steps:
19
+ - process_item
20
+ until: "{{output['counter'] >= 5}}"
21
+ max_iterations: 10
22
+
23
+ # Iterate over a collection
24
+ - each: "{{output['items'].filter { |item| item.active? }}}"
25
+ as: "current_item"
26
+ steps:
27
+ - process_item
28
+ ```
29
+
30
+ ### 2. Bash Commands with `$(...)` Syntax
31
+
32
+ For executing shell commands and using their results:
33
+
34
+ ```yaml
35
+ # Repeat until a command succeeds
36
+ - repeat:
37
+ steps:
38
+ - check_service
39
+ until: "$(curl -s -o /dev/null -w '%{http_code}' http://service.local/ | grep -q 200)"
40
+ max_iterations: 20
41
+
42
+ # Iterate over files returned by a command
43
+ - each: "$(find . -name '*.rb' -type f)"
44
+ as: "current_file"
45
+ steps:
46
+ - process_file
47
+ ```
48
+
49
+ ### 3. Step Names (as strings)
50
+
51
+ For using the result of another step:
52
+
53
+ ```yaml
54
+ # Repeat until a step returns a truthy value
55
+ - repeat:
56
+ steps:
57
+ - process_batch
58
+ until: "check_completion"
59
+ max_iterations: 100
60
+
61
+ # Iterate over items returned by a step
62
+ - each: "get_pending_items"
63
+ as: "pending_item"
64
+ steps:
65
+ - process_pending_item
66
+ ```
67
+
68
+ ### 4. Prompt Content
69
+
70
+ For defining prompts directly in the workflow:
71
+
72
+ ```yaml
73
+ # Using a prompt to determine continuation
74
+ - repeat:
75
+ steps:
76
+ - process_content
77
+ until:
78
+ prompt: prompts/check_completion.md
79
+ model: claude-3-haiku
80
+ max_iterations: 10
81
+
82
+ # Using a prompt to generate a collection
83
+ - each:
84
+ prompt: prompts/generate_test_cases.md
85
+ model: claude-3-haiku
86
+ as: "test_case"
87
+ steps:
88
+ - run_test
89
+ ```
90
+
91
+ ## Type Coercion
92
+
93
+ Values are automatically coerced to the appropriate types:
94
+
95
+ - For `until` conditions: Values are coerced to booleans (using Ruby's truthiness rules)
96
+ - For `each` collections: Values are coerced to iterables (converted to arrays of lines if needed)
97
+
98
+ ## Migrating Existing Workflows
99
+
100
+ If you're updating existing workflows:
101
+
102
+ 1. For Ruby expressions, wrap them in `{{...}}`:
103
+ ```yaml
104
+ # Old
105
+ until: "output['counter'] >= 5"
106
+
107
+ # New
108
+ until: "{{output['counter'] >= 5}}"
109
+ ```
110
+
111
+ 2. Bash commands, step names, and prompts can remain unchanged.
112
+
113
+ ## Best Practices
114
+
115
+ - Use `{{...}}` for all Ruby expressions to make them explicit
116
+ - For complex conditions, consider creating a dedicated step that returns a boolean
117
+ - For collections, ensure they return iterable objects (arrays, hashes, etc.)
118
+ - Always set reasonable `max_iterations` limits on repeat loops
119
+ - Use meaningful variable names in `each` loops
@@ -0,0 +1,161 @@
1
+ # Conditional Execution in Roast Workflows
2
+
3
+ This example demonstrates how to use conditional execution (`if` and `unless`) in Roast workflows.
4
+
5
+ ## Overview
6
+
7
+ Conditional execution allows workflows to execute different steps based on runtime conditions. This feature supports:
8
+
9
+ - `if` conditions - execute steps when a condition is true
10
+ - `unless` conditions - execute steps when a condition is false
11
+ - `then` branches - steps to execute when the condition matches
12
+ - `else` branches - steps to execute when the condition doesn't match (optional, only for `if`)
13
+
14
+ ## Syntax
15
+
16
+ ### If Statement
17
+
18
+ ```yaml
19
+ - if: "{{expression}}"
20
+ then:
21
+ - step1
22
+ - step2
23
+ else:
24
+ - step3
25
+ - step4
26
+ ```
27
+
28
+ ### Unless Statement
29
+
30
+ ```yaml
31
+ - unless: "{{expression}}"
32
+ then:
33
+ - step1
34
+ - step2
35
+ ```
36
+
37
+ ## Condition Types
38
+
39
+ Conditions can be:
40
+
41
+ 1. **Ruby Expressions** - Wrapped in `{{...}}`
42
+ ```yaml
43
+ - if: "{{output.previous_step.success == true}}"
44
+ ```
45
+
46
+ 2. **Bash Commands** - Wrapped in `$(...)`
47
+ ```yaml
48
+ - if: "$(test -f /path/to/file && echo true || echo false)"
49
+ ```
50
+
51
+ 3. **Step References** - Reference to previous step output
52
+ ```yaml
53
+ - if: "check_condition" # References a previous step
54
+ ```
55
+
56
+ 4. **File Checks**
57
+ ```yaml
58
+ - if: "{{File.exist?('/tmp/myfile.txt')}}"
59
+ ```
60
+
61
+ ## Examples
62
+
63
+ ### Basic Example
64
+
65
+ ```yaml
66
+ name: Conditional Example
67
+ tools:
68
+ - Roast::Tools::Cmd
69
+
70
+ steps:
71
+ - check_status: "echo 'success'"
72
+
73
+ - if: "{{output.check_status.strip == 'success'}}"
74
+ then:
75
+ - success_action: "echo 'Operation succeeded!'"
76
+ else:
77
+ - failure_action: "echo 'Operation failed!'"
78
+ ```
79
+
80
+ ### Unless Example
81
+
82
+ ```yaml
83
+ name: Unless Example
84
+ tools: []
85
+
86
+ steps:
87
+ - check_file: "test -f /tmp/important.txt && echo exists || echo missing"
88
+
89
+ - unless: "{{output.check_file.strip == 'exists'}}"
90
+ then:
91
+ - create_file: "touch /tmp/important.txt"
92
+ - notify: "echo 'Created missing file'"
93
+ ```
94
+
95
+ ### Nested Conditionals
96
+
97
+ ```yaml
98
+ name: Nested Conditionals
99
+ tools: []
100
+
101
+ steps:
102
+ - outer_check: "echo 'true'"
103
+ - inner_check: "echo 'false'"
104
+
105
+ - if: "{{output.outer_check.strip == 'true'}}"
106
+ then:
107
+ - if: "{{output.inner_check.strip == 'true'}}"
108
+ then:
109
+ - both_true: "echo 'Both conditions are true'"
110
+ else:
111
+ - only_outer: "echo 'Only outer condition is true'"
112
+ else:
113
+ - outer_false: "echo 'Outer condition is false'"
114
+ ```
115
+
116
+ ### Platform-Specific Actions
117
+
118
+ ```yaml
119
+ name: Platform Detection
120
+ tools:
121
+ - Roast::Tools::Cmd
122
+
123
+ steps:
124
+ - detect_os: "uname -s"
125
+
126
+ - if: "{{output.detect_os.strip == 'Darwin'}}"
127
+ then:
128
+ - mac_setup: "brew --version || echo 'Homebrew not installed'"
129
+ else:
130
+ - if: "{{output.detect_os.strip == 'Linux'}}"
131
+ then:
132
+ - linux_setup: "apt-get --version || yum --version"
133
+ else:
134
+ - unknown_os: "echo 'Unknown operating system'"
135
+ ```
136
+
137
+ ## Best Practices
138
+
139
+ 1. **Use Clear Conditions**: Make your conditions explicit and easy to understand
140
+ 2. **Handle Edge Cases**: Always consider what happens when conditions fail
141
+ 3. **Test Both Branches**: Ensure both `then` and `else` branches work correctly
142
+ 4. **Avoid Deep Nesting**: Keep conditional logic simple and readable
143
+ 5. **Use Unless Sparingly**: `unless` can be less intuitive than `if` with negation
144
+
145
+ ## Debugging
146
+
147
+ To debug conditional execution:
148
+
149
+ 1. Check the workflow output to see which branch was executed
150
+ 2. Look for keys like `if_condition_name` or `unless_condition_name` in the output
151
+ 3. These keys contain information about the condition evaluation and branch taken
152
+
153
+ ## Running the Example
154
+
155
+ ```bash
156
+ # Run the simple conditional example
157
+ roast execute examples/conditional/simple_workflow.yml
158
+
159
+ # Run the full conditional example (requires API configuration)
160
+ roast execute examples/conditional/workflow.yml
161
+ ```
@@ -0,0 +1 @@
1
+ Check if the OS is macOS or Linux and return true if it's macOS, false otherwise.