roast-ai 0.2.0 → 0.2.2

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +5 -1
  3. data/.gitignore +29 -1
  4. data/CHANGELOG.md +36 -0
  5. data/CLAUDE.md +5 -0
  6. data/CLAUDE_NOTES.md +68 -0
  7. data/Gemfile.lock +3 -4
  8. data/README.md +235 -10
  9. data/docs/ITERATION_SYNTAX.md +31 -3
  10. data/examples/case_when/README.md +58 -0
  11. data/examples/case_when/detect_language/prompt.md +16 -0
  12. data/examples/case_when/workflow.yml +58 -0
  13. data/examples/direct_coerce_syntax/README.md +32 -0
  14. data/examples/direct_coerce_syntax/workflow.yml +36 -0
  15. data/examples/grading/generate_recommendations/output.txt +6 -6
  16. data/examples/grading/workflow.yml +6 -4
  17. data/examples/json_handling/README.md +32 -0
  18. data/examples/json_handling/workflow.yml +52 -0
  19. data/examples/pre_post_processing/README.md +111 -0
  20. data/examples/pre_post_processing/analyze_test_file/prompt.md +23 -0
  21. data/examples/pre_post_processing/improve_test_coverage/prompt.md +17 -0
  22. data/examples/pre_post_processing/optimize_test_performance/prompt.md +25 -0
  23. data/examples/pre_post_processing/post_processing/aggregate_metrics/prompt.md +31 -0
  24. data/examples/pre_post_processing/post_processing/cleanup_environment/prompt.md +28 -0
  25. data/examples/pre_post_processing/post_processing/generate_summary_report/prompt.md +32 -0
  26. data/examples/pre_post_processing/post_processing/output.txt +24 -0
  27. data/examples/pre_post_processing/pre_processing/gather_baseline_metrics/prompt.md +26 -0
  28. data/examples/pre_post_processing/pre_processing/setup_test_environment/prompt.md +11 -0
  29. data/examples/pre_post_processing/validate_changes/prompt.md +24 -0
  30. data/examples/pre_post_processing/workflow.yml +21 -0
  31. data/examples/single_target_prepost/README.md +36 -0
  32. data/examples/single_target_prepost/post_processing/output.txt +27 -0
  33. data/examples/single_target_prepost/pre_processing/gather_dependencies/prompt.md +11 -0
  34. data/examples/single_target_prepost/workflow.yml +20 -0
  35. data/examples/smart_coercion_defaults/README.md +65 -0
  36. data/examples/smart_coercion_defaults/workflow.yml +44 -0
  37. data/examples/step_configuration/README.md +87 -0
  38. data/examples/step_configuration/workflow.yml +60 -0
  39. data/gemfiles/activesupport7.gemfile +4 -0
  40. data/gemfiles/activesupport8.gemfile +4 -0
  41. data/lib/roast/tools/grep.rb +13 -4
  42. data/lib/roast/tools/search_file.rb +2 -2
  43. data/lib/roast/tools.rb +16 -1
  44. data/lib/roast/value_objects/uri_base.rb +49 -0
  45. data/lib/roast/value_objects.rb +1 -0
  46. data/lib/roast/version.rb +1 -1
  47. data/lib/roast/workflow/api_configuration.rb +9 -1
  48. data/lib/roast/workflow/base_iteration_step.rb +22 -3
  49. data/lib/roast/workflow/base_step.rb +40 -3
  50. data/lib/roast/workflow/base_workflow.rb +4 -1
  51. data/lib/roast/workflow/case_executor.rb +49 -0
  52. data/lib/roast/workflow/case_step.rb +82 -0
  53. data/lib/roast/workflow/command_executor.rb +5 -2
  54. data/lib/roast/workflow/conditional_step.rb +6 -43
  55. data/lib/roast/workflow/configuration.rb +4 -2
  56. data/lib/roast/workflow/configuration_loader.rb +14 -0
  57. data/lib/roast/workflow/error_handler.rb +18 -0
  58. data/lib/roast/workflow/expression_evaluator.rb +86 -0
  59. data/lib/roast/workflow/iteration_executor.rb +18 -0
  60. data/lib/roast/workflow/prompt_step.rb +4 -1
  61. data/lib/roast/workflow/repeat_step.rb +2 -2
  62. data/lib/roast/workflow/step_executor_coordinator.rb +50 -8
  63. data/lib/roast/workflow/step_loader.rb +21 -7
  64. data/lib/roast/workflow/step_type_resolver.rb +16 -0
  65. data/lib/roast/workflow/workflow_execution_context.rb +39 -0
  66. data/lib/roast/workflow/workflow_executor.rb +22 -2
  67. data/lib/roast/workflow/workflow_initializer.rb +11 -2
  68. data/lib/roast/workflow/workflow_runner.rb +127 -5
  69. data/lib/roast/workflow.rb +1 -0
  70. data/lib/roast.rb +7 -1
  71. data/roast.gemspec +1 -1
  72. data/schema/workflow.json +41 -0
  73. metadata +40 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ab27704514fc6ddfa09003464d2aa324b852379b0548619e2f0023bebe793ae
