roast-ai 0.4.1 → 0.4.3

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +43 -0
  4. data/Gemfile +0 -1
  5. data/Gemfile.lock +48 -23
  6. data/README.md +228 -29
  7. data/examples/coding_agent_with_model.yml +20 -0
  8. data/examples/coding_agent_with_retries.yml +30 -0
  9. data/examples/grading/rb_test_runner +1 -1
  10. data/lib/roast/errors.rb +3 -0
  11. data/lib/roast/helpers/metadata_access.rb +39 -0
  12. data/lib/roast/helpers/timeout_handler.rb +1 -1
  13. data/lib/roast/tools/coding_agent.rb +99 -27
  14. data/lib/roast/tools/grep.rb +4 -0
  15. data/lib/roast/version.rb +1 -1
  16. data/lib/roast/workflow/agent_step.rb +57 -4
  17. data/lib/roast/workflow/base_workflow.rb +4 -2
  18. data/lib/roast/workflow/command_executor.rb +3 -1
  19. data/lib/roast/workflow/configuration_parser.rb +2 -0
  20. data/lib/roast/workflow/each_step.rb +5 -3
  21. data/lib/roast/workflow/input_step.rb +2 -0
  22. data/lib/roast/workflow/interpolator.rb +23 -1
  23. data/lib/roast/workflow/metadata_manager.rb +47 -0
  24. data/lib/roast/workflow/output_handler.rb +1 -0
  25. data/lib/roast/workflow/replay_handler.rb +8 -0
  26. data/lib/roast/workflow/shell_script_step.rb +115 -0
  27. data/lib/roast/workflow/sqlite_state_repository.rb +17 -17
  28. data/lib/roast/workflow/state_manager.rb +8 -0
  29. data/lib/roast/workflow/step_executor_coordinator.rb +43 -8
  30. data/lib/roast/workflow/step_executor_with_reporting.rb +2 -2
  31. data/lib/roast/workflow/step_loader.rb +55 -9
  32. data/lib/roast/workflow/workflow_executor.rb +3 -4
  33. data/lib/roast/workflow/workflow_initializer.rb +95 -4
  34. data/lib/roast/workflow/workflow_runner.rb +2 -2
  35. data/lib/roast.rb +2 -0
  36. data/roast.gemspec +3 -2
  37. metadata +36 -18
  38. data/lib/roast/workflow/step_orchestrator.rb +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b31e95a8d65d0b1fa798c0baed8bcbf9249cc67fca752154865d914d01ffbd8d
4
- data.tar.gz: 21205a0a50e2301dda0936e67b02d05af94085b0e383f859aec282827649ebb3
3
+ metadata.gz: 22264361960a52b5d21b2dcc715a77c23ad8601170dbf02daa531416b2180035
4
+ data.tar.gz: 581b96d84ca59f702e591f18b0ae1e10823fccaaef6ad44757f38b607d146d94
5
5
  SHA512:
6
- metadata.gz: 80b5ceada69faff53f5893e9378e628474b103c3d6b60a8701be80b1bfe11cb6e4bde252670d890a2ae7e460d90d956daed7813d251cf6d84399ab70d321b0d8
7
- data.tar.gz: 620e7d46fc6f7e9b10a71807f213ba101cb2bbd9a3bad073a153057bd67bc842cf74e4ffaa179596a2ed3655dc7a864c366034a37f4a720d2fd6faafa86f5541
6
+ metadata.gz: a1316b637cb27f19c467faebc6e21853cdac1ce69439db50e56a1bc7c8d69240f0eb7ae51e4a856d2ac0be3bf2f406b21d5a5e51131e7bcee2a5298b8b9799ae
7
+ data.tar.gz: d41214b70dd0d1755d7c8b49673ccc4b0296d911e97ebbc18c72c08ecf80b916277518bf0087d0286fc32a471c807f225a3262c5049381f42999ace482e196b6
data/.gitignore CHANGED
@@ -41,3 +41,4 @@ bin/thor
41
41
 
