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
@@ -0,0 +1,58 @@
1
+ name: Ruby Method Counter
2
+ tools:
3
+ - Roast::Tools::ReadFile
4
+ - Roast::Tools::Grep
5
+ - Roast::Tools::WriteFile
6
+ - Roast::Tools::CodingAgent
7
+
8
+ steps:
9
+ # Find all Ruby files in the specified directory
10
+ - find_ruby_files:
11
+ $(find lib/roast/workflow -type f -name "*.rb")
12
+
13
+ # Initialize a report object to store our results
14
+ - initialize_report:
15
+ $(echo '{"files_analyzed": 0, "total_methods": 0, "results": []}')
16
+
17
+ # Process each file found
18
+ - each: "{{output['find_ruby_files'].split('\n')}}"
19
+ as: "current_file"
20
+ steps:
21
+ # Read the file content
22
+ - read_file:
23
+ prompt: examples/iteration/prompts/analyze_file
24
+ model: claude-3-haiku-20240307
25
+ vars:
26
+ file_path: "{{ current_file }}"
27
+
28
+ # Update the report with the analysis result
29
+ - update_report:
30
+ prompt: examples/iteration/prompts/update_report
31
+ model: claude-3-haiku-20240307
32
+ vars:
33
+ file_path: "{{ current_file }}"
34
+ method_count: "{{ output['read_file']['method_count'] }}"
35
+ current_report: "{{ output['initialize_report'] }}"
36
+
37
+ # Generate the summary report after processing all files
38
+ - report_count:
39
+ $(echo "{{ output.initialize_report.files_analyzed }}")
40
+
41
+ # Repeat to generate the final summary - demonstrates the repeat construct
42
+ - repeat:
43
+ steps:
44
+ - generate_summary:
45
+ prompt: examples/iteration/prompts/generate_summary
46
+ model: claude-3-haiku-20240307
47
+ vars:
48
+ report_data: "{{ output['initialize_report'] }}"
49
+ until: "{{true}}"
50
+ max_iterations: 1
51
+
52
+ # Write the report to a file
53
+ - write_report:
54
+ prompt: examples/iteration/prompts/write_report
55
+ model: claude-3-haiku-20240307
56
+ vars:
57
+ report_data: "{{ output['initialize_report'] }}"
58
+ summary: "{{ output['generate_summary']['summary'] }}"
@@ -0,0 +1,26 @@
1
+ I'll update the count of fixes that have been successfully applied.
2
+
3
+ Current fix count:
4
+ ```
5
+ {{output.update_fix_count || 0}}
6
+ ```
7
+
8
+ Verification result from the previous step:
9
+ ```json
10
+ {{output.verify_fix}}
11
+ ```
12
+
13
+ I'll increment the fix count if the verification was successful or partial, but not if it failed.
14
+
15
+ ```javascript
16
+ let currentCount = parseInt({{output.update_fix_count || 0}});
17
+ let verificationStatus = "{{output.verify_fix.status}}";
18
+
19
+ if (verificationStatus === "success" || verificationStatus === "partial") {
20
+ currentCount += 1;
21
+ }
22
+
23
+ return { fixes_applied: currentCount };
24
+ ```
25
+
26
+ This updated count will be used to determine whether we've met our target for the number of fixes to implement.
@@ -0,0 +1,29 @@
1
+ I'll verify that the fix implemented in the previous step correctly addresses the identified issue.
2
+
3
+ Here are the details of the issue that was fixed:
4
+ ```json
5
+ {{output.select_next_issue}}
6
+ ```
7
+
8
+ And here is the implementation of the fix:
9
+ ```json
10
+ {{output.implement_fix}}
11
+ ```
12
+
13
+ Now I'll read the updated file to verify the changes:
14
+ ```ruby
15
+ {{read_file(output.select_next_issue.file_path)}}
16
+ ```
17
+
18
+ I'll evaluate the fix based on these criteria:
19
+ 1. Does it fully address the identified issue?
20
+ 2. Did it introduce any new issues or regressions?
21
+ 3. Does it maintain the original functionality?
22
+ 4. Does it follow Ruby best practices and style conventions?
23
+ 5. Is it minimal and focused (changing only what was necessary)?
24
+
25
+ Based on this evaluation, I'll provide:
26
+ 1. A verification status (success, partial, failure)
27
+ 2. Detailed reasoning for the status
28
+ 3. Any recommendations for further improvements or adjustments
29
+ 4. An overall assessment of the code quality improvement
@@ -0,0 +1,42 @@
1
+ name: Code Quality Analyzer
2
+ tools:
3
+ - Roast::Tools::ReadFile
4
+ - Roast::Tools::Grep
5
+ - Roast::Tools::WriteFile
6
+ - Roast::Tools::UpdateFiles
7
+ - Roast::Tools::CodingAgent
8
+ - Roast::Tools::Cmd
9
+
10
+ steps:
11
+ # Get all Ruby files from the target directory
12
+ - get_files_to_analyze:
13
+ $(find {{resource.target}} -name "*.rb" -not -path "*/vendor/*" | grep -v "test")
14
+
15
+ # Analyze each file and generate improvement recommendations
16
+ - each: "{{output['get_files_to_analyze'].split('\n')}}"
17
+ as: "current_file"
18
+ steps:
19
+ - read_file:
20
+ $(cat {{current_file}})
21
+ - analyze_complexity
22
+ - generate_recommendations
23
+
24
+ # After analyzing all files, sort issues by priority
25
+ - prioritize_issues
26
+
27
+ # Process the highest priority issues first, until we've addressed a sufficient number
28
+ # or reached our iteration limit
29
+ - initialize_fixes:
30
+ $(echo "0")
31
+
32
+ - repeat:
33
+ steps:
34
+ - select_next_issue
35
+ - implement_fix
36
+ - verify_fix
37
+ - update_fix_count
38
+ until: "{{output['update_fix_count']['fixes_applied'] >= 5 || output['select_next_issue']['no_issues_left'] == true}}"
39
+ max_iterations: 10
40
+
41
+ # Generate a summary report of all changes made
42
+ - generate_report
@@ -0,0 +1,32 @@
1
+ # JSON Handling Example
2
+
3
+ This example demonstrates how Roast handles JSON responses from LLM steps.
4
+
5
+ ## Key Features
6
+
7
+ 1. **JSON Arrays**: When a step has `json: true` and returns an array, the result is `flatten.first` - the first element after flattening the array. This is useful when the LLM returns an array with a single object.
8
+
9
+ 2. **JSON Objects**: Hash/object responses are maintained as proper Ruby hashes in the workflow output.
10
+
11
+ 3. **Replay Support**: JSON data structures are properly serialized and deserialized when saving/loading workflow state for replay functionality.
12
+
13
+ ## Running the Example
14
+
15
+ ```bash
16
+ bin/roast examples/json_handling/workflow.yml
17
+ ```
18
+
19
+ ## How It Works
20
+
21
+ 1. The `fetch_users` step generates a JSON array of user objects
22
+ 2. The `fetch_metadata` step generates a JSON object with metadata
23
+ 3. The `process_data` step can access the structured data using `{{output.fetch_users}}` and `{{output.fetch_metadata}}`
24
+ 4. The structured data is preserved through the workflow, including during replay scenarios
25
+
26
+ ## Implementation Details
27
+
28
+ When `json: true` is set on a step:
29
+ - Array responses return `flatten.first` - the first element after flattening
30
+ - Object responses are preserved as hashes
31
+ - Non-JSON array responses (when `json: false`) are joined with newlines
32
+ - The data maintains its structure when saved to and loaded from workflow state files
@@ -0,0 +1,52 @@
1
+ name: JSON Data Handling Example
2
+ api_provider: openai
3
+ model: gpt-4
4
+
5
+ tools:
6
+ - type: write_file
7
+ allowed_paths:
8
+ - examples/json_handling/
9
+
10
+ steps:
11
+ - name: fetch_users
12
+ prompt: |
13
+ Generate a JSON array of 3 user objects. Each user should have:
14
+ - id (number)
15
+ - name (string)
16
+ - email (string)
17
+ - active (boolean)
18
+ json: true
19
+
20
+ - name: fetch_metadata
21
+ prompt: |
22
+ Generate a JSON object with metadata about a dataset:
23
+ - total_records (number)
24
+ - last_updated (ISO date string)
25
+ - categories (array of strings)
26
+ - filters (object with status and sort fields)
27
+ json: true
28
+
29
+ - name: process_data
30
+ prompt: |
31
+ Based on the users data: {{output.fetch_users}}
32
+ And metadata: {{output.fetch_metadata}}
33
+
34
+ Create a summary report describing:
35
+ 1. How many active users there are
36
+ 2. The categories available
37
+ 3. When the data was last updated
38
+
39
+ - name: save_report
40
+ tool: write_file
41
+ path: examples/json_handling/report.txt
42
+ content: |
43
+ # JSON Data Processing Report
44
+
45
+ ## Users Data
46
+ {{output.fetch_users}}
47
+
48
+ ## Metadata
49
+ {{output.fetch_metadata}}
50
+
51
+ ## Summary
52
+ {{output.process_data}}
@@ -1,7 +1,7 @@
1
1
  name: OpenRouter Example