4
- data.tar.gz: 18c0936f2b5dc2b54ef420be2ac089b8252f64a02227ab33d98db9d2e6249136
3
+ metadata.gz: 64b0bfad5bc7ce9abd2d750ff52695ba997a750556a403e3ab4fc449dfb28946
4
+ data.tar.gz: 526beebc0ca0697df5ce2f871468fff246440615634ae516439b4c0740f8ce90
5
5
  SHA512:
6
- metadata.gz: 50552385cdb280592a1e331c4a3ec5de2924fa05636af5deb742b96f2fde229e3448e61e6516e570c02a8dc0f416a3b9a3c3d884a800eb89987c90c856a0258d
7
- data.tar.gz: 8feeb510d87e6e6256c46cbca3e13fbc65701ceb28b049452e5bea2f44815f7770e688f8422487b431a85c5f459beffadcce590e7b860cde8a1ab4ee939ff50e
6
+ metadata.gz: 0600537a7242638e683a884a0b22b4d5f7fd58bb02d0c7a04240c8720d197a1f4f5e2d5f44c1a7f0335506f7822b51b6c245fd1c5f25627c8a580319d63b1250
7
+ data.tar.gz: e368630d036c6c3ac6efe00d102ddbf89d438332916ed1adb5aa54fe6cb5f9b34f9adf637c6e36c5baab07015c6f0fb848f675476d4233accd005a98b9630642
@@ -17,10 +17,14 @@ jobs:
17
17
  matrix:
18
18
  os: [ubuntu]
19
19
  ruby: ['3.2', '3.3', '3.4']
20
+ gemfile: ['activesupport7', 'activesupport8']
20
21
  runs-on: ${{ matrix.os }}-latest
21
- continue-on-error: ${{ matrix.ruby == 'ruby-head' }}
22
+ env:
23
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
22
24
  steps:
23
25
  - uses: actions/checkout@v4
26
+ - name: Install ripgrep
27
+ run: sudo apt-get update && sudo apt-get install -y ripgrep
24
28
  - uses: ruby/setup-ruby@v1
25
29
  with:
26
30
  ruby-version: ${{ matrix.ruby }}
data/.gitignore CHANGED
@@ -11,4 +11,32 @@
11
11
  **/.claude/settings.local.json
12
12
 
13
13
  **/CLAUDE.local.md
