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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yaml +1 -1
- data/CHANGELOG.md +40 -1
- data/CLAUDE.md +20 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +9 -6
- data/README.md +81 -14
- data/bin/roast +27 -0
- data/docs/ITERATION_SYNTAX.md +119 -0
- data/examples/conditional/README.md +161 -0
- data/examples/conditional/check_condition/prompt.md +1 -0
- data/examples/conditional/simple_workflow.yml +15 -0
- data/examples/conditional/workflow.yml +23 -0
- data/examples/dot_notation/README.md +37 -0
- data/examples/dot_notation/workflow.yml +44 -0
- data/examples/exit_on_error/README.md +50 -0
- data/examples/exit_on_error/analyze_lint_output/prompt.md +9 -0
- data/examples/exit_on_error/apply_fixes/prompt.md +2 -0
- data/examples/exit_on_error/workflow.yml +19 -0
- data/examples/grading/workflow.yml +5 -1
- data/examples/iteration/IMPLEMENTATION.md +88 -0
- data/examples/iteration/README.md +68 -0
- data/examples/iteration/analyze_complexity/prompt.md +22 -0
- data/examples/iteration/generate_recommendations/prompt.md +21 -0
- data/examples/iteration/generate_report/prompt.md +129 -0
- data/examples/iteration/implement_fix/prompt.md +25 -0
- data/examples/iteration/prioritize_issues/prompt.md +24 -0
- data/examples/iteration/prompts/analyze_file.md +28 -0
- data/examples/iteration/prompts/generate_summary.md +24 -0
- data/examples/iteration/prompts/update_report.md +29 -0
- data/examples/iteration/prompts/write_report.md +22 -0
- data/examples/iteration/read_file/prompt.md +9 -0
- data/examples/iteration/select_next_issue/prompt.md +25 -0
- data/examples/iteration/simple_workflow.md +39 -0
- data/examples/iteration/simple_workflow.yml +58 -0
- data/examples/iteration/update_fix_count/prompt.md +26 -0
- data/examples/iteration/verify_fix/prompt.md +29 -0
- data/examples/iteration/workflow.yml +42 -0
- data/examples/openrouter_example/workflow.yml +2 -2
- data/examples/workflow_generator/README.md +27 -0
- data/examples/workflow_generator/analyze_user_request/prompt.md +34 -0
- data/examples/workflow_generator/create_workflow_files/prompt.md +32 -0
- data/examples/workflow_generator/get_user_input/prompt.md +14 -0
- data/examples/workflow_generator/info_from_roast.rb +22 -0
- data/examples/workflow_generator/workflow.yml +35 -0
- data/lib/roast/errors.rb +9 -0
- data/lib/roast/factories/api_provider_factory.rb +61 -0
- data/lib/roast/helpers/function_caching_interceptor.rb +1 -1
- data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
- data/lib/roast/helpers/prompt_loader.rb +50 -1
- data/lib/roast/resources/base_resource.rb +7 -0
- data/lib/roast/resources.rb +6 -6
- data/lib/roast/tools/ask_user.rb +40 -0
- data/lib/roast/tools/cmd.rb +1 -1
- data/lib/roast/tools/search_file.rb +1 -1
- data/lib/roast/tools.rb +11 -1
- data/lib/roast/value_objects/api_token.rb +49 -0
- data/lib/roast/value_objects/step_name.rb +39 -0
- data/lib/roast/value_objects/workflow_path.rb +77 -0
- data/lib/roast/value_objects.rb +5 -0
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/api_configuration.rb +61 -0
- data/lib/roast/workflow/base_iteration_step.rb +165 -0
- data/lib/roast/workflow/base_step.rb +4 -24
- data/lib/roast/workflow/base_workflow.rb +76 -73
- data/lib/roast/workflow/command_executor.rb +88 -0
- data/lib/roast/workflow/conditional_executor.rb +50 -0
- data/lib/roast/workflow/conditional_step.rb +96 -0
- data/lib/roast/workflow/configuration.rb +35 -158
- data/lib/roast/workflow/configuration_loader.rb +78 -0
- data/lib/roast/workflow/configuration_parser.rb +13 -248
- data/lib/roast/workflow/context_path_resolver.rb +43 -0
- data/lib/roast/workflow/dot_access_hash.rb +198 -0
- data/lib/roast/workflow/each_step.rb +86 -0
- data/lib/roast/workflow/error_handler.rb +97 -0
- data/lib/roast/workflow/expression_utils.rb +36 -0
- data/lib/roast/workflow/file_state_repository.rb +3 -2
- data/lib/roast/workflow/interpolator.rb +34 -0
- data/lib/roast/workflow/iteration_executor.rb +85 -0
- data/lib/roast/workflow/llm_boolean_coercer.rb +55 -0
- data/lib/roast/workflow/output_handler.rb +35 -0
- data/lib/roast/workflow/output_manager.rb +77 -0
- data/lib/roast/workflow/parallel_executor.rb +49 -0
- data/lib/roast/workflow/repeat_step.rb +75 -0
- data/lib/roast/workflow/replay_handler.rb +123 -0
- data/lib/roast/workflow/resource_resolver.rb +77 -0
- data/lib/roast/workflow/session_manager.rb +6 -2
- data/lib/roast/workflow/state_manager.rb +97 -0
- data/lib/roast/workflow/step_executor_coordinator.rb +205 -0
- data/lib/roast/workflow/step_executor_factory.rb +47 -0
- data/lib/roast/workflow/step_executor_registry.rb +79 -0
- data/lib/roast/workflow/step_executors/base_step_executor.rb +23 -0
- data/lib/roast/workflow/step_executors/hash_step_executor.rb +43 -0
- data/lib/roast/workflow/step_executors/parallel_step_executor.rb +54 -0
- data/lib/roast/workflow/step_executors/string_step_executor.rb +29 -0
- data/lib/roast/workflow/step_finder.rb +97 -0
- data/lib/roast/workflow/step_loader.rb +154 -0
- data/lib/roast/workflow/step_orchestrator.rb +45 -0
- data/lib/roast/workflow/step_runner.rb +23 -0
- data/lib/roast/workflow/step_type_resolver.rb +117 -0
- data/lib/roast/workflow/workflow_context.rb +60 -0
- data/lib/roast/workflow/workflow_executor.rb +90 -209
- data/lib/roast/workflow/workflow_initializer.rb +112 -0
- data/lib/roast/workflow/workflow_runner.rb +87 -0
- data/lib/roast/workflow.rb +3 -0
- data/lib/roast.rb +96 -3
- data/roast.gemspec +2 -1
- data/schema/workflow.json +85 -0
- metadata +97 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ab27704514fc6ddfa09003464d2aa324b852379b0548619e2f0023bebe793ae
|
4
|
+
data.tar.gz: 18c0936f2b5dc2b54ef420be2ac089b8252f64a02227ab33d98db9d2e6249136
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50552385cdb280592a1e331c4a3ec5de2924fa05636af5deb742b96f2fde229e3448e61e6516e570c02a8dc0f416a3b9a3c3d884a800eb89987c90c856a0258d
|
7
|
+
data.tar.gz: 8feeb510d87e6e6256c46cbca3e13fbc65701ceb28b049452e5bea2f44815f7770e688f8422487b431a85c5f459beffadcce590e7b860cde8a1ab4ee939ff50e
|
data/.github/workflows/ci.yaml
CHANGED
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
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
roast-ai (0.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
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
|
-
|
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.
|