2
2
  api_provider: openrouter
3
3
  api_token: $(echo $OPENROUTER_API_KEY)
4
- model: anthropic/claude-3-haiku-20240307
4
+ model: google/gemini-2.0-flash-001
5
5
 
6
6
  tools:
7
7
  - Roast::Tools::ReadFile
@@ -9,4 +9,4 @@ tools:
9
9
 
10
10
  steps:
11
11
  - analyze_input
12
- - generate_response
12
+ - generate_response
@@ -0,0 +1,65 @@
1
+ # Smart Coercion Defaults
2
+
3
+ This example demonstrates how Roast applies intelligent defaults for boolean coercion based on the type of expression being evaluated.
4
+
5
+ ## Default Coercion Rules
6
+
7
+ When a step is used in a boolean context (like `if`, `unless`, or `until` conditions) and no explicit `coerce_to` is specified, Roast applies these smart defaults:
8
+
9
+ 1. **Ruby Expressions** (`{{expression}}`) → Regular boolean coercion (`!!value`)
10
+ - `nil` and `false` are falsy
11
+ - Everything else is truthy (including 0, empty arrays, etc.)
12
+
13
+ 2. **Bash Commands** (`$(command)`) → Exit code interpretation
14
+ - Exit code 0 = true (success)
15
+ - Non-zero exit code = false (failure)
16
+
17
+ 3. **Prompt/Step Names** → LLM boolean interpretation
18
+ - Analyzes natural language responses for yes/no intent
19
+ - "Yes", "True", "Affirmative" → true
20
+ - "No", "False", "Negative" → false
21
+
22
+ 4. **Non-string Values** → Regular boolean coercion
23
+
24
+ ## Examples
25
+
26
+ ### Ruby Expression (Regular Boolean)
27
+ ```yaml
28
+ - repeat:
29
+ until: "{{counter >= 5}}" # Uses !! coercion
30
+ steps:
31
+ - increment: counter
32
+ ```
33
+
34
+ ### Bash Command (Exit Code)
35
+ ```yaml
36
+ - repeat:
37
+ until: "$(test -f /tmp/done)" # True when file exists (exit 0)
38
+ steps:
39
+ - wait: 1
40
+ ```
41
+
42
+ ### Prompt Response (LLM Boolean)
43
+ ```yaml
44
+ - if: "Should we continue?" # Interprets "Yes, let's continue" as true
45
+ then:
46
+ - proceed: "Continuing..."
47
+ ```
48
+
49
+ ## Overriding Defaults
50
+
51
+ You can always override the default coercion by specifying `coerce_to` directly in the step:
52
+
53
+ ```yaml
54
+ - each: "get_items"
55
+ as: "item"
56
+ coerce_to: iterable # Override default to split into array
57
+ steps:
58
+ - process: "{{item}}"
59
+ ```
60
+
61
+ ## Supported Coercion Types
62
+
63
+ - `boolean` - Standard Ruby truthiness (!! operator)
64
+ - `llm_boolean` - Natural language yes/no interpretation
65
+ - `iterable` - Convert to array (splits strings on newlines)
@@ -0,0 +1,44 @@
1
+ name: Smart Coercion Defaults Demo
2
+ description: Demonstrates how different step types get smart boolean coercion defaults
3
+
4
+ steps:
5
+ # Example 1: Ruby expressions default to regular boolean coercion
6
+ - set_counter:
7
+ value: 3
8
+
9
+ - repeat:
10
+ until: "{{counter >= 5}}" # Ruby expression defaults to boolean
11
+ steps:
12
+ - increment_counter:
13
+ value: "{{counter + 1}}"
14
+ - log: "Counter is now {{counter}}"
15
+
16
+ # Example 2: Bash commands default to exit code interpretation
17
+ - check_file_exists:
18
+ repeat:
19
+ until: "$(ls /tmp/important_file 2>/dev/null)" # Bash command defaults to exit code
20
+ max_iterations: 3
21
+ steps:
22
+ - create_file: "$(mkdir -p /tmp && touch /tmp/important_file)"
23
+ - log: "Waiting for file to exist..."
24
+
25
+ # Example 3: Prompt/step names default to LLM boolean interpretation
26
+ - ask_user_ready:
27
+ prompt: "Are you ready to continue? Please respond yes or no."
28
+
29
+ - conditional:
30
+ if: "ask_user_ready" # Step name defaults to llm_boolean
31
+ then:
32
+ - proceed: "Great! Let's continue..."
33
+ else:
34
+ - wait: "Okay, take your time."
35
+
36
+ # Example 4: Explicit coerce_to overrides smart defaults
37
+ - get_items:
38
+ prompt: "List three fruits, one per line"
39
+
40
+ - each: "get_items"
41
+ as: "fruit"
42
+ coerce_to: iterable # Override default llm_boolean to iterable
43
+ steps:
44
+ - process_fruit: "Processing {{fruit}}"
@@ -0,0 +1,87 @@
1
+ # Step Configuration Example
2
+
3
+ This example demonstrates how to configure various step types in Roast workflows, including:
4
+ - Inline prompts
5
+ - Iterator steps (each/repeat)
6
+ - Regular steps
7
+
8
+ ## Configuration Options
9
+
10
+ All step types support the following configuration options:
11
+
12
+ - `model`: The AI model to use (e.g., "gpt-4o", "claude-3-opus")
13
+ - `loop`: Whether to enable auto-looping (true/false)
14
+ - `print_response`: Whether to print the response to stdout (true/false)
15
+ - `json`: Whether to expect a JSON response (true/false)
16
+ - `params`: Additional parameters to pass to the model (e.g., temperature, max_tokens)
17
+ - `coerce_to`: How to convert the step result (options: "boolean", "llm_boolean", "iterable")
18
+
19
+ ## Configuration Precedence
20
+
21
+ 1. **Step-specific configuration** takes highest precedence
22
+ 2. **Global configuration** (defined at the workflow level) applies to all steps without specific configuration
23
+ 3. **Default values** are used when no configuration is provided
24
+
25
+ ## Inline Prompt Configuration
26
+
27
+ Inline prompts can be configured in two ways:
28
+
29
+ ### As a top-level key:
30
+ ```yaml
31
+ analyze the code:
32
+ model: gpt-4o
33
+ loop: false
34
+ print_response: true
35
+ ```
36
+
37
+ ### Inline in the steps array:
38
+ ```yaml
39
+ steps:
40
+ - suggest improvements:
41
+ model: claude-3-opus
42
+ params:
43
+ temperature: 0.9
44
+ ```
45
+
46
+ ## Iterator Configuration
47
+
48
+ Both `each` and `repeat` steps support configuration:
49
+
50
+ ```yaml
51
+ each:
52
+ each: "{{files}}"
53
+ as: file
54
+ model: gpt-3.5-turbo
55
+ loop: false
56
+ steps:
57
+ - process {{file}}
58
+
59
+ repeat:
60
+ repeat: true
61
+ until: "{{done}}"
62
+ model: gpt-4o
63
+ print_response: true
64
+ steps:
65
+ - check status
66
+ ```
67
+
68
+ ## Coercion Types
69
+
70
+ The `coerce_to` option allows you to convert step results to specific types:
71
+
72
+ - **`boolean`**: Converts any value to true/false (nil, false, empty string → false; everything else → true)
73
+ - **`llm_boolean`**: Interprets natural language responses as boolean (e.g., "Yes", "Definitely!" → true; "No", "Not yet" → false)
74
+ - **`iterable`**: Ensures the result can be iterated over (splits strings by newlines if needed)
75
+
76
+ This is particularly useful when:
77
+ - Using steps in conditional contexts (if/unless)
78
+ - Using steps as conditions in repeat loops
79
+ - Processing step results in each loops
80
+
81
+ ## Running the Example
82
+
83
+ ```bash
84
+ bin/roast examples/step_configuration/workflow.yml
85
+ ```
86
+
87
+ Note: This example is for demonstration purposes and shows the configuration syntax. You'll need to adapt it to your specific use case with appropriate prompts and logic.
@@ -0,0 +1,60 @@
1
+ name: Step Configuration Example
2
+ description: Demonstrates how to configure various step types including inline prompts, iterators, and regular steps
3
+
4
+ # Global configuration that applies to all steps
5
+ model: openai/gpt-4o-mini
6
+ loop: true
7
+
8
+ # Configuration for specific steps
9
+ summarize the code:
10
+ model: claude-3-opus # Override global model
11
+ loop: false # Disable auto-loop for this step
12
+ print_response: true # Print the response
13
+ json: false # Don't expect JSON response
14
+ params:
15
+ temperature: 0.7 # Custom temperature
16
+
17
+ analyze complexity:
18
+ model: gpt-4o
19
+ json: true # Expect JSON response
20
+ params:
21
+ temperature: 0.2 # Lower temperature for more consistent analysis
22
+
23
+ # Iterator step configuration
24
+ each:
25
+ each: "{{files}}"
26
+ as: file
27
+ model: gpt-3.5-turbo # Use a faster model for iteration
28
+ loop: false # Don't loop on each iteration
29
+ steps:
30
+ - process {{file}}
31
+
32
+ repeat:
33
+ repeat: true
34
+ until: "are all tests passing" # This will be evaluated as a step
35
+ max_iterations: 5
36
+ model: gpt-4o
37
+ print_response: true
38
+ coerce_to: llm_boolean # Interpret natural language response as boolean
39
+ steps:
40
+ - run tests
41
+ - fix failing tests
42
+
43
+ # Step used in boolean context
44
+ are all tests passing:
45
+ model: gpt-4o
46
+ coerce_to: llm_boolean # Convert "Yes, all tests are passing!" to true
47
+ params:
48
+ temperature: 0.2
49
+
50
+ # Steps can mix configured and unconfigured inline prompts
51
+ steps:
52
+ - list all source files # Uses global configuration
53
+ - summarize the code # Uses step-specific configuration
54
+ - analyze complexity # Uses step-specific configuration
55
+ - suggest improvements: # Step-specific configuration inline
56
+ model: claude-3-opus
57
+ params:
58
+ temperature: 0.9
59
+ - each # Iterator with configuration
60
+ - repeat # Iterator with configuration
@@ -0,0 +1,27 @@
1
+ # Workflow Generator
2
+
3
+ This workflow generates new Roast workflows based on user descriptions. It's the engine behind the "New from prompt" option in `roast init`.
4
+
5
+ ## How It Works
6
+
7
+ The workflow generator takes a user description and workflow name, then:
8
+
9
+ 1. **Analyzes the request** - Understands what type of workflow is needed, what steps are required, and what tools should be used
10
+ 2. **Generates structure** - Creates a complete workflow configuration including YAML and step prompts
11
+ 3. **Creates files** - Writes all the necessary files and directories to disk
12
+
13
+ ## Usage
14
+
15
+ This workflow is typically invoked automatically by the `roast init` command, but can also be run directly:
16
+
17
+ ```bash
18
+ # Run the generator workflow
19
+ roast execute examples/workflow_generator/workflow.yml
20
+ ```
21
+
22
+ ## Generated Output
23
+
24
+ The workflow creates a new directory with:
25
+ - `workflow.yml` - Main workflow configuration
26
+ - `step_name/prompt.md` - Individual step prompts
27
+ - `README.md` - Documentation for the generated workflow
@@ -0,0 +1,34 @@
1
+ You are an assistant that analyzes user requests to understand what kind of workflow they want to create.
2
+
3
+ Based on the user input from the previous step:
4
+ <%= workflow.output["get_user_input"] %>
5
+
6
+ Roast information from previous step:
7
+ <%= workflow.output["info_from_roast"] %>
8
+
9
+ First, explore existing workflow examples in the examples directory to understand common patterns and structures. Look for ones that may be related to the user's intention.
10
+
11
+ Then analyze the user's request and determine:
12
+
13
+ 1. **Required Steps**: Break down the workflow into logical steps. Each step should be a discrete task that can be accomplished with an AI prompt.
14
+
15
+ 2. **Tools Needed**: What Roast tools will be needed? Base this on the actual tools you read from info provided above.
16
+
17
+ 3. **Target Strategy**: Will this workflow:
18
+ - Process specific files (needs target configuration)
19
+ - Be targetless (works without specific input files)
20
+ - Use shell commands to find targets dynamically
21
+
22
+ 4. **Model Requirements**: Should this use a specific model, or is the default (gpt-4o-mini) sufficient?
23
+
24
+ Respond with a structured analysis in this format:
25
+
26
+ ```
27
+ STEPS: [list of 3-5 logical steps]
28
+ TOOLS: [list of required tools]
29
+ TARGET_STRATEGY: [targetless/files/dynamic]
30
+ MODEL: [model recommendation]
31
+ COMPLEXITY: [simple/moderate/complex]
32
+ ```
33
+
34
+ Be specific and actionable in your analysis.
@@ -0,0 +1,32 @@
1
+ You are an assistant that creates the actual workflow files in the filesystem.
2
+
3
+ Based on the user input:
4
+ <%= workflow.output["get_user_input"] %>
5
+
6
+ And the generated workflow structure from the previous step:
7
+ <%= workflow.output["analyze_user_request"] %>
8
+
9
+ And info from roast:
10
+ <%= workflow.output["info_from_roast"] %>
11
+
12
+ Your task is to create all the necessary files and directories for the workflow.
13
+
14
+ Extract the workflow name from the user input JSON and create the workflow in the current directory under that folder name.
15
+
16
+ Steps to complete:
17
+
18
+ 1. **Create the main directory**: Use Cmd to create the "{{ workflow_name }}" directory
19
+ 2. **Create step directories**: Create subdirectories for each workflow step
20
+ 3. **Create workflow.yml**: Write the main workflow configuration file
21
+ 4. **Create step prompt files**: Write each step's prompt.md file
22
+ 5. **Create README.md**: Generate a helpful README explaining the workflow
23
+
24
+ When writing files, extract the content from the structured response and write each file separately.
25
+
26
+ Important notes:
27
+ - Make sure all directories exist before writing files to them
28
+ - Follow the exact structure specified in the previous step
29
+ - Include helpful comments in the workflow.yml file
30
+ - Make the README.md informative and include usage instructions
31
+
32
+ At the end, confirm that all files have been created by listing the directory structure.
@@ -0,0 +1,14 @@
1
+ You need to collect user input for generating a new workflow.
2
+
3
+ Step 1: Ask for the workflow description using ask_user tool with prompt: "What should your workflow do?"
4
+
5
+ Step 2: Ask for the workflow name using ask_user tool with prompt: "Enter workflow directory name:"
6
+
7
+ Step 3: Return the result in JSON format:
8
+
9
+ <json>
10
+ {
11
+ "user_description": "what the user wants the workflow to do",
12
+ "workflow_name": "directory_name_provided_by_user"
13
+ }
14
+ </json>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class InfoFromRoast < Roast::Workflow::BaseStep
4
+ def call
5
+ examples_path = File.join(Roast::ROOT, "examples")
6
+ tools_path = File.join(Roast::ROOT, "lib", "roast", "tools")
7
+
8
+ # Get list of available tools
9
+ available_tools = Dir.entries(tools_path)
10
+ .select { |file| file.end_with?(".rb") }
11
+ .map { |file| file.gsub(".rb", "") }
12
+ .reject { |tool| tool == "." || tool == ".." }
13
+ .sort
14
+
15
+ {
16
+ examples_directory: examples_path,
17
+ tools_directory: tools_path,
18
+ available_tools: available_tools,
19
+ message: "Examples directory and available tools provided for analysis step",
20
+ }
21
+ end
22
+ end