roast-ai 0.4.9 → 0.5.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/.claude/commands/docs/write-comments.md +36 -0
- data/.github/CODEOWNERS +1 -1
- data/.github/workflows/ci.yaml +10 -6
- data/.gitignore +0 -1
- data/.rubocop.yml +7 -1
- data/CLAUDE.md +2 -2
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +18 -18
- data/Gemfile.lock +46 -57
- data/README.md +118 -1432
- data/README_LEGACY.md +1464 -0
- data/Rakefile +39 -4
- data/dev.yml +29 -0
- data/dsl/agent_sessions.rb +20 -0
- data/dsl/async_cogs.rb +49 -0
- data/dsl/async_cogs_complex.rb +67 -0
- data/dsl/call.rb +44 -0
- data/dsl/collect_from.rb +72 -0
- data/dsl/demo/Gemfile +4 -0
- data/dsl/demo/Gemfile.lock +120 -0
- data/dsl/demo/cogs/local.rb +15 -0
- data/dsl/demo/simple_external_cog.rb +17 -0
- data/dsl/json_output.rb +28 -0
- data/dsl/map.rb +55 -0
- data/dsl/map_reduce.rb +37 -0
- data/dsl/map_with_index.rb +49 -0
- data/dsl/next_break.rb +40 -0
- data/dsl/next_break_parallel.rb +44 -0
- data/dsl/outputs.rb +39 -0
- data/dsl/outputs_bang.rb +36 -0
- data/dsl/parallel_map.rb +37 -0
- data/dsl/plugin-gem-example/.gitignore +8 -0
- data/dsl/plugin-gem-example/Gemfile +13 -0
- data/dsl/plugin-gem-example/Gemfile.lock +178 -0
- data/dsl/plugin-gem-example/lib/other.rb +17 -0
- data/dsl/plugin-gem-example/lib/plugin_gem_example.rb +5 -0
- data/dsl/plugin-gem-example/lib/simple.rb +15 -0
- data/dsl/plugin-gem-example/lib/version.rb +10 -0
- data/dsl/plugin-gem-example/plugin-gem-example.gemspec +28 -0
- data/dsl/prompts/simple_prompt.md.erb +3 -0
- data/dsl/prototype.rb +10 -4
- data/dsl/repeat_loop_results.rb +53 -0
- data/dsl/ruby_cog.rb +72 -0
- data/dsl/simple_agent.rb +18 -0
- data/dsl/simple_chat.rb +26 -0
- data/dsl/simple_repeat.rb +29 -0
- data/dsl/skip.rb +36 -0
- data/dsl/step_communication.rb +10 -5
- data/dsl/targets_and_params.rb +57 -0
- data/dsl/temperature.rb +17 -0
- data/dsl/temporary_directory.rb +22 -0
- data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
- data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
- data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
- data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
- data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
- data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
- data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
- data/dsl/tutorial/03_targets_and_params/README.md +230 -0
- data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
- data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
- data/dsl/tutorial/04_configuration_options/README.md +209 -0
- data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
- data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
- data/dsl/tutorial/05_control_flow/README.md +156 -0
- data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
- data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
- data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
- data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
- data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
- data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
- data/dsl/tutorial/07_processing_collections/README.md +152 -0
- data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
- data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
- data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
- data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
- data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
- data/dsl/tutorial/09_async_cogs/README.md +197 -0
- data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
- data/dsl/tutorial/README.md +222 -0
- data/dsl/working_directory.rb +16 -0
- data/exe/roast +1 -1
- data/internal/documentation/architectural-notes.md +115 -0
- data/internal/documentation/doc-comments-external.md +686 -0
- data/internal/documentation/doc-comments-internal.md +342 -0
- data/internal/documentation/doc-comments.md +211 -0
- data/lib/roast/dsl/cog/config.rb +280 -4
- data/lib/roast/dsl/cog/input.rb +73 -0
- data/lib/roast/dsl/cog/output.rb +313 -0
- data/lib/roast/dsl/cog/registry.rb +71 -0
- data/lib/roast/dsl/cog/stack.rb +3 -2
- data/lib/roast/dsl/cog/store.rb +11 -8
- data/lib/roast/dsl/cog.rb +108 -31
- data/lib/roast/dsl/cog_input_context.rb +44 -0
- data/lib/roast/dsl/cog_input_manager.rb +156 -0
- data/lib/roast/dsl/cogs/agent/config.rb +465 -0
- data/lib/roast/dsl/cogs/agent/input.rb +81 -0
- data/lib/roast/dsl/cogs/agent/output.rb +59 -0
- data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
- data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
- data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
- data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
- data/lib/roast/dsl/cogs/agent.rb +75 -0
- data/lib/roast/dsl/cogs/chat/config.rb +453 -0
- data/lib/roast/dsl/cogs/chat/input.rb +92 -0
- data/lib/roast/dsl/cogs/chat/output.rb +64 -0
- data/lib/roast/dsl/cogs/chat/session.rb +68 -0
- data/lib/roast/dsl/cogs/chat.rb +81 -0
- data/lib/roast/dsl/cogs/cmd.rb +291 -27
- data/lib/roast/dsl/cogs/ruby.rb +171 -0
- data/lib/roast/dsl/command_runner.rb +191 -0
- data/lib/roast/dsl/config_context.rb +2 -47
- data/lib/roast/dsl/config_manager.rb +143 -0
- data/lib/roast/dsl/control_flow.rb +41 -0
- data/lib/roast/dsl/execution_context.rb +9 -0
- data/lib/roast/dsl/execution_manager.rb +267 -0
- data/lib/roast/dsl/nil_assertions.rb +23 -0
- data/lib/roast/dsl/system_cog/params.rb +32 -0
- data/lib/roast/dsl/system_cog.rb +36 -0
- data/lib/roast/dsl/system_cogs/call.rb +162 -0
- data/lib/roast/dsl/system_cogs/map.rb +448 -0
- data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
- data/lib/roast/dsl/workflow.rb +123 -0
- data/lib/roast/dsl/workflow_context.rb +20 -0
- data/lib/roast/dsl/workflow_params.rb +24 -0
- data/lib/roast/sorbet_runtime_stub.rb +154 -0
- data/lib/roast/tools/apply_diff.rb +1 -3
- data/lib/roast/tools/cmd.rb +4 -3
- data/lib/roast/tools/read_file.rb +1 -1
- data/lib/roast/tools/update_files.rb +1 -1
- data/lib/roast/tools/write_file.rb +1 -1
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_workflow.rb +4 -0
- data/lib/roast/workflow/step_loader.rb +14 -2
- data/lib/roast-ai.rb +4 -0
- data/lib/roast.rb +60 -22
- data/{roast.gemspec → roast-ai.gemspec} +10 -13
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
- data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
- data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
- data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
- data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
- data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
- data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
- data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
- data/sorbet/rbi/gems/marcel@1.1.0.rbi +239 -0
- data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
- data/sorbet/rbi/gems/ruby_llm@1.8.2.rbi +5703 -0
- data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
- data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1197 -0
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +314 -2
- data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +498 -0
- data/sorbet/tapioca/config.yml +6 -0
- data/sorbet/tapioca/require.rb +2 -0
- metadata +198 -34
- data/dsl/less_simple.rb +0 -112
- data/dsl/simple.rb +0 -8
- data/lib/roast/dsl/cog_execution_context.rb +0 -29
- data/lib/roast/dsl/cogs/graph.rb +0 -53
- data/lib/roast/dsl/cogs.rb +0 -65
- data/lib/roast/dsl/executor.rb +0 -82
- data/lib/roast/dsl/workflow_execution_context.rb +0 -47
- data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
- data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
- data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
- data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
- data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
- data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
- data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
- data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
- data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
- data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
- data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
- data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
- data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
- data/sorbet/rbi/gems/rack@2.2.18.rbi +0 -5659
- data/sorbet/rbi/gems/rbs-inline@0.12.0.rbi +0 -2170
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
- data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
- data/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi +0 -11
data/README_LEGACY.md
ADDED
|
@@ -0,0 +1,1464 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# Roast
|
|
4
|
+
|
|
5
|
+
> **📢 Code Freeze Notice**: This project is currently under a limited code freeze while we work on an alternative frontend (a DSL). We are still accepting **bug fixes**, but **new features** will not be merged until the DSL is released. Thank you for your understanding!
|
|
6
|
+
|
|
7
|
+
A convention-oriented framework for creating structured AI workflows, maintained by the Augmented Engineering team at Shopify.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
$ gem install roast-ai
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'roast-ai'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Why you should use Roast
|
|
22
|
+
|
|
23
|
+
Roast provides a structured, declarative approach to building AI workflows with:
|
|
24
|
+
|
|
25
|
+
- **Convention over configuration**: Define powerful workflows using simple YAML configuration files and prompts written in markdown (with ERB support)
|
|
26
|
+
- **Built-in tools**: Ready-to-use tools for file operations, search, and AI interactions
|
|
27
|
+
- **Ruby integration**: When prompts aren't enough, write custom steps in Ruby using a clean, extensible architecture
|
|
28
|
+
- **Shared context**: Each step shares its conversation transcript with its parent workflow by default
|
|
29
|
+
- **Step customization**: Steps can be fully configured with their own AI models and parameters.
|
|
30
|
+
- **Session replay**: Rerun previous sessions starting at a specified step to speed up development time
|
|
31
|
+
- **Parallel execution**: Run multiple steps concurrently to speed up workflow execution
|
|
32
|
+
- **Function caching**: Flexibly cache the results of tool function calls to speed up workflows
|
|
33
|
+
- **Extensive instrumentation**: Monitor and track workflow execution, AI calls, and tool usage ([see instrumentation documentation](docs/INSTRUMENTATION.md))
|
|
34
|
+
|
|
35
|
+
## What does it look like?
|
|
36
|
+
|
|
37
|
+
Here's a simple workflow that analyzes test files:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
name: analyze_tests
|
|
41
|
+
# Default model for all steps
|
|
42
|
+
model: gpt-4o-mini
|
|
43
|
+
tools:
|
|
44
|
+
- Roast::Tools::ReadFile
|
|
45
|
+
- Roast::Tools::Grep
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- read_test_file
|
|
49
|
+
- analyze_coverage
|
|
50
|
+
- generate_report
|
|
51
|
+
|
|
52
|
+
# Step-specific model overrides the global model
|
|
53
|
+
analyze_coverage:
|
|
54
|
+
model: gpt-4-turbo
|
|
55
|
+
json: true
|
|
56
|
+
|
|
57
|
+
# Step-specific config that specifies a custom path, not in the current directory
|
|
58
|
+
generate_report:
|
|
59
|
+
path: ../reporting/generate_report
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Each step can have its own prompt file (e.g., `analyze_coverage/prompt.md`) and configuration. Steps can be run in parallel by nesting them in arrays:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
steps:
|
|
66
|
+
- prepare_data
|
|
67
|
+
-
|
|
68
|
+
- analyze_code_quality
|
|
69
|
+
- check_test_coverage
|
|
70
|
+
- verify_documentation
|
|
71
|
+
- generate_final_report
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Workflows can include steps that run bash commands (wrap in `$()`), use interpolation with `{{}}` syntax, and even simple inlined prompts as a natural language string.
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
steps:
|
|
78
|
+
- analyze_spec
|
|
79
|
+
- create_minitest
|
|
80
|
+
- run_and_improve
|
|
81
|
+
- $(bundle exec rubocop -A {{file}})
|
|
82
|
+
- Summarize the changes made to {{File.basename(file)}}.
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Try it
|
|
86
|
+
|
|
87
|
+
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 and credits applied to the associated project. Make sure that a basic completion works:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
export OPENAI_API_KEY=sk-proj-....
|
|
91
|
+
|
|
92
|
+
curl -H "Content-Type: application/json" \
|
|
93
|
+
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
|
94
|
+
-d '{"model":"gpt-4.1-mini","messages":[{"role":"user","content":"What is 1+1?"}]}' \
|
|
95
|
+
https://api.openai.com/v1/chat/completions
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
Try the workflow.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
./exe/roast execute examples/grading/workflow.yml test/roast/resources_test.rb
|
|
104
|
+
|
|
105
|
+
🔥🔥🔥 Everyone loves a good roast 🔥🔥🔥
|
|
106
|
+
...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This will output a test grade.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
========== TEST GRADE REPORT ==========
|
|
113
|
+
Test file: test/roast/resources_test.rb
|
|
114
|
+
|
|
115
|
+
FINAL GRADE:
|
|
116
|
+
Score: 80/100
|
|
117
|
+
Letter Grade: B
|
|
118
|
+
```
|
|
119
|
+
Note that you may also need `shadowenv` and `rg`, on MacOS run `brew install shadowenv` and `brew install rg`.
|
|
120
|
+
|
|
121
|
+
## How to use Roast
|
|
122
|
+
|
|
123
|
+
1. Create a workflow YAML file defining your steps and tools
|
|
124
|
+
2. Create prompt files for each step (e.g., `step_name/prompt.md`)
|
|
125
|
+
3. Run the workflow:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# With a target file
|
|
129
|
+
roast execute workflow.yml target_file.rb
|
|
130
|
+
|
|
131
|
+
# Or for a targetless workflow (API calls, data generation, etc.)
|
|
132
|
+
roast execute workflow.yml
|
|
133
|
+
|
|
134
|
+
# Roast will automatically search in `project_root/roast/workflow_name` if the path is incomplete.
|
|
135
|
+
roast execute my_cool_workflow # Equivalent to `roast execute roast/my_cool_workflow/workflow.yml
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Understanding Workflows
|
|
139
|
+
|
|
140
|
+
In Roast, workflows maintain a single conversation with the AI model throughout execution. Each step represents one or more user-assistant interactions within this conversation, with optional tool calls. Steps naturally build upon each other through the shared context.
|
|
141
|
+
|
|
142
|
+
#### Step Types
|
|
143
|
+
|
|
144
|
+
Roast supports several types of steps:
|
|
145
|
+
|
|
146
|
+
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.
|
|
147
|
+
```yaml
|
|
148
|
+
steps:
|
|
149
|
+
- analyze_code
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
As an alternative to a directory, you can also implement a custom step as a Ruby class, optionally extending `Roast::Workflow::BaseStep`.
|
|
153
|
+
|
|
154
|
+
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.
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
2. **Parallel steps**: Groups of steps executed concurrently
|
|
158
|
+
```yaml
|
|
159
|
+
steps:
|
|
160
|
+
-
|
|
161
|
+
- analyze_code_quality
|
|
162
|
+
- check_test_coverage
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
3. **Command execution step**: Executes shell commands directly, just wrap in `$(expr)`
|
|
166
|
+
```yaml
|
|
167
|
+
steps:
|
|
168
|
+
- $(command line expr)
|
|
169
|
+
- rubocop: $(bundle exec rubocop -A)
|
|
170
|
+
```
|
|
171
|
+
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).
|
|
172
|
+
|
|
173
|
+
By default, commands that exit with non-zero status will halt the workflow. You can configure steps to continue on error:
|
|
174
|
+
```yaml
|
|
175
|
+
steps:
|
|
176
|
+
- lint_check: $(rubocop {{file}})
|
|
177
|
+
- fix_issues
|
|
178
|
+
|
|
179
|
+
# Step configuration
|
|
180
|
+
lint_check:
|
|
181
|
+
exit_on_error: false # Continue workflow even if command fails
|
|
182
|
+
```
|
|
183
|
+
When `exit_on_error: false`, the command output will include the exit status, allowing subsequent steps to process error information.
|
|
184
|
+
|
|
185
|
+
4. **Conditional steps**: Execute different steps based on conditions using `if/unless`
|
|
186
|
+
```yaml
|
|
187
|
+
steps:
|
|
188
|
+
- check_environment:
|
|
189
|
+
if: "{{ENV['RAILS_ENV'] == 'production'}}"
|
|
190
|
+
then:
|
|
191
|
+
- run_production_checks
|
|
192
|
+
- notify_team
|
|
193
|
+
else:
|
|
194
|
+
- run_development_setup
|
|
195
|
+
|
|
196
|
+
- verify_dependencies:
|
|
197
|
+
unless: "$(bundle check)"
|
|
198
|
+
then:
|
|
199
|
+
- bundle_install: "$(bundle install)"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Conditions can be:
|
|
203
|
+
- Ruby expressions: `if: "{{output['count'] > 5}}"`
|
|
204
|
+
- Bash commands: `if: "$(test -f config.yml && echo true)"` (exit code 0 = true)
|
|
205
|
+
- Step references: `if: "previous_step_name"` (uses the step's output)
|
|
206
|
+
- Direct values: `if: "true"` or `if: "false"`
|
|
207
|
+
|
|
208
|
+
5. **Iteration steps**: Loop over collections or repeat steps with conditions
|
|
209
|
+
```yaml
|
|
210
|
+
steps:
|
|
211
|
+
# Loop over a collection
|
|
212
|
+
- process_files:
|
|
213
|
+
each: "{{Dir.glob('**/*.rb')}}"
|
|
214
|
+
as: current_file
|
|
215
|
+
steps:
|
|
216
|
+
- analyze_file
|
|
217
|
+
- Generate a report for {{current_file}}
|
|
218
|
+
|
|
219
|
+
# Repeat until a condition is met
|
|
220
|
+
- improve_code:
|
|
221
|
+
repeat:
|
|
222
|
+
until: "{{output['test_pass'] == true}}"
|
|
223
|
+
max_iterations: 5
|
|
224
|
+
steps:
|
|
225
|
+
- run_tests
|
|
226
|
+
- fix_issues
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Each loops support:
|
|
230
|
+
- Collections from Ruby expressions: `each: "{{[1, 2, 3]}}"`
|
|
231
|
+
- Command output: `each: "$(ls *.rb)"`
|
|
232
|
+
- Step references: `each: "file_list"`
|
|
233
|
+
|
|
234
|
+
Repeat loops support:
|
|
235
|
+
- Until conditions: `until: "{{condition}}"`
|
|
236
|
+
- Maximum iterations: `max_iterations: 10`
|
|
237
|
+
|
|
238
|
+
6. **Case/when/else steps**: Select different execution paths based on a value (similar to Ruby's case statement)
|
|
239
|
+
```yaml
|
|
240
|
+
steps:
|
|
241
|
+
- detect_language
|
|
242
|
+
|
|
243
|
+
- case: "{{ workflow.output.detect_language }}"
|
|
244
|
+
when:
|
|
245
|
+
ruby:
|
|
246
|
+
- lint_with_rubocop
|
|
247
|
+
- test_with_rspec
|
|
248
|
+
javascript:
|
|
249
|
+
- lint_with_eslint
|
|
250
|
+
- test_with_jest
|
|
251
|
+
python:
|
|
252
|
+
- lint_with_pylint
|
|
253
|
+
- test_with_pytest
|
|
254
|
+
else:
|
|
255
|
+
- analyze_generic
|
|
256
|
+
- generate_basic_report
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Case expressions can be:
|
|
260
|
+
- Workflow outputs: `case: "{{ workflow.output.variable }}"`
|
|
261
|
+
- Ruby expressions: `case: "{{ count > 10 ? 'high' : 'low' }}"`
|
|
262
|
+
- Bash commands: `case: "$(echo $ENVIRONMENT)"`
|
|
263
|
+
- Direct values: `case: "production"`
|
|
264
|
+
|
|
265
|
+
The value is compared against each key in the `when` clause, and matching steps are executed.
|
|
266
|
+
If no match is found, the `else` steps are executed (if provided).
|
|
267
|
+
|
|
268
|
+
7. **Raw prompt step**: Simple text prompts for the model without tools
|
|
269
|
+
```yaml
|
|
270
|
+
steps:
|
|
271
|
+
- Summarize the changes made to the codebase.
|
|
272
|
+
```
|
|
273
|
+
This creates a simple prompt-response interaction without tool calls or looping. It's detected by the presence of spaces in the step name and is useful for summarization or simple questions at the end of a workflow.
|
|
274
|
+
|
|
275
|
+
8. **Agent step**: Direct pass-through to coding agents (e.g., Claude Code)
|
|
276
|
+
```yaml
|
|
277
|
+
steps:
|
|
278
|
+
- ^fix_linting_errors # File-based agent prompt
|
|
279
|
+
- ^Review the code and identify any performance issues # Inline agent prompt
|
|
280
|
+
- regular_analysis # Normal step through LLM
|
|
281
|
+
```
|
|
282
|
+
Agent steps are prefixed with `^` and send the prompt content directly to the CodingAgent tool without LLM translation. This is useful when you want to give precise instructions to a coding agent without the intermediate interpretation layer. Agent steps support both file-based prompts (`fix_linting_errors/prompt.md`) and inline prompts (text with spaces).
|
|
283
|
+
|
|
284
|
+
**Session continuity for agent steps:**
|
|
285
|
+
|
|
286
|
+
Agent steps support two options for maintaining Claude context across steps:
|
|
287
|
+
|
|
288
|
+
1. **`continue: true`** - Continues from the immediately previous Claude Code session (note, if multiple Claude Code sessions are being run in parallel in the same working directory, this might not be the previous Claude Code session from this workflow)
|
|
289
|
+
2. **`resume: step_name`** - Resumes from a specific earlier step's Claude Code session
|
|
290
|
+
|
|
291
|
+
**Continue option:**
|
|
292
|
+
|
|
293
|
+
The `continue` option allows sequential agent steps to maintain a continuous conversation:
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
steps:
|
|
297
|
+
- ^analyze_codebase
|
|
298
|
+
- ^implement_feature
|
|
299
|
+
- ^add_tests
|
|
300
|
+
|
|
301
|
+
# Configuration
|
|
302
|
+
analyze_codebase:
|
|
303
|
+
continue: false # Start fresh (default)
|
|
304
|
+
|
|
305
|
+
implement_feature:
|
|
306
|
+
continue: true # Continue from immediately previous analyze_codebase step
|
|
307
|
+
|
|
308
|
+
add_tests:
|
|
309
|
+
continue: true # Continue from immediately previous implement_feature step
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Resume functionality for agent steps:**
|
|
313
|
+
|
|
314
|
+
Agent steps can resume from specific previous Claude Code sessions:
|
|
315
|
+
|
|
316
|
+
```yaml
|
|
317
|
+
steps:
|
|
318
|
+
- ^analyze_codebase
|
|
319
|
+
- ^implement_feature
|
|
320
|
+
- ^polish_implementation
|
|
321
|
+
|
|
322
|
+
# Configuration
|
|
323
|
+
analyze_codebase:
|
|
324
|
+
continue: false # Start fresh
|
|
325
|
+
|
|
326
|
+
implement_feature:
|
|
327
|
+
continue: true # Continue from previous conversation
|
|
328
|
+
|
|
329
|
+
polish_implementation:
|
|
330
|
+
resume: analyze_codebase # Resume from a specific step's session not the immediately previous one
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Note: Session IDs are only available when the CodingAgent is configured to output JSON format (includes `--output-format stream-json` in the command). If you are using a custom CodingAgent command that does not produce JSON output, resume functionality will not be available.
|
|
334
|
+
|
|
335
|
+
If `resume` is specified but the step name given does not have CodingAgent session to resume from, the CodingAgent will start Claude Code with a fresh session.
|
|
336
|
+
|
|
337
|
+
9. **Shell script step**: Execute shell scripts directly as workflow steps
|
|
338
|
+
```yaml
|
|
339
|
+
steps:
|
|
340
|
+
- setup_environment # Executes setup_environment.sh
|
|
341
|
+
- run_tests # Executes run_tests.sh
|
|
342
|
+
- cleanup
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Shell script steps allow you to execute `.sh` files directly as workflow steps alongside Ruby steps and AI prompts. Scripts are automatically discovered in the same locations as other step types.
|
|
346
|
+
|
|
347
|
+
**Configuration options:**
|
|
348
|
+
```yaml
|
|
349
|
+
# Step configuration
|
|
350
|
+
my_script:
|
|
351
|
+
json: true # Parse stdout as JSON
|
|
352
|
+
exit_on_error: false # Don't fail workflow on non-zero exit
|
|
353
|
+
env: # Custom environment variables
|
|
354
|
+
CUSTOM_VAR: "value"
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Environment integration:** Shell scripts automatically receive workflow context:
|
|
358
|
+
- `ROAST_WORKFLOW_RESOURCE`: Current workflow resource
|
|
359
|
+
- `ROAST_STEP_NAME`: Current step name
|
|
360
|
+
- `ROAST_WORKFLOW_OUTPUT`: Previous step outputs as JSON
|
|
361
|
+
|
|
362
|
+
**Example script (`setup_environment.sh`):**
|
|
363
|
+
```bash
|
|
364
|
+
#!/bin/bash
|
|
365
|
+
echo "Setting up environment for: $ROAST_WORKFLOW_RESOURCE"
|
|
366
|
+
|
|
367
|
+
# Create a config file that subsequent steps can use
|
|
368
|
+
mkdir -p tmp
|
|
369
|
+
echo "DATABASE_URL=sqlite://test.db" > tmp/config.env
|
|
370
|
+
|
|
371
|
+
# Output data for the workflow (available via ROAST_WORKFLOW_OUTPUT in later steps)
|
|
372
|
+
echo '{"status": "configured", "database": "sqlite://test.db", "config_file": "tmp/config.env"}'
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
10. **Input step**: Interactive prompts for user input during workflow execution
|
|
376
|
+
```yaml
|
|
377
|
+
steps:
|
|
378
|
+
- analyze_code
|
|
379
|
+
- input:
|
|
380
|
+
name: get_user_feedback
|
|
381
|
+
prompt: "Should we proceed with the refactoring? (yes/no)"
|
|
382
|
+
type: confirm
|
|
383
|
+
- input:
|
|
384
|
+
name: review_changes
|
|
385
|
+
prompt: "Enter your review comments"
|
|
386
|
+
type: text
|
|
387
|
+
- input:
|
|
388
|
+
name: select_strategy
|
|
389
|
+
prompt: "Choose optimization strategy"
|
|
390
|
+
type: choice
|
|
391
|
+
options:
|
|
392
|
+
- "Performance optimization"
|
|
393
|
+
- "Memory optimization"
|
|
394
|
+
- "Code clarity"
|
|
395
|
+
- input:
|
|
396
|
+
name: api_configuration
|
|
397
|
+
prompt: "Enter API key"
|
|
398
|
+
type: password
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Input steps pause workflow execution to collect user input. They support several types:
|
|
402
|
+
- `text`: Free-form text input (default if type not specified)
|
|
403
|
+
- `confirm`: Yes/No confirmation prompts
|
|
404
|
+
- `select`: Choice from a list of options
|
|
405
|
+
- `password`: Masked input for sensitive data
|
|
406
|
+
|
|
407
|
+
The user's input is stored in the workflow output using the step name as the key and can be accessed in subsequent steps via interpolation (e.g., `{{output.get_user_feedback}}`).
|
|
408
|
+
|
|
409
|
+
#### Step Configuration
|
|
410
|
+
|
|
411
|
+
Steps can be configured with various options to control their behavior:
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
steps:
|
|
415
|
+
- analyze_code # Simple step reference
|
|
416
|
+
- generate_report: # Step with configuration
|
|
417
|
+
model: gpt-4o # Override the global model for this step
|
|
418
|
+
print_response: true # Explicitly control output printing
|
|
419
|
+
json: true # Request JSON-formatted response
|
|
420
|
+
params: # Additional parameters for the API call
|
|
421
|
+
temperature: 0.8
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Configuration options:**
|
|
425
|
+
- `model`: Override the workflow's default model for this specific step
|
|
426
|
+
- `print_response`: Control whether the step's response is included in the final output (default: `false`, except for the last step which defaults to `true` as of v0.3.1)
|
|
427
|
+
- `json`: Request a JSON-formatted response from the model
|
|
428
|
+
- `params`: Additional parameters passed to the model API (temperature, max_tokens, etc.)
|
|
429
|
+
- `path`: Custom directory path for the step's prompt files
|
|
430
|
+
- `coerce_to`: Type coercion for the step result (`:boolean`, `:llm_boolean`, `:iterable`)
|
|
431
|
+
|
|
432
|
+
**Automatic Last Step Output**: As of version 0.3.1, the last step in a workflow automatically has `print_response: true` unless explicitly configured otherwise. This ensures that newcomers to Roast see output from their workflows by default.
|
|
433
|
+
|
|
434
|
+
#### Shared Configuration
|
|
435
|
+
|
|
436
|
+
Roast supports sharing common configuration and steps across multiple workflows using a `shared.yml` file.
|
|
437
|
+
|
|
438
|
+
1. Place a `shared.yml` file one level above your workflow directory
|
|
439
|
+
2. Define YAML anchors for common configurations like tools, models or steps
|
|
440
|
+
3. Reference these anchors in your workflow files using YAML alias syntax
|
|
441
|
+
|
|
442
|
+
**Example structure:**
|
|
443
|
+
```
|
|
444
|
+
my_project/
|
|
445
|
+
├── shared.yml # Common configuration anchors
|
|
446
|
+
└── workflows/
|
|
447
|
+
├── analyze_code.yml
|
|
448
|
+
├── generate_docs.yml
|
|
449
|
+
└── test_suite.yml
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**Example `shared.yml`:**
|
|
453
|
+
```yaml
|
|
454
|
+
# Define common tools
|
|
455
|
+
standard_tools: &standard_tools
|
|
456
|
+
- Roast::Tools::Grep
|
|
457
|
+
- Roast::Tools::ReadFile
|
|
458
|
+
- Roast::Tools::WriteFile
|
|
459
|
+
- Roast::Tools::SearchFile
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Using in workflows:**
|
|
463
|
+
```yaml
|
|
464
|
+
name: Code Analysis Workflow
|
|
465
|
+
tools: *standard_tools # Reference shared tools
|
|
466
|
+
|
|
467
|
+
steps:
|
|
468
|
+
...
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
#### Data Flow Between Steps
|
|
472
|
+
|
|
473
|
+
Roast handles data flow between steps in three primary ways:
|
|
474
|
+
|
|
475
|
+
1. **Conversation Context (Implicit)**: The LLM naturally remembers the entire conversation history, including all previous prompts and responses. In most cases, this is all you need for a step to reference and build upon previous results. This is the preferred approach for most prompt-oriented workflows.
|
|
476
|
+
|
|
477
|
+
2. **Output Hash (Explicit)**: Each step's result is automatically stored in the `workflow.output` hash using the step name as the key. This programmatic access is mainly useful when:
|
|
478
|
+
- You need to perform non-LLM transformations on data
|
|
479
|
+
- You're writing custom output logic
|
|
480
|
+
- You need to access specific values for presentation or logging
|
|
481
|
+
|
|
482
|
+
3. **Interpolation (Dynamic)**: You can use `{{expression}}` syntax to inject values from the workflow context directly into step names, commands, or prompt text. For example:
|
|
483
|
+
```yaml
|
|
484
|
+
steps:
|
|
485
|
+
- analyze_file
|
|
486
|
+
- $(rubocop -A {{file}})
|
|
487
|
+
- Generate a summary for {{file}}
|
|
488
|
+
- result_for_{{file}}: store_results
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Interpolation supports:
|
|
492
|
+
- Simple variable access: `{{file}}`, `{{resource.target}}`
|
|
493
|
+
- Access to step outputs: `{{output['previous_step']}}`
|
|
494
|
+
- Any valid Ruby expression evaluated in the workflow context: `{{File.basename(file)}}`
|
|
495
|
+
|
|
496
|
+
For typical AI workflows, the continuous conversation history provides seamless data flow without requiring explicit access to the output hash. Steps can simply refer to previous information in their prompts, and the AI model will use its memory of the conversation to provide context-aware responses. For more dynamic requirements, the interpolation syntax provides a convenient way to inject context-specific values into steps.
|
|
497
|
+
|
|
498
|
+
### Command Line Options
|
|
499
|
+
|
|
500
|
+
#### Basic Options
|
|
501
|
+
- `-o, --output FILE`: Save results to a file instead of outputting to STDOUT
|
|
502
|
+
- `-c, --concise`: Use concise output templates (exposed as a boolean flag on `workflow`)
|
|
503
|
+
- `-v, --verbose`: Show output from all steps as they execute
|
|
504
|
+
- `-r, --replay STEP_NAME`: Resume a workflow from a specific step, optionally with a specific session timestamp
|
|
505
|
+
- `-f, --file-storage`: Use filesystem storage for sessions instead of SQLite (default: SQLite)
|
|
506
|
+
|
|
507
|
+
#### Workflow Validation
|
|
508
|
+
|
|
509
|
+
Roast provides a `validate` command to check workflow configuration files for errors and potential issues before execution:
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
# Validate a specific workflow
|
|
513
|
+
roast validate workflow.yml
|
|
514
|
+
|
|
515
|
+
# Validate a workflow in a subdirectory
|
|
516
|
+
roast validate my_workflow
|
|
517
|
+
|
|
518
|
+
# Validate with strict mode (treats warnings as errors)
|
|
519
|
+
roast validate workflow.yml --strict
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
The validator checks for:
|
|
523
|
+
- YAML syntax errors
|
|
524
|
+
- Missing required fields
|
|
525
|
+
- Invalid step references
|
|
526
|
+
- Circular dependencies
|
|
527
|
+
- Tool availability
|
|
528
|
+
- Prompt file existence
|
|
529
|
+
- Configuration consistency
|
|
530
|
+
|
|
531
|
+
This helps catch configuration errors early and ensures workflows will run smoothly.
|
|
532
|
+
|
|
533
|
+
#### Session Storage and Management
|
|
534
|
+
|
|
535
|
+
Roast uses SQLite by default for session storage, providing better performance and advanced querying capabilities. Sessions are automatically saved during workflow execution, capturing each step's state including conversation transcripts and outputs.
|
|
536
|
+
|
|
537
|
+
**Storage Options:**
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# Use default SQLite storage (recommended)
|
|
541
|
+
roast execute workflow.yml
|
|
542
|
+
|
|
543
|
+
# Use legacy filesystem storage
|
|
544
|
+
roast execute workflow.yml --file-storage
|
|
545
|
+
|
|
546
|
+
# Set storage type via environment variable
|
|
547
|
+
ROAST_STATE_STORAGE=file roast execute workflow.yml
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**Session Management Commands:**
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
# List all sessions
|
|
554
|
+
roast sessions
|
|
555
|
+
|
|
556
|
+
# Filter sessions by status
|
|
557
|
+
roast sessions --status waiting
|
|
558
|
+
|
|
559
|
+
# Filter sessions by workflow
|
|
560
|
+
roast sessions --workflow my_workflow
|
|
561
|
+
|
|
562
|
+
# Show sessions older than 7 days
|
|
563
|
+
roast sessions --older-than 7d
|
|
564
|
+
|
|
565
|
+
# Clean up old sessions
|
|
566
|
+
roast sessions --cleanup --older-than 30d
|
|
567
|
+
|
|
568
|
+
# View detailed session information
|
|
569
|
+
roast session <session_id>
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### Session Replay
|
|
573
|
+
|
|
574
|
+
The session replay feature allows you to resume workflows from specific steps, saving time during development and debugging:
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
# Resume from a specific step
|
|
578
|
+
roast execute workflow.yml -r step_name
|
|
579
|
+
|
|
580
|
+
# Resume from a specific step in a specific session
|
|
581
|
+
roast execute workflow.yml -r 20250507_123456_789:step_name
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
This feature is particularly useful when:
|
|
585
|
+
- Debugging specific steps in a long workflow
|
|
586
|
+
- Iterating on prompts without rerunning the entire workflow
|
|
587
|
+
- Resuming after failures in long-running workflows
|
|
588
|
+
|
|
589
|
+
**Storage Locations:**
|
|
590
|
+
- SQLite: `~/.roast/sessions.db` (configurable via `ROAST_SESSIONS_DB`)
|
|
591
|
+
- Filesystem: `.roast/sessions/` directory in your project
|
|
592
|
+
|
|
593
|
+
#### Target Option (`-t, --target`)
|
|
594
|
+
|
|
595
|
+
The target option is highly flexible and accepts several formats:
|
|
596
|
+
|
|
597
|
+
**Single file path:**
|
|
598
|
+
```bash
|
|
599
|
+
roast execute workflow.yml -t path/to/file.rb
|
|
600
|
+
|
|
601
|
+
# is equivalent to
|
|
602
|
+
roast execute workflow.yml path/to/file.rb
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**Directory path:**
|
|
606
|
+
```bash
|
|
607
|
+
roast execute workflow.yml -t path/to/directory
|
|
608
|
+
|
|
609
|
+
# Roast will run on the directory as a resource
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**Glob patterns:**
|
|
613
|
+
```bash
|
|
614
|
+
roast execute workflow.yml -t "**/*_test.rb"
|
|
615
|
+
|
|
616
|
+
# Roast will run the workflow on each matching file
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**URL as target:**
|
|
620
|
+
```bash
|
|
621
|
+
roast execute workflow.yml -t "https://api.example.com/data"
|
|
622
|
+
|
|
623
|
+
# Roast will run the workflow using the URL as a resource
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**API configuration (Fetch API-style):**
|
|
627
|
+
```bash
|
|
628
|
+
roast execute workflow.yml -t '{
|
|
629
|
+
"url": "https://api.example.com/resource",
|
|
630
|
+
"options": {
|
|
631
|
+
"method": "POST",
|
|
632
|
+
"headers": {
|
|
633
|
+
"Content-Type": "application/json",
|
|
634
|
+
"Authorization": "Bearer ${API_TOKEN}"
|
|
635
|
+
},
|
|
636
|
+
"body": {
|
|
637
|
+
"query": "search term",
|
|
638
|
+
"limit": 10
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}'
|
|
642
|
+
|
|
643
|
+
# Roast will recognize this as an API configuration with Fetch API-style format
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
**Shell command execution with $(...):**
|
|
647
|
+
```bash
|
|
648
|
+
roast execute workflow.yml -t "$(find . -name '*.rb' -mtime -1)"
|
|
649
|
+
|
|
650
|
+
# Roast will run the workflow on each file returned (expects one per line)
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
**Git integration examples:**
|
|
654
|
+
```bash
|
|
655
|
+
# Process changed test files
|
|
656
|
+
roast execute workflow.yml -t "$(git diff --name-only HEAD | grep _test.rb)"
|
|
657
|
+
|
|
658
|
+
# Process staged files
|
|
659
|
+
roast execute workflow.yml -t "$(git diff --cached --name-only)"
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
#### Targetless Workflows
|
|
663
|
+
|
|
664
|
+
Roast also supports workflows that don't operate on a specific pre-defined set of target files:
|
|
665
|
+
|
|
666
|
+
**API-driven workflows:**
|
|
667
|
+
```yaml
|
|
668
|
+
name: API Integration Workflow
|
|
669
|
+
tools:
|
|
670
|
+
- Roast::Tools::ReadFile
|
|
671
|
+
- Roast::Tools::WriteFile
|
|
672
|
+
|
|
673
|
+
# Dynamic API token using shell command
|
|
674
|
+
api_token: $(cat ~/.my_token)
|
|
675
|
+
|
|
676
|
+
# Option 1: Use a targetless workflow with API logic in steps
|
|
677
|
+
steps:
|
|
678
|
+
- fetch_api_data # Step will make API calls
|
|
679
|
+
- transform_data
|
|
680
|
+
- generate_report
|
|
681
|
+
|
|
682
|
+
# Option 2: Specify an API target directly in the workflow
|
|
683
|
+
target: '{
|
|
684
|
+
"url": "https://api.example.com/resource",
|
|
685
|
+
"options": {
|
|
686
|
+
"method": "GET",
|
|
687
|
+
"headers": {
|
|
688
|
+
"Authorization": "Bearer ${API_TOKEN}"
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}'
|
|
692
|
+
|
|
693
|
+
steps:
|
|
694
|
+
- process_api_response
|
|
695
|
+
- generate_report
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
**Data generation workflows:**
|
|
699
|
+
```yaml
|
|
700
|
+
name: Generate Documentation
|
|
701
|
+
tools:
|
|
702
|
+
- Roast::Tools::WriteFile
|
|
703
|
+
steps:
|
|
704
|
+
- generate_outline
|
|
705
|
+
- write_documentation
|
|
706
|
+
- create_examples
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
These targetless workflows are ideal for:
|
|
710
|
+
- API integrations
|
|
711
|
+
- Content generation
|
|
712
|
+
- Report creation
|
|
713
|
+
- Interactive tools
|
|
714
|
+
- Scheduled automation tasks
|
|
715
|
+
|
|
716
|
+
#### Global Model Configuration
|
|
717
|
+
|
|
718
|
+
You can set a default model for all steps in your workflow by specifying the `model` parameter at the top level:
|
|
719
|
+
|
|
720
|
+
```yaml
|
|
721
|
+
name: My Workflow
|
|
722
|
+
model: gpt-4o-mini # Will be used for all steps unless overridden
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Individual steps can override this setting with their own model parameter:
|
|
726
|
+
|
|
727
|
+
```yaml
|
|
728
|
+
analyze_data:
|
|
729
|
+
model: anthropic/claude-3-haiku # Takes precedence over the global model
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
#### API Provider Configuration
|
|
733
|
+
|
|
734
|
+
Roast supports both OpenAI and OpenRouter as API providers. By default, Roast uses OpenAI, but you can specify OpenRouter:
|
|
735
|
+
|
|
736
|
+
```yaml
|
|
737
|
+
name: My Workflow
|
|
738
|
+
api_provider: openrouter
|
|
739
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
|
740
|
+
model: anthropic/claude-3-opus-20240229
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Benefits of using OpenRouter:
|
|
744
|
+
- Access to multiple model providers through a single API
|
|
745
|
+
- Support for models from Anthropic, Meta, Mistral, and more
|
|
746
|
+
- Consistent API interface across different model providers
|
|
747
|
+
|
|
748
|
+
When using OpenRouter, specify fully qualified model names including the provider prefix (e.g., `anthropic/claude-3-opus-20240229`).
|
|
749
|
+
|
|
750
|
+
#### Dynamic API Tokens and URIs
|
|
751
|
+
|
|
752
|
+
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:
|
|
753
|
+
|
|
754
|
+
```yaml
|
|
755
|
+
# This will execute the shell command and use the result as the API token
|
|
756
|
+
api_token: $(print-token --key)
|
|
757
|
+
|
|
758
|
+
# For OpenAI (default)
|
|
759
|
+
api_token: $(echo $OPENAI_API_KEY)
|
|
760
|
+
|
|
761
|
+
# For OpenRouter (requires api_provider setting)
|
|
762
|
+
api_provider: openrouter
|
|
763
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
|
764
|
+
|
|
765
|
+
# Static Proxy URI
|
|
766
|
+
uri_base: https://proxy.example.com/v1
|
|
767
|
+
|
|
768
|
+
# Dynamic Proxy URI
|
|
769
|
+
uri_base: $(echo $AI_PROXY_URI_BASE)
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
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.
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
### Template Output with ERB
|
|
776
|
+
|
|
777
|
+
Each step can have an `output.txt` file that uses ERB templating to format the final output. This allows you to customize how the AI's response is processed and displayed.
|
|
778
|
+
|
|
779
|
+
Example `step_name/output.txt`:
|
|
780
|
+
```erb
|
|
781
|
+
<% if workflow.verbose %>
|
|
782
|
+
Detailed Analysis:
|
|
783
|
+
<%= response %>
|
|
784
|
+
<% else %>
|
|
785
|
+
Summary: <%= response.lines.first %>
|
|
786
|
+
<% end %>
|
|
787
|
+
|
|
788
|
+
Files analyzed: <%= workflow.file %>
|
|
789
|
+
Status: <%= workflow.output['status'] || 'completed' %>
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
This is an example of where the `workflow.output` hash is useful - formatting output for display based on data from previous steps.
|
|
793
|
+
|
|
794
|
+
Available in templates:
|
|
795
|
+
- `response`: The AI's response for this step
|
|
796
|
+
- `workflow`: Access to the workflow object
|
|
797
|
+
- `workflow.output`: The shared hash containing results from all steps when you need programmatic access
|
|
798
|
+
- `workflow.file`: Current file being processed (or `nil` for targetless workflows)
|
|
799
|
+
- All workflow configuration options
|
|
800
|
+
|
|
801
|
+
For most workflows, you'll mainly use `response` to access the current step's results. The `workflow.output` hash becomes valuable when you need to reference specific data points from previous steps in your templates or for conditional display logic.
|
|
802
|
+
|
|
803
|
+
## Advanced Features
|
|
804
|
+
|
|
805
|
+
### Workflow Metadata
|
|
806
|
+
|
|
807
|
+
Roast workflows maintain a metadata store that allows steps to share structured data beyond the standard output hash. This is particularly useful for tracking state that needs to persist across steps but shouldn't be part of the conversation context.
|
|
808
|
+
|
|
809
|
+
#### Setting Metadata
|
|
810
|
+
|
|
811
|
+
Metadata can be set by custom Ruby steps that extend `BaseStep`:
|
|
812
|
+
|
|
813
|
+
```ruby
|
|
814
|
+
# workflow/analyze_codebase.rb
|
|
815
|
+
class AnalyzeCodebase < Roast::Workflow::BaseStep
|
|
816
|
+
include Roast::Helpers::MetadataAccess
|
|
817
|
+
|
|
818
|
+
def call
|
|
819
|
+
# Perform analysis
|
|
820
|
+
analysis_results = perform_deep_analysis
|
|
821
|
+
|
|
822
|
+
# Store metadata for other steps to use
|
|
823
|
+
workflow.metadata[name.to_s] ||= {}
|
|
824
|
+
workflow.metadata[name.to_s]["total_files"] = analysis_results[:file_count]
|
|
825
|
+
workflow.metadata[name.to_s]["complexity_score"] = analysis_results[:complexity]
|
|
826
|
+
workflow.metadata[name.to_s]["analysis_id"] = SecureRandom.uuid
|
|
827
|
+
|
|
828
|
+
# Return the normal output for the conversation
|
|
829
|
+
"Analyzed #{analysis_results[:file_count]} files with average complexity of #{analysis_results[:complexity]}"
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
private
|
|
833
|
+
|
|
834
|
+
def perform_deep_analysis
|
|
835
|
+
# Your analysis logic here
|
|
836
|
+
{ file_count: 42, complexity: 7.5 }
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
#### Accessing Metadata
|
|
842
|
+
|
|
843
|
+
Metadata from previous steps can be accessed in:
|
|
844
|
+
|
|
845
|
+
1. **Custom Ruby steps:**
|
|
846
|
+
```ruby
|
|
847
|
+
class GenerateReport < Roast::Workflow::BaseStep
|
|
848
|
+
def call
|
|
849
|
+
# Access metadata from a previous step
|
|
850
|
+
total_files = workflow.metadata.dig("analyze_codebase", "total_files")
|
|
851
|
+
complexity = workflow.metadata.dig("analyze_codebase", "complexity_score")
|
|
852
|
+
|
|
853
|
+
"Generated report for #{total_files} files with complexity score: #{complexity}"
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
2. **Workflow configuration via interpolation:**
|
|
859
|
+
```yaml
|
|
860
|
+
steps:
|
|
861
|
+
- analyze_codebase
|
|
862
|
+
- validate_threshold
|
|
863
|
+
- generate_report
|
|
864
|
+
|
|
865
|
+
# Use metadata in step configuration
|
|
866
|
+
validate_threshold:
|
|
867
|
+
if: "{{metadata.analyze_codebase.complexity_score > 8.0}}"
|
|
868
|
+
then:
|
|
869
|
+
- send_alert
|
|
870
|
+
- create_ticket
|
|
871
|
+
else:
|
|
872
|
+
- mark_as_passed
|
|
873
|
+
|
|
874
|
+
# Pass metadata to command steps
|
|
875
|
+
send_alert:
|
|
876
|
+
$(slack-notify "High complexity detected: {{metadata.analyze_codebase.complexity_score}}")
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
3. **Prompt templates (ERB):**
|
|
880
|
+
```erb
|
|
881
|
+
# In analyze_codebase/output.txt
|
|
882
|
+
Analysis Summary:
|
|
883
|
+
Files analyzed: <%= workflow.metadata.dig(name.to_s, "total_files") %>
|
|
884
|
+
Complexity score: <%= workflow.metadata.dig(name.to_s, "complexity_score") %>
|
|
885
|
+
Analysis ID: <%= workflow.metadata.dig(name.to_s, "analysis_id") %>
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
#### Metadata Best Practices
|
|
889
|
+
|
|
890
|
+
- **Use metadata for data that shouldn't be in the conversation**
|
|
891
|
+
- **Don't duplicate output data:** Metadata complements the output hash, it doesn't replace it
|
|
892
|
+
|
|
893
|
+
The metadata system is particularly useful for:
|
|
894
|
+
- Tracking session or transaction IDs across multiple steps
|
|
895
|
+
- Storing configuration or state that tools need to access
|
|
896
|
+
- Passing data between steps without cluttering the AI conversation
|
|
897
|
+
- Implementing complex conditional logic based on computed values
|
|
898
|
+
|
|
899
|
+
### Instrumentation
|
|
900
|
+
|
|
901
|
+
Roast provides extensive instrumentation capabilities using ActiveSupport::Notifications. You can monitor workflow execution, track AI model usage, measure performance, and integrate with external monitoring systems. [Read the full instrumentation documentation](docs/INSTRUMENTATION.md).
|
|
902
|
+
|
|
903
|
+
### Built-in Tools
|
|
904
|
+
|
|
905
|
+
Roast provides several built-in tools that you can use in your workflows:
|
|
906
|
+
|
|
907
|
+
#### Tool Configuration
|
|
908
|
+
|
|
909
|
+
Tools can be configured using a hash format in your workflow YAML:
|
|
910
|
+
|
|
911
|
+
```yaml
|
|
912
|
+
tools:
|
|
913
|
+
- Roast::Tools::ReadFile # No configuration needed
|
|
914
|
+
- Roast::Tools::Cmd: # With configuration
|
|
915
|
+
allowed_commands:
|
|
916
|
+
- git
|
|
917
|
+
- npm
|
|
918
|
+
- yarn
|
|
919
|
+
- Roast::Tools::CodingAgent: # Optional configuration
|
|
920
|
+
coding_agent_command: claude --model opus -p --allowedTools "Bash, Glob, Grep, LS, Read"
|
|
921
|
+
model: opus # Model to use for all CodingAgent invocations
|
|
922
|
+
retries: 3 # Number of automatic retries on failure (default: 0)
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
Currently supported configurations:
|
|
926
|
+
- `Roast::Tools::Cmd` via `allowed_commands`: restricts which commands can be executed (defaults to: `pwd`, `find`, `ls`, `rake`, `ruby`, `dev`, `mkdir`)
|
|
927
|
+
- `Roast::Tools::CodingAgent` via:
|
|
928
|
+
- `coding_agent_command`: customizes the Claude Code CLI command used by the agent
|
|
929
|
+
- `model`: sets the model for all CodingAgent invocations (e.g., `opus`, `sonnet`)
|
|
930
|
+
- `retries`: number of times to automatically retry if the agent encounters an error (default: 0, no retries)
|
|
931
|
+
|
|
932
|
+
##### Cmd Tool Configuration
|
|
933
|
+
|
|
934
|
+
The `Cmd` tool's `allowed_commands` can be configured in two ways:
|
|
935
|
+
|
|
936
|
+
**1. Simple String Format** (uses default descriptions):
|
|
937
|
+
```yaml
|
|
938
|
+
tools:
|
|
939
|
+
- Roast::Tools::Cmd:
|
|
940
|
+
allowed_commands:
|
|
941
|
+
- pwd
|
|
942
|
+
- ls
|
|
943
|
+
- git
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
**2. Hash Format with Custom Descriptions**:
|
|
947
|
+
```yaml
|
|
948
|
+
tools:
|
|
949
|
+
- Roast::Tools::Cmd:
|
|
950
|
+
allowed_commands:
|
|
951
|
+
- pwd
|
|
952
|
+
- name: git
|
|
953
|
+
description: "git CLI - version control system with subcommands like status, commit, push"
|
|
954
|
+
- name: npm
|
|
955
|
+
description: "npm CLI - Node.js package manager with subcommands like install, run"
|
|
956
|
+
- name: docker
|
|
957
|
+
description: "Docker CLI - container platform with subcommands like build, run, ps"
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
Custom descriptions help the LLM understand when and how to use each command, making your workflows more effective.
|
|
961
|
+
|
|
962
|
+
### Step-Level Tool Filtering
|
|
963
|
+
|
|
964
|
+
You can restrict which tools are available to specific steps using the `available_tools` configuration:
|
|
965
|
+
|
|
966
|
+
```yaml
|
|
967
|
+
# Define all tools globally
|
|
968
|
+
tools:
|
|
969
|
+
- Roast::Tools::Grep
|
|
970
|
+
- Roast::Tools::ReadFile
|
|
971
|
+
- Roast::Tools::WriteFile
|
|
972
|
+
- Roast::Tools::Cmd:
|
|
973
|
+
allowed_commands:
|
|
974
|
+
- pwd
|
|
975
|
+
- ls
|
|
976
|
+
- echo
|
|
977
|
+
|
|
978
|
+
# Configure steps with specific tool access
|
|
979
|
+
explore_directory:
|
|
980
|
+
available_tools:
|
|
981
|
+
- pwd
|
|
982
|
+
- ls
|
|
983
|
+
|
|
984
|
+
analyze_files:
|
|
985
|
+
available_tools:
|
|
986
|
+
- grep
|
|
987
|
+
- read_file
|
|
988
|
+
|
|
989
|
+
write_summary:
|
|
990
|
+
available_tools:
|
|
991
|
+
- write_file
|
|
992
|
+
- echo
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
This feature provides:
|
|
996
|
+
- **Security**: Each step only has access to the tools it needs
|
|
997
|
+
- **Performance**: Reduces the tool list sent to the LLM
|
|
998
|
+
- **Clarity**: Makes tool usage explicit for each step
|
|
999
|
+
|
|
1000
|
+
Key points:
|
|
1001
|
+
- Use snake_case tool names (e.g., `read_file` for `Roast::Tools::ReadFile`)
|
|
1002
|
+
- For `Cmd` tool, use the specific command names (e.g., `pwd`, `ls`)
|
|
1003
|
+
- When `available_tools` is not specified, all tools remain available (backward compatible)
|
|
1004
|
+
- Empty array (`available_tools: []`) means no tools for that step
|
|
1005
|
+
|
|
1006
|
+
See the [available_tools_demo](examples/available_tools_demo/) for a complete example.
|
|
1007
|
+
|
|
1008
|
+
#### ReadFile
|
|
1009
|
+
|
|
1010
|
+
Reads the contents of a file from the filesystem.
|
|
1011
|
+
|
|
1012
|
+
```ruby
|
|
1013
|
+
# Basic usage
|
|
1014
|
+
read_file(path: "path/to/file.txt")
|
|
1015
|
+
|
|
1016
|
+
# Reading a specific portion of a file
|
|
1017
|
+
read_file(path: "path/to/large_file.txt", offset: 100, limit: 50)
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
- The `path` can be absolute or relative to the current working directory
|
|
1021
|
+
- Use `offset` and `limit` for large files to read specific sections (line numbers)
|
|
1022
|
+
- Returns the file content as a string
|
|
1023
|
+
|
|
1024
|
+
#### WriteFile
|
|
1025
|
+
|
|
1026
|
+
Writes content to a file, creating the file if it doesn't exist or overwriting it if it does.
|
|
1027
|
+
|
|
1028
|
+
```ruby
|
|
1029
|
+
# Basic usage
|
|
1030
|
+
write_file(path: "output.txt", content: "This is the file content")
|
|
1031
|
+
|
|
1032
|
+
# With path restriction for security
|
|
1033
|
+
write_file(
|
|
1034
|
+
path: "output.txt",
|
|
1035
|
+
content: "Restricted content",
|
|
1036
|
+
restrict: "/safe/directory" # Only allows writing to files under this path
|
|
1037
|
+
)
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
- Creates missing directories automatically
|
|
1041
|
+
- Can restrict file operations to specific directories for security
|
|
1042
|
+
- Returns a success message with the number of lines written
|
|
1043
|
+
|
|
1044
|
+
#### UpdateFiles
|
|
1045
|
+
|
|
1046
|
+
Applies a unified diff/patch to one or more files. Changes are applied atomically when possible.
|
|
1047
|
+
|
|
1048
|
+
```ruby
|
|
1049
|
+
update_files(
|
|
1050
|
+
diff: <<~DIFF,
|
|
1051
|
+
--- a/file1.txt
|
|
1052
|
+
+++ b/file1.txt
|
|
1053
|
+
@@ -1,3 +1,4 @@
|
|
1054
|
+
line1
|
|
1055
|
+
+new line
|
|
1056
|
+
line2
|
|
1057
|
+
line3
|
|
1058
|
+
|
|
1059
|
+
--- a/file2.txt
|
|
1060
|
+
+++ b/file2.txt
|
|
1061
|
+
@@ -5,7 +5,7 @@
|
|
1062
|
+
line5
|
|
1063
|
+
line6
|
|
1064
|
+
-old line7
|
|
1065
|
+
+updated line7
|
|
1066
|
+
line8
|
|
1067
|
+
DIFF
|
|
1068
|
+
base_path: "/path/to/project", # Optional, defaults to current working directory
|
|
1069
|
+
restrict_path: "/path/to/allowed", # Optional, restricts where files can be modified
|
|
1070
|
+
create_files: true, # Optional, defaults to true
|
|
1071
|
+
)
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
- Accepts standard unified diff format from tools like `git diff`
|
|
1075
|
+
- Supports multiple file changes in a single operation
|
|
1076
|
+
- Handles file creation, deletion, and modification
|
|
1077
|
+
- Performs atomic operations with rollback on failure
|
|
1078
|
+
- Includes fuzzy matching to handle minor context differences
|
|
1079
|
+
- This tool is especially useful for making targeted changes to multiple files at once
|
|
1080
|
+
|
|
1081
|
+
#### Grep
|
|
1082
|
+
|
|
1083
|
+
Searches file contents for a specific pattern using regular expressions.
|
|
1084
|
+
|
|
1085
|
+
```ruby
|
|
1086
|
+
# Basic usage
|
|
1087
|
+
grep(pattern: "function\\s+myFunction")
|
|
1088
|
+
|
|
1089
|
+
# With file filtering
|
|
1090
|
+
grep(pattern: "class\\s+User", include: "*.rb")
|
|
1091
|
+
|
|
1092
|
+
# With directory scope
|
|
1093
|
+
grep(pattern: "TODO:", path: "src/components")
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
- Uses regular expressions for powerful pattern matching
|
|
1097
|
+
- Can filter by file types using the `include` parameter
|
|
1098
|
+
- Can scope searches to specific directories with the `path` parameter
|
|
1099
|
+
- Returns a list of files containing matches
|
|
1100
|
+
|
|
1101
|
+
#### SearchFile
|
|
1102
|
+
|
|
1103
|
+
Provides advanced file search capabilities beyond basic pattern matching.
|
|
1104
|
+
|
|
1105
|
+
```ruby
|
|
1106
|
+
search_file(query: "class User", file_path: "app/models")
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
- Combines pattern matching with contextual search
|
|
1110
|
+
- Useful for finding specific code structures or patterns
|
|
1111
|
+
- Returns matched lines with context
|
|
1112
|
+
|
|
1113
|
+
#### Cmd
|
|
1114
|
+
|
|
1115
|
+
Executes shell commands with configurable restrictions. By default, only allows specific safe commands.
|
|
1116
|
+
|
|
1117
|
+
```ruby
|
|
1118
|
+
# Execute allowed commands (pwd, find, ls, rake, ruby, dev, mkdir by default)
|
|
1119
|
+
pwd(args: "-L")
|
|
1120
|
+
ls(args: "-la")
|
|
1121
|
+
ruby(args: "-e 'puts RUBY_VERSION'")
|
|
1122
|
+
|
|
1123
|
+
# Or use the legacy cmd function with full command
|
|
1124
|
+
cmd(command: "ls -la")
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
- Commands are registered as individual functions based on allowed_commands configuration
|
|
1128
|
+
- Default allowed commands: pwd, find, ls, rake, ruby, dev, mkdir
|
|
1129
|
+
- Each command has built-in descriptions to help the LLM understand usage
|
|
1130
|
+
- Configurable via workflow YAML (see Tool Configuration section)
|
|
1131
|
+
|
|
1132
|
+
#### Bash
|
|
1133
|
+
|
|
1134
|
+
Executes shell commands without restrictions. **⚠️ WARNING: Use only in trusted environments!**
|
|
1135
|
+
|
|
1136
|
+
```ruby
|
|
1137
|
+
# Execute any command - no restrictions
|
|
1138
|
+
bash(command: "curl https://api.example.com | jq '.data'")
|
|
1139
|
+
|
|
1140
|
+
# Complex operations with pipes and redirects
|
|
1141
|
+
bash(command: "find . -name '*.log' -mtime +30 -delete")
|
|
1142
|
+
|
|
1143
|
+
# System administration tasks
|
|
1144
|
+
bash(command: "ps aux | grep ruby | awk '{print $2}'")
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
- **No command restrictions** - full shell access
|
|
1148
|
+
- Designed for prototyping and development environments
|
|
1149
|
+
- Logs warnings by default (disable with `ROAST_BASH_WARNINGS=false`)
|
|
1150
|
+
- Should NOT be used in production or untrusted contexts
|
|
1151
|
+
- See `examples/bash_prototyping/` for usage examples
|
|
1152
|
+
|
|
1153
|
+
#### CodingAgent
|
|
1154
|
+
|
|
1155
|
+
Creates a specialized agent for complex coding tasks or long-running operations.
|
|
1156
|
+
|
|
1157
|
+
```ruby
|
|
1158
|
+
# Basic usage
|
|
1159
|
+
coding_agent(
|
|
1160
|
+
prompt: "Refactor the authentication module to use JWT tokens",
|
|
1161
|
+
include_context_summary: true, # Include workflow context in the agent prompt
|
|
1162
|
+
continue: true # Continue from previous agent session
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1165
|
+
# With automatic retries on failure
|
|
1166
|
+
coding_agent(
|
|
1167
|
+
prompt: "Implement complex feature with error handling",
|
|
1168
|
+
retries: 3 # Retry up to 3 times if the agent encounters errors
|
|
1169
|
+
)
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
- Delegates complex tasks to a specialized coding agent (Claude Code)
|
|
1173
|
+
- Useful for tasks that require deep code understanding or multi-step changes
|
|
1174
|
+
- Can work across multiple files and languages
|
|
1175
|
+
- Supports automatic retries on transient failures (network issues, API errors)
|
|
1176
|
+
- Retries can be configured globally (see Tool Configuration) or per invocation
|
|
1177
|
+
|
|
1178
|
+
### MCP (Model Context Protocol) Tools
|
|
1179
|
+
|
|
1180
|
+
Roast supports MCP tools, allowing you to integrate external services and tools through the Model Context Protocol standard. MCP enables seamless connections to databases, APIs, and specialized tools.
|
|
1181
|
+
|
|
1182
|
+
#### Configuring MCP Tools
|
|
1183
|
+
|
|
1184
|
+
MCP tools are configured in the `tools` section of your workflow YAML alongside traditional Roast tools:
|
|
1185
|
+
|
|
1186
|
+
```yaml
|
|
1187
|
+
tools:
|
|
1188
|
+
# Traditional Roast tools
|
|
1189
|
+
- Roast::Tools::ReadFile
|
|
1190
|
+
|
|
1191
|
+
# MCP tools with SSE (Server-Sent Events)
|
|
1192
|
+
- Documentation:
|
|
1193
|
+
url: https://gitmcp.io/myorg/myrepo/docs
|
|
1194
|
+
env:
|
|
1195
|
+
- "Authorization: Bearer {{ENV['API_TOKEN']}}"
|
|
1196
|
+
|
|
1197
|
+
# MCP tools with stdio
|
|
1198
|
+
- GitHub:
|
|
1199
|
+
command: npx
|
|
1200
|
+
args: ["-y", "@modelcontextprotocol/server-github"]
|
|
1201
|
+
env:
|
|
1202
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: "{{ENV['GITHUB_TOKEN']}}"
|
|
1203
|
+
only:
|
|
1204
|
+
- search_repositories
|
|
1205
|
+
- get_issue
|
|
1206
|
+
- create_issue
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
#### SSE MCP Tools
|
|
1210
|
+
|
|
1211
|
+
Connect to HTTP endpoints implementing the MCP protocol:
|
|
1212
|
+
|
|
1213
|
+
```yaml
|
|
1214
|
+
- Tool Name:
|
|
1215
|
+
url: https://example.com/mcp-endpoint
|
|
1216
|
+
env:
|
|
1217
|
+
- "Authorization: Bearer {{resource.api_token}}"
|
|
1218
|
+
only: [function1, function2] # Optional whitelist
|
|
1219
|
+
except: [function3] # Optional blacklist
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
#### Stdio MCP Tools
|
|
1223
|
+
|
|
1224
|
+
Connect to local processes implementing the MCP protocol:
|
|
1225
|
+
|
|
1226
|
+
```yaml
|
|
1227
|
+
- Tool Name:
|
|
1228
|
+
command: docker
|
|
1229
|
+
args: ["run", "-i", "--rm", "ghcr.io/example/mcp-server"]
|
|
1230
|
+
env:
|
|
1231
|
+
API_KEY: "{{ENV['API_KEY']}}"
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
See the [MCP tools example](examples/mcp/) for complete documentation and more examples.
|
|
1235
|
+
|
|
1236
|
+
### Custom Tools
|
|
1237
|
+
|
|
1238
|
+
You can create your own tools using the [Raix function dispatch pattern](https://github.com/OlympiaAI/raix-rails?tab=readme-ov-file#use-of-toolsfunctions). Custom tools should be placed in `.roast/initializers/` (subdirectories are supported):
|
|
1239
|
+
|
|
1240
|
+
```ruby
|
|
1241
|
+
# .roast/initializers/tools/git_analyzer.rb
|
|
1242
|
+
module MyProject
|
|
1243
|
+
module Tools
|
|
1244
|
+
module GitAnalyzer
|
|
1245
|
+
extend self
|
|
1246
|
+
|
|
1247
|
+
def self.included(base)
|
|
1248
|
+
base.class_eval do
|
|
1249
|
+
function(
|
|
1250
|
+
:analyze_commit,
|
|
1251
|
+
"Analyze a git commit for code quality and changes",
|
|
1252
|
+
commit_sha: { type: "string", description: "The SHA of the commit to analyze" },
|
|
1253
|
+
include_diff: { type: "boolean", description: "Include the full diff in the analysis", default: false }
|
|
1254
|
+
) do |params|
|
|
1255
|
+
GitAnalyzer.call(params[:commit_sha], params[:include_diff])
|
|
1256
|
+
end
|
|
1257
|
+
end
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1260
|
+
def call(commit_sha, include_diff = false)
|
|
1261
|
+
Roast::Helpers::Logger.info("🔍 Analyzing commit: #{commit_sha}\n")
|
|
1262
|
+
|
|
1263
|
+
# Your implementation here
|
|
1264
|
+
commit_info = `git show #{commit_sha} --stat`
|
|
1265
|
+
commit_info += "\n\n" + `git show #{commit_sha}` if include_diff
|
|
1266
|
+
|
|
1267
|
+
commit_info
|
|
1268
|
+
rescue StandardError => e
|
|
1269
|
+
"Error analyzing commit: #{e.message}".tap do |error_message|
|
|
1270
|
+
Roast::Helpers::Logger.error(error_message + "\n")
|
|
1271
|
+
end
|
|
1272
|
+
end
|
|
1273
|
+
end
|
|
1274
|
+
end
|
|
1275
|
+
end
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
Then include your tool in the workflow:
|
|
1279
|
+
|
|
1280
|
+
```yaml
|
|
1281
|
+
tools:
|
|
1282
|
+
- MyProject::Tools::GitAnalyzer
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
The tool will be available to the AI model during workflow execution, and it can call `analyze_commit` with the appropriate parameters.
|
|
1286
|
+
|
|
1287
|
+
### Project-specific Configuration
|
|
1288
|
+
|
|
1289
|
+
You can extend Roast with project-specific configuration by creating initializers in `.roast/initializers/`. These are automatically loaded when workflows run, allowing you to:
|
|
1290
|
+
|
|
1291
|
+
- Add custom instrumentation
|
|
1292
|
+
- Configure monitoring and metrics
|
|
1293
|
+
- Set up project-specific tools
|
|
1294
|
+
- Customize workflow behavior
|
|
1295
|
+
|
|
1296
|
+
Example structure:
|
|
1297
|
+
```
|
|
1298
|
+
your-project/
|
|
1299
|
+
├── .roast/
|
|
1300
|
+
│ └── initializers/
|
|
1301
|
+
│ ├── metrics.rb
|
|
1302
|
+
│ ├── logging.rb
|
|
1303
|
+
│ └── custom_tools.rb
|
|
1304
|
+
└── ...
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
### Pre/Post Processing Framework
|
|
1308
|
+
|
|
1309
|
+
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.
|
|
1310
|
+
|
|
1311
|
+
#### Overview
|
|
1312
|
+
|
|
1313
|
+
- **Pre-processing**: Steps executed once before any targets are processed
|
|
1314
|
+
- **Post-processing**: Steps executed once after all targets have been processed
|
|
1315
|
+
- **Shared state**: Pre-processing results are available to all subsequent steps
|
|
1316
|
+
- **Result aggregation**: Post-processing has access to all workflow execution results
|
|
1317
|
+
- **Single-target support**: Pre/post processing works with single-target workflows too
|
|
1318
|
+
- **Output templates**: Post-processing supports `output.txt` templates for custom formatting
|
|
1319
|
+
|
|
1320
|
+
#### Configuration
|
|
1321
|
+
|
|
1322
|
+
```yaml
|
|
1323
|
+
name: optimize_tests
|
|
1324
|
+
model: gpt-4o
|
|
1325
|
+
target: "test/**/*_test.rb"
|
|
1326
|
+
|
|
1327
|
+
# Pre-processing steps run once before any test files
|
|
1328
|
+
pre_processing:
|
|
1329
|
+
- gather_baseline_metrics
|
|
1330
|
+
- setup_test_environment
|
|
1331
|
+
|
|
1332
|
+
# Main workflow steps run for each test file
|
|
1333
|
+
steps:
|
|
1334
|
+
- analyze_test
|
|
1335
|
+
- improve_coverage
|
|
1336
|
+
- optimize_performance
|
|
1337
|
+
|
|
1338
|
+
# Post-processing steps run once after all test files
|
|
1339
|
+
post_processing:
|
|
1340
|
+
- aggregate_results
|
|
1341
|
+
- generate_report
|
|
1342
|
+
- cleanup_environment
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
#### Directory Structure
|
|
1346
|
+
|
|
1347
|
+
Pre and post-processing steps follow the same conventions as regular steps but are organized in their own directories:
|
|
1348
|
+
|
|
1349
|
+
```
|
|
1350
|
+
workflow.yml
|
|
1351
|
+
pre_processing/
|
|
1352
|
+
├── gather_baseline_metrics/
|
|
1353
|
+
│ └── prompt.md
|
|
1354
|
+
└── setup_test_environment/
|
|
1355
|
+
└── prompt.md
|
|
1356
|
+
analyze_test/
|
|
1357
|
+
└── prompt.md
|
|
1358
|
+
improve_coverage/
|
|
1359
|
+
└── prompt.md
|
|
1360
|
+
optimize_performance/
|
|
1361
|
+
└── prompt.md
|
|
1362
|
+
post_processing/
|
|
1363
|
+
├── output.txt
|
|
1364
|
+
├── aggregate_results/
|
|
1365
|
+
│ └── prompt.md
|
|
1366
|
+
├── generate_report/
|
|
1367
|
+
│ └── prompt.md
|
|
1368
|
+
└── cleanup_environment/
|
|
1369
|
+
└── prompt.md
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
#### Data Access
|
|
1373
|
+
|
|
1374
|
+
**Pre-processing results in target workflows:**
|
|
1375
|
+
|
|
1376
|
+
Target workflows have access to pre-processing results through the `pre_processing_data` variable with dot notation:
|
|
1377
|
+
|
|
1378
|
+
```erb
|
|
1379
|
+
# In a target workflow step prompt
|
|
1380
|
+
The baseline metrics from pre-processing:
|
|
1381
|
+
<%= pre_processing_data.gather_baseline_metrics %>
|
|
1382
|
+
|
|
1383
|
+
Environment setup details:
|
|
1384
|
+
<%= pre_processing_data.setup_test_environment %>
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1387
|
+
**Post-processing data access:**
|
|
1388
|
+
|
|
1389
|
+
Post-processing steps have access to:
|
|
1390
|
+
|
|
1391
|
+
- `pre_processing`: Direct access to pre-processing results with dot notation
|
|
1392
|
+
- `targets`: Hash of all target workflow results, keyed by file paths
|
|
1393
|
+
|
|
1394
|
+
Example post-processing prompt:
|
|
1395
|
+
```markdown
|
|
1396
|
+
# Generate Summary Report
|
|
1397
|
+
|
|
1398
|
+
Based on the baseline metrics:
|
|
1399
|
+
<%= pre_processing.gather_baseline_metrics %>
|
|
1400
|
+
|
|
1401
|
+
Environment configuration:
|
|
1402
|
+
<%= pre_processing.setup_test_environment %>
|
|
1403
|
+
|
|
1404
|
+
And the results from processing all files:
|
|
1405
|
+
<% targets.each do |file, target| %>
|
|
1406
|
+
File: <%= file %>
|
|
1407
|
+
Analysis results: <%= target.output.analyze_test %>
|
|
1408
|
+
Coverage improvements: <%= target.output.improve_coverage %>
|
|
1409
|
+
Performance optimizations: <%= target.output.optimize_performance %>
|
|
1410
|
+
<% end %>
|
|
1411
|
+
|
|
1412
|
+
Please generate a comprehensive summary report showing:
|
|
1413
|
+
1. Overall improvements achieved
|
|
1414
|
+
2. Files with the most significant changes
|
|
1415
|
+
3. Recommendations for further optimization
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
#### Output Templates
|
|
1419
|
+
|
|
1420
|
+
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:
|
|
1421
|
+
|
|
1422
|
+
```erb
|
|
1423
|
+
# post_processing/output.txt
|
|
1424
|
+
=== Workflow Summary Report ===
|
|
1425
|
+
Generated at: <%= Time.now.strftime("%Y-%m-%d %H:%M:%S") %>
|
|
1426
|
+
|
|
1427
|
+
Environment: <%= pre_processing.setup_test_environment %>
|
|
1428
|
+
|
|
1429
|
+
Files Processed: <%= targets.size %>
|
|
1430
|
+
|
|
1431
|
+
<% targets.each do |file, target| %>
|
|
1432
|
+
- <%= file %>: <%= target.output.analyze_test %>
|
|
1433
|
+
<% end %>
|
|
1434
|
+
|
|
1435
|
+
<%= output.generate_report %>
|
|
1436
|
+
===============================
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
The template has access to:
|
|
1440
|
+
- `pre_processing`: All pre-processing step outputs with dot notation
|
|
1441
|
+
- `targets`: Hash of all target workflow results with dot notation (each target has `.output` and `.final_output`)
|
|
1442
|
+
- `output`: Post-processing step outputs with dot notation
|
|
1443
|
+
|
|
1444
|
+
#### Use Cases
|
|
1445
|
+
|
|
1446
|
+
This pattern is ideal for:
|
|
1447
|
+
|
|
1448
|
+
- **Code migrations**: Setup migration tools, process files, generate migration report
|
|
1449
|
+
- **Test optimization**: Baseline metrics, optimize tests, aggregate improvements
|
|
1450
|
+
- **Documentation generation**: Analyze codebase, generate docs per module, create index
|
|
1451
|
+
- **Dependency updates**: Check versions, update files, verify compatibility
|
|
1452
|
+
- **Security audits**: Setup scanners, check each file, generate security report
|
|
1453
|
+
- **Performance analysis**: Establish baselines, analyze components, summarize findings
|
|
1454
|
+
|
|
1455
|
+
See the [pre/post processing example](examples/pre_post_processing) for a complete working demonstration.
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
## Development
|
|
1459
|
+
|
|
1460
|
+
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.
|
|
1461
|
+
|
|
1462
|
+
## License
|
|
1463
|
+
|
|
1464
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|