roast-ai 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yaml +2 -2
- data/CHANGELOG.md +65 -0
- data/CLAUDE.md +55 -9
- data/Gemfile +1 -0
- data/Gemfile.lock +8 -1
- data/README.md +69 -3
- data/bin/console +1 -0
- data/docs/AGENT_STEPS.md +33 -9
- data/docs/VALIDATION.md +178 -0
- data/examples/agent_continue/add_documentation/prompt.md +5 -0
- data/examples/agent_continue/add_error_handling/prompt.md +5 -0
- data/examples/agent_continue/analyze_codebase/prompt.md +7 -0
- data/examples/agent_continue/combined_workflow.yml +24 -0
- data/examples/agent_continue/continue_adding_features/prompt.md +4 -0
- data/examples/agent_continue/create_integration_tests/prompt.md +3 -0
- data/examples/agent_continue/document_with_context/prompt.md +5 -0
- data/examples/agent_continue/explore_api/prompt.md +6 -0
- data/examples/agent_continue/implement_client/prompt.md +6 -0
- data/examples/agent_continue/inline_workflow.yml +20 -0
- data/examples/agent_continue/refactor_code/prompt.md +2 -0
- data/examples/agent_continue/verify_changes/prompt.md +6 -0
- data/examples/agent_continue/workflow.yml +27 -0
- data/examples/agent_workflow/workflow.png +0 -0
- data/examples/api_workflow/workflow.png +0 -0
- data/examples/apply_diff_demo/README.md +58 -0
- data/examples/apply_diff_demo/apply_simple_change/prompt.md +13 -0
- data/examples/apply_diff_demo/create_sample_file/prompt.md +11 -0
- data/examples/apply_diff_demo/workflow.yml +24 -0
- data/examples/available_tools_demo/workflow.png +0 -0
- data/examples/bash_prototyping/api_testing.png +0 -0
- data/examples/bash_prototyping/system_analysis.png +0 -0
- data/examples/case_when/workflow.png +0 -0
- data/examples/cmd/basic_workflow.png +0 -0
- data/examples/cmd/dev_workflow.png +0 -0
- data/examples/cmd/explorer_workflow.png +0 -0
- data/examples/conditional/simple_workflow.png +0 -0
- data/examples/conditional/workflow.png +0 -0
- data/examples/context_management_demo/README.md +43 -0
- data/examples/context_management_demo/workflow.yml +42 -0
- data/examples/direct_coerce_syntax/workflow.png +0 -0
- data/examples/dot_notation/workflow.png +0 -0
- data/examples/exit_on_error/workflow.png +0 -0
- data/examples/grading/workflow.png +0 -0
- data/examples/interpolation/workflow.png +0 -0
- data/examples/interpolation/workflow.yml +1 -1
- data/examples/iteration/workflow.png +0 -0
- data/examples/json_handling/workflow.png +0 -0
- data/examples/mcp/database_workflow.png +0 -0
- data/examples/mcp/env_demo/workflow.png +0 -0
- data/examples/mcp/filesystem_demo/workflow.png +0 -0
- data/examples/mcp/github_workflow.png +0 -0
- data/examples/mcp/multi_mcp_workflow.png +0 -0
- data/examples/mcp/workflow.png +0 -0
- data/examples/no_model_fallback/README.md +17 -0
- data/examples/no_model_fallback/analyze_file/prompt.md +1 -0
- data/examples/no_model_fallback/analyze_patterns/prompt.md +27 -0
- data/examples/no_model_fallback/generate_report_for_md/prompt.md +10 -0
- data/examples/no_model_fallback/generate_report_for_rb/prompt.md +3 -0
- data/examples/no_model_fallback/sample.rb +42 -0
- data/examples/no_model_fallback/workflow.yml +19 -0
- data/examples/openrouter_example/workflow.png +0 -0
- data/examples/pre_post_processing/workflow.png +0 -0
- data/examples/rspec_to_minitest/workflow.png +0 -0
- data/examples/shared_config/example_with_shared_config/workflow.png +0 -0
- data/examples/shared_config/shared.png +0 -0
- data/examples/single_target_prepost/workflow.png +0 -0
- data/examples/smart_coercion_defaults/workflow.png +0 -0
- data/examples/step_configuration/workflow.png +0 -0
- data/examples/swarm_example.yml +25 -0
- data/examples/tool_config_example/workflow.png +0 -0
- data/examples/user_input/funny_name/workflow.png +0 -0
- data/examples/user_input/simple_input_demo/workflow.png +0 -0
- data/examples/user_input/survey_workflow.png +0 -0
- data/examples/user_input/workflow.png +0 -0
- data/examples/workflow_generator/workflow.png +0 -0
- data/lib/roast/helpers/timeout_handler.rb +91 -0
- data/lib/roast/services/context_threshold_checker.rb +42 -0
- data/lib/roast/services/token_counting_service.rb +44 -0
- data/lib/roast/tools/apply_diff.rb +128 -0
- data/lib/roast/tools/bash.rb +15 -9
- data/lib/roast/tools/cmd.rb +32 -12
- data/lib/roast/tools/coding_agent.rb +64 -9
- data/lib/roast/tools/context_summarizer.rb +108 -0
- data/lib/roast/tools/swarm.rb +124 -0
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/agent_step.rb +9 -2
- data/lib/roast/workflow/base_iteration_step.rb +3 -2
- data/lib/roast/workflow/base_workflow.rb +41 -2
- data/lib/roast/workflow/configuration.rb +2 -1
- data/lib/roast/workflow/configuration_loader.rb +63 -1
- data/lib/roast/workflow/context_manager.rb +89 -0
- data/lib/roast/workflow/each_step.rb +1 -1
- data/lib/roast/workflow/output_handler.rb +1 -1
- data/lib/roast/workflow/repeat_step.rb +1 -1
- data/lib/roast/workflow/replay_handler.rb +1 -1
- data/lib/roast/workflow/sqlite_state_repository.rb +342 -0
- data/lib/roast/workflow/state_manager.rb +2 -2
- data/lib/roast/workflow/state_repository_factory.rb +36 -0
- data/lib/roast/workflow/step_completion_reporter.rb +27 -0
- data/lib/roast/workflow/step_executor_coordinator.rb +19 -18
- data/lib/roast/workflow/step_executor_with_reporting.rb +68 -0
- data/lib/roast/workflow/step_loader.rb +1 -1
- data/lib/roast/workflow/step_name_extractor.rb +84 -0
- data/lib/roast/workflow/validation_command.rb +197 -0
- data/lib/roast/workflow/validators/base_validator.rb +44 -0
- data/lib/roast/workflow/validators/dependency_validator.rb +223 -0
- data/lib/roast/workflow/validators/linting_validator.rb +113 -0
- data/lib/roast/workflow/validators/schema_validator.rb +90 -0
- data/lib/roast/workflow/validators/step_collector.rb +57 -0
- data/lib/roast/workflow/validators/validation_orchestrator.rb +52 -0
- data/lib/roast/workflow/workflow_executor.rb +11 -4
- data/lib/roast/workflow/workflow_runner.rb +6 -0
- data/lib/roast/workflow_diagram_generator.rb +298 -0
- data/lib/roast.rb +157 -0
- data/roast.gemspec +2 -1
- data/schema/workflow.json +77 -1
- metadata +101 -1
@@ -0,0 +1,20 @@
|
|
1
|
+
description: Example workflow using inline agent prompts with continuation
|
2
|
+
|
3
|
+
# This example demonstrates using inline agent prompts (direct text after ^)
|
4
|
+
# combined with the new parameters
|
5
|
+
|
6
|
+
steps:
|
7
|
+
# Start with a fresh context
|
8
|
+
- ^Create a simple Ruby class for managing a todo list with add, remove, and list methods
|
9
|
+
|
10
|
+
# Continue from previous session to add more features
|
11
|
+
- ^continue_adding_features
|
12
|
+
|
13
|
+
# Use context summary to understand what was built
|
14
|
+
- ^document_with_context
|
15
|
+
|
16
|
+
continue_adding_features:
|
17
|
+
continue: true # This continues from the previous agent session
|
18
|
+
|
19
|
+
document_with_context:
|
20
|
+
include_context_summary: true # This will include context about previous steps
|
@@ -0,0 +1,27 @@
|
|
1
|
+
description: Example workflow demonstrating agent continuation and context summary features
|
2
|
+
|
3
|
+
# This example shows how to use the new coding agent parameters:
|
4
|
+
# - continue: true - continues from previous agent session
|
5
|
+
# - include_context_summary: true - includes workflow context in the prompt
|
6
|
+
|
7
|
+
target: "**/*.rb"
|
8
|
+
|
9
|
+
steps:
|
10
|
+
# First agent step - starts fresh
|
11
|
+
- ^analyze_codebase
|
12
|
+
|
13
|
+
# Second agent step - continues from previous session
|
14
|
+
- ^refactor_code
|
15
|
+
|
16
|
+
# Third agent step - includes context summary from previous steps
|
17
|
+
- ^add_documentation
|
18
|
+
|
19
|
+
# Final verification step
|
20
|
+
- verify_changes
|
21
|
+
|
22
|
+
# Step configurations
|
23
|
+
refactor_code:
|
24
|
+
continue: true # Continue from the previous agent session
|
25
|
+
|
26
|
+
add_documentation:
|
27
|
+
include_context_summary: true # Include workflow context in the prompt
|
Binary file
|
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Apply Diff Demo
|
2
|
+
|
3
|
+
This example demonstrates the `apply_diff` tool, which shows users a colored diff of proposed changes and applies them only after user confirmation.
|
4
|
+
|
5
|
+
## What this workflow does
|
6
|
+
|
7
|
+
1. **Creates a sample file** - Generates `hello.txt` with simple text content
|
8
|
+
2. **Applies a simple change** - Uses `apply_diff` to modify the greeting and ask for user confirmation
|
9
|
+
|
10
|
+
## Key features demonstrated
|
11
|
+
|
12
|
+
- **Interactive approval** - The `apply_diff` tool shows a clear, colored diff and waits for user confirmation
|
13
|
+
- **Safe modifications** - Changes are only applied when the user explicitly approves them
|
14
|
+
- **Colored visualization** - Diff format shows exactly what will be changed with:
|
15
|
+
- **Red** lines starting with `-` for removed content
|
16
|
+
- **Green** lines starting with `+` for added content
|
17
|
+
- **Cyan** line numbers and context (`@@` lines)
|
18
|
+
- **Bold** diff headers
|
19
|
+
- **Optional descriptions** - You can provide context about why a change is being made
|
20
|
+
|
21
|
+
## Running the workflow
|
22
|
+
|
23
|
+
```bash
|
24
|
+
bin/roast examples/apply_diff_demo/workflow.yml
|
25
|
+
```
|
26
|
+
|
27
|
+
## Expected interaction
|
28
|
+
|
29
|
+
When you run this workflow, you'll see:
|
30
|
+
|
31
|
+
1. The workflow creates a simple `hello.txt` file
|
32
|
+
2. It proposes changing "Hello World!" to "Hello, Apply Diff Demo!"
|
33
|
+
3. It shows you a colored diff of the proposed change:
|
34
|
+
```
|
35
|
+
📝 Proposed change for hello.txt:
|
36
|
+
Description: Update greeting to be more specific to the demo
|
37
|
+
|
38
|
+
diff --git a/hello.txt b/hello.txt
|
39
|
+
index 1234567..abcdefg 100644
|
40
|
+
--- a/hello.txt
|
41
|
+
+++ b/hello.txt
|
42
|
+
@@ -1,3 +1,3 @@
|
43
|
+
-Hello World!
|
44
|
+
+Hello, Apply Diff Demo!
|
45
|
+
This is a demo file.
|
46
|
+
We will modify this file in the next step.
|
47
|
+
```
|
48
|
+
4. It asks for your confirmation: `Apply this change? (y/n)`
|
49
|
+
5. If you say "y", it applies the change; if "n", it cancels
|
50
|
+
6. Finally, it reads the file again to show the result
|
51
|
+
|
52
|
+
## Tools used
|
53
|
+
|
54
|
+
- `Roast::Tools::WriteFile` - Creates the initial sample file
|
55
|
+
- `Roast::Tools::ReadFile` - Reads files to show results
|
56
|
+
- `Roast::Tools::ApplyDiff` - Shows colored diffs and applies changes with user confirmation
|
57
|
+
|
58
|
+
This pattern is useful for any workflow where you want to make targeted changes to files but give users control over what actually gets applied.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Apply Simple Change
|
2
|
+
|
3
|
+
Now use the apply_diff function to modify the `hello.txt` file. Let's change the greeting from "Hello World!" to "Hello, Apply Diff Demo!".
|
4
|
+
|
5
|
+
Use the apply_diff function with:
|
6
|
+
- `file_path`: "hello.txt"
|
7
|
+
- `old_content`: "Hello World!"
|
8
|
+
- `new_content`: "Hello, Apply Diff Demo!"
|
9
|
+
- `description`: "Update greeting to be more specific to the demo"
|
10
|
+
|
11
|
+
This will show the user a colored diff of the proposed change and ask for their confirmation before applying it.
|
12
|
+
|
13
|
+
After the change is applied (or declined), read the file again to show the final result.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Apply Diff Demo
|
2
|
+
#
|
3
|
+
# This workflow demonstrates the apply_diff tool which shows users a diff
|
4
|
+
# and applies changes based on their confirmation. It's useful for making
|
5
|
+
# targeted changes to files with user approval.
|
6
|
+
|
7
|
+
name: Apply Diff Demo
|
8
|
+
model: gpt-4o-mini
|
9
|
+
|
10
|
+
tools:
|
11
|
+
- Roast::Tools::WriteFile
|
12
|
+
- Roast::Tools::ReadFile
|
13
|
+
- Roast::Tools::ApplyDiff
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- create_sample_file
|
17
|
+
- apply_simple_change
|
18
|
+
|
19
|
+
# Step configurations
|
20
|
+
create_sample_file:
|
21
|
+
print_response: false
|
22
|
+
|
23
|
+
apply_simple_change:
|
24
|
+
print_response: true
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Context Management Demo
|
2
|
+
|
3
|
+
This example demonstrates Roast's automatic context management feature, which helps prevent workflow failures when conversation history exceeds the LLM's context window.
|
4
|
+
|
5
|
+
## Features Demonstrated
|
6
|
+
|
7
|
+
1. **Automatic Token Tracking**: Monitors token usage throughout workflow execution
|
8
|
+
2. **Configurable Thresholds**: Set when to trigger warnings or compaction
|
9
|
+
3. **Context Preservation**: Specify critical steps to retain during compaction
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
|
13
|
+
```yaml
|
14
|
+
context_management:
|
15
|
+
enabled: true # Enable context management
|
16
|
+
strategy: auto # Compaction strategy (auto, summarize, prune, none)
|
17
|
+
threshold: 0.8 # Trigger at 80% of context window
|
18
|
+
max_tokens: 10000 # Override default limit (for demo purposes)
|
19
|
+
retain_steps: # Steps to always keep in full
|
20
|
+
- analyze_requirements
|
21
|
+
- generate_summary
|
22
|
+
```
|
23
|
+
|
24
|
+
## Running the Demo
|
25
|
+
|
26
|
+
```bash
|
27
|
+
roast execute context_management_demo
|
28
|
+
```
|
29
|
+
|
30
|
+
The workflow intentionally generates verbose responses to demonstrate how context management handles large amounts of text without failing.
|
31
|
+
|
32
|
+
## What to Observe
|
33
|
+
|
34
|
+
1. **Token Usage Warnings**: Watch for warnings as the context approaches limits
|
35
|
+
2. **Automatic Handling**: The workflow continues even with large outputs
|
36
|
+
3. **Preserved Context**: Critical steps remain accessible throughout execution
|
37
|
+
|
38
|
+
## Customization
|
39
|
+
|
40
|
+
Try modifying the configuration:
|
41
|
+
- Lower `max_tokens` to trigger compaction sooner
|
42
|
+
- Change `strategy` to test different compaction approaches
|
43
|
+
- Add more steps to `retain_steps` to preserve additional context
|
@@ -0,0 +1,42 @@
|
|
1
|
+
name: Context Management Demo
|
2
|
+
tools:
|
3
|
+
- Roast::Tools::ReadFile
|
4
|
+
- Roast::Tools::WriteFile
|
5
|
+
|
6
|
+
# Context management configuration
|
7
|
+
context_management:
|
8
|
+
enabled: true
|
9
|
+
strategy: auto
|
10
|
+
threshold: 0.8 # Trigger compaction at 80% of context window
|
11
|
+
max_tokens: 10000 # Demo with smaller limit for testing
|
12
|
+
retain_steps:
|
13
|
+
- analyze_requirements
|
14
|
+
- generate_summary
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- analyze_requirements: |
|
18
|
+
Analyze this text and list the key requirements:
|
19
|
+
|
20
|
+
We need a system that can:
|
21
|
+
1. Process customer orders
|
22
|
+
2. Track inventory levels
|
23
|
+
3. Generate reports
|
24
|
+
4. Handle refunds
|
25
|
+
5. Integrate with payment systems
|
26
|
+
|
27
|
+
- expand_details: |
|
28
|
+
For each requirement from the previous step, provide detailed implementation notes,
|
29
|
+
technical considerations, and potential challenges. Be very thorough and verbose
|
30
|
+
to help test the context management system.
|
31
|
+
|
32
|
+
- generate_more_context: |
|
33
|
+
Now describe the database schema needed for this system. Include all tables,
|
34
|
+
relationships, indexes, and data types. Be extremely detailed.
|
35
|
+
|
36
|
+
- add_api_design: |
|
37
|
+
Design a complete REST API for this system. Include all endpoints, request/response
|
38
|
+
formats, authentication, and error handling. Provide examples for each endpoint.
|
39
|
+
|
40
|
+
- generate_summary: |
|
41
|
+
Create a concise executive summary of the system design. Focus on the key decisions
|
42
|
+
and trade-offs made during the design process.
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# No Model Fallback Example
|
2
|
+
|
3
|
+
This example demonstrates the issue where workflows without explicit model specification do not properly fall back to a default model.
|
4
|
+
|
5
|
+
## Purpose
|
6
|
+
|
7
|
+
This workflow is based on the interpolation example but intentionally omits the `model` field to test the fallback behavior.
|
8
|
+
|
9
|
+
## Expected Behavior
|
10
|
+
|
11
|
+
The workflow should fall back to a default model when no model is specified at the workflow level.
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bin/roast examples/no_model_fallback/workflow.yml --file examples/no_model_fallback/sample.rb
|
17
|
+
```
|
@@ -0,0 +1 @@
|
|
1
|
+
Analyze the file at: <%= workflow.file %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Extract some patterns about this file and return in json format like this:
|
2
|
+
|
3
|
+
<json>
|
4
|
+
{
|
5
|
+
"code_patterns": {
|
6
|
+
"class_structure": {
|
7
|
+
"name": "Calculator",
|
8
|
+
"instance_variables": ["@memory"],
|
9
|
+
"method_count": 7,
|
10
|
+
"method_types": {
|
11
|
+
"constructor": ["initialize"],
|
12
|
+
"operations": ["add", "subtract", "multiply", "divide"],
|
13
|
+
"accessors": ["memory"],
|
14
|
+
"utility": ["clear"]
|
15
|
+
}
|
16
|
+
},
|
17
|
+
"error_handling": {
|
18
|
+
"techniques": ["conditional raise", "zero check"],
|
19
|
+
"examples": ["raise \"Division by zero!\" if number.zero?"]
|
20
|
+
},
|
21
|
+
"design_patterns": {
|
22
|
+
"state": "Uses instance variable to maintain calculator state",
|
23
|
+
"command": "Each operation method modifies the internal state"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
</json>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Generate a comprehensive report for the markdown file.
|
2
|
+
|
3
|
+
File content: {{steps.analyze_file}}
|
4
|
+
|
5
|
+
Patterns found: {{steps.analyze_patterns}}
|
6
|
+
|
7
|
+
Please create a detailed report that includes:
|
8
|
+
1. Summary of the file content
|
9
|
+
2. Key patterns identified
|
10
|
+
3. Any recommendations or observations
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Sample Ruby file for testing interpolation in workflows
|
4
|
+
|
5
|
+
class Calculator
|
6
|
+
def initialize
|
7
|
+
@memory = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(number)
|
11
|
+
@memory += number
|
12
|
+
end
|
13
|
+
|
14
|
+
def subtract(number)
|
15
|
+
@memory -= number
|
16
|
+
end
|
17
|
+
|
18
|
+
def multiply(number)
|
19
|
+
@memory *= number
|
20
|
+
end
|
21
|
+
|
22
|
+
def divide(number)
|
23
|
+
raise "Division by zero!" if number.zero?
|
24
|
+
|
25
|
+
@memory /= number
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :memory
|
29
|
+
|
30
|
+
def clear
|
31
|
+
@memory = 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Example usage
|
36
|
+
if __FILE__ == $PROGRAM_NAME
|
37
|
+
calc = Calculator.new
|
38
|
+
calc.add(10)
|
39
|
+
calc.multiply(2)
|
40
|
+
calc.subtract(5)
|
41
|
+
puts "Result: #{calc.memory}"
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: no_model_fallback_example
|
2
|
+
|
3
|
+
tools:
|
4
|
+
- Roast::Tools::ReadFile
|
5
|
+
|
6
|
+
steps:
|
7
|
+
- analyze_file
|
8
|
+
- analyze_patterns
|
9
|
+
- generate_report_for_{{File.extname(workflow.file).sub('.', '')}}
|
10
|
+
- '$(echo "Processing completed for file: {{File.basename(workflow.file)}}")'
|
11
|
+
|
12
|
+
analyze_patterns:
|
13
|
+
json: true
|
14
|
+
|
15
|
+
generate_report_for_rb:
|
16
|
+
print_response: true
|
17
|
+
|
18
|
+
generate_report_for_md:
|
19
|
+
print_response: true
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: Swarm Example Workflow
|
2
|
+
|
3
|
+
# Example workflow demonstrating Roast's integration with Claude Swarm
|
4
|
+
# The Swarm tool is available to the LLM, which can choose to use it when appropriate
|
5
|
+
|
6
|
+
tools:
|
7
|
+
- Roast::Tools::Swarm:
|
8
|
+
path: ".swarm.yml" # Optional - will use default locations if not specified
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- orchestrate_refactoring: |
|
12
|
+
Help me refactor this codebase for better performance. Coordinate multiple
|
13
|
+
Claude agents using the swarm configuration to:
|
14
|
+
1. Analyze the current code structure
|
15
|
+
2. Identify performance bottlenecks
|
16
|
+
3. Implement optimizations
|
17
|
+
4. Ensure backward compatibility
|
18
|
+
|
19
|
+
- specialized_analysis: |
|
20
|
+
Now use the specialized swarm configuration at ./specialized-swarm.yml to run
|
21
|
+
a comprehensive code analysis that includes:
|
22
|
+
- Architecture review
|
23
|
+
- Security audit
|
24
|
+
- Documentation generation
|
25
|
+
- Test coverage analysis
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "timeout"
|
4
|
+
require "open3"
|
5
|
+
|
6
|
+
module Roast
|
7
|
+
module Helpers
|
8
|
+
# Shared timeout handling logic for command-based tools
|
9
|
+
#
|
10
|
+
# This class provides centralized timeout functionality for executing shell commands
|
11
|
+
# with proper process management and resource cleanup.
|
12
|
+
#
|
13
|
+
# @example Basic usage
|
14
|
+
# output, status = TimeoutHandler.call("echo hello", timeout: 5)
|
15
|
+
#
|
16
|
+
# @example With custom working directory
|
17
|
+
# output, status = TimeoutHandler.call("pwd", timeout: 10, working_directory: "/tmp")
|
18
|
+
class TimeoutHandler
|
19
|
+
DEFAULT_TIMEOUT = 30
|
20
|
+
MAX_TIMEOUT = 300
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Execute a command with timeout using Open3 with proper process cleanup
|
24
|
+
# @param command [String] The command to execute
|
25
|
+
# @param timeout [Integer] Timeout in seconds
|
26
|
+
# @param working_directory [String] Directory to execute in (default: Dir.pwd)
|
27
|
+
# @return [Array<String, Integer>] [output, exit_status]
|
28
|
+
# @raise [Timeout::Error] When command exceeds timeout duration
|
29
|
+
def call(command, timeout: DEFAULT_TIMEOUT, working_directory: Dir.pwd)
|
30
|
+
timeout = validate_timeout(timeout)
|
31
|
+
output = ""
|
32
|
+
exit_status = nil
|
33
|
+
wait_thr = nil
|
34
|
+
|
35
|
+
begin
|
36
|
+
Timeout.timeout(timeout) do
|
37
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(command, chdir: working_directory)
|
38
|
+
stdin.close # Prevent hanging on stdin-waiting commands
|
39
|
+
output = stdout.read + stderr.read
|
40
|
+
wait_thr.join
|
41
|
+
exit_status = wait_thr.value.exitstatus
|
42
|
+
|
43
|
+
[stdout, stderr].each(&:close)
|
44
|
+
end
|
45
|
+
rescue Timeout::Error
|
46
|
+
# Clean up any remaining processes to prevent zombies
|
47
|
+
cleanup_process(wait_thr) if wait_thr&.alive?
|
48
|
+
raise Timeout::Error, "Command '#{command}' in '#{working_directory}' timed out after #{timeout} seconds"
|
49
|
+
end
|
50
|
+
|
51
|
+
[output, exit_status]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Validate and normalize timeout value
|
55
|
+
# @param timeout [Integer, nil] Raw timeout value
|
56
|
+
# @return [Integer] Validated timeout between 1 and MAX_TIMEOUT
|
57
|
+
def validate_timeout(timeout)
|
58
|
+
return DEFAULT_TIMEOUT if timeout.nil? || timeout <= 0
|
59
|
+
|
60
|
+
[timeout, MAX_TIMEOUT].min
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Clean up process on timeout to prevent zombie processes
|
66
|
+
# @param wait_thr [Process::Waiter] The process thread to clean up
|
67
|
+
def cleanup_process(wait_thr)
|
68
|
+
return unless wait_thr&.alive?
|
69
|
+
|
70
|
+
pid = wait_thr.pid
|
71
|
+
# First try graceful termination
|
72
|
+
Process.kill("TERM", pid)
|
73
|
+
sleep(0.1)
|
74
|
+
|
75
|
+
# Force kill if still alive
|
76
|
+
if wait_thr.alive?
|
77
|
+
Process.kill("KILL", pid)
|
78
|
+
end
|
79
|
+
rescue Errno::ESRCH
|
80
|
+
# Process already terminated, which is fine
|
81
|
+
rescue Errno::EPERM
|
82
|
+
# Permission denied - process may be owned by different user
|
83
|
+
Roast::Helpers::Logger.debug("Could not kill process #{pid}: Permission denied")
|
84
|
+
rescue => e
|
85
|
+
# Catch any other unexpected errors during cleanup
|
86
|
+
Roast::Helpers::Logger.debug("Unexpected error during process cleanup: #{e.message}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|