roast-ai 0.3.1 → 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/.gitignore +1 -0
- data/CHANGELOG.md +85 -0
- data/CLAUDE.md +106 -9
- data/Gemfile +4 -1
- data/Gemfile.lock +70 -16
- data/README.md +159 -8
- data/bin/console +1 -0
- data/bin/roast +1 -1
- data/claude-swarm.yml +210 -0
- data/docs/AGENT_STEPS.md +288 -0
- 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/README.md +75 -0
- data/examples/agent_workflow/apply_refactorings/prompt.md +22 -0
- data/examples/agent_workflow/identify_code_smells/prompt.md +15 -0
- data/examples/agent_workflow/summarize_improvements/prompt.md +18 -0
- data/examples/agent_workflow/workflow.png +0 -0
- data/examples/agent_workflow/workflow.yml +16 -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/README.md +42 -0
- data/examples/available_tools_demo/analyze_files/prompt.md +6 -0
- data/examples/available_tools_demo/explore_directory/prompt.md +6 -0
- data/examples/available_tools_demo/workflow.png +0 -0
- data/examples/available_tools_demo/workflow.yml +32 -0
- data/examples/available_tools_demo/write_summary/prompt.md +6 -0
- data/examples/bash_prototyping/api_testing.png +0 -0
- data/examples/bash_prototyping/system_analysis.png +0 -0
- data/examples/case_when/detect_language/prompt.md +2 -2
- 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/run_coverage.rb +0 -2
- data/examples/grading/workflow.png +0 -0
- data/examples/interpolation/workflow.png +0 -0
- data/examples/interpolation/workflow.yml +1 -1
- data/examples/iteration/analyze_complexity/prompt.md +2 -2
- data/examples/iteration/generate_recommendations/prompt.md +2 -2
- data/examples/iteration/implement_fix/prompt.md +2 -2
- data/examples/iteration/prioritize_issues/prompt.md +1 -1
- data/examples/iteration/prompts/analyze_file.md +2 -2
- data/examples/iteration/prompts/generate_summary.md +1 -1
- data/examples/iteration/prompts/update_report.md +3 -3
- data/examples/iteration/prompts/write_report.md +3 -3
- data/examples/iteration/read_file/prompt.md +2 -2
- data/examples/iteration/select_next_issue/prompt.md +2 -2
- data/examples/iteration/update_fix_count/prompt.md +4 -4
- data/examples/iteration/verify_fix/prompt.md +3 -3
- data/examples/iteration/workflow.png +0 -0
- data/examples/json_handling/workflow.png +0 -0
- data/examples/mcp/README.md +3 -3
- data/examples/mcp/analyze_changes/prompt.md +1 -1
- data/examples/mcp/database_workflow.png +0 -0
- data/examples/mcp/database_workflow.yml +1 -1
- data/examples/mcp/env_demo/workflow.png +0 -0
- data/examples/mcp/fetch_pr_context/prompt.md +1 -1
- data/examples/mcp/filesystem_demo/workflow.png +0 -0
- data/examples/mcp/github_workflow.png +0 -0
- data/examples/mcp/github_workflow.yml +1 -1
- data/examples/mcp/multi_mcp_workflow.png +0 -0
- data/examples/mcp/post_review/prompt.md +1 -1
- 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/analyze_test_file/prompt.md +1 -1
- data/examples/pre_post_processing/improve_test_coverage/prompt.md +1 -1
- data/examples/pre_post_processing/optimize_test_performance/prompt.md +1 -1
- data/examples/pre_post_processing/post_processing/aggregate_metrics/prompt.md +2 -2
- data/examples/pre_post_processing/post_processing/generate_summary_report/prompt.md +1 -1
- data/examples/pre_post_processing/pre_processing/setup_test_environment/prompt.md +1 -1
- data/examples/pre_post_processing/validate_changes/prompt.md +2 -2
- 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/README.md +90 -0
- data/examples/user_input/funny_name/create_backstory/prompt.md +10 -0
- data/examples/user_input/funny_name/workflow.png +0 -0
- data/examples/user_input/funny_name/workflow.yml +26 -0
- data/examples/user_input/generate_summary/prompt.md +11 -0
- data/examples/user_input/simple_input_demo/workflow.png +0 -0
- data/examples/user_input/simple_input_demo/workflow.yml +35 -0
- data/examples/user_input/survey_workflow.png +0 -0
- data/examples/user_input/survey_workflow.yml +71 -0
- data/examples/user_input/welcome_message/prompt.md +3 -0
- data/examples/user_input/workflow.png +0 -0
- data/examples/user_input/workflow.yml +73 -0
- data/examples/workflow_generator/create_workflow_files/prompt.md +1 -1
- data/examples/workflow_generator/workflow.png +0 -0
- data/lib/roast/errors.rb +6 -4
- data/lib/roast/helpers/function_caching_interceptor.rb +0 -2
- data/lib/roast/helpers/logger.rb +12 -35
- data/lib/roast/helpers/minitest_coverage_runner.rb +0 -1
- data/lib/roast/helpers/prompt_loader.rb +0 -2
- data/lib/roast/helpers/timeout_handler.rb +91 -0
- data/lib/roast/resources/api_resource.rb +0 -4
- data/lib/roast/resources/url_resource.rb +0 -3
- data/lib/roast/resources.rb +0 -8
- 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/ask_user.rb +0 -2
- data/lib/roast/tools/bash.rb +12 -9
- data/lib/roast/tools/cmd.rb +29 -12
- data/lib/roast/tools/coding_agent.rb +65 -17
- data/lib/roast/tools/context_summarizer.rb +108 -0
- data/lib/roast/tools/grep.rb +0 -3
- data/lib/roast/tools/helpers/coding_agent_message_formatter.rb +1 -4
- data/lib/roast/tools/read_file.rb +0 -2
- data/lib/roast/tools/search_file.rb +0 -2
- data/lib/roast/tools/swarm.rb +124 -0
- data/lib/roast/tools/update_files.rb +0 -4
- data/lib/roast/tools/write_file.rb +0 -3
- data/lib/roast/tools.rb +0 -13
- data/lib/roast/value_objects/step_name.rb +14 -3
- data/lib/roast/value_objects/workflow_path.rb +0 -2
- data/lib/roast/value_objects.rb +4 -4
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/agent_step.rb +33 -0
- data/lib/roast/workflow/api_configuration.rb +0 -4
- data/lib/roast/workflow/base_iteration_step.rb +3 -6
- data/lib/roast/workflow/base_step.rb +54 -28
- data/lib/roast/workflow/base_workflow.rb +43 -23
- data/lib/roast/workflow/case_executor.rb +0 -1
- data/lib/roast/workflow/case_step.rb +0 -4
- data/lib/roast/workflow/command_executor.rb +0 -2
- data/lib/roast/workflow/conditional_executor.rb +0 -1
- data/lib/roast/workflow/conditional_step.rb +0 -4
- data/lib/roast/workflow/configuration.rb +5 -67
- data/lib/roast/workflow/configuration_loader.rb +63 -3
- data/lib/roast/workflow/configuration_parser.rb +1 -7
- data/lib/roast/workflow/context_manager.rb +89 -0
- data/lib/roast/workflow/dot_access_hash.rb +16 -1
- data/lib/roast/workflow/each_step.rb +1 -1
- data/lib/roast/workflow/error_handler.rb +0 -3
- data/lib/roast/workflow/expression_evaluator.rb +0 -3
- data/lib/roast/workflow/file_state_repository.rb +0 -5
- data/lib/roast/workflow/input_executor.rb +41 -0
- data/lib/roast/workflow/input_step.rb +163 -0
- data/lib/roast/workflow/iteration_executor.rb +0 -2
- data/lib/roast/workflow/output_handler.rb +1 -3
- data/lib/roast/workflow/output_manager.rb +0 -2
- data/lib/roast/workflow/repeat_step.rb +1 -1
- data/lib/roast/workflow/replay_handler.rb +1 -4
- data/lib/roast/workflow/resource_resolver.rb +0 -3
- data/lib/roast/workflow/session_manager.rb +0 -3
- data/lib/roast/workflow/sqlite_state_repository.rb +342 -0
- data/lib/roast/workflow/state_manager.rb +2 -4
- 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 +48 -24
- data/lib/roast/workflow/step_executor_factory.rb +0 -5
- data/lib/roast/workflow/step_executor_registry.rb +1 -4
- data/lib/roast/workflow/step_executor_with_reporting.rb +68 -0
- data/lib/roast/workflow/step_executors/hash_step_executor.rb +0 -3
- data/lib/roast/workflow/step_executors/parallel_step_executor.rb +0 -3
- data/lib/roast/workflow/step_executors/string_step_executor.rb +0 -2
- data/lib/roast/workflow/step_factory.rb +56 -0
- data/lib/roast/workflow/step_loader.rb +31 -17
- data/lib/roast/workflow/step_name_extractor.rb +84 -0
- data/lib/roast/workflow/step_orchestrator.rb +3 -2
- data/lib/roast/workflow/step_type_resolver.rb +28 -1
- data/lib/roast/workflow/validation_command.rb +197 -0
- data/lib/roast/workflow/validator.rb +0 -4
- 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 -20
- data/lib/roast/workflow/workflow_initializer.rb +1 -8
- data/lib/roast/workflow/workflow_runner.rb +6 -7
- data/lib/roast/workflow.rb +0 -15
- data/lib/roast/workflow_diagram_generator.rb +298 -0
- data/lib/roast.rb +212 -10
- data/roast.gemspec +4 -2
- data/schema/workflow.json +123 -1
- metadata +143 -6
- data/lib/roast/helpers.rb +0 -12
data/lib/roast.rb
CHANGED
@@ -1,24 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Standard library requires
|
4
|
+
require "digest"
|
5
|
+
require "English"
|
6
|
+
require "erb"
|
7
|
+
require "fileutils"
|
8
|
+
require "json"
|
9
|
+
require "logger"
|
10
|
+
require "net/http"
|
11
|
+
require "open3"
|
12
|
+
require "pathname"
|
13
|
+
require "securerandom"
|
14
|
+
require "tempfile"
|
15
|
+
require "uri"
|
16
|
+
require "yaml"
|
17
|
+
|
18
|
+
# Third-party gem requires
|
3
19
|
require "active_support"
|
4
20
|
require "active_support/cache"
|
5
|
-
require "active_support/notifications"
|
6
21
|
require "active_support/core_ext/hash/indifferent_access"
|
22
|
+
require "active_support/core_ext/module/delegation"
|
7
23
|
require "active_support/core_ext/string"
|
8
24
|
require "active_support/core_ext/string/inflections"
|
9
|
-
require "active_support/core_ext/module/delegation"
|
10
25
|
require "active_support/isolated_execution_state"
|
11
|
-
require "
|
26
|
+
require "active_support/notifications"
|
12
27
|
require "cli/ui"
|
28
|
+
require "diff/lcs"
|
29
|
+
require "json-schema"
|
13
30
|
require "raix"
|
31
|
+
require "raix/chat_completion"
|
32
|
+
require "raix/function_dispatch"
|
33
|
+
require "ruby-graphviz"
|
14
34
|
require "thor"
|
15
|
-
|
16
|
-
|
17
|
-
require "
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
35
|
+
|
36
|
+
# Autoloading setup
|
37
|
+
require "zeitwerk"
|
38
|
+
|
39
|
+
# Set up Zeitwerk autoloader
|
40
|
+
loader = Zeitwerk::Loader.for_gem
|
41
|
+
loader.setup
|
22
42
|
|
23
43
|
module Roast
|
24
44
|
ROOT = File.expand_path("../..", __FILE__)
|
@@ -31,6 +51,7 @@ module Roast
|
|
31
51
|
option :target, type: :string, aliases: "-t", desc: "Override target files. Can be file path, glob pattern, or $(shell command)"
|
32
52
|
option :replay, type: :string, aliases: "-r", desc: "Resume workflow from a specific step. Format: step_name or session_timestamp:step_name"
|
33
53
|
option :pause, type: :string, aliases: "-p", desc: "Pause workflow after a specific step. Format: step_name"
|
54
|
+
option :file_storage, type: :boolean, aliases: "-f", desc: "Use filesystem storage for sessions instead of SQLite"
|
34
55
|
|
35
56
|
def execute(*paths)
|
36
57
|
raise Thor::Error, "Workflow configuration file is required" if paths.empty?
|
@@ -48,6 +69,44 @@ module Roast
|
|
48
69
|
Roast::Workflow::ConfigurationParser.new(expanded_workflow_path, files, options.transform_keys(&:to_sym)).begin!
|
49
70
|
end
|
50
71
|
|
72
|
+
desc "resume WORKFLOW_FILE", "Resume a paused workflow with an event"
|
73
|
+
option :event, type: :string, aliases: "-e", required: true, desc: "Event name to trigger"
|
74
|
+
option :session_id, type: :string, aliases: "-s", desc: "Specific session ID to resume (defaults to most recent)"
|
75
|
+
option :event_data, type: :string, desc: "JSON data to pass with the event"
|
76
|
+
def resume(workflow_path)
|
77
|
+
expanded_workflow_path = if workflow_path.include?("workflow.yml")
|
78
|
+
File.expand_path(workflow_path)
|
79
|
+
else
|
80
|
+
File.expand_path("roast/#{workflow_path}/workflow.yml")
|
81
|
+
end
|
82
|
+
|
83
|
+
unless File.exist?(expanded_workflow_path)
|
84
|
+
raise Thor::Error, "Workflow file not found: #{expanded_workflow_path}"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Store the event in the session
|
88
|
+
repository = Workflow::StateRepositoryFactory.create
|
89
|
+
|
90
|
+
unless repository.respond_to?(:add_event)
|
91
|
+
raise Thor::Error, "Event resumption requires SQLite storage. Set ROAST_STATE_STORAGE=sqlite"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Parse event data if provided
|
95
|
+
event_data = options[:event_data] ? JSON.parse(options[:event_data]) : nil
|
96
|
+
|
97
|
+
# Add the event to the session
|
98
|
+
session_id = options[:session_id]
|
99
|
+
repository.add_event(expanded_workflow_path, session_id, options[:event], event_data)
|
100
|
+
|
101
|
+
# Resume workflow execution from the wait state
|
102
|
+
resume_options = options.transform_keys(&:to_sym).merge(
|
103
|
+
resume_from_event: options[:event],
|
104
|
+
session_id: session_id,
|
105
|
+
)
|
106
|
+
|
107
|
+
Roast::Workflow::ConfigurationParser.new(expanded_workflow_path, [], resume_options).begin!
|
108
|
+
end
|
109
|
+
|
51
110
|
desc "version", "Display the current version of Roast"
|
52
111
|
def version
|
53
112
|
puts "Roast version #{Roast::VERSION}"
|
@@ -63,6 +122,149 @@ module Roast
|
|
63
122
|
end
|
64
123
|
end
|
65
124
|
|
125
|
+
desc "list", "List workflows visible to Roast and their source"
|
126
|
+
def list
|
127
|
+
roast_dir = File.join(Dir.pwd, "roast")
|
128
|
+
|
129
|
+
unless File.directory?(roast_dir)
|
130
|
+
raise Thor::Error, "No roast/ directory found in current path"
|
131
|
+
end
|
132
|
+
|
133
|
+
workflow_files = Dir.glob(File.join(roast_dir, "**/workflow.yml")).sort
|
134
|
+
|
135
|
+
if workflow_files.empty?
|
136
|
+
raise Thor::Error, "No workflow.yml files found in roast/ directory"
|
137
|
+
end
|
138
|
+
|
139
|
+
puts "Available workflows:"
|
140
|
+
puts
|
141
|
+
|
142
|
+
workflow_files.each do |file|
|
143
|
+
workflow_name = File.dirname(file.sub("#{roast_dir}/", ""))
|
144
|
+
puts " #{workflow_name} (from project)"
|
145
|
+
end
|
146
|
+
|
147
|
+
puts
|
148
|
+
puts "Run a workflow with: roast execute <workflow_name>"
|
149
|
+
end
|
150
|
+
|
151
|
+
desc "validate [WORKFLOW_CONFIGURATION_FILE]", "Validate a workflow configuration"
|
152
|
+
option :strict, type: :boolean, aliases: "-s", desc: "Treat warnings as errors"
|
153
|
+
def validate(workflow_path = nil)
|
154
|
+
validation_command = Roast::Workflow::ValidationCommand.new(options)
|
155
|
+
validation_command.execute(workflow_path)
|
156
|
+
end
|
157
|
+
|
158
|
+
desc "sessions", "List stored workflow sessions"
|
159
|
+
option :status, type: :string, aliases: "-s", desc: "Filter by status (running, waiting, completed, failed)"
|
160
|
+
option :workflow, type: :string, aliases: "-w", desc: "Filter by workflow name"
|
161
|
+
option :older_than, type: :string, desc: "Show sessions older than specified time (e.g., '7d', '1h')"
|
162
|
+
option :cleanup, type: :boolean, desc: "Clean up old sessions"
|
163
|
+
def sessions
|
164
|
+
repository = Workflow::StateRepositoryFactory.create
|
165
|
+
|
166
|
+
unless repository.respond_to?(:list_sessions)
|
167
|
+
raise Thor::Error, "Session listing is only available with SQLite storage. Set ROAST_STATE_STORAGE=sqlite"
|
168
|
+
end
|
169
|
+
|
170
|
+
if options[:cleanup] && options[:older_than]
|
171
|
+
count = repository.cleanup_old_sessions(options[:older_than])
|
172
|
+
puts "Cleaned up #{count} old sessions"
|
173
|
+
return
|
174
|
+
end
|
175
|
+
|
176
|
+
sessions = repository.list_sessions(
|
177
|
+
status: options[:status],
|
178
|
+
workflow_name: options[:workflow],
|
179
|
+
older_than: options[:older_than],
|
180
|
+
)
|
181
|
+
|
182
|
+
if sessions.empty?
|
183
|
+
puts "No sessions found"
|
184
|
+
return
|
185
|
+
end
|
186
|
+
|
187
|
+
puts "Found #{sessions.length} session(s):"
|
188
|
+
puts
|
189
|
+
|
190
|
+
sessions.each do |session|
|
191
|
+
id, workflow_name, _, status, current_step, created_at, updated_at = session
|
192
|
+
|
193
|
+
puts "Session: #{id}"
|
194
|
+
puts " Workflow: #{workflow_name}"
|
195
|
+
puts " Status: #{status}"
|
196
|
+
puts " Current step: #{current_step || "N/A"}"
|
197
|
+
puts " Created: #{created_at}"
|
198
|
+
puts " Updated: #{updated_at}"
|
199
|
+
puts
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
desc "session SESSION_ID", "Show details for a specific session"
|
204
|
+
def session(session_id)
|
205
|
+
repository = Workflow::StateRepositoryFactory.create
|
206
|
+
|
207
|
+
unless repository.respond_to?(:get_session_details)
|
208
|
+
raise Thor::Error, "Session details are only available with SQLite storage. Set ROAST_STATE_STORAGE=sqlite"
|
209
|
+
end
|
210
|
+
|
211
|
+
details = repository.get_session_details(session_id)
|
212
|
+
|
213
|
+
unless details
|
214
|
+
raise Thor::Error, "Session not found: #{session_id}"
|
215
|
+
end
|
216
|
+
|
217
|
+
session = details[:session]
|
218
|
+
states = details[:states]
|
219
|
+
events = details[:events]
|
220
|
+
|
221
|
+
puts "Session: #{session[0]}"
|
222
|
+
puts "Workflow: #{session[1]}"
|
223
|
+
puts "Path: #{session[2]}"
|
224
|
+
puts "Status: #{session[3]}"
|
225
|
+
puts "Created: #{session[6]}"
|
226
|
+
puts "Updated: #{session[7]}"
|
227
|
+
|
228
|
+
if session[5]
|
229
|
+
puts
|
230
|
+
puts "Final output:"
|
231
|
+
puts session[5]
|
232
|
+
end
|
233
|
+
|
234
|
+
if states && !states.empty?
|
235
|
+
puts
|
236
|
+
puts "Steps executed:"
|
237
|
+
states.each do |step_index, step_name, created_at|
|
238
|
+
puts " #{step_index}: #{step_name} (#{created_at})"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
if events && !events.empty?
|
243
|
+
puts
|
244
|
+
puts "Events:"
|
245
|
+
events.each do |event_name, event_data, received_at|
|
246
|
+
puts " #{event_name} at #{received_at}"
|
247
|
+
puts " Data: #{event_data}" if event_data
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
desc "diagram WORKFLOW_FILE", "Generate a visual diagram of a workflow"
|
253
|
+
option :output, type: :string, aliases: "-o", desc: "Output file path (defaults to workflow_name_diagram.png)"
|
254
|
+
def diagram(workflow_file)
|
255
|
+
unless File.exist?(workflow_file)
|
256
|
+
raise Thor::Error, "Workflow file not found: #{workflow_file}"
|
257
|
+
end
|
258
|
+
|
259
|
+
workflow = Workflow::Configuration.new(workflow_file)
|
260
|
+
generator = WorkflowDiagramGenerator.new(workflow, workflow_file)
|
261
|
+
output_path = generator.generate(options[:output])
|
262
|
+
|
263
|
+
puts ::CLI::UI.fmt("{{success:✓}} Diagram generated: #{output_path}")
|
264
|
+
rescue StandardError => e
|
265
|
+
raise Thor::Error, "Error generating diagram: #{e.message}"
|
266
|
+
end
|
267
|
+
|
66
268
|
private
|
67
269
|
|
68
270
|
def show_example_picker
|
data/roast.gemspec
CHANGED
@@ -30,18 +30,20 @@ Gem::Specification.new do |spec|
|
|
30
30
|
# Specify which files should be added to the gem when it is released.
|
31
31
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
32
|
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
33
|
-
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
+
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.end_with?(".gem") }
|
34
34
|
end
|
35
35
|
spec.bindir = "exe"
|
36
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
37
|
spec.require_paths = ["lib"]
|
38
38
|
|
39
39
|
spec.add_dependency("activesupport", ">= 7.0")
|
40
|
-
spec.add_dependency("cli-ui")
|
40
|
+
spec.add_dependency("cli-ui", "2.3.0")
|
41
41
|
spec.add_dependency("diff-lcs", "~> 1.5")
|
42
42
|
spec.add_dependency("faraday-retry")
|
43
43
|
spec.add_dependency("json-schema")
|
44
44
|
spec.add_dependency("open_router", "~> 0.3")
|
45
45
|
spec.add_dependency("raix", "~> 1.0")
|
46
|
+
spec.add_dependency("ruby-graphviz", "~> 1.2")
|
46
47
|
spec.add_dependency("thor", "~> 1.3")
|
48
|
+
spec.add_dependency("zeitwerk", "~> 2.6")
|
47
49
|
end
|
data/schema/workflow.json
CHANGED
@@ -8,7 +8,46 @@
|
|
8
8
|
"tools": {
|
9
9
|
"type": "array",
|
10
10
|
"items": {
|
11
|
-
"
|
11
|
+
"oneOf": [
|
12
|
+
{
|
13
|
+
"type": "string"
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"type": "object",
|
17
|
+
"additionalProperties": {
|
18
|
+
"type": "object",
|
19
|
+
"properties": {
|
20
|
+
"url": {
|
21
|
+
"type": "string"
|
22
|
+
},
|
23
|
+
"command": {
|
24
|
+
"type": "string"
|
25
|
+
},
|
26
|
+
"args": {
|
27
|
+
"type": "array",
|
28
|
+
"items": {
|
29
|
+
"type": "string"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"env": {
|
33
|
+
"type": "object"
|
34
|
+
},
|
35
|
+
"only": {
|
36
|
+
"type": "array",
|
37
|
+
"items": {
|
38
|
+
"type": "string"
|
39
|
+
}
|
40
|
+
},
|
41
|
+
"except": {
|
42
|
+
"type": "array",
|
43
|
+
"items": {
|
44
|
+
"type": "string"
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
]
|
12
51
|
}
|
13
52
|
},
|
14
53
|
"target": {
|
@@ -23,6 +62,43 @@
|
|
23
62
|
"type": "string",
|
24
63
|
"description": "Default AI model to use for all steps in the workflow"
|
25
64
|
},
|
65
|
+
"context_management": {
|
66
|
+
"type": "object",
|
67
|
+
"description": "Configuration for automatic context management and compaction",
|
68
|
+
"properties": {
|
69
|
+
"enabled": {
|
70
|
+
"type": "boolean",
|
71
|
+
"description": "Whether to enable context management",
|
72
|
+
"default": true
|
73
|
+
},
|
74
|
+
"strategy": {
|
75
|
+
"type": "string",
|
76
|
+
"description": "Compaction strategy to use when threshold is exceeded",
|
77
|
+
"enum": ["auto", "summarize", "prune", "none"],
|
78
|
+
"default": "auto"
|
79
|
+
},
|
80
|
+
"threshold": {
|
81
|
+
"type": "number",
|
82
|
+
"description": "Percentage of context window to trigger compaction (0.0 to 1.0)",
|
83
|
+
"minimum": 0.0,
|
84
|
+
"maximum": 1.0,
|
85
|
+
"default": 0.8
|
86
|
+
},
|
87
|
+
"max_tokens": {
|
88
|
+
"type": "integer",
|
89
|
+
"description": "Maximum number of tokens allowed in context (defaults to model's limit)",
|
90
|
+
"minimum": 1000
|
91
|
+
},
|
92
|
+
"retain_steps": {
|
93
|
+
"type": "array",
|
94
|
+
"description": "Step names to always keep in full when compacting",
|
95
|
+
"items": {
|
96
|
+
"type": "string"
|
97
|
+
}
|
98
|
+
}
|
99
|
+
},
|
100
|
+
"additionalProperties": false
|
101
|
+
},
|
26
102
|
"inputs": {
|
27
103
|
"type": "array",
|
28
104
|
"items": {
|
@@ -206,6 +282,52 @@
|
|
206
282
|
}
|
207
283
|
},
|
208
284
|
"required": ["case", "when"]
|
285
|
+
},
|
286
|
+
{
|
287
|
+
"type": "object",
|
288
|
+
"properties": {
|
289
|
+
"input": {
|
290
|
+
"type": "object",
|
291
|
+
"properties": {
|
292
|
+
"prompt": {
|
293
|
+
"type": "string",
|
294
|
+
"description": "The prompt text to display to the user"
|
295
|
+
},
|
296
|
+
"name": {
|
297
|
+
"type": "string",
|
298
|
+
"description": "Optional name to store the input value in workflow state"
|
299
|
+
},
|
300
|
+
"type": {
|
301
|
+
"type": "string",
|
302
|
+
"enum": ["text", "boolean", "choice", "password"],
|
303
|
+
"default": "text",
|
304
|
+
"description": "The type of input to collect"
|
305
|
+
},
|
306
|
+
"required": {
|
307
|
+
"type": "boolean",
|
308
|
+
"default": false,
|
309
|
+
"description": "Whether the input is required"
|
310
|
+
},
|
311
|
+
"default": {
|
312
|
+
"description": "Default value for the input"
|
313
|
+
},
|
314
|
+
"timeout": {
|
315
|
+
"type": "number",
|
316
|
+
"description": "Optional timeout in seconds"
|
317
|
+
},
|
318
|
+
"options": {
|
319
|
+
"type": "array",
|
320
|
+
"items": {
|
321
|
+
"type": "string"
|
322
|
+
},
|
323
|
+
"description": "Options for choice type inputs"
|
324
|
+
}
|
325
|
+
},
|
326
|
+
"required": ["prompt"],
|
327
|
+
"additionalProperties": false
|
328
|
+
}
|
329
|
+
},
|
330
|
+
"required": ["input"]
|
209
331
|
}
|
210
332
|
]
|
211
333
|
}
|