14
- .roast/
14
+ .roast/
15
+
16
+ bin/_guard-core
17
+ bin/bundle
18
+ bin/coderay
19
+ bin/dotenv
20
+ bin/erb
21
+ bin/guard
22
+ bin/htmldiff
23
+ bin/irb
24
+ bin/ldiff
25
+ bin/listen
26
+ bin/pry
27
+ bin/racc
28
+ bin/rake
29
+ bin/rbs
30
+ bin/rdbg
31
+ bin/rdoc
32
+ bin/ri
33
+ bin/rubocop
34
+ bin/ruby-lsp
35
+ bin/ruby-lsp-check
36
+ bin/ruby-lsp-launcher
37
+ bin/ruby-lsp-test-exec
38
+ bin/ruby-parse
39
+ bin/ruby-rewrite
40
+ bin/thor
41
+
42
+ gemfiles/*.lock
data/CHANGELOG.md CHANGED
@@ -5,6 +5,42 @@ 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.2] - 2025-05-29
9
+
10
+ ### Added
11
+ - Pre/post processing framework for workflows with `pre_processing` and `post_processing` sections (#86)
12
+ - Support for `output.txt` ERB templates in post-processing phase for custom output formatting
13
+ - Pre/post processing support for single-target workflows (not just multi-target)
14
+ - Simplified access to pre-processing data in target workflows (removed `output` intermediary level)
15
+ - Verbose mode improvements for better debugging experience (#98)
16
+ - Command outputs are now displayed when using the `--verbose` flag
17
+ - Commands executed within conditional branches also show output in verbose mode
18
+ - User-friendly error reporting for workflow failures (#98)
19
+ - Clear ❌ indicators when commands or steps fail
20
+ - Command failures show exit status and output (no verbose needed for failures)
21
+ - Step failures provide helpful context about what might be wrong
22
+ - Exit handler displays actionable suggestions for resolving issues
23
+ - Automatic workflow discovery by name (#97)
24
+ - Can now run workflows by name without full path: `roast execute my_workflow`
25
+ - Automatically looks for `roast/my_workflow/workflow.yml` in current directory
26
+ - Configurable base URI for API endpoints (#83)
27
+
28
+ ### Fixed
29
+ - Search file tool now correctly prefixes paths when searching (#92)
30
+ - Support for Ruby projects using ActiveSupport 7.0+ (#95)
31
+
32
+ ### Changed
33
+ - ActiveSupport dependency relaxed to >= 7.0 for broader compatibility
34
+
35
+ ## [0.2.1]
36
+
37
+ ### Added
38
+ - Smart coercion defaults for boolean expressions based on step type
39
+ - Ruby expressions (`{{expr}}`) default to regular boolean coercion
40
+ - Bash commands (`$(cmd)`) default to exit code interpretation
41
+ - Inline prompts and regular steps default to "smart" LLM-powered interpretation (looks for truthy or falsy language)
42
+ - Direct syntax for step configuration - `coerce_to` and other options are now specified directly on iteration steps
43
+
8
44
  ## [0.2.0] - 2025-05-26
9
45
 
10
46
  ### Added
data/CLAUDE.md CHANGED
@@ -50,6 +50,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
50
50
  - IterationExecutor handles iterations (each, repeat)
51
51
  - ConditionalExecutor handles conditionals (if, unless)
52
52
  - Don't combine different responsibilities in one class
53
+ - **Do not implement prompts "inline" using a prompt: attribute nested under step names, that violates the primary design architecture of Roast**
54
+
55
+ ## Guidance and Expectations
56
+
57
+ - Do not decide unilaterally to leave code for the sake of "backwards compatibility"... always run those decisions by me first.
53
58
 
54
59
  ## Git Workflow Practices
55
60
 
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.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (0.2.0)
5
- activesupport (~> 8.0)
4
+ roast-ai (0.2.2)
5
+ activesupport (>= 7.0)
6
6
  cli-ui
7
7
  diff-lcs (~> 1.5)
8
8
  faraday-retry
@@ -13,7 +13,7 @@ PATH
13
13
  GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
- activesupport (8.0.2)
16
+ activesupport (7.2.2.1)
17
17
  base64
18
18
  benchmark (>= 0.3)
19
19
  bigdecimal
@@ -25,7 +25,6 @@ GEM
25
25
  minitest (>= 5.1)
26
26
  securerandom (>= 0.3)
27
27
  tzinfo (~> 2.0, >= 2.0.5)
28
- uri (>= 0.13.1)
29
28
  addressable (2.8.7)
30
29
  public_suffix (>= 2.0.2, < 7.0)
31
30
  ast (2.4.3)
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:
@@ -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
@@ -76,6 +124,9 @@ roast execute workflow.yml target_file.rb
76
124
 
77
125
  # Or for a targetless workflow (API calls, data generation, etc.)
78
126
  roast execute workflow.yml
127
+
128
+ # Roast will automatically search in `project_root/roast/workflow_name` if the path is incomplete.
129
+ roast execute my_cool_workflow # Equivalent to `roast execute roast/my_cool_workflow/workflow.yml
79
130
  ```
80
131
 
81
132
  ### Understanding Workflows
@@ -178,7 +229,37 @@ Roast supports several types of steps:
178
229
  - Until conditions: `until: "{{condition}}"`
179
230
  - Maximum iterations: `max_iterations: 10`
180
231
 
181
- 6. **Raw prompt step**: Simple text prompts for the model without tools
232
+ 6. **Case/when/else steps**: Select different execution paths based on a value (similar to Ruby's case statement)
233
+ ```yaml
234
+ steps:
235
+ - detect_language
236
+
237
+ - case: "{{ workflow.output.detect_language }}"
238
+ when:
239
+ ruby:
240
+ - lint_with_rubocop
241
+ - test_with_rspec
242
+ javascript:
243
+ - lint_with_eslint
244
+ - test_with_jest
245
+ python:
246
+ - lint_with_pylint
247
+ - test_with_pytest
248
+ else:
249
+ - analyze_generic
250
+ - generate_basic_report
251
+ ```
252
+
253
+ Case expressions can be:
254
+ - Workflow outputs: `case: "{{ workflow.output.variable }}"`
255
+ - Ruby expressions: `case: "{{ count > 10 ? 'high' : 'low' }}"`
256
+ - Bash commands: `case: "$(echo $ENVIRONMENT)"`
257
+ - Direct values: `case: "production"`
258
+
259
+ The value is compared against each key in the `when` clause, and matching steps are executed.
260
+ If no match is found, the `else` steps are executed (if provided).
261
+
262
+ 7. **Raw prompt step**: Simple text prompts for the model without tools
182
263
  ```yaml
183
264
  steps:
184
265
  - Summarize the changes made to the codebase.
@@ -398,9 +479,9 @@ Benefits of using OpenRouter:
398
479
 
399
480
  When using OpenRouter, specify fully qualified model names including the provider prefix (e.g., `anthropic/claude-3-opus-20240229`).
400
481
 
401
- #### Dynamic API Tokens
482
+ #### Dynamic API Tokens and URIs
402
483
 
403
- Roast allows you to dynamically fetch API tokens using shell commands directly in your workflow configuration:
484
+ Roast allows you to dynamically fetch attributes such as API token and URI base (to use with a proxy) via shell commands directly in your workflow configuration:
404
485
 
405
486
  ```yaml
406
487
  # This will execute the shell command and use the result as the API token
@@ -412,8 +493,13 @@ api_token: $(echo $OPENAI_API_KEY)
412
493
  # For OpenRouter (requires api_provider setting)
413
494
  api_provider: openrouter
414
495
  api_token: $(echo $OPENROUTER_API_KEY)
415
- ```
416
496
 
497
+ # Static Proxy URI
498
+ uri_base: https://proxy.example.com/v1
499
+
500
+ # Dynamic Proxy URI
501
+ uri_base: $(echo $AI_PROXY_URI_BASE)
502
+ ```
417
503
 
418
504
  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
505
 
@@ -668,18 +754,157 @@ your-project/
668
754
  └── ...
669
755
  ```
670
756
 
671
- ## Installation
757
+ ### Pre/Post Processing Framework
672
758
 
673
- ```bash
674
- $ gem install roast-ai
759
+ Roast supports pre-processing and post-processing phases for workflows. This enables powerful workflows that need setup/teardown or result aggregation across all processed files.
760
+
761
+ #### Overview
762
+
763
+ - **Pre-processing**: Steps executed once before any targets are processed
764
+ - **Post-processing**: Steps executed once after all targets have been processed
765
+ - **Shared state**: Pre-processing results are available to all subsequent steps
766
+ - **Result aggregation**: Post-processing has access to all workflow execution results
767
+ - **Single-target support**: Pre/post processing works with single-target workflows too
768
+ - **Output templates**: Post-processing supports `output.txt` templates for custom formatting
769
+
770
+ #### Configuration
771
+
772
+ ```yaml
773
+ name: optimize_tests
774
+ model: gpt-4o
775
+ target: "test/**/*_test.rb"
776
+
777
+ # Pre-processing steps run once before any test files
778
+ pre_processing:
779
+ - gather_baseline_metrics
780
+ - setup_test_environment
781
+
782
+ # Main workflow steps run for each test file
783
+ steps:
784
+ - analyze_test
785
+ - improve_coverage
786
+ - optimize_performance
787
+
788
+ # Post-processing steps run once after all test files
789
+ post_processing:
790
+ - aggregate_results
791
+ - generate_report
792
+ - cleanup_environment
675
793
  ```
676
794
 
677
- Or add to your Gemfile:
795
+ #### Directory Structure
678
796
 
679
- ```ruby
680
- gem 'roast-ai'
797
+ Pre and post-processing steps follow the same conventions as regular steps but are organized in their own directories:
798
+
799
+ ```
800
+ workflow.yml
801
+ pre_processing/
802
+ ├── gather_baseline_metrics/
803
+ │ └── prompt.md
804
+ └── setup_test_environment/
805
+ └── prompt.md
806
+ analyze_test/
807
+ └── prompt.md
808
+ improve_coverage/
809
+ └── prompt.md
810
+ optimize_performance/
811
+ └── prompt.md
812
+ post_processing/
813
+ ├── output.txt
814
+ ├── aggregate_results/
815
+ │ └── prompt.md
816
+ ├── generate_report/
817
+ │ └── prompt.md
818
+ └── cleanup_environment/
819
+ └── prompt.md
681
820
  ```
