roast-ai 0.1.7 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +1 -1
  3. data/CHANGELOG.md +49 -1
  4. data/CLAUDE.md +20 -0
  5. data/CLAUDE_NOTES.md +68 -0
  6. data/Gemfile +1 -0
  7. data/Gemfile.lock +9 -6
  8. data/README.md +159 -26
  9. data/bin/roast +27 -0
  10. data/docs/ITERATION_SYNTAX.md +147 -0
  11. data/examples/case_when/README.md +58 -0
  12. data/examples/case_when/detect_language/prompt.md +16 -0
  13. data/examples/case_when/workflow.yml +58 -0
  14. data/examples/conditional/README.md +161 -0
  15. data/examples/conditional/check_condition/prompt.md +1 -0
  16. data/examples/conditional/simple_workflow.yml +15 -0
  17. data/examples/conditional/workflow.yml +23 -0
  18. data/examples/direct_coerce_syntax/README.md +32 -0
  19. data/examples/direct_coerce_syntax/workflow.yml +36 -0
  20. data/examples/dot_notation/README.md +37 -0
  21. data/examples/dot_notation/workflow.yml +44 -0
  22. data/examples/exit_on_error/README.md +50 -0
  23. data/examples/exit_on_error/analyze_lint_output/prompt.md +9 -0
  24. data/examples/exit_on_error/apply_fixes/prompt.md +2 -0
  25. data/examples/exit_on_error/workflow.yml +19 -0
  26. data/examples/grading/workflow.yml +10 -4
  27. data/examples/iteration/IMPLEMENTATION.md +88 -0
  28. data/examples/iteration/README.md +68 -0
  29. data/examples/iteration/analyze_complexity/prompt.md +22 -0
  30. data/examples/iteration/generate_recommendations/prompt.md +21 -0
  31. data/examples/iteration/generate_report/prompt.md +129 -0
  32. data/examples/iteration/implement_fix/prompt.md +25 -0
  33. data/examples/iteration/prioritize_issues/prompt.md +24 -0
  34. data/examples/iteration/prompts/analyze_file.md +28 -0
  35. data/examples/iteration/prompts/generate_summary.md +24 -0
  36. data/examples/iteration/prompts/update_report.md +29 -0
  37. data/examples/iteration/prompts/write_report.md +22 -0
  38. data/examples/iteration/read_file/prompt.md +9 -0
  39. data/examples/iteration/select_next_issue/prompt.md +25 -0
  40. data/examples/iteration/simple_workflow.md +39 -0
  41. data/examples/iteration/simple_workflow.yml +58 -0
  42. data/examples/iteration/update_fix_count/prompt.md +26 -0
  43. data/examples/iteration/verify_fix/prompt.md +29 -0
  44. data/examples/iteration/workflow.yml +42 -0
  45. data/examples/json_handling/README.md +32 -0
  46. data/examples/json_handling/workflow.yml +52 -0
  47. data/examples/openrouter_example/workflow.yml +2 -2
  48. data/examples/smart_coercion_defaults/README.md +65 -0
  49. data/examples/smart_coercion_defaults/workflow.yml +44 -0
  50. data/examples/step_configuration/README.md +87 -0
  51. data/examples/step_configuration/workflow.yml +60 -0
  52. data/examples/workflow_generator/README.md +27 -0
  53. data/examples/workflow_generator/analyze_user_request/prompt.md +34 -0
  54. data/examples/workflow_generator/create_workflow_files/prompt.md +32 -0
  55. data/examples/workflow_generator/get_user_input/prompt.md +14 -0
  56. data/examples/workflow_generator/info_from_roast.rb +22 -0
  57. data/examples/workflow_generator/workflow.yml +35 -0
  58. data/lib/roast/errors.rb +9 -0
  59. data/lib/roast/factories/api_provider_factory.rb +61 -0
  60. data/lib/roast/helpers/function_caching_interceptor.rb +1 -1
  61. data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
  62. data/lib/roast/helpers/prompt_loader.rb +50 -1
  63. data/lib/roast/resources/base_resource.rb +7 -0
  64. data/lib/roast/resources.rb +6 -6
  65. data/lib/roast/tools/ask_user.rb +40 -0
  66. data/lib/roast/tools/cmd.rb +1 -1
  67. data/lib/roast/tools/search_file.rb +1 -1
  68. data/lib/roast/tools.rb +11 -1
  69. data/lib/roast/value_objects/api_token.rb +49 -0
  70. data/lib/roast/value_objects/step_name.rb +39 -0
  71. data/lib/roast/value_objects/workflow_path.rb +77 -0
  72. data/lib/roast/value_objects.rb +5 -0
  73. data/lib/roast/version.rb +1 -1
  74. data/lib/roast/workflow/api_configuration.rb +61 -0
  75. data/lib/roast/workflow/base_iteration_step.rb +184 -0
  76. data/lib/roast/workflow/base_step.rb +44 -27
  77. data/lib/roast/workflow/base_workflow.rb +76 -73
  78. data/lib/roast/workflow/case_executor.rb +49 -0
  79. data/lib/roast/workflow/case_step.rb +82 -0
  80. data/lib/roast/workflow/command_executor.rb +88 -0
  81. data/lib/roast/workflow/conditional_executor.rb +50 -0
  82. data/lib/roast/workflow/conditional_step.rb +59 -0
  83. data/lib/roast/workflow/configuration.rb +35 -158
  84. data/lib/roast/workflow/configuration_loader.rb +78 -0
  85. data/lib/roast/workflow/configuration_parser.rb +13 -248
  86. data/lib/roast/workflow/context_path_resolver.rb +43 -0
  87. data/lib/roast/workflow/dot_access_hash.rb +198 -0
  88. data/lib/roast/workflow/each_step.rb +86 -0
  89. data/lib/roast/workflow/error_handler.rb +97 -0
  90. data/lib/roast/workflow/expression_evaluator.rb +78 -0
  91. data/lib/roast/workflow/expression_utils.rb +36 -0
  92. data/lib/roast/workflow/file_state_repository.rb +3 -2
  93. data/lib/roast/workflow/interpolator.rb +34 -0
  94. data/lib/roast/workflow/iteration_executor.rb +103 -0
  95. data/lib/roast/workflow/llm_boolean_coercer.rb +55 -0
  96. data/lib/roast/workflow/output_handler.rb +35 -0
  97. data/lib/roast/workflow/output_manager.rb +77 -0
  98. data/lib/roast/workflow/parallel_executor.rb +49 -0
  99. data/lib/roast/workflow/prompt_step.rb +4 -1
  100. data/lib/roast/workflow/repeat_step.rb +75 -0
  101. data/lib/roast/workflow/replay_handler.rb +123 -0
  102. data/lib/roast/workflow/resource_resolver.rb +77 -0
  103. data/lib/roast/workflow/session_manager.rb +6 -2
  104. data/lib/roast/workflow/state_manager.rb +97 -0
  105. data/lib/roast/workflow/step_executor_coordinator.rb +221 -0
  106. data/lib/roast/workflow/step_executor_factory.rb +47 -0
  107. data/lib/roast/workflow/step_executor_registry.rb +79 -0
  108. data/lib/roast/workflow/step_executors/base_step_executor.rb +23 -0
  109. data/lib/roast/workflow/step_executors/hash_step_executor.rb +43 -0
  110. data/lib/roast/workflow/step_executors/parallel_step_executor.rb +54 -0
  111. data/lib/roast/workflow/step_executors/string_step_executor.rb +29 -0
  112. data/lib/roast/workflow/step_finder.rb +97 -0
  113. data/lib/roast/workflow/step_loader.rb +155 -0
  114. data/lib/roast/workflow/step_orchestrator.rb +45 -0
  115. data/lib/roast/workflow/step_runner.rb +23 -0
  116. data/lib/roast/workflow/step_type_resolver.rb +133 -0
  117. data/lib/roast/workflow/workflow_context.rb +60 -0
  118. data/lib/roast/workflow/workflow_executor.rb +90 -209
  119. data/lib/roast/workflow/workflow_initializer.rb +112 -0
  120. data/lib/roast/workflow/workflow_runner.rb +87 -0
  121. data/lib/roast/workflow.rb +3 -0
  122. data/lib/roast.rb +96 -3
  123. data/roast.gemspec +2 -1
  124. data/schema/workflow.json +112 -0
  125. metadata +112 -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: db58f659180113d7a2bb1bface8da2898be6717033107247156a464b4d14159d
