roast-ai 0.1.6 → 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 +48 -0
- data/CLAUDE.md +20 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +11 -6
- data/README.md +225 -13
- data/bin/roast +27 -0
- data/docs/INSTRUMENTATION.md +42 -1
- 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/update_files.rb +413 -0
- data/lib/roast/tools.rb +12 -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 +3 -1
- data/schema/workflow.json +85 -0
- metadata +112 -4
@@ -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.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
name: Simple Conditional Test
|
2
|
+
tools: []
|
3
|
+
|
4
|
+
steps:
|
5
|
+
- set_value: "$(echo 'true')"
|
6
|
+
|
7
|
+
- if: "{{output.set_value.strip == 'true'}}"
|
8
|
+
then:
|
9
|
+
- success: "$(echo 'If condition worked!')"
|
10
|
+
else:
|
11
|
+
- failure: "$(echo 'If condition failed!')"
|
12
|
+
|
13
|
+
- unless: "{{output.set_value.strip == 'false'}}"
|
14
|
+
then:
|
15
|
+
- unless_success: "$(echo 'Unless condition worked!')"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: Conditional Execution Example
|
2
|
+
tools:
|
3
|
+
- Roast::Tools::Cmd
|
4
|
+
|
5
|
+
steps:
|
6
|
+
- check_os: "$(uname -s)"
|
7
|
+
|
8
|
+
- if: "{{output.check_os.strip == 'Darwin'}}"
|
9
|
+
then:
|
10
|
+
- mac_message: "$(echo 'Running on macOS!')"
|
11
|
+
- mac_info: "$(sw_vers)"
|
12
|
+
else:
|
13
|
+
- linux_message: "$(echo 'Running on Linux!')"
|
14
|
+
- linux_info: "$(lsb_release -a)"
|
15
|
+
|
16
|
+
- check_file: "$(test -f /tmp/roast_test.txt && echo exists || echo missing)"
|
17
|
+
|
18
|
+
- unless: "{{output.check_file.strip == 'exists'}}"
|
19
|
+
then:
|
20
|
+
- create_file: "$(touch /tmp/roast_test.txt && echo 'File created')"
|
21
|
+
|
22
|
+
- verify_file: "$(ls -la /tmp/roast_test.txt)"
|
23
|
+
- cleanup: "$(rm -f /tmp/roast_test.txt)"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Dot Notation Access Example
|
2
|
+
|
3
|
+
This example demonstrates the new dot notation access feature for workflow outputs.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
With the new dot notation feature, you can access output values using Ruby's method syntax instead of hash syntax:
|
8
|
+
|
9
|
+
### Before (hash syntax):
|
10
|
+
```yaml
|
11
|
+
until: "output[:update_fix_count][:fixes_applied] >= 5 || output[:select_next_issue][:no_issues_left] == true"
|
12
|
+
```
|
13
|
+
|
14
|
+
### After (dot notation):
|
15
|
+
```yaml
|
16
|
+
until: "output.update_fix_count.fixes_applied >= 5 || output.select_next_issue.no_issues_left?"
|
17
|
+
```
|
18
|
+
|
19
|
+
### Even cleaner (omitting output prefix):
|
20
|
+
```yaml
|
21
|
+
until: "update_fix_count.fixes_applied >= 5 || select_next_issue.no_issues_left?"
|
22
|
+
```
|
23
|
+
|
24
|
+
## Features
|
25
|
+
|
26
|
+
1. **Nested access**: `output.step_name.nested.value`
|
27
|
+
2. **Boolean predicates**: `output.step_name.is_complete?` returns false for nil/false values
|
28
|
+
3. **Direct access**: Omit the `output.` prefix for cleaner syntax
|
29
|
+
4. **Backward compatible**: Hash syntax still works (`output[:step_name][:value]`)
|
30
|
+
|
31
|
+
## Example Workflow
|
32
|
+
|
33
|
+
See `workflow.yml` for a complete example that demonstrates:
|
34
|
+
- Setting values in output
|
35
|
+
- Using dot notation in conditions
|
36
|
+
- Boolean predicate methods
|
37
|
+
- Nested value access
|
@@ -0,0 +1,44 @@
|
|
1
|
+
name: dot_notation_example
|
2
|
+
description: Example demonstrating dot notation access for workflow outputs
|
3
|
+
|
4
|
+
steps:
|
5
|
+
initialize:
|
6
|
+
prompt: |
|
7
|
+
Initialize the workflow with some sample data.
|
8
|
+
|
9
|
+
Set output.counter to 0
|
10
|
+
Set output.config.max_iterations to 5
|
11
|
+
Set output.config.enabled to true
|
12
|
+
|
13
|
+
process_items:
|
14
|
+
repeat:
|
15
|
+
# Using dot notation in conditions
|
16
|
+
until: "counter >= config.max_iterations || !config.enabled?"
|
17
|
+
steps:
|
18
|
+
- increment_counter
|
19
|
+
- check_status
|
20
|
+
|
21
|
+
increment_counter:
|
22
|
+
prompt: |
|
23
|
+
Increment the counter by 1.
|
24
|
+
Current counter value: {{counter}}
|
25
|
+
|
26
|
+
Set output.counter to {{counter}} + 1
|
27
|
+
|
28
|
+
check_status:
|
29
|
+
prompt: |
|
30
|
+
Check if we should continue processing.
|
31
|
+
|
32
|
+
Current counter: {{counter}}
|
33
|
+
Max iterations: {{config.max_iterations}}
|
34
|
+
|
35
|
+
If counter is 3, set output.config.enabled to false
|
36
|
+
Set output.status.last_checked to current counter value
|
37
|
+
|
38
|
+
summarize:
|
39
|
+
prompt: |
|
40
|
+
Summarize the results:
|
41
|
+
- Total iterations: {{counter}}
|
42
|
+
- Last checked at: {{status.last_checked}}
|
43
|
+
- Was enabled: {{config.enabled}}
|
44
|
+
- Hit max iterations: {{counter >= config.max_iterations ? "Yes" : "No"}}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Exit on Error Example
|
2
|
+
|
3
|
+
This example demonstrates how to use the `exit_on_error` configuration option to continue workflow execution even when a command fails.
|
4
|
+
|
5
|
+
## Use Case
|
6
|
+
|
7
|
+
When running a linter like RuboCop on a file with syntax errors or style violations, the command will exit with a non-zero status. By default, this would halt the workflow. However, we often want to:
|
8
|
+
|
9
|
+
1. Capture the linter output (including errors)
|
10
|
+
2. Analyze what went wrong
|
11
|
+
3. Apply fixes based on the analysis
|
12
|
+
|
13
|
+
## Configuration
|
14
|
+
|
15
|
+
The key configuration is in the step configuration section:
|
16
|
+
|
17
|
+
```yaml
|
18
|
+
lint_check:
|
19
|
+
exit_on_error: false
|
20
|
+
```
|
21
|
+
|
22
|
+
This tells Roast to:
|
23
|
+
- Continue workflow execution even if the command fails
|
24
|
+
- Capture the full output (stdout and stderr)
|
25
|
+
- Append the exit status to the output
|
26
|
+
|
27
|
+
## Output Format
|
28
|
+
|
29
|
+
When a command fails with `exit_on_error: false`, the output will look like:
|
30
|
+
|
31
|
+
```
|
32
|
+
lib/example.rb:5:3: C: Style/StringLiterals: Prefer double-quoted strings
|
33
|
+
'hello'
|
34
|
+
^^^^^^^
|
35
|
+
[Exit status: 1]
|
36
|
+
```
|
37
|
+
|
38
|
+
This allows subsequent steps to process both the error output and the exit status.
|
39
|
+
|
40
|
+
## Running the Example
|
41
|
+
|
42
|
+
```bash
|
43
|
+
roast execute workflow.yml path/to/file.rb
|
44
|
+
```
|
45
|
+
|
46
|
+
The workflow will:
|
47
|
+
1. Run RuboCop on the file
|
48
|
+
2. Continue even if RuboCop finds issues
|
49
|
+
3. Analyze the linter output
|
50
|
+
4. Apply fixes based on the analysis
|
@@ -0,0 +1,9 @@
|
|
1
|
+
The linter output from the previous step shows issues with the code.
|
2
|
+
Please analyze the output and identify the specific problems that need to be fixed.
|
3
|
+
|
4
|
+
Focus on:
|
5
|
+
- Syntax errors
|
6
|
+
- Style violations
|
7
|
+
- Best practice violations
|
8
|
+
|
9
|
+
Provide a structured list of issues found.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: Linting with Error Recovery
|
2
|
+
tools:
|
3
|
+
- Roast::Tools::ReadFile
|
4
|
+
- Roast::Tools::WriteFile
|
5
|
+
- Roast::Tools::CodingAgent
|
6
|
+
|
7
|
+
steps:
|
8
|
+
# Run linter on the file - may fail if there are syntax errors
|
9
|
+
- lint_check: $(rubocop {{file}})
|
10
|
+
|
11
|
+
# Analyze linter output and fix issues even if linter failed
|
12
|
+
- analyze_lint_output
|
13
|
+
|
14
|
+
# Apply fixes based on the analysis
|
15
|
+
- apply_fixes
|
16
|
+
|
17
|
+
# Step configuration
|
18
|
+
lint_check:
|
19
|
+
exit_on_error: false # Continue even if rubocop exits with non-zero status
|
@@ -1,10 +1,12 @@
|
|
1
|
-
name: Grading
|
1
|
+
name: Test Grading
|
2
|
+
model: anthropic:claude-opus-4
|
2
3
|
|
3
4
|
tools:
|
4
5
|
- Roast::Tools::Grep
|
5
6
|
- Roast::Tools::ReadFile
|
6
7
|
- Roast::Tools::SearchFile
|
7
8
|
|
9
|
+
# Uncomment this to run the workflow on modified tests automatically
|
8
10
|
# each: '% cd $(git rev-parse --show-toplevel) && git status --porcelain | grep "_test\.rb" | cut -c4- | xargs realpath'
|
9
11
|
|
10
12
|
steps:
|
@@ -35,3 +37,5 @@ generate_recommendations:
|
|
35
37
|
json: true
|
36
38
|
params:
|
37
39
|
max_completion_tokens: 5_000
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Iteration Mechanisms Implementation
|
2
|
+
|
3
|
+
This document provides an overview of how the iteration mechanisms are implemented in Roast.
|
4
|
+
|
5
|
+
## Core Components
|
6
|
+
|
7
|
+
### 1. Schema Extensions
|
8
|
+
|
9
|
+
The workflow schema has been extended to support two new iteration constructs:
|
10
|
+
|
11
|
+
- **Repeat** - For conditional repetition until a condition is met
|
12
|
+
- **Each** - For iterating over collections with a variable binding
|
13
|
+
|
14
|
+
These schema extensions define the structure and validation rules for the iteration YAML syntax.
|
15
|
+
|
16
|
+
### 2. Step Classes
|
17
|
+
|
18
|
+
Two new step classes handle the actual iteration logic:
|
19
|
+
|
20
|
+
- **RepeatStep** - Executes steps repeatedly until a condition is met or a maximum iteration count is reached
|
21
|
+
- **EachStep** - Iterates over a collection, binding each item to a variable, and executes steps for each item
|
22
|
+
|
23
|
+
Both inherit from a common `BaseIterationStep` class that provides shared functionality.
|
24
|
+
|
25
|
+
### 3. Pattern Matching
|
26
|
+
|
27
|
+
The `WorkflowExecutor` has been enhanced with pattern matching to recognize the `repeat` and `each` keywords and dispatch to the appropriate step classes:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
case name
|
31
|
+
when "repeat"
|
32
|
+
execute_repeat_step(command)
|
33
|
+
when "each"
|
34
|
+
# Handle 'each' step with its special format
|
35
|
+
execute_each_step(step)
|
36
|
+
else
|
37
|
+
# Handle regular steps
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
### 4. State Management
|
42
|
+
|
43
|
+
Both iteration types include state management to:
|
44
|
+
|
45
|
+
- Track current iteration number
|
46
|
+
- Save state after each iteration
|
47
|
+
- Support resumption after failures
|
48
|
+
- Provide safety limits against infinite loops
|
49
|
+
|
50
|
+
## Iteration Flow
|
51
|
+
|
52
|
+
### RepeatStep Flow
|
53
|
+
|
54
|
+
1. Start with iteration count = 0
|
55
|
+
2. Execute the nested steps in sequence
|
56
|
+
3. Evaluate the until condition
|
57
|
+
4. If condition is true or max_iterations is reached, stop
|
58
|
+
5. Otherwise, increment iteration count and go back to step 2
|
59
|
+
6. Return the results of all iterations
|
60
|
+
|
61
|
+
### EachStep Flow
|
62
|
+
|
63
|
+
1. Resolve the collection expression
|
64
|
+
2. For each item in the collection:
|
65
|
+
a. Set the named variable (accessible in steps through a getter method)
|
66
|
+
b. Execute the nested steps
|
67
|
+
c. Save state
|
68
|
+
3. Return the results from all iterations
|
69
|
+
|
70
|
+
## Safety Mechanisms
|
71
|
+
|
72
|
+
- **max_iterations** parameter prevents infinite loops
|
73
|
+
- State is saved after each iteration for resumption capability
|
74
|
+
- Robust error handling during condition evaluation and step execution
|
75
|
+
- Collection type checking ensures iterable objects
|
76
|
+
|
77
|
+
## Usage Examples
|
78
|
+
|
79
|
+
The workflow.yml and step files in this directory demonstrate practical applications of these iteration mechanisms for code quality analysis.
|
80
|
+
|
81
|
+
## Integration with Existing Workflow Engine
|
82
|
+
|
83
|
+
The iteration mechanism integrates seamlessly with the existing workflow engine:
|
84
|
+
|
85
|
+
- Uses the same state persistence mechanisms
|
86
|
+
- Follows the same execution models
|
87
|
+
- Maintains compatibility with all existing steps
|
88
|
+
- Supports interpolation within iteration constructs
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Code Quality Analysis Workflow
|
2
|
+
|
3
|
+
This example demonstrates the use of Roast's iteration features to analyze and improve code quality across a codebase.
|
4
|
+
|
5
|
+
## What it does
|
6
|
+
|
7
|
+
1. Collects Ruby files from the codebase for analysis
|
8
|
+
2. Analyzes each file for complexity, code smells, and potential improvements
|
9
|
+
3. Generates recommendations for each file
|
10
|
+
4. Prioritizes the identified issues by impact and difficulty
|
11
|
+
5. Automatically implements fixes for the highest-priority issues
|
12
|
+
6. Verifies each fix before moving to the next one
|
13
|
+
7. Continues until either 5 fixes have been applied or all issues are addressed
|
14
|
+
8. Generates a summary report of changes made
|
15
|
+
|
16
|
+
## Iteration Features Demonstrated
|
17
|
+
|
18
|
+
### Collection Iteration with `each`
|
19
|
+
|
20
|
+
The workflow uses the `each` construct to iterate through Ruby files:
|
21
|
+
|
22
|
+
```yaml
|
23
|
+
- each: "output['get_files_to_analyze'].split('\n')"
|
24
|
+
as: "current_file"
|
25
|
+
steps:
|
26
|
+
- read_file
|
27
|
+
- analyze_complexity
|
28
|
+
- generate_recommendations
|
29
|
+
```
|
30
|
+
|
31
|
+
This makes the current file available as `current_file` in each step, allowing the analysis steps to process each file individually.
|
32
|
+
|
33
|
+
### Conditional Repetition with `repeat`
|
34
|
+
|
35
|
+
The workflow uses the `repeat` construct to iteratively fix issues until a condition is met:
|
36
|
+
|
37
|
+
```yaml
|
38
|
+
- repeat:
|
39
|
+
steps:
|
40
|
+
- select_next_issue
|
41
|
+
- implement_fix
|
42
|
+
- verify_fix
|
43
|
+
- update_fix_count
|
44
|
+
until: "output['update_fix_count']['fixes_applied'] >= 5 || output['select_next_issue']['no_issues_left'] == true"
|
45
|
+
max_iterations: 10
|
46
|
+
```
|
47
|
+
|
48
|
+
This continues applying fixes until either:
|
49
|
+
- 5 fixes have been successfully applied
|
50
|
+
- No more issues remain to be fixed
|
51
|
+
- The maximum of 10 iterations is reached (safety limit)
|
52
|
+
|
53
|
+
## Running the Example
|
54
|
+
|
55
|
+
To run this workflow:
|
56
|
+
|
57
|
+
```bash
|
58
|
+
roast run examples/iteration/workflow.yml --target=/path/to/your/project
|
59
|
+
```
|
60
|
+
|
61
|
+
The workflow will analyze the Ruby files in your project, suggest improvements, and apply the highest-priority fixes.
|
62
|
+
|
63
|
+
## Customizing
|
64
|
+
|
65
|
+
- Adjust the file selection criteria in `get_files_to_analyze`
|
66
|
+
- Modify the analysis criteria in `analyze_complexity`
|
67
|
+
- Change the fix limit in the `until` condition
|
68
|
+
- Set a different `max_iterations` value to control the maximum number of fixes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
I'll analyze the code complexity of the file {{current_file}}, which I've read in the previous step.
|
2
|
+
|
3
|
+
I'll examine the following aspects:
|
4
|
+
1. Cyclomatic complexity (number of decision paths)
|
5
|
+
2. Method length and complexity
|
6
|
+
3. Class size and responsibilities
|
7
|
+
4. Code smells (long parameter lists, deeply nested blocks, etc.)
|
8
|
+
5. Potential performance bottlenecks
|
9
|
+
6. Opportunities for refactoring
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
{{output.read_file}}
|
13
|
+
```
|
14
|
+
|
15
|
+
Based on this analysis, I'll provide a structured assessment of the code quality issues found.
|
16
|
+
|
17
|
+
For each issue identified, I'll assign:
|
18
|
+
- Severity (high, medium, low)
|
19
|
+
- Type (complexity, maintainability, performance, style)
|
20
|
+
- Location (line numbers, method/class names)
|
21
|
+
- Brief description of the issue
|
22
|
+
- Suggested improvement
|