roast-ai 0.4.6 → 0.4.8
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/.github/workflows/ci.yaml +3 -1
- data/.gitignore +7 -0
- data/.rubocop.yml +14 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +9 -1
- data/Rakefile +16 -4
- data/examples/README.md +9 -0
- data/examples/available_tools_demo/workflow.yml +2 -2
- data/examples/basic_prompt_workflow/workflow.md +1 -0
- data/examples/basic_prompt_workflow/workflow.yml +14 -0
- data/lib/roast/dsl/executor.rb +2 -1
- data/lib/roast/helpers/cmd_runner.rb +199 -0
- data/lib/roast/initializers.rb +1 -1
- data/lib/roast/tools/apply_diff.rb +1 -1
- data/lib/roast/tools/bash.rb +4 -4
- data/lib/roast/tools/cmd.rb +3 -5
- data/lib/roast/tools/coding_agent.rb +1 -1
- data/lib/roast/tools/grep.rb +6 -2
- data/lib/roast/tools/read_file.rb +2 -1
- data/lib/roast/tools/swarm.rb +2 -7
- data/lib/roast/tools.rb +10 -1
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_step.rb +2 -3
- data/lib/roast/workflow/command_executor.rb +3 -3
- data/lib/roast/workflow/resource_resolver.rb +1 -1
- data/lib/roast/workflow/shell_script_step.rb +1 -1
- data/lib/roast/workflow/step_loader.rb +2 -7
- data/lib/roast.rb +7 -1
- data/rubocop/cop/roast/use_cmd_runner.rb +93 -0
- data/rubocop/cop/roast.rb +4 -0
- data/sorbet/rbi/gems/docile@1.4.1.rbi +377 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +233 -2
- data/sorbet/rbi/gems/racc@1.8.1.rbi +6 -4
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +396 -2
- data/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +3788 -2
- data/sorbet/rbi/gems/rubocop-ast@1.45.1.rbi +7747 -2
- data/sorbet/rbi/gems/rubocop-sorbet@0.10.5.rbi +2386 -0
- data/sorbet/rbi/gems/rubocop@1.77.0.rbi +62813 -2
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1311 -2
- data/sorbet/rbi/gems/simplecov-html@0.13.2.rbi +225 -0
- data/sorbet/rbi/gems/simplecov@0.22.0.rbi +2259 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +9 -0
- data/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +125 -2
- data/sorbet/rbi/gems/unicode-emoji@4.0.4.rbi +244 -2
- data/sorbet/tapioca/require.rb +2 -1
- metadata +12 -240
- data/CHANGELOG.md +0 -364
- data/examples/agent_continue/add_documentation/prompt.md +0 -5
- data/examples/agent_continue/add_error_handling/prompt.md +0 -5
- data/examples/agent_continue/analyze_codebase/prompt.md +0 -7
- data/examples/agent_continue/combined_workflow.yml +0 -24
- data/examples/agent_continue/continue_adding_features/prompt.md +0 -4
- data/examples/agent_continue/create_integration_tests/prompt.md +0 -3
- data/examples/agent_continue/document_with_context/prompt.md +0 -5
- data/examples/agent_continue/explore_api/prompt.md +0 -6
- data/examples/agent_continue/implement_client/prompt.md +0 -6
- data/examples/agent_continue/inline_workflow.yml +0 -20
- data/examples/agent_continue/refactor_code/prompt.md +0 -2
- data/examples/agent_continue/verify_changes/prompt.md +0 -6
- data/examples/agent_continue/workflow.yml +0 -27
- data/examples/agent_workflow/README.md +0 -75
- data/examples/agent_workflow/apply_refactorings/prompt.md +0 -22
- data/examples/agent_workflow/identify_code_smells/prompt.md +0 -15
- data/examples/agent_workflow/summarize_improvements/prompt.md +0 -18
- data/examples/agent_workflow/workflow.png +0 -0
- data/examples/agent_workflow/workflow.yml +0 -16
- data/examples/api_workflow/README.md +0 -85
- data/examples/api_workflow/fetch_api_data/prompt.md +0 -10
- data/examples/api_workflow/generate_report/prompt.md +0 -10
- data/examples/api_workflow/prompt.md +0 -10
- data/examples/api_workflow/transform_data/prompt.md +0 -10
- data/examples/api_workflow/workflow.png +0 -0
- data/examples/api_workflow/workflow.yml +0 -30
- data/examples/apply_diff_demo/README.md +0 -58
- data/examples/apply_diff_demo/apply_simple_change/prompt.md +0 -13
- data/examples/apply_diff_demo/create_sample_file/prompt.md +0 -11
- data/examples/apply_diff_demo/workflow.yml +0 -24
- data/examples/available_tools_demo/workflow.png +0 -0
- data/examples/bash_prototyping/README.md +0 -53
- data/examples/bash_prototyping/analyze_network/prompt.md +0 -13
- data/examples/bash_prototyping/analyze_system/prompt.md +0 -11
- data/examples/bash_prototyping/api_testing.png +0 -0
- data/examples/bash_prototyping/api_testing.yml +0 -14
- data/examples/bash_prototyping/check_processes/prompt.md +0 -11
- data/examples/bash_prototyping/generate_report/prompt.md +0 -16
- data/examples/bash_prototyping/process_json_response/prompt.md +0 -24
- data/examples/bash_prototyping/system_analysis.png +0 -0
- data/examples/bash_prototyping/system_analysis.yml +0 -14
- data/examples/bash_prototyping/test_public_api/prompt.md +0 -22
- data/examples/case_when/README.md +0 -58
- data/examples/case_when/detect_language/prompt.md +0 -16
- data/examples/case_when/workflow.png +0 -0
- data/examples/case_when/workflow.yml +0 -58
- data/examples/cmd/README.md +0 -99
- data/examples/cmd/analyze_project/prompt.md +0 -57
- data/examples/cmd/basic_demo/prompt.md +0 -48
- data/examples/cmd/basic_workflow.png +0 -0
- data/examples/cmd/basic_workflow.yml +0 -17
- data/examples/cmd/check_repository/prompt.md +0 -57
- data/examples/cmd/create_and_verify/prompt.md +0 -56
- data/examples/cmd/dev_workflow.png +0 -0
- data/examples/cmd/dev_workflow.yml +0 -26
- data/examples/cmd/explore_project/prompt.md +0 -67
- data/examples/cmd/explorer_workflow.png +0 -0
- data/examples/cmd/explorer_workflow.yml +0 -21
- data/examples/cmd/smart_tool_selection/prompt.md +0 -99
- data/examples/coding_agent_with_model.yml +0 -20
- data/examples/coding_agent_with_retries.yml +0 -30
- data/examples/conditional/README.md +0 -161
- data/examples/conditional/check_condition/prompt.md +0 -1
- data/examples/conditional/simple_workflow.png +0 -0
- data/examples/conditional/simple_workflow.yml +0 -15
- data/examples/conditional/workflow.png +0 -0
- data/examples/conditional/workflow.yml +0 -23
- data/examples/context_management_demo/README.md +0 -43
- data/examples/context_management_demo/workflow.yml +0 -42
- data/examples/direct_coerce_syntax/README.md +0 -32
- data/examples/direct_coerce_syntax/workflow.png +0 -0
- data/examples/direct_coerce_syntax/workflow.yml +0 -36
- data/examples/dot_notation/README.md +0 -37
- data/examples/dot_notation/workflow.png +0 -0
- data/examples/dot_notation/workflow.yml +0 -44
- data/examples/exit_on_error/README.md +0 -50
- data/examples/exit_on_error/analyze_lint_output/prompt.md +0 -9
- data/examples/exit_on_error/apply_fixes/prompt.md +0 -2
- data/examples/exit_on_error/workflow.png +0 -0
- data/examples/exit_on_error/workflow.yml +0 -19
- data/examples/grading/README.md +0 -71
- data/examples/grading/analyze_coverage/prompt.md +0 -52
- data/examples/grading/calculate_final_grade.rb +0 -67
- data/examples/grading/format_result.rb +0 -64
- data/examples/grading/generate_grades/prompt.md +0 -105
- data/examples/grading/generate_recommendations/output.txt +0 -17
- data/examples/grading/generate_recommendations/prompt.md +0 -60
- data/examples/grading/js_test_runner +0 -31
- data/examples/grading/rb_test_runner +0 -19
- data/examples/grading/read_dependencies/prompt.md +0 -16
- data/examples/grading/run_coverage.rb +0 -54
- data/examples/grading/verify_mocks_and_stubs/prompt.md +0 -12
- data/examples/grading/verify_test_helpers/prompt.md +0 -53
- data/examples/grading/workflow.md +0 -8
- data/examples/grading/workflow.png +0 -0
- data/examples/grading/workflow.rb.md +0 -6
- data/examples/grading/workflow.ts+tsx.md +0 -6
- data/examples/grading/workflow.yml +0 -41
- data/examples/instrumentation.rb +0 -76
- data/examples/interpolation/README.md +0 -50
- data/examples/interpolation/analyze_file/prompt.md +0 -1
- data/examples/interpolation/analyze_patterns/prompt.md +0 -27
- data/examples/interpolation/generate_report_for_js/prompt.md +0 -3
- data/examples/interpolation/generate_report_for_rb/prompt.md +0 -3
- data/examples/interpolation/sample.js +0 -48
- data/examples/interpolation/sample.rb +0 -42
- data/examples/interpolation/workflow.md +0 -1
- data/examples/interpolation/workflow.png +0 -0
- data/examples/interpolation/workflow.yml +0 -21
- data/examples/iteration/IMPLEMENTATION.md +0 -88
- data/examples/iteration/README.md +0 -68
- data/examples/iteration/analyze_complexity/prompt.md +0 -22
- data/examples/iteration/generate_recommendations/prompt.md +0 -21
- data/examples/iteration/generate_report/prompt.md +0 -129
- data/examples/iteration/implement_fix/prompt.md +0 -25
- data/examples/iteration/prioritize_issues/prompt.md +0 -24
- data/examples/iteration/prompts/analyze_file.md +0 -28
- data/examples/iteration/prompts/generate_summary.md +0 -24
- data/examples/iteration/prompts/update_report.md +0 -29
- data/examples/iteration/prompts/write_report.md +0 -22
- data/examples/iteration/read_file/prompt.md +0 -9
- data/examples/iteration/select_next_issue/prompt.md +0 -25
- data/examples/iteration/simple_workflow.md +0 -39
- data/examples/iteration/simple_workflow.yml +0 -58
- data/examples/iteration/update_fix_count/prompt.md +0 -26
- data/examples/iteration/verify_fix/prompt.md +0 -29
- data/examples/iteration/workflow.png +0 -0
- data/examples/iteration/workflow.yml +0 -42
- data/examples/json_handling/README.md +0 -32
- data/examples/json_handling/workflow.png +0 -0
- data/examples/json_handling/workflow.yml +0 -52
- data/examples/mcp/README.md +0 -223
- data/examples/mcp/analyze_changes/prompt.md +0 -8
- data/examples/mcp/analyze_issues/prompt.md +0 -4
- data/examples/mcp/analyze_schema/prompt.md +0 -4
- data/examples/mcp/check_data_quality/prompt.md +0 -5
- data/examples/mcp/check_documentation/prompt.md +0 -4
- data/examples/mcp/create_recommendations/prompt.md +0 -5
- data/examples/mcp/database_workflow.png +0 -0
- data/examples/mcp/database_workflow.yml +0 -29
- data/examples/mcp/env_demo/workflow.png +0 -0
- data/examples/mcp/env_demo/workflow.yml +0 -34
- data/examples/mcp/fetch_pr_context/prompt.md +0 -4
- data/examples/mcp/filesystem_demo/create_test_file/prompt.md +0 -2
- data/examples/mcp/filesystem_demo/list_files/prompt.md +0 -6
- data/examples/mcp/filesystem_demo/read_with_mcp/prompt.md +0 -7
- data/examples/mcp/filesystem_demo/workflow.png +0 -0
- data/examples/mcp/filesystem_demo/workflow.yml +0 -38
- data/examples/mcp/generate_insights/prompt.md +0 -4
- data/examples/mcp/generate_report/prompt.md +0 -6
- data/examples/mcp/generate_review/prompt.md +0 -16
- data/examples/mcp/github_workflow.png +0 -0
- data/examples/mcp/github_workflow.yml +0 -32
- data/examples/mcp/multi_mcp_workflow.png +0 -0
- data/examples/mcp/multi_mcp_workflow.yml +0 -58
- data/examples/mcp/post_review/prompt.md +0 -3
- data/examples/mcp/save_report/prompt.md +0 -6
- data/examples/mcp/search_issues/prompt.md +0 -2
- data/examples/mcp/summarize/prompt.md +0 -1
- data/examples/mcp/test_filesystem/prompt.md +0 -6
- data/examples/mcp/test_github/prompt.md +0 -8
- data/examples/mcp/test_read/prompt.md +0 -1
- data/examples/mcp/workflow.png +0 -0
- data/examples/mcp/workflow.yml +0 -35
- data/examples/no_model_fallback/README.md +0 -17
- data/examples/no_model_fallback/analyze_file/prompt.md +0 -1
- data/examples/no_model_fallback/analyze_patterns/prompt.md +0 -27
- data/examples/no_model_fallback/generate_report_for_md/prompt.md +0 -10
- data/examples/no_model_fallback/generate_report_for_rb/prompt.md +0 -3
- data/examples/no_model_fallback/sample.rb +0 -42
- data/examples/no_model_fallback/workflow.yml +0 -19
- data/examples/openrouter_example/README.md +0 -48
- data/examples/openrouter_example/analyze_input/prompt.md +0 -16
- data/examples/openrouter_example/generate_response/prompt.md +0 -9
- data/examples/openrouter_example/workflow.png +0 -0
- data/examples/openrouter_example/workflow.yml +0 -12
- data/examples/pre_post_processing/README.md +0 -111
- data/examples/pre_post_processing/analyze_test_file/prompt.md +0 -23
- data/examples/pre_post_processing/improve_test_coverage/prompt.md +0 -17
- data/examples/pre_post_processing/optimize_test_performance/prompt.md +0 -25
- data/examples/pre_post_processing/post_processing/aggregate_metrics/prompt.md +0 -31
- data/examples/pre_post_processing/post_processing/cleanup_environment/prompt.md +0 -28
- data/examples/pre_post_processing/post_processing/generate_summary_report/prompt.md +0 -32
- data/examples/pre_post_processing/post_processing/output.txt +0 -24
- data/examples/pre_post_processing/pre_processing/gather_baseline_metrics/prompt.md +0 -26
- data/examples/pre_post_processing/pre_processing/setup_test_environment/prompt.md +0 -11
- data/examples/pre_post_processing/validate_changes/prompt.md +0 -24
- data/examples/pre_post_processing/workflow.png +0 -0
- data/examples/pre_post_processing/workflow.yml +0 -21
- data/examples/retry/workflow.yml +0 -23
- data/examples/rspec_to_minitest/README.md +0 -68
- data/examples/rspec_to_minitest/analyze_spec/prompt.md +0 -30
- data/examples/rspec_to_minitest/create_minitest/prompt.md +0 -33
- data/examples/rspec_to_minitest/run_and_improve/prompt.md +0 -35
- data/examples/rspec_to_minitest/workflow.md +0 -10
- data/examples/rspec_to_minitest/workflow.png +0 -0
- data/examples/rspec_to_minitest/workflow.yml +0 -40
- data/examples/shared_config/README.md +0 -52
- data/examples/shared_config/example_with_shared_config/workflow.png +0 -0
- data/examples/shared_config/example_with_shared_config/workflow.yml +0 -6
- data/examples/shared_config/shared.png +0 -0
- data/examples/shared_config/shared.yml +0 -7
- data/examples/single_target_prepost/README.md +0 -36
- data/examples/single_target_prepost/post_processing/output.txt +0 -27
- data/examples/single_target_prepost/pre_processing/gather_dependencies/prompt.md +0 -11
- data/examples/single_target_prepost/workflow.png +0 -0
- data/examples/single_target_prepost/workflow.yml +0 -20
- data/examples/smart_coercion_defaults/README.md +0 -65
- data/examples/smart_coercion_defaults/workflow.png +0 -0
- data/examples/smart_coercion_defaults/workflow.yml +0 -44
- data/examples/step_configuration/README.md +0 -84
- data/examples/step_configuration/workflow.png +0 -0
- data/examples/step_configuration/workflow.yml +0 -57
- data/examples/swarm_example.yml +0 -25
- data/examples/tool_config_example/README.md +0 -109
- data/examples/tool_config_example/example_step/prompt.md +0 -42
- data/examples/tool_config_example/workflow.png +0 -0
- data/examples/tool_config_example/workflow.yml +0 -17
- data/examples/user_input/README.md +0 -90
- data/examples/user_input/funny_name/create_backstory/prompt.md +0 -10
- data/examples/user_input/funny_name/workflow.png +0 -0
- data/examples/user_input/funny_name/workflow.yml +0 -26
- data/examples/user_input/generate_summary/prompt.md +0 -11
- data/examples/user_input/simple_input_demo/workflow.png +0 -0
- data/examples/user_input/simple_input_demo/workflow.yml +0 -35
- data/examples/user_input/survey_workflow.png +0 -0
- data/examples/user_input/survey_workflow.yml +0 -71
- data/examples/user_input/welcome_message/prompt.md +0 -3
- data/examples/user_input/workflow.png +0 -0
- data/examples/user_input/workflow.yml +0 -73
- data/examples/workflow_generator/README.md +0 -27
- data/examples/workflow_generator/analyze_user_request/prompt.md +0 -34
- data/examples/workflow_generator/create_workflow_files/prompt.md +0 -32
- data/examples/workflow_generator/get_user_input/prompt.md +0 -14
- data/examples/workflow_generator/info_from_roast.rb +0 -22
- data/examples/workflow_generator/workflow.png +0 -0
- data/examples/workflow_generator/workflow.yml +0 -34
- data/lib/roast/helpers/timeout_handler.rb +0 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0019e9658942394301e91581ecc45b259c76f1999f17cc8f1ddf4f97692ea639'
|
4
|
+
data.tar.gz: feb3d19b4316a2ea2751347a614f816c22cbd53182821eed661488c760a9897a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8c5442247e1e0aae1cb7aa4509a3724fb1867bf9b05b15cfe1da4a8287db1db8229026ad3be66cc450b57650e8dd00d4eca632021680f3320d2bbf8dd19fc89
|
7
|
+
data.tar.gz: bbf18e07720a9cfe48abc2780f0f2cdba7d091bb3b2103318556910693dc54b09980ac0f2133876a811700345cdb6fa9e32d87cd4622f9039fe1bbbb7ffe3151
|
data/.github/workflows/ci.yaml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
2
2
|
|
3
|
+
require:
|
4
|
+
- ./rubocop/cop/roast
|
5
|
+
|
3
6
|
plugins:
|
4
7
|
- rubocop-sorbet
|
5
8
|
|
@@ -23,3 +26,14 @@ Sorbet/FalseSigil:
|
|
23
26
|
Exclude:
|
24
27
|
- "test/**/*"
|
25
28
|
- "examples/**/*"
|
29
|
+
|
30
|
+
Roast/UseCmdRunner:
|
31
|
+
Enabled: true
|
32
|
+
Exclude:
|
33
|
+
- 'lib/roast/helpers/cmd_runner.rb'
|
34
|
+
- 'roast.gemspec'
|
35
|
+
|
36
|
+
Style/MethodCallWithArgsParentheses:
|
37
|
+
Enabled: true
|
38
|
+
Exclude:
|
39
|
+
- 'test/**/*.rb'
|
data/Gemfile
CHANGED
@@ -16,9 +16,10 @@ gem "mocha"
|
|
16
16
|
gem "rake", require: false
|
17
17
|
gem "rubocop-shopify", require: false
|
18
18
|
gem "rubocop-sorbet", require: false
|
19
|
+
gem "simplecov", require: false
|
20
|
+
gem "minitest-rg"
|
19
21
|
gem "vcr", require: false
|
20
22
|
gem "webmock", require: false
|
21
|
-
gem "minitest-rg"
|
22
23
|
|
23
24
|
gem "sorbet", require: false
|
24
25
|
gem "tapioca", require: false
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
roast-ai (0.4.
|
4
|
+
roast-ai (0.4.8)
|
5
5
|
activesupport (>= 7.0)
|
6
6
|
cli-kit (~> 5.0)
|
7
7
|
cli-ui (= 2.3.0)
|
@@ -50,6 +50,7 @@ GEM
|
|
50
50
|
bigdecimal
|
51
51
|
rexml
|
52
52
|
diff-lcs (1.6.2)
|
53
|
+
docile (1.4.1)
|
53
54
|
dotenv (3.1.8)
|
54
55
|
drb (2.2.3)
|
55
56
|
dry-configurable (1.3.0)
|
@@ -214,6 +215,12 @@ GEM
|
|
214
215
|
ruby2_keywords (0.0.5)
|
215
216
|
securerandom (0.4.1)
|
216
217
|
shellany (0.0.1)
|
218
|
+
simplecov (0.22.0)
|
219
|
+
docile (~> 1.1)
|
220
|
+
simplecov-html (~> 0.11)
|
221
|
+
simplecov_json_formatter (~> 0.1)
|
222
|
+
simplecov-html (0.13.2)
|
223
|
+
simplecov_json_formatter (0.1.4)
|
217
224
|
sorbet (0.5.12414)
|
218
225
|
sorbet-static (= 0.5.12414)
|
219
226
|
sorbet-runtime (0.5.12414)
|
@@ -277,6 +284,7 @@ DEPENDENCIES
|
|
277
284
|
roast-ai!
|
278
285
|
rubocop-shopify
|
279
286
|
rubocop-sorbet
|
287
|
+
simplecov
|
280
288
|
sorbet
|
281
289
|
tapioca
|
282
290
|
vcr
|
data/Rakefile
CHANGED
@@ -4,20 +4,32 @@ require "bundler/gem_tasks"
|
|
4
4
|
require "rubocop/rake_task"
|
5
5
|
require "rake/testtask"
|
6
6
|
|
7
|
-
Rake::TestTask.new(:
|
7
|
+
Rake::TestTask.new(:minitest_all) do |t|
|
8
8
|
t.libs << "test"
|
9
9
|
t.libs << "lib"
|
10
10
|
t.test_files = FileList["test/**/*_test.rb"]
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
Rake::TestTask.new(:minitest_functional) do |t|
|
14
|
+
t.libs << "test"
|
15
|
+
t.libs << "lib"
|
16
|
+
t.test_files = FileList["test/functional/**/*_test.rb"]
|
17
|
+
end
|
14
18
|
|
15
|
-
|
19
|
+
Rake::TestTask.new(:minitest_old) do |t|
|
20
|
+
t.libs << "test"
|
21
|
+
t.libs << "lib"
|
22
|
+
t.test_files = FileList["test/functional/**/*_test.rb"]
|
23
|
+
end
|
16
24
|
|
17
|
-
task
|
25
|
+
task test: [:minitest_all]
|
26
|
+
|
27
|
+
RuboCop::RakeTask.new(:rubocop_ci)
|
18
28
|
|
19
29
|
RuboCop::RakeTask.new(:rubocop) do |task|
|
20
30
|
task.options = ["--autocorrect"]
|
21
31
|
end
|
22
32
|
|
23
33
|
task default: [:test, :rubocop]
|
34
|
+
|
35
|
+
task lint: [:rubocop]
|
data/examples/README.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
These examples demonstrate various usages and features of Roast. They are automatically tested and verified by functional tests (see `test/functional/roast_examples_test.rb`).
|
2
|
+
|
3
|
+
### available_tools_demo
|
4
|
+
|
5
|
+
Demonstrates use of Roast tool implementations in LLM steps. Steps are implemented with `prompt.md` in directories that match the step name
|
6
|
+
|
7
|
+
### basic_prompt_workflow
|
8
|
+
|
9
|
+
Demonstrates using LLM steps to glean insights from a provided data file (`test/fixtures/sample_data/skateboard_orders.csv`).
|
@@ -0,0 +1 @@
|
|
1
|
+
You are a skateboard shop owner looking for insights on their business.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
name: Analyze My Business
|
2
|
+
description: Examines a CSV list of orders for insights
|
3
|
+
|
4
|
+
# Default model for all steps
|
5
|
+
model: google:gemini-2.5-flash
|
6
|
+
tools:
|
7
|
+
- Roast::Tools::ReadFile
|
8
|
+
- Roast::Tools::Grep
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- Read the provided CSV files.
|
12
|
+
- "Where are most of my customers from?"
|
13
|
+
- "What is my most popular product?"
|
14
|
+
- Summarize the insights from the report.
|
data/lib/roast/dsl/executor.rb
CHANGED
@@ -0,0 +1,199 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module Helpers
|
6
|
+
class CmdRunner
|
7
|
+
DEFAULT_TIMEOUT = 30
|
8
|
+
MAX_TIMEOUT = 3600 # 1 hour
|
9
|
+
|
10
|
+
@child_processes = {}
|
11
|
+
@child_processes_mutex = Mutex.new
|
12
|
+
|
13
|
+
class << self
|
14
|
+
#: (*untyped, **untyped) -> [String, Process::Status]
|
15
|
+
def capture2(*args, **options)
|
16
|
+
args = args #: as untyped
|
17
|
+
stdout, _stderr, status = capture3(*args, **options)
|
18
|
+
[stdout, status]
|
19
|
+
end
|
20
|
+
|
21
|
+
#: (*untyped, **untyped) -> [String, Process::Status]
|
22
|
+
def capture2e(*args, **options)
|
23
|
+
args = args #: as untyped
|
24
|
+
stdout, stderr, status = capture3(*args, **options)
|
25
|
+
combined_output = stdout + stderr
|
26
|
+
[combined_output, status]
|
27
|
+
end
|
28
|
+
|
29
|
+
#: (*untyped, **untyped) -> [String?, String?, Process::Status?]
|
30
|
+
def capture3(*args, **options)
|
31
|
+
args = args #: as untyped
|
32
|
+
popen3(*args, **options) do |stdin, stdout, stderr, wait_thr|
|
33
|
+
stdin.close # Prevent hanging on stdin-waiting commands
|
34
|
+
|
35
|
+
stdout_thread = threaded_read(stdout)
|
36
|
+
stderr_thread = threaded_read(stderr)
|
37
|
+
|
38
|
+
[stdout_thread.value, stderr_thread.value, wait_thr.value]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#: (*untyped, **untyped) -> bool
|
43
|
+
def system(*args, **options)
|
44
|
+
args = args #: as untyped
|
45
|
+
popen3(*args, **options) do |stdin, stdout, stderr, wait_thr|
|
46
|
+
stdin.close # Prevent hanging on stdin-waiting commands
|
47
|
+
|
48
|
+
stdout_thread = threaded_stream(from: stdout, to: $stdout)
|
49
|
+
stderr_thread = threaded_stream(from: stderr, to: $stderr)
|
50
|
+
|
51
|
+
stdout_thread.join
|
52
|
+
stderr_thread.join
|
53
|
+
|
54
|
+
wait_thr.value.success?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#: (*untyped, **untyped) ?{ (IO, IO, IO, Thread) -> untyped } -> [IO, IO, IO, Thread] | untyped
|
59
|
+
def popen3(*args, **options, &block)
|
60
|
+
args = args #: as untyped
|
61
|
+
|
62
|
+
timeout = options.delete(:timeout)
|
63
|
+
validate_timeout(timeout) unless timeout.nil?
|
64
|
+
|
65
|
+
raise ArgumentError, "Timeout provided but no block given" if !timeout.nil? && !block_given?
|
66
|
+
|
67
|
+
# Mirror Open3.popen3 behavior - if no block, return the IO objects and thread
|
68
|
+
unless block_given?
|
69
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(*args, **options)
|
70
|
+
track_child_process(wait_thr.pid, presentable_command(args))
|
71
|
+
return [stdin, stdout, stderr, wait_thr]
|
72
|
+
end
|
73
|
+
|
74
|
+
Open3.popen3(*args, **options) do |stdin, stdout, stderr, wait_thr|
|
75
|
+
track_child_process(wait_thr.pid, presentable_command(args))
|
76
|
+
|
77
|
+
runnable = proc { yield(stdin, stdout, stderr, wait_thr) } #: Proc
|
78
|
+
timeout.nil? ? runnable.call : Timeout.timeout(timeout, &runnable)
|
79
|
+
rescue Timeout::Error => e
|
80
|
+
raise e.class, "Command '#{presentable_command(args)}' timed out after #{timeout} seconds: #{e.message}"
|
81
|
+
ensure
|
82
|
+
cleanup_child_process(wait_thr.pid) unless wait_thr.nil?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#: -> void
|
87
|
+
def cleanup_all_children
|
88
|
+
Thread.new do # Thread to avoid issues with calling a mutex in a signal handler
|
89
|
+
child_processes = all_child_processes
|
90
|
+
Thread.current.exit if child_processes.empty?
|
91
|
+
|
92
|
+
child_processes.each do |pid, info|
|
93
|
+
Roast::Helpers::Logger.info("Cleaning up PID #{pid}: #{info[:command]}")
|
94
|
+
cleanup_child_process(pid)
|
95
|
+
end
|
96
|
+
end.join
|
97
|
+
end
|
98
|
+
|
99
|
+
#: (Integer?) -> Integer
|
100
|
+
def normalize_timeout(timeout)
|
101
|
+
return DEFAULT_TIMEOUT if timeout.nil? || timeout <= 0
|
102
|
+
|
103
|
+
[timeout, MAX_TIMEOUT].min
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
#: (Integer) -> void
|
109
|
+
def validate_timeout(timeout)
|
110
|
+
if timeout <= 0 || timeout > MAX_TIMEOUT
|
111
|
+
raise ArgumentError, "Invalid timeout value: #{timeout.inspect}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
#: (Array) -> String
|
116
|
+
def presentable_command(args)
|
117
|
+
args.flatten.map(&:to_s).join(" ")
|
118
|
+
end
|
119
|
+
|
120
|
+
#: (IO) -> Thread
|
121
|
+
def threaded_read(stream)
|
122
|
+
Thread.new do
|
123
|
+
buffer = ""
|
124
|
+
stream.each_line do |line|
|
125
|
+
buffer += line
|
126
|
+
end
|
127
|
+
buffer
|
128
|
+
rescue IOError => e
|
129
|
+
Roast::Helpers::Logger.debug("IOError while capturing output: #{e.message}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#: (from: IO, to: IO) -> Thread
|
134
|
+
def threaded_stream(from:, to:)
|
135
|
+
Thread.new do
|
136
|
+
from.each_line do |line|
|
137
|
+
to.puts(line)
|
138
|
+
end
|
139
|
+
rescue IOError => e
|
140
|
+
Roast::Helpers::Logger.debug("IOError while streaming output: #{e.message}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
#: (Integer, String) -> void
|
145
|
+
def track_child_process(pid, command)
|
146
|
+
@child_processes_mutex.synchronize do
|
147
|
+
@child_processes[pid] = {
|
148
|
+
command: command,
|
149
|
+
started_at: Time.now,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
#: (Integer) -> void
|
155
|
+
def untrack_child_process(pid)
|
156
|
+
@child_processes_mutex.synchronize { @child_processes.delete(pid) }
|
157
|
+
end
|
158
|
+
|
159
|
+
#: -> Hash[Integer, { command: String, started_at: Time }]
|
160
|
+
def all_child_processes
|
161
|
+
@child_processes_mutex.synchronize { @child_processes.dup }
|
162
|
+
end
|
163
|
+
|
164
|
+
#: (Integer) -> void
|
165
|
+
def cleanup_child_process(pid)
|
166
|
+
untrack_child_process(pid)
|
167
|
+
|
168
|
+
return unless process_running?(pid)
|
169
|
+
|
170
|
+
[0.1, 0.2, 0.5].each do |sleep_time|
|
171
|
+
Process.kill("TERM", pid)
|
172
|
+
break unless process_running?(pid)
|
173
|
+
|
174
|
+
sleep(sleep_time) # Grace period to let the process terminate
|
175
|
+
end
|
176
|
+
|
177
|
+
# Force kill if still alive
|
178
|
+
Process.kill("KILL", pid) if process_running?(pid)
|
179
|
+
rescue Errno::ESRCH
|
180
|
+
# Process already terminated, which is fine
|
181
|
+
rescue Errno::EPERM
|
182
|
+
# Permission denied - process may be owned by different user
|
183
|
+
Roast::Helpers::Logger.debug("Could not kill process #{pid}: Permission denied")
|
184
|
+
rescue => e
|
185
|
+
# Catch any other unexpected errors during cleanup
|
186
|
+
Roast::Helpers::Logger.debug("Unexpected error during process cleanup: #{e.message}")
|
187
|
+
end
|
188
|
+
|
189
|
+
#: (Integer) -> bool
|
190
|
+
def process_running?(pid)
|
191
|
+
Process.getpgid(pid)
|
192
|
+
true
|
193
|
+
rescue Errno::ESRCH
|
194
|
+
false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
data/lib/roast/initializers.rb
CHANGED
@@ -7,7 +7,7 @@ module Roast
|
|
7
7
|
def config_root(starting_path = Dir.pwd, ending_path = File.dirname(Dir.home))
|
8
8
|
paths = []
|
9
9
|
candidate = starting_path
|
10
|
-
while candidate != ending_path
|
10
|
+
while candidate != ending_path && candidate != "/"
|
11
11
|
paths << File.join(candidate, ".roast")
|
12
12
|
candidate = File.dirname(candidate)
|
13
13
|
end
|
@@ -93,7 +93,7 @@ module Roast
|
|
93
93
|
File.write(temp_file, updated_content)
|
94
94
|
|
95
95
|
# Run git diff
|
96
|
-
diff_output =
|
96
|
+
diff_output, _status = Roast::Helpers::CmdRunner.capture2e("git", "diff", "--no-index", "--no-prefix", file_path, temp_file)
|
97
97
|
|
98
98
|
if diff_output.empty?
|
99
99
|
Roast::Helpers::Logger.info("No differences found (files are identical)\n")
|
data/lib/roast/tools/bash.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
require "English"
|
5
5
|
require "roast/helpers/logger"
|
6
|
-
require "roast/helpers/timeout_handler"
|
7
6
|
|
8
7
|
module Roast
|
9
8
|
module Tools
|
@@ -33,13 +32,14 @@ module Roast
|
|
33
32
|
Roast::Helpers::Logger.warn("⚠️ WARNING: Unrestricted bash execution - use with caution!\n")
|
34
33
|
end
|
35
34
|
|
36
|
-
|
35
|
+
timeout = Roast::Helpers::CmdRunner.normalize_timeout(timeout)
|
36
|
+
|
37
|
+
result, status = Roast::Helpers::CmdRunner.capture2e(
|
37
38
|
"#{command} 2>&1",
|
38
39
|
timeout: timeout,
|
39
|
-
working_directory: Dir.pwd,
|
40
40
|
)
|
41
41
|
|
42
|
-
format_output(command, result,
|
42
|
+
format_output(command, result, status.exitstatus)
|
43
43
|
rescue Timeout::Error => e
|
44
44
|
Roast::Helpers::Logger.error(e.message + "\n")
|
45
45
|
e.message
|
data/lib/roast/tools/cmd.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
require "English"
|
5
5
|
require "roast/helpers/logger"
|
6
|
-
require "roast/helpers/timeout_handler"
|
7
6
|
|
8
7
|
module Roast
|
9
8
|
module Tools
|
@@ -140,7 +139,7 @@ module Roast
|
|
140
139
|
end
|
141
140
|
|
142
141
|
def execute_command(command, command_prefix, timeout)
|
143
|
-
timeout = Roast::Helpers::
|
142
|
+
timeout = Roast::Helpers::CmdRunner.normalize_timeout(timeout)
|
144
143
|
|
145
144
|
full_command = if command_prefix == "dev"
|
146
145
|
"bash -l -c '#{command.gsub("'", "\\'")}'"
|
@@ -148,13 +147,12 @@ module Roast
|
|
148
147
|
command
|
149
148
|
end
|
150
149
|
|
151
|
-
result,
|
150
|
+
result, status = Roast::Helpers::CmdRunner.capture2e(
|
152
151
|
full_command,
|
153
152
|
timeout: timeout,
|
154
|
-
working_directory: Dir.pwd,
|
155
153
|
)
|
156
154
|
|
157
|
-
format_output(command, result,
|
155
|
+
format_output(command, result, status.exitstatus)
|
158
156
|
rescue Timeout::Error => e
|
159
157
|
Roast::Helpers::Logger.error(e.message + "\n")
|
160
158
|
e.message
|
@@ -105,7 +105,7 @@ module Roast
|
|
105
105
|
command = "cat #{temp_file.path} | #{command_to_run}"
|
106
106
|
result = ""
|
107
107
|
|
108
|
-
|
108
|
+
Roast::Helpers::CmdRunner.popen3(command) do |stdin, stdout, stderr, wait_thread|
|
109
109
|
stdin.close
|
110
110
|
if expect_json_output
|
111
111
|
stdout.each_line do |line|
|
data/lib/roast/tools/grep.rb
CHANGED
@@ -28,13 +28,17 @@ module Roast
|
|
28
28
|
def call(string)
|
29
29
|
Roast::Helpers::Logger.info("🔍 Grepping for string: #{string}\n")
|
30
30
|
|
31
|
-
|
31
|
+
# Check if ripgrep is available by trying to run it with --version
|
32
|
+
unless Roast::Helpers::CmdRunner.system("rg --version > /dev/null 2>&1")
|
32
33
|
raise "ripgrep is not available. Please install it using your package manager (e.g., brew install rg) and make sure it's on your PATH."
|
33
34
|
end
|
34
35
|
|
35
36
|
# Use Open3 to safely pass the string as an argument, avoiding shell injection
|
36
37
|
cmd = ["rg", "-C", "4", "--trim", "--color=never", "--heading", "-F", "--", string, "."]
|
37
|
-
stdout,
|
38
|
+
stdout, stderr, status = Roast::Helpers::CmdRunner.capture3(*cmd)
|
39
|
+
unless status.success?
|
40
|
+
return "Error grepping for string: Command failed: #{stderr}"
|
41
|
+
end
|
38
42
|
|
39
43
|
# Limit output to MAX_RESULT_LINES
|
40
44
|
lines = stdout.lines
|
@@ -34,7 +34,8 @@ module Roast
|
|
34
34
|
path = File.expand_path(path)
|
35
35
|
Roast::Helpers::Logger.info("📖 Reading file: #{path}\n")
|
36
36
|
if File.directory?(path)
|
37
|
-
|
37
|
+
output, _status = Roast::Helpers::CmdRunner.capture2e("ls", "-la", path)
|
38
|
+
output
|
38
39
|
else
|
39
40
|
File.read(path)
|
40
41
|
end
|
data/lib/roast/tools/swarm.rb
CHANGED
@@ -85,14 +85,9 @@ module Roast
|
|
85
85
|
# Build the swarm command with proper escaping
|
86
86
|
command = build_swarm_command(prompt, config_path)
|
87
87
|
|
88
|
-
result = ""
|
89
|
-
|
90
88
|
# Execute the command directly with the prompt included
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
exit_status = $CHILD_STATUS.exitstatus
|
89
|
+
result, status = Roast::Helpers::CmdRunner.capture2e(command)
|
90
|
+
exit_status = status.exitstatus
|
96
91
|
|
97
92
|
format_output(command, result, exit_status)
|
98
93
|
end
|
data/lib/roast/tools.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "roast/helpers/cmd_runner"
|
5
|
+
|
4
6
|
module Roast
|
5
7
|
# @requires_ancestor: Kernel
|
6
8
|
module Tools
|
@@ -33,7 +35,14 @@ module Roast
|
|
33
35
|
Signal.trap("INT") do
|
34
36
|
puts "\n\nCaught CTRL-C! Printing before exiting:\n"
|
35
37
|
puts JSON.pretty_generate(object_to_inspect)
|
36
|
-
|
38
|
+
|
39
|
+
begin
|
40
|
+
Roast::Helpers::CmdRunner.cleanup_all_children
|
41
|
+
rescue => e
|
42
|
+
puts "Error interrupting tracked child processes: #{e.message}"
|
43
|
+
end
|
44
|
+
|
45
|
+
exit(130)
|
37
46
|
end
|
38
47
|
end
|
39
48
|
|
data/lib/roast/version.rb
CHANGED
@@ -10,10 +10,9 @@ module Roast
|
|
10
10
|
delegate :append_to_final_output, :transcript, to: :workflow
|
11
11
|
delegate_missing_to :workflow
|
12
12
|
|
13
|
-
|
14
|
-
def initialize(workflow, model: "anthropic:claude-opus-4", name: nil, context_path: nil)
|
13
|
+
def initialize(workflow, model: nil, name: nil, context_path: nil)
|
15
14
|
@workflow = workflow
|
16
|
-
@model = model
|
15
|
+
@model = model || workflow.model || StepLoader::DEFAULT_MODEL
|
17
16
|
@name = normalize_name(name)
|
18
17
|
@context_path = context_path || ContextPathResolver.resolve(self.class)
|
19
18
|
@print_response = false
|
@@ -24,14 +24,14 @@ module Roast
|
|
24
24
|
def execute(command_string, exit_on_error: true)
|
25
25
|
command = extract_command(command_string)
|
26
26
|
|
27
|
-
output =
|
28
|
-
exit_status =
|
27
|
+
output, status = Roast::Helpers::CmdRunner.capture2e(command)
|
28
|
+
exit_status = status.exitstatus
|
29
29
|
|
30
30
|
handle_execution_result(
|
31
31
|
command: command,
|
32
32
|
output: output,
|
33
33
|
exit_status: exit_status,
|
34
|
-
success:
|
34
|
+
success: status.success?,
|
35
35
|
exit_on_error: exit_on_error,
|
36
36
|
)
|
37
37
|
rescue ArgumentError, CommandExecutionError
|
@@ -52,7 +52,7 @@ module Roast
|
|
52
52
|
def process_shell_command(command)
|
53
53
|
# If it's a bash command with the $(command) syntax
|
54
54
|
if command =~ /^\$\((.*)\)$/
|
55
|
-
return
|
55
|
+
return Roast::Helpers::CmdRunner.capture2e({}, ::Regexp.last_match(1).to_s).first.strip
|
56
56
|
end
|
57
57
|
|
58
58
|
# Not a shell command, return as is
|
@@ -208,8 +208,8 @@ module Roast
|
|
208
208
|
def configure_step(step, step_name, is_last_step: nil)
|
209
209
|
step_config = config_hash[step_name]
|
210
210
|
|
211
|
-
#
|
212
|
-
step.model =
|
211
|
+
# Only set the model if explicitly specified for this step
|
212
|
+
step.model = step_config["model"] if step_config&.key?("model")
|
213
213
|
|
214
214
|
# Pass resource to step if supported
|
215
215
|
step.resource = workflow.resource if step.respond_to?(:resource=)
|
@@ -223,11 +223,6 @@ module Roast
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
-
# Determine which model to use for the step
|
227
|
-
def determine_model(step_config)
|
228
|
-
step_config&.dig("model") || config_hash["model"] || DEFAULT_MODEL
|
229
|
-
end
|
230
|
-
|
231
226
|
# Apply configuration settings to a step
|
232
227
|
def apply_step_configuration(step, step_config)
|
233
228
|
step.print_response = step_config["print_response"] if step_config.key?("print_response")
|