42
42
  gemfiles/*.lock
43
43
  bin/claude-swarm
44
+ *.gem
data/CHANGELOG.md CHANGED
@@ -5,6 +5,49 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.3] - 2025-07-10
9
+
10
+ ### Changed
11
+ - **Updated to raix-openai-eight gem** - Upgraded from `raix` to `raix-openai-eight` gem which supports OpenAI Ruby client v8.1
12
+
13
+ ## [0.4.2] - 2025-06-20
14
+
15
+ ### Added
16
+ - **Multiline bash command support** (#289)
17
+ - Enhanced CommandExecutor to properly handle commands spanning multiple lines
18
+ - Enables sophisticated bash scripts in workflow steps
19
+ - Maintains backward compatibility with single-line commands
20
+ - **Comprehensive shell security enhancements** (#289)
21
+ - Smart interpolation that detects shell commands and escapes dangerous characters
22
+ - Protection against shell injection for all major metacharacters:
23
+ - Backslashes (`\`) to prevent path injection
24
+ - Double quotes (`"`) to prevent breaking quoted contexts
25
+ - Dollar signs (`$`) to prevent variable expansion
26
+ - Backticks (`` ` ``) to prevent command substitution
27
+ - Context-aware escaping only in shell commands, preserving text elsewhere
28
+ - **Early detection for missing Raix configuration** (#292)
29
+ - Provides helpful error messages when Raix is not properly initialized
30
+ - Shows example configuration for both OpenAI and OpenRouter
31
+ - Prevents cryptic "undefined method 'chat' for nil" errors
32
+ - **Exit early feature for input steps** (#291)
33
+ - Pressing Ctrl-C during input steps now exits cleanly
34
+ - No more confusing stack traces when canceling input
35
+ - **Default --dangerously-skip-permissions flag for CodingAgent** (#290)
36
+ - Avoids permission prompts during automated workflows
37
+ - Improves workflow automation experience
38
+
39
+ ### Fixed
40
+ - Test isolation issue causing CI failures (#289)
41
+ - Flaky test in StepExecutorRegistryTest due to executor registration conflicts (#289)
42
+ - Shell command interpolation security vulnerabilities (#289)
43
+ - Missing dependency declarations (cli-kit, sqlite3) (#292)
44
+
45
+ ### Changed
46
+ - Updated cli-kit dependency to ~> 5.0 for better error handling
47
+ - Updated sqlite3 dependency to ~> 2.6 to resolve version conflicts
48
+
49
+ [0.4.2]: https://github.com/Shopify/roast/compare/v0.4.1...v0.4.2
50
+
8
51
  ## [0.4.1] - 2025-06-18
9
52
 
10
53
  ### Added
data/Gemfile CHANGED
@@ -18,6 +18,5 @@ gem "rubocop-shopify", require: false
18
18
  gem "vcr", require: false
19
19
  gem "webmock", require: false
20
20
  gem "minitest-rg"
21
- gem "sqlite3", "~> 1.7"
22
21
 
23
22
  gem "claude_swarm"
data/Gemfile.lock CHANGED
@@ -1,15 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (0.4.1)
4
+ roast-ai (0.4.3)
5
5
  activesupport (>= 7.0)
6
+ cli-kit (~> 5.0)
6
7
  cli-ui (= 2.3.0)
7
8
  diff-lcs (~> 1.5)
8
- faraday-retry
9
9
  json-schema
10
10
  open_router (~> 0.3)
11
- raix (~> 1.0)
11
+ raix-openai-eight (~> 1.0)
12
12
  ruby-graphviz (~> 1.2)
13
+ sqlite3 (~> 2.6)
13
14
  thor (~> 1.3)
14
15
  zeitwerk (~> 2.6)
15
16
 
@@ -36,9 +37,11 @@ GEM
36
37
  benchmark (0.4.1)
37
38
  bigdecimal (3.2.2)
38
39
  cgi (0.5.0)
39
- claude_swarm (0.1.15)
40
+ claude_swarm (0.1.19)
40
41
  fast-mcp-annotations
41
42
  thor (~> 1.3)
43
+ cli-kit (5.0.1)
44
+ cli-ui (~> 2.0)
42
45
  cli-ui (2.3.0)
43
46
  coderay (1.1.3)
44
47
  concurrent-ruby (1.3.5)
@@ -83,21 +86,29 @@ GEM
83
86
  faraday-net_http (>= 2.0, < 3.5)
84
87
  json
85
88
  logger
86
- faraday-multipart (1.1.0)
89
+ faraday-multipart (1.1.1)
87
90
  multipart-post (~> 2.0)
88
- faraday-net_http (3.4.0)
91
+ faraday-net_http (3.4.1)
89
92
  net-http (>= 0.5.0)
90
- faraday-retry (2.3.1)
93
+ faraday-retry (2.3.2)
91
94
  faraday (~> 2.0)
92
- fast-mcp-annotations (1.5.2)
95
+ fast-mcp-annotations (1.5.3)
93
96
  addressable (~> 2.8)
94
97
  base64
95
98
  dry-schema (~> 1.14)
96
99
  json (~> 2.0)
97
100
  mime-types (~> 3.4)
98
- rack (~> 3.1)
101
+ rack (< 3)
102
+ ffi (1.17.2-aarch64-linux-gnu)
103
+ ffi (1.17.2-aarch64-linux-musl)
104
+ ffi (1.17.2-arm-linux-gnu)
105
+ ffi (1.17.2-arm-linux-musl)
99
106
  ffi (1.17.2-arm64-darwin)
107
+ ffi (1.17.2-x86-linux-gnu)
108
+ ffi (1.17.2-x86-linux-musl)
109
+ ffi (1.17.2-x86_64-darwin)
100
110
  ffi (1.17.2-x86_64-linux-gnu)
111
+ ffi (1.17.2-x86_64-linux-musl)
101
112
  formatador (1.1.0)
102
113
  guard (2.19.1)
103
114
  formatador (>= 0.2.4)
@@ -132,8 +143,7 @@ GEM
132
143
  mime-types (3.7.0)
133
144
  logger
134
145
  mime-types-data (~> 3.2025, >= 3.2025.0507)
135
- mime-types-data (3.2025.0603)
136
- mini_portile2 (2.8.9)
146
+ mime-types-data (3.2025.0617)
137
147
  minitest (5.25.5)
138
148
  minitest-rg (5.3.0)
139
149
  minitest (~> 5.0)
@@ -151,7 +161,7 @@ GEM
151
161
  dotenv (>= 2)
152
162
  faraday (>= 1)
153
163
  faraday-multipart (>= 1)
154
- ostruct (0.6.1)
164
+ ostruct (0.6.2)
155
165
  parallel (1.27.0)
156
166
  parser (3.3.8.0)
157
167
  ast (~> 2.4.1)
@@ -162,21 +172,21 @@ GEM
162
172
  method_source (~> 1.0)
163
173
  public_suffix (6.0.2)
164
174
  racc (1.8.1)
165
- rack (3.1.16)
175
+ rack (2.2.17)
166
176
  rainbow (3.1.1)
167
- raix (1.0.1)
177
+ raix-openai-eight (1.0.1)
168
178
  activesupport (>= 6.0)
169
179
  faraday-retry (~> 2.0)
170
180
  open_router (~> 0.2)
171
181
  ostruct
172
- ruby-openai (~> 7)
182
+ ruby-openai (~> 8.1)
173
183
  rake (13.3.0)
174
184
  rb-fsevent (0.11.2)
175
185
  rb-inotify (0.11.1)
176
186
  ffi (~> 1.0)
177
187
  regexp_parser (2.10.0)
178
188
  rexml (3.4.1)
179
- rubocop (1.76.0)
189
+ rubocop (1.77.0)
180
190
  json (~> 2.3)
181
191
  language_server-protocol (~> 3.17.0.2)
182
192
  lint_roller (~> 1.1.0)
@@ -184,17 +194,17 @@ GEM
184
194
  parser (>= 3.3.0.2)
185
195
  rainbow (>= 2.2.2, < 4.0)
186
196
  regexp_parser (>= 2.9.3, < 3.0)
187
- rubocop-ast (>= 1.45.0, < 2.0)
197
+ rubocop-ast (>= 1.45.1, < 2.0)
188
198
  ruby-progressbar (~> 1.7)
189
199
  unicode-display_width (>= 2.4.0, < 4.0)
190
- rubocop-ast (1.45.0)
200
+ rubocop-ast (1.45.1)
191
201
  parser (>= 3.3.7.2)
192
202
  prism (~> 1.4)
193
203
  rubocop-shopify (2.17.1)
194
204
  rubocop (~> 1.62)
195
205
  ruby-graphviz (1.2.5)
196
206
  rexml
197
- ruby-openai (7.4.0)
207
+ ruby-openai (8.1.0)
198
208
  event_stream_parser (>= 0.3.0, < 2.0.0)
199
209
  faraday (>= 1)
200
210
  faraday-multipart (>= 1)
@@ -202,8 +212,16 @@ GEM
202
212
  ruby2_keywords (0.0.5)
203
213
  securerandom (0.4.1)
204
214
  shellany (0.0.1)
205
- sqlite3 (1.7.3)
206
- mini_portile2 (~> 2.8.0)
215
+ sqlite3 (2.7.0-aarch64-linux-gnu)
216
+ sqlite3 (2.7.0-aarch64-linux-musl)
217
+ sqlite3 (2.7.0-arm-linux-gnu)
218
+ sqlite3 (2.7.0-arm-linux-musl)
219
+ sqlite3 (2.7.0-arm64-darwin)
220
+ sqlite3 (2.7.0-x86-linux-gnu)
221
+ sqlite3 (2.7.0-x86-linux-musl)
222
+ sqlite3 (2.7.0-x86_64-darwin)
223
+ sqlite3 (2.7.0-x86_64-linux-gnu)
224
+ sqlite3 (2.7.0-x86_64-linux-musl)
207
225
  thor (1.3.2)
208
226
  tzinfo (2.0.6)
209
227
  concurrent-ruby (~> 1.0)
@@ -220,8 +238,16 @@ GEM
220
238
  zeitwerk (2.7.3)
221
239
 
222
240
  PLATFORMS
241
+ aarch64-linux-gnu
242
+ aarch64-linux-musl
243
+ arm-linux-gnu
244
+ arm-linux-musl
223
245
  arm64-darwin
224
- x86_64-linux
246
+ x86-linux-gnu
247
+ x86-linux-musl
248
+ x86_64-darwin
249
+ x86_64-linux-gnu
250
+ x86_64-linux-musl
225
251
 
226
252
  DEPENDENCIES
227
253
  cgi
@@ -235,7 +261,6 @@ DEPENDENCIES
235
261
  rake
236
262
  roast-ai!
237
263
  rubocop-shopify
238
- sqlite3 (~> 1.7)
239
264
  vcr
240
265
  webmock
241
266
 
data/README.md CHANGED
@@ -279,35 +279,126 @@ Roast supports several types of steps:
279
279
  ```
280
280
  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).
281
281
 
282
- 9. **Input step**: Interactive prompts for user input during workflow execution
282
+ **Session continuity for agent steps:**
283
+
284
+ Agent steps support two options for maintaining Claude context across steps:
285
+
286
+ 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)
287
+ 2. **`resume: step_name`** - Resumes from a specific earlier step's Claude Code session
288
+
289
+ **Continue option:**
290
+
291
+ The `continue` option allows sequential agent steps to maintain a continuous conversation:
292
+
283
293
  ```yaml
284
294
  steps:
285
- - analyze_code
286
- - get_user_feedback:
287
- prompt: "Should we proceed with the refactoring? (yes/no)"
288
- type: confirm
289
- - review_changes:
290
- prompt: "Enter your review comments"
291
- type: text
292
- - select_strategy:
293
- prompt: "Choose optimization strategy"
294
- type: select
295
- options:
296
- - "Performance optimization"
297
- - "Memory optimization"
298
- - "Code clarity"
299
- - api_configuration:
300
- prompt: "Enter API key"
301
- type: password
295
+ - ^analyze_codebase
296
+ - ^implement_feature
297
+ - ^add_tests
298
+
299
+ # Configuration
300
+ analyze_codebase:
301
+ continue: false # Start fresh (default)
302
+
303
+ implement_feature:
304
+ continue: true # Continue from immediately previous analyze_codebase step
305
+
306
+ add_tests:
307
+ continue: true # Continue from immediately previous implement_feature step
302
308
  ```
303
309
 
304
- Input steps pause workflow execution to collect user input. They support several types:
305
- - `text`: Free-form text input (default if type not specified)
306
- - `confirm`: Yes/No confirmation prompts
307
- - `select`: Choice from a list of options
308
- - `password`: Masked input for sensitive data
310
+ **Resume functionality for agent steps:**
311
+
312
+ Agent steps can resume from specific previous Claude Code sessions:
309
313
 
310
- 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}}`).
314
+ ```yaml
315
+ steps:
316
+ - ^analyze_codebase
317
+ - ^implement_feature
318
+ - ^polish_implementation
319
+
320
+ # Configuration
321
+ analyze_codebase:
322
+ continue: false # Start fresh
323
+
324
+ implement_feature:
325
+ continue: true # Continue from previous conversation
326
+
327
+ polish_implementation:
328
+ resume: analyze_codebase # Resume from a specific step's session not the immediately previous one
329
+ ```
330
+
331
+ 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.
332
+
333
+ 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.
334
+
335
+ 9. **Shell script step**: Execute shell scripts directly as workflow steps
336
+ ```yaml
337
+ steps:
338
+ - setup_environment # Executes setup_environment.sh
339
+ - run_tests # Executes run_tests.sh
340
+ - cleanup
341
+ ```
342
+
343
+ 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.
344
+
345
+ **Configuration options:**
346
+ ```yaml
347
+ # Step configuration
348
+ my_script:
349
+ json: true # Parse stdout as JSON
350
+ exit_on_error: false # Don't fail workflow on non-zero exit
351
+ env: # Custom environment variables
352
+ CUSTOM_VAR: "value"
353
+ ```
354
+
355
+ **Environment integration:** Shell scripts automatically receive workflow context:
356
+ - `ROAST_WORKFLOW_RESOURCE`: Current workflow resource
357
+ - `ROAST_STEP_NAME`: Current step name
358
+ - `ROAST_WORKFLOW_OUTPUT`: Previous step outputs as JSON
359
+
360
+ **Example script (`setup_environment.sh`):**
361
+ ```bash
362
+ #!/bin/bash
363
+ echo "Setting up environment for: $ROAST_WORKFLOW_RESOURCE"
364
+
365
+ # Create a config file that subsequent steps can use
366
+ mkdir -p tmp
367
+ echo "DATABASE_URL=sqlite://test.db" > tmp/config.env
368
+
369
+ # Output data for the workflow (available via ROAST_WORKFLOW_OUTPUT in later steps)
370
+ echo '{"status": "configured", "database": "sqlite://test.db", "config_file": "tmp/config.env"}'
371
+ ```
372
+
373
+ 10. **Input step**: Interactive prompts for user input during workflow execution
374
+ ```yaml
375
+ steps:
376
+ - analyze_code
377
+ - get_user_feedback:
378
+ prompt: "Should we proceed with the refactoring? (yes/no)"
379
+ type: confirm
380
+ - review_changes:
381
+ prompt: "Enter your review comments"
382
+ type: text
383
+ - select_strategy:
384
+ prompt: "Choose optimization strategy"
385
+ type: select
386
+ options:
387
+ - "Performance optimization"
388
+ - "Memory optimization"
389
+ - "Code clarity"
390
+ - api_configuration:
391
+ prompt: "Enter API key"
392
+ type: password
393
+ ```
394
+
395
+ Input steps pause workflow execution to collect user input. They support several types:
396
+ - `text`: Free-form text input (default if type not specified)
397
+ - `confirm`: Yes/No confirmation prompts
398
+ - `select`: Choice from a list of options
399
+ - `password`: Masked input for sensitive data
400
+
401
+ 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}}`).
311
402
 
312
403
  #### Step Configuration
313
404
 
@@ -705,6 +796,100 @@ For most workflows, you'll mainly use `response` to access the current step's re
705
796
 
706
797
  ## Advanced Features
707
798
 
799
+ ### Workflow Metadata
800
+
801
+ 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.
802
+
803
+ #### Setting Metadata
804
+
805
+ Metadata can be set by custom Ruby steps that extend `BaseStep`:
806
+
807
+ ```ruby
808
+ # workflow/analyze_codebase.rb
809
+ class AnalyzeCodebase < Roast::Workflow::BaseStep
810
+ include Roast::Helpers::MetadataAccess
811
+
812
+ def call
813
+ # Perform analysis
814
+ analysis_results = perform_deep_analysis
815
+
816
+ # Store metadata for other steps to use
817
+ workflow.metadata[name.to_s] ||= {}
818
+ workflow.metadata[name.to_s]["total_files"] = analysis_results[:file_count]
819
+ workflow.metadata[name.to_s]["complexity_score"] = analysis_results[:complexity]
820
+ workflow.metadata[name.to_s]["analysis_id"] = SecureRandom.uuid
821
+
822
+ # Return the normal output for the conversation
823
+ "Analyzed #{analysis_results[:file_count]} files with average complexity of #{analysis_results[:complexity]}"
824
+ end
825
+
826
+ private
827
+
828
+ def perform_deep_analysis
829
+ # Your analysis logic here
830
+ { file_count: 42, complexity: 7.5 }
831
+ end
832
+ end
833
+ ```
834
+
835
+ #### Accessing Metadata
836
+
837
+ Metadata from previous steps can be accessed in:
838
+
839
+ 1. **Custom Ruby steps:**
840
+ ```ruby
841
+ class GenerateReport < Roast::Workflow::BaseStep
842
+ def call
843
+ # Access metadata from a previous step
844
+ total_files = workflow.metadata.dig("analyze_codebase", "total_files")
845
+ complexity = workflow.metadata.dig("analyze_codebase", "complexity_score")
846
+
847
+ "Generated report for #{total_files} files with complexity score: #{complexity}"
848
+ end
849
+ end
850
+ ```
851
+
852
+ 2. **Workflow configuration via interpolation:**
853
+ ```yaml
854
+ steps:
855
+ - analyze_codebase
856
+ - validate_threshold
857
+ - generate_report
858
+
859
+ # Use metadata in step configuration
860
+ validate_threshold:
861
+ if: "{{metadata.analyze_codebase.complexity_score > 8.0}}"
862
+ then:
863
+ - send_alert
864
+ - create_ticket
865
+ else:
866
+ - mark_as_passed
867
+
868
+ # Pass metadata to command steps
869
+ send_alert:
870
+ $(slack-notify "High complexity detected: {{metadata.analyze_codebase.complexity_score}}")
871
+ ```
872
+
873
+ 3. **Prompt templates (ERB):**
874
+ ```erb
875
+ # In analyze_codebase/output.txt
876
+ Analysis Summary:
877
+ Files analyzed: <%= workflow.metadata.dig(name.to_s, "total_files") %>
878
+ Complexity score: <%= workflow.metadata.dig(name.to_s, "complexity_score") %>
879
+ Analysis ID: <%= workflow.metadata.dig(name.to_s, "analysis_id") %>
880
+ ```
881
+
882
+ #### Metadata Best Practices
883
+
884
+ - **Use metadata for data that shouldn't be in the conversation**
885
+ - **Don't duplicate output data:** Metadata complements the output hash, it doesn't replace it
886
+
887
+ The metadata system is particularly useful for:
888
+ - Tracking session or transaction IDs across multiple steps
889
+ - Storing configuration or state that tools need to access
890
+ - Passing data between steps without cluttering the AI conversation
891
+ - Implementing complex conditional logic based on computed values
892
+
708
893
  ### Instrumentation
709
894
 
710
895
  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).
@@ -727,11 +912,16 @@ tools:
727
912
  - yarn
728
913
  - Roast::Tools::CodingAgent: # Optional configuration
729
914
  coding_agent_command: claude --model opus -p --allowedTools "Bash, Glob, Grep, LS, Read"
915
+ model: opus # Model to use for all CodingAgent invocations
916
+ retries: 3 # Number of automatic retries on failure (default: 0)
730
917
  ```
731
918
 
732
919
  Currently supported configurations:
733
920
  - `Roast::Tools::Cmd` via `allowed_commands`: restricts which commands can be executed (defaults to: `pwd`, `find`, `ls`, `rake`, `ruby`, `dev`, `mkdir`)
734
- - `Roast::Tools::CodingAgent` via `coding_agent_command`: customizes the Claude Code CLI command used by the agent
921
+ - `Roast::Tools::CodingAgent` via:
922
+ - `coding_agent_command`: customizes the Claude Code CLI command used by the agent
923
+ - `model`: sets the model for all CodingAgent invocations (e.g., `opus`, `sonnet`)
924
+ - `retries`: number of times to automatically retry if the agent encounters an error (default: 0, no retries)
735
925
 
736
926
  ##### Cmd Tool Configuration
737
927
 
@@ -959,16 +1149,25 @@ bash(command: "ps aux | grep ruby | awk '{print $2}'")
959
1149
  Creates a specialized agent for complex coding tasks or long-running operations.
960
1150
 
961
1151
  ```ruby
1152
+ # Basic usage
1153
+ coding_agent(
1154
+ prompt: "Refactor the authentication module to use JWT tokens",
1155
+ include_context_summary: true, # Include workflow context in the agent prompt
1156
+ continue: true # Continue from previous agent session
1157
+ )
1158
+
1159
+ # With automatic retries on failure
962
1160
  coding_agent(
963
- task: "Refactor the authentication module to use JWT tokens",
964
- language: "ruby",
965
- files: ["app/models/user.rb", "app/controllers/auth_controller.rb"]
1161
+ prompt: "Implement complex feature with error handling",
1162
+ retries: 3 # Retry up to 3 times if the agent encounters errors
966
1163
  )
967
1164
  ```
968
1165
 
969
- - Delegates complex tasks to a specialized coding agent
1166
+ - Delegates complex tasks to a specialized coding agent (Claude Code)
970
1167
  - Useful for tasks that require deep code understanding or multi-step changes
971
1168
  - Can work across multiple files and languages
1169
+ - Supports automatic retries on transient failures (network issues, API errors)
1170
+ - Retries can be configured globally (see Tool Configuration) or per invocation
972
1171
 
973
1172
  ### MCP (Model Context Protocol) Tools
974
1173
 
@@ -0,0 +1,20 @@
1
+ name: CodingAgent with Model Configuration
2
+ description: |
3
+ Example workflow demonstrating how to configure the CodingAgent tool
4
+ with specific model options like opus
5
+
6
+ tools:
7
+ - Roast::Tools::CodingAgent:
8
+ model: opus
9
+ # You can also add other claude options here:
10
+ # temperature: 0.7
11
+ # max_tokens: 1000
12
+
13
+ steps:
14
+ - analyze_code: |
15
+ Analyze the Ruby code in lib/roast/tools/coding_agent.rb
16
+ and explain how the CodingAgent tool works.
17
+
18
+ - implement_feature: |
19
+ Create a simple Ruby script that demonstrates using command-line
20
+ options similar to how CodingAgent builds its commands.
@@ -0,0 +1,30 @@
1
+ name: CodingAgent with Retries Configuration
2
+ description: |
3
+ Example workflow demonstrating how to configure the CodingAgent tool
4
+ with automatic retries on failure. The retries option will automatically
5
+ retry the coding agent if it encounters an error during execution.
6
+ Note: this is not the same as running the step
7
+
8
+ tools:
9
+ - Roast::Tools::CodingAgent:
10
+ retries: 2 # Automatically retry up to 2 times on failure
11
+
12
+ steps:
13
+ # This step invokes the coding agent directly using the specified number of retries
14
+ - ^implement_a_feature: |
15
+ Create a Ruby script that demonstrates robust error handling.
16
+ The script should:
17
+ 1. Attempt to read a file that might not exist
18
+ 2. Handle any errors gracefully
19
+ 3. Log the results
20
+
21
+
22
+ # This step invokes the general workflow LLM which can in turn invoke the coding agent.
23
+ # When the general LLM invokes the coding agent, it will execute with the specified number of retries.
24
+ - add_tests: |
25
+ Add test for the feature you just implemented.
26
+ Run the tests and iterate until they all pass.
27
+ Use the CodingAgent tool to write the tests.
28
+
29
+ add_tests:
30
+ retries: 4
@@ -4,7 +4,7 @@
4
4
  require "rubygems"
5
5
  require "bundler/setup"
6
6
 
7
- require_relative "../../lib/roast/helpers/minitest_coverage_runner"
7
+ require "roast/helpers/minitest_coverage_runner"
8
8
 
9
9
  # Suppress fancy minitest reporting
10
10
  ENV["RM_INFO"] = "true"
data/lib/roast/errors.rb CHANGED
@@ -7,5 +7,8 @@ module Roast
7
7
 
8
8
  # Custom error for when API authentication fails
9
9
  class AuthenticationError < StandardError; end
10
+
11
+ # Exit the app, for instance via Ctrl-C during an InputStep
12
+ class ExitEarly < StandardError; end
10
13
  end
11
14
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Roast
4
+ module Helpers
5
+ module MetadataAccess
6
+ def step_metadata(step_name = nil)
7
+ step_name ||= current_step_name
8
+ return {} unless step_name
9
+
10
+ metadata = workflow_metadata || {}
11
+ metadata[step_name] || {}
12
+ end
13
+
14
+ def set_current_step_metadata(key, value)
15
+ step_name = current_step_name
16
+ metadata = workflow_metadata
17
+
18
+ return unless step_name && metadata
19
+
20
+ metadata[step_name] ||= {}
21
+ metadata[step_name][key] = value
22
+ end
23
+
24
+ private
25
+
26
+ def workflow_metadata
27
+ metadata = Thread.current[:workflow_metadata]
28
+ Roast::Helpers::Logger.warn("MetadataAccess#workflow_metadata is not present") if metadata.nil?
29
+ metadata
30
+ end
31
+
32
+ def current_step_name
33
+ step_name = Thread.current[:current_step_name]
34
+ Roast::Helpers::Logger.warn("MetadataAccess#current_step_name is not present") if step_name.nil?
35
+ step_name
36
+ end
37
+ end
38
+ end
39
+ end
@@ -17,7 +17,7 @@ module Roast
17
17
  # output, status = TimeoutHandler.call("pwd", timeout: 10, working_directory: "/tmp")
18
18
  class TimeoutHandler
19
19
  DEFAULT_TIMEOUT = 30
20
- MAX_TIMEOUT = 300
20
+ MAX_TIMEOUT = 3600
21
21
 
22
22
  class << self
23
23
  # Execute a command with timeout using Open3 with proper process cleanup