682
821
 
822
+ #### Data Access
823
+
824
+ **Pre-processing results in target workflows:**
825
+
826
+ Target workflows have access to pre-processing results through the `pre_processing_data` variable with dot notation:
827
+
828
+ ```erb
829
+ # In a target workflow step prompt
830
+ The baseline metrics from pre-processing:
831
+ <%= pre_processing_data.gather_baseline_metrics %>
832
+
833
+ Environment setup details:
834
+ <%= pre_processing_data.setup_test_environment %>
835
+ ```
836
+
837
+ **Post-processing data access:**
838
+
839
+ Post-processing steps have access to:
840
+
841
+ - `pre_processing`: Direct access to pre-processing results with dot notation
842
+ - `targets`: Hash of all target workflow results, keyed by file paths
843
+
844
+ Example post-processing prompt:
845
+ ```markdown
846
+ # Generate Summary Report
847
+
848
+ Based on the baseline metrics:
849
+ <%= pre_processing.gather_baseline_metrics %>
850
+
851
+ Environment configuration:
852
+ <%= pre_processing.setup_test_environment %>
853
+
854
+ And the results from processing all files:
855
+ <% targets.each do |file, target| %>
856
+ File: <%= file %>
857
+ Analysis results: <%= target.output.analyze_test %>
858
+ Coverage improvements: <%= target.output.improve_coverage %>
859
+ Performance optimizations: <%= target.output.optimize_performance %>
860
+ <% end %>
861
+
862
+ Please generate a comprehensive summary report showing:
863
+ 1. Overall improvements achieved
864
+ 2. Files with the most significant changes
865
+ 3. Recommendations for further optimization
866
+ ```
867
+
868
+ #### Output Templates
869
+
870
+ Post-processing supports custom output formatting using ERB templates. Create an `output.txt` file in your `post_processing` directory to format the final workflow output:
871
+
872
+ ```erb
873
+ # post_processing/output.txt
874
+ === Workflow Summary Report ===
875
+ Generated at: <%= Time.now.strftime("%Y-%m-%d %H:%M:%S") %>
876
+
877
+ Environment: <%= pre_processing.setup_test_environment %>
878
+
879
+ Files Processed: <%= targets.size %>
880
+
881
+ <% targets.each do |file, target| %>
882
+ - <%= file %>: <%= target.output.analyze_test %>
883
+ <% end %>
884
+
885
+ <%= output.generate_report %>
886
+ ===============================
887
+ ```
888
+
889
+ The template has access to:
890
+ - `pre_processing`: All pre-processing step outputs with dot notation
891
+ - `targets`: Hash of all target workflow results with dot notation (each target has `.output` and `.final_output`)
892
+ - `output`: Post-processing step outputs with dot notation
893
+
894
+ #### Use Cases
895
+
896
+ This pattern is ideal for:
897
+
898
+ - **Code migrations**: Setup migration tools, process files, generate migration report
899
+ - **Test optimization**: Baseline metrics, optimize tests, aggregate improvements
900
+ - **Documentation generation**: Analyze codebase, generate docs per module, create index
901
+ - **Dependency updates**: Check versions, update files, verify compatibility
902
+ - **Security audits**: Setup scanners, check each file, generate security report
903
+ - **Performance analysis**: Establish baselines, analyze components, summarize findings
904
+
905
+ See the [pre/post processing example](examples/pre_post_processing) for a complete working demonstration.
906
+
907
+
683
908
  ## Development
684
909
 
685
910
  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.
@@ -90,10 +90,38 @@ For defining prompts directly in the workflow:
90
90
 
91
91
  ## Type Coercion
92
92
 
93
- Values are automatically coerced to the appropriate types:
93
+ ### Smart Defaults
94
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)
95
+ Roast applies intelligent defaults for boolean coercion based on the type of expression:
96
+
97
+ - **Ruby expressions** (`{{expr}}`) → Regular boolean coercion (`!!value`)
98
+ - **Bash commands** (`$(cmd)`) → Exit code interpretation (0 = true, non-zero = false)
99
+ - **Inline prompts/step names** → LLM boolean interpretation (analyzes yes/no intent)
100
+
101
+ ### Manual Coercion
102
+
103
+ You can override the smart defaults by specifying `coerce_to` directly in the step:
104
+
105
+ ```yaml
106
+ # Override prompt to use regular boolean instead of LLM boolean
107
+ - repeat:
108
+ until: "check_condition"
109
+ coerce_to: boolean
110
+ steps:
111
+ - process_item
112
+
113
+ # Force a step result to be treated as iterable
114
+ - each: "get_items"
115
+ as: "item"
116
+ coerce_to: iterable
117
+ steps:
118
+ - process: "{{item}}"
119
+ ```
120
+
121
+ Available coercion types:
122
+ - `boolean` - Standard Ruby truthiness (`!!` operator)
123
+ - `llm_boolean` - Natural language yes/no interpretation
124
+ - `iterable` - Convert to array (splits strings on newlines)
97
125
 