4
+ data.tar.gz: f14c2caae89fab3353b66ae3eb4457c56975e65c464037a2d401cfca30840d81
5
5
  SHA512:
6
- metadata.gz: 2560cd52f21eb2aa0c954d1759ac762c12f2840a1533cc41017003a022f002212e9fb8d6e6b3d11fc1245ff413a86bd68d3aad7a142de9a30d4c534277a80f51
7
- data.tar.gz: 371d573330873f1e76aec168e72ed8e2c53d0a127af83683205fae7d060dfa758aeb2352133b4f8b263803b5384b77be76b91a564d945cfd8966a682959a0d13
6
+ metadata.gz: 88c3f83651c535077bdfe29408a19b874f66f19d8b7e743dd4933922f82fd7aee53ee215968e3d10c0207e92d4c53018a0b190475ba7ac4b70d15cda07a634ff
7
+ data.tar.gz: 5ebae3dcc5d7d775a3f19ab79ead81d7c11f53fc31b5b20f1db4781a950032e693a2d331fdc4509eb0661c04072f421defcd56d15b9d6ce74f7bfc12357ee1b0
@@ -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,53 @@ 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.1]
9
+
10
+ ### Added
11
+ - Smart coercion defaults for boolean expressions based on step type
12
+ - Ruby expressions (`{{expr}}`) default to regular boolean coercion
13
+ - Bash commands (`$(cmd)`) default to exit code interpretation
14
+ - Inline prompts and regular steps default to "smart" LLM-powered interpretation (looks for truthy or falsy language)
15
+ - Direct syntax for step configuration - `coerce_to` and other options are now specified directly on iteration steps
16
+
17
+ ## [0.2.0] - 2025-05-26
18
+
19
+ ### Added
20
+ - Conditional execution support with `if` and `unless` clauses for workflow steps
21
+ - Iteration mechanisms for workflows with `repeat` and `each` constructs (resolving issue #48)
22
+ - Support for conditional repetition with `until` condition and safety limits
23
+ - Collection iteration with variable binding for processing lists of items
24
+ - State persistence for loop iterations to enable resumption after failure
25
+ - Standardized evaluation of Ruby expressions in iteration constructs using `{{}}` syntax
26
+ - Support for using bash commands, step names, and Ruby expressions in iteration conditions and collections
27
+ - Intelligent LLM response to boolean conversion with pattern-based recognition for natural language responses
28
+ - `exit_on_error` configuration option for command steps to continue workflow on failure (resolving issue #71)
29
+ - Dot notation access for workflow outputs (e.g., `workflow.output.step.field`)
30
+ - `--pause` flag for stepping through workflow execution interactively
31
+
32
+ ### Fixed
33
+ - Automatically add `.gitignore` file to cache directory when created (completing issue #22)
34
+ - Load initializers before trying to load tools in case custom tools are defined in initializers (thanks @palkan)
35
+ - Fix loading of targetless workflows (thanks @palkan)
36
+ - Fix OpenRouter support (thanks @xrendan)
37
+ - API authentication error handling and model access issues
38
+ - Conditional step transcript replay regression
39
+ - DotAccessHash serialization for AI prompts
40
+
41
+ ### Improved
42
+ - Enhanced search file tool logging to show full expanded paths instead of relative paths
43
+ - Major refactoring to eliminate circular dependencies and improve architecture
44
+ - Extracted command execution logic into dedicated CommandExecutor class
45
+ - Separated conditional execution from iteration logic for better SOLID compliance
46
+ - Enhanced error messages for API authentication failures
47
+ - Replaced all `require_relative` with `require` statements for consistency
48
+
49
+ ### Changed
50
+ - Refactored god objects to improve code organization and maintainability
51
+ - Improved separation of concerns between workflow components
52
+
53
+ [0.2.0]: https://github.com/Shopify/roast/compare/v0.1.7...v0.2.0
54
+
8
55
  ## [0.1.7] - 2024-05-16
9
56
 
10
57
  ### Added
@@ -13,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
60
  - Comprehensive documentation for all built-in tools
14
61
  - Enhanced README with detailed tool usage examples
15
62
 
63
+ ``[0.1.7]: https://github.com/Shopify/roast/compare/v0.1.6...v0.1.7
64
+
16
65
  ## [0.1.6] - 2024-05-15
17
66
 
18
67
  ### Added
@@ -33,7 +82,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
33
82
  - Improved initializer loading and error handling
34
83
  - Fixed tests for nested .roast folders
35
84
 
36
- [0.1.7]: https://github.com/Shopify/roast/compare/v0.1.6...v0.1.7
37
85
  [0.1.6]: https://github.com/Shopify/roast/compare/v0.1.5...v0.1.6
38
86
 
39
87
  ## [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/CLAUDE_NOTES.md ADDED
@@ -0,0 +1,68 @@
1
+ # Important Notes for Claude
2
+
3
+ ## Roast Workflow Syntax
4
+
5
+ ### IMPORTANT: No inline configuration for steps!
6
+
7
+ Roast workflows DO NOT support inline configuration syntax like this:
8
+ ```yaml
9
+ # WRONG - This syntax does NOT exist:
10
+ steps:
11
+ - step_name:
12
+ prompt: "some prompt"
13
+ output: variable_name
14
+ ```
15
+
16
+ The correct syntax is:
17
+ ```yaml
18
+ # CORRECT - Steps are just names or hash assignments:
19
+ steps:
20
+ - step_name
21
+ - variable_name: step_name
22
+ - variable_name: $(command)
23
+ ```
24
+
25
+ ### Control flow structures
26
+
27
+ Only control flow structures (if/unless, case/when/else, each, repeat) support nested configuration:
28
+
29
+ ```yaml
30
+ steps:
31
+ # Simple step - just the name
32
+ - detect_language
33
+
34
+ # Variable assignment
35
+ - my_var: detect_language
36
+
37
+ # Command execution with assignment
38
+ - env_type: $(echo $ENVIRONMENT)
39
+
40
+ # Control flow - these DO have nested structure
41
+ - if: "{{ condition }}"
42
+ then:
43
+ - step1
44
+ - step2
45
+
46
+ - case: "{{ expression }}"
47
+ when:
48
+ value1:
49
+ - step1
50
+ value2:
51
+ - step2
52
+ else:
53
+ - step3
54
+ ```
55
+
56
+ ### Step Configuration
57
+
58
+ Step configuration (prompts, outputs, etc.) is handled through:
59
+ 1. File naming conventions (step_name/prompt.md, step_name/output.txt)
60
+ 2. The workflow configuration file itself (tools, target, etc.)
61
+
62
+ NOT through inline step configuration in the steps array.
63
+
64
+ ## Remember:
65
+ - Steps array contains step names, not step configurations
66
+ - Only control flow structures have nested configuration
67
+ - Variable assignment uses hash syntax: `var_name: step_name`
68
+ - Commands use $() syntax: `var_name: $(command)`
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.1)
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
@@ -4,6 +4,18 @@
4
4
 
5
5
  A convention-oriented framework for creating structured AI workflows, maintained by the Augmented Engineering team at Shopify.
6
6
 
7
+ ## Installation
8
+
9
+ ```bash
10
+ $ gem install roast-ai
11
+ ```
12
+
13
+ Or add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'roast-ai'
17
+ ```
18
+
7
19
  ## Why you should use Roast
8
20
 
9
21
  Roast provides a structured, declarative approach to building AI workflows with:
@@ -46,7 +58,7 @@ Each step can have its own prompt file (e.g., `analyze_coverage/prompt.md`) and
46
58
  ```yaml
47
59
  steps:
48
60
  - prepare_data
49
- -
61
+ -
50
62
  - analyze_code_quality
51
63
  - check_test_coverage
52
64
  - verify_documentation
@@ -64,6 +76,42 @@ steps:
64
76
  - Summarize the changes made to {{File.basename(file)}}.
65
77
  ```
66
78
 
79
+ ## Try it
80
+
81
+ If you don’t have one already, get an OpenAI key from [here](https://platform.openai.com/settings/organization/api-keys). You will need an account with a credit card, make sure that a basic completion works.
82
+
83
+ ```bash
84
+ export OPENAI_API_KEY=sk-proj-....
85
+
86
+ curl -H "Content-Type: application/json" \
87
+ -H "Authorization: Bearer $API_TOKEN" \
88
+ -d '{"model":"gpt-4.1-mini","messages":[{"role":"user","content":"What is 1+1?"}]}' \
89
+ https://api.openai.com/v1/chat/completions
90
+ ```
91
+
92
+ The [test grading workflow](examples/grading/workflow.md) in this repository is a senior software engineer and testing expert that evaluates the quality of a test based on guidelines.
93
+
94
+ Try the workflow.
95
+
96
+ ```bash
97
+ ./exe/roast execute examples/grading/workflow.yml test/roast/resources_test.rb
98
+
99
+ 🔥🔥🔥 Everyone loves a good roast 🔥🔥🔥
100
+ ...
101
+ ```
102
+
103
+ This will output a test grade.
104
+
105
+ ```
106
+ ========== TEST GRADE REPORT ==========
107
+ Test file: test/roast/resources_test.rb
108
+
109
+ FINAL GRADE:
110
+ Score: 80/100
111
+ Letter Grade: B
112
+ ```
113
+ Note that you may also need `shadowenv` and `rg`, on MacOS run `brew install shadowenv` and `brew install rg`.
114
+
67
115
  ## How to use Roast
68
116
 
69
117
  1. Create a workflow YAML file defining your steps and tools
@@ -86,21 +134,21 @@ In Roast, workflows maintain a single conversation with the AI model throughout
86
134
 
87
135
  Roast supports several types of steps:
88
136
 
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.
137
+ 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
138
  ```yaml
91
139
  steps:
92
140
  - analyze_code
93
141
  ```
94
142
 
95
143
  As an alternative to a directory, you can also implement a custom step as a Ruby class, optionally extending `Roast::Workflow::BaseStep`.
96
-
144
+
97
145
  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
146
 
99
147
 
100
148
  2. **Parallel steps**: Groups of steps executed concurrently
101
149
  ```yaml
102
150
  steps:
103
- -
151
+ -
104
152
  - analyze_code_quality
105
153
  - check_test_coverage
106
154
  ```
@@ -113,7 +161,102 @@ Roast supports several types of steps:
113
161
  ```
114
162
  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
163
 
116
- 4. **Raw prompt step**: Simple text prompts for the model without tools
164
+ By default, commands that exit with non-zero status will halt the workflow. You can configure steps to continue on error:
165
+ ```yaml
166
+ steps:
167
+ - lint_check: $(rubocop {{file}})
168
+ - fix_issues
169
+
170
+ # Step configuration
171
+ lint_check:
172
+ exit_on_error: false # Continue workflow even if command fails
173
+ ```
174
+ When `exit_on_error: false`, the command output will include the exit status, allowing subsequent steps to process error information.
175
+
176
+ 4. **Conditional steps**: Execute different steps based on conditions using `if/unless`
177
+ ```yaml
178
+ steps:
179
+ - check_environment:
180
+ if: "{{ENV['RAILS_ENV'] == 'production'}}"
181
+ then:
182
+ - run_production_checks
183
+ - notify_team
184
+ else:
185
+ - run_development_setup
186
+
187
+ - verify_dependencies:
188
+ unless: "$(bundle check)"
189
+ then:
190
+ - bundle_install: "$(bundle install)"
191
+ ```
192
+
193
+ Conditions can be:
194
+ - Ruby expressions: `if: "{{output['count'] > 5}}"`
195
+ - Bash commands: `if: "$(test -f config.yml && echo true)"` (exit code 0 = true)
196
+ - Step references: `if: "previous_step_name"` (uses the step's output)
197
+ - Direct values: `if: "true"` or `if: "false"`
198
+
199
+ 5. **Iteration steps**: Loop over collections or repeat steps with conditions
200
+ ```yaml
201
+ steps:
202
+ # Loop over a collection
203
+ - process_files:
204
+ each: "{{Dir.glob('**/*.rb')}}"
205
+ as: current_file
206
+ steps:
207
+ - analyze_file
208
+ - Generate a report for {{current_file}}
209
+
210
+ # Repeat until a condition is met
211
+ - improve_code:
212
+ repeat:
213
+ until: "{{output['test_pass'] == true}}"
214
+ max_iterations: 5
215
+ steps:
216
+ - run_tests
217
+ - fix_issues
218
+ ```
219
+
220
+ Each loops support:
221
+ - Collections from Ruby expressions: `each: "{{[1, 2, 3]}}"`
222
+ - Command output: `each: "$(ls *.rb)"`
223
+ - Step references: `each: "file_list"`
224
+
225
+ Repeat loops support:
226
+ - Until conditions: `until: "{{condition}}"`
227
+ - Maximum iterations: `max_iterations: 10`
228
+
229
+ 6. **Case/when/else steps**: Select different execution paths based on a value (similar to Ruby's case statement)
230
+ ```yaml
231
+ steps:
232
+ - detect_language
233
+
234
+ - case: "{{ workflow.output.detect_language }}"
235
+ when:
236
+ ruby:
237
+ - lint_with_rubocop
238
+ - test_with_rspec
239
+ javascript:
240
+ - lint_with_eslint
241
+ - test_with_jest
242
+ python:
243
+ - lint_with_pylint
244
+ - test_with_pytest
245
+ else:
246
+ - analyze_generic
247
+ - generate_basic_report
248
+ ```
249
+
250
+ Case expressions can be:
251
+ - Workflow outputs: `case: "{{ workflow.output.variable }}"`
252
+ - Ruby expressions: `case: "{{ count > 10 ? 'high' : 'low' }}"`
253
+ - Bash commands: `case: "$(echo $ENVIRONMENT)"`
254
+ - Direct values: `case: "production"`
255
+
256
+ The value is compared against each key in the `when` clause, and matching steps are executed.
257
+ If no match is found, the `else` steps are executed (if provided).
258
+
259
+ 7. **Raw prompt step**: Simple text prompts for the model without tools
117
260
  ```yaml
118
261
  steps:
119
262
  - Summarize the changes made to the codebase.
@@ -139,7 +282,7 @@ Roast handles data flow between steps in three primary ways:
139
282
  - Generate a summary for {{file}}
140
283
  - result_for_{{file}}: store_results
141
284
  ```
142
-
285
+
143
286
  Interpolation supports:
144
287
  - Simple variable access: `{{file}}`, `{{resource.target}}`
145
288
  - Access to step outputs: `{{output['previous_step']}}`
@@ -150,7 +293,7 @@ For typical AI workflows, the continuous conversation history provides seamless
150
293
  ### Command Line Options
151
294
 
152
295
  #### Basic Options
153
- - `-o, --output FILE`: Save results to a file instead of outputting to STDOUT
296
+ - `-o, --output FILE`: Save results to a file instead of outputting to STDOUT
154
297
  - `-c, --concise`: Use concise output templates (exposed as a boolean flag on `workflow`)
155
298
  - `-v, --verbose`: Show output from all steps as they execute
156
299
  - `-r, --replay STEP_NAME`: Resume a workflow from a specific step, optionally with a specific session timestamp
@@ -241,7 +384,7 @@ roast execute workflow.yml -t "$(find . -name '*.rb' -mtime -1)"
241
384
  # Process changed test files
242
385
  roast execute workflow.yml -t "$(git diff --name-only HEAD | grep _test.rb)"
243
386
 
244
- # Process staged files
387
+ # Process staged files
245
388
  roast execute workflow.yml -t "$(git diff --cached --name-only)"
246
389
  ```
247
390
 
@@ -255,7 +398,7 @@ name: API Integration Workflow
255
398
  tools:
256
399
  - Roast::Tools::ReadFile
257
400
  - Roast::Tools::WriteFile
258
-
401
+
259
402
  # Dynamic API token using shell command
260
403
  api_token: $(cat ~/.my_token)
261
404
 
@@ -349,7 +492,9 @@ api_provider: openrouter
349
492
  api_token: $(echo $OPENROUTER_API_KEY)
350
493
  ```
351
494
 
352
- This makes it easy to use environment-specific tokens without hardcoding credentials, especially useful in development environments or CI/CD pipelines.
495
+
496
+ 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.
497
+
353
498
 
354
499
  ### Template Output with ERB
355
500
 
@@ -372,7 +517,7 @@ This is an example of where the `workflow.output` hash is useful - formatting ou
372
517
 
373
518
  Available in templates:
374
519
  - `response`: The AI's response for this step
375
- - `workflow`: Access to the workflow object
520
+ - `workflow`: Access to the workflow object
376
521
  - `workflow.output`: The shared hash containing results from all steps when you need programmatic access
377
522
  - `workflow.file`: Current file being processed (or `nil` for targetless workflows)
378
523
  - All workflow configuration options
@@ -439,7 +584,7 @@ update_files(
439
584
  +new line
440
585
  line2
441
586
  line3
442
-
587
+
443
588
  --- a/file2.txt
444
589
  +++ b/file2.txt
445
590
  @@ -5,7 +5,7 @@
@@ -556,11 +701,11 @@ module MyProject
556
701
 
557
702
  def call(commit_sha, include_diff = false)
558
703
  Roast::Helpers::Logger.info("🔍 Analyzing commit: #{commit_sha}\n")
559
-
704
+
560
705
  # Your implementation here
561
706
  commit_info = `git show #{commit_sha} --stat`
562
707
  commit_info += "\n\n" + `git show #{commit_sha}` if include_diff
563
-
708
+
564
709
  commit_info
565
710
  rescue StandardError => e
566
711
  "Error analyzing commit: #{e.message}".tap do |error_message|
@@ -601,18 +746,6 @@ your-project/
601
746
  └── ...
602
747
  ```
603
748
 
604
- ## Installation
605
-
606
- ```bash
607
- $ gem install roast-ai
608
- ```
609
-
610
- Or add to your Gemfile:
611
-
612
- ```ruby
613
- gem 'roast-ai'
614
- ```
615
-
616
749
  ## Development
617
750
 
618
751
  After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake` to run the tests and linter. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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")