98
126
  ## Migrating Existing Workflows
99
127
 
@@ -0,0 +1,58 @@
1
+ # Case/When/Else Example
2
+
3
+ This example demonstrates the use of `case/when/else` control flow in Roast workflows.
4
+
5
+ ## Overview
6
+
7
+ The `case/when/else` construct allows you to execute different steps based on the value of an expression, similar to Ruby's case statement or switch statements in other languages.
8
+
9
+ ## Syntax
10
+
11
+ ```yaml
12
+ - case: <expression>
13
+ when:
14
+ <value1>:
15
+ - <steps>
16
+ <value2>:
17
+ - <steps>
18
+ else:
19
+ - <steps>
20
+ ```
21
+
22
+ ## Features Demonstrated
23
+
24
+ 1. **Basic case/when/else**: Detect file language and execute language-specific analysis
25
+ 2. **Bash command evaluation**: Use environment variables to determine deployment strategy
26
+ 3. **Complex expressions**: Use Ruby expressions to categorize numeric values
27
+
28
+ ## Expression Types
29
+
30
+ The `case` expression can be:
31
+ - A simple string value
32
+ - An interpolated workflow output: `{{ workflow.output.variable }}`
33
+ - A Ruby expression: `{{ workflow.output.count > 10 ? 'high' : 'low' }}`
34
+ - A bash command: `$(echo $ENVIRONMENT)`
35
+ - A reference to a previous step's output
36
+
37
+ ## How It Works
38
+
39
+ 1. The `case` expression is evaluated to produce a value
40
+ 2. The value is compared against each key in the `when` clause
41
+ 3. If a match is found, the steps under that key are executed
42
+ 4. If no match is found and an `else` clause exists, those steps are executed
43
+ 5. If no match is found and no `else` clause exists, execution continues
44
+
45
+ ## Running the Example
46
+
47
+ ```bash
48
+ roast execute examples/case_when/workflow.yml
49
+ ```
50
+
51
+ This will process all Ruby, JavaScript, Python, and Go files in the current directory, detecting their language and running appropriate analysis steps.
52
+
53
+ ## Use Cases
54
+
55
+ - **Multi-language projects**: Different linting/testing for different file types
56
+ - **Environment-specific workflows**: Different deployment steps for prod/staging/dev
57
+ - **Conditional processing**: Different handling based on file size, complexity, or other metrics
58
+ - **Error handling**: Different recovery strategies based on error types
@@ -0,0 +1,16 @@
1
+ # Detect Programming Language
2
+
3
+ Based on the file extension and content, determine the primary programming language of this file:
4
+ - If it's a `.rb` file, return "ruby"
5
+ - If it's a `.js` file, return "javascript"
6
+ - If it's a `.py` file, return "python"
7
+ - If it's a `.go` file, return "go"
8
+ - Otherwise, return "unknown"
9
+
10
+ Return ONLY the language name in lowercase, nothing else.
11
+
12
+ File: {{ context.resource_uri }}
13
+ Content:
14
+ ```
15
+ {{ context.resource }}
16
+ ```
@@ -0,0 +1,58 @@
1
+ name: "Case/When/Else Example"
2
+
3
+ tools:
4
+ - Roast::Tools::Cmd
5
+ - Roast::Tools::ReadFile
6
+ - Roast::Tools::WriteFile
7
+
8
+ target: "**/*.{rb,js,py,go}"
9
+
10
+ steps:
11
+ - detect_language
12
+
13
+ - case: "{{ workflow.output.detect_language }}"
14
+ when:
15
+ ruby:
16
+ - analyze_ruby
17
+ - generate_ruby_report
18
+ javascript:
19
+ - analyze_javascript
20
+ - generate_js_report
21
+ python:
22
+ - analyze_python
23
+ - generate_python_report
24
+ go:
25
+ - analyze_go
26
+ - generate_go_report
27
+ else:
28
+ - analyze_generic
29
+ - generate_generic_report
30
+
31
+ # Another example using bash command for case expression
32
+ - get_environment: $(echo $ENVIRONMENT || echo "development")
33
+
34
+ - case: "{{ workflow.output.get_environment }}"
35
+ when:
36
+ production:
37
+ - production_checks
38
+ - deploy_production
39
+ staging:
40
+ - staging_checks
41
+ - deploy_staging
42
+ development:
43
+ - run_tests
44
+ - local_deploy
45
+ else:
46
+ - unknown_environment
47
+
48
+ # Example with numeric case values
49
+ - count_issues
50
+
51
+ - case: "{{ workflow.output.count_issues.to_i > 10 ? 'high' : workflow.output.count_issues.to_i > 5 ? 'medium' : 'low' }}"
52
+ when:
53
+ high:
54
+ - high_priority_alert
55
+ medium:
56
+ - medium_priority_notice
57
+ low:
58
+ - low_priority_info