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.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +2 -2
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +85 -0
  5. data/CLAUDE.md +106 -9
  6. data/Gemfile +4 -1
  7. data/Gemfile.lock +70 -16
  8. data/README.md +159 -8
  9. data/bin/console +1 -0
  10. data/bin/roast +1 -1
  11. data/claude-swarm.yml +210 -0
  12. data/docs/AGENT_STEPS.md +288 -0
  13. data/docs/VALIDATION.md +178 -0
  14. data/examples/agent_continue/add_documentation/prompt.md +5 -0
  15. data/examples/agent_continue/add_error_handling/prompt.md +5 -0
  16. data/examples/agent_continue/analyze_codebase/prompt.md +7 -0
  17. data/examples/agent_continue/combined_workflow.yml +24 -0
  18. data/examples/agent_continue/continue_adding_features/prompt.md +4 -0
  19. data/examples/agent_continue/create_integration_tests/prompt.md +3 -0
  20. data/examples/agent_continue/document_with_context/prompt.md +5 -0
  21. data/examples/agent_continue/explore_api/prompt.md +6 -0
  22. data/examples/agent_continue/implement_client/prompt.md +6 -0
  23. data/examples/agent_continue/inline_workflow.yml +20 -0
  24. data/examples/agent_continue/refactor_code/prompt.md +2 -0
  25. data/examples/agent_continue/verify_changes/prompt.md +6 -0
  26. data/examples/agent_continue/workflow.yml +27 -0
  27. data/examples/agent_workflow/README.md +75 -0
  28. data/examples/agent_workflow/apply_refactorings/prompt.md +22 -0
  29. data/examples/agent_workflow/identify_code_smells/prompt.md +15 -0
  30. data/examples/agent_workflow/summarize_improvements/prompt.md +18 -0
  31. data/examples/agent_workflow/workflow.png +0 -0
  32. data/examples/agent_workflow/workflow.yml +16 -0
  33. data/examples/api_workflow/workflow.png +0 -0
  34. data/examples/apply_diff_demo/README.md +58 -0
  35. data/examples/apply_diff_demo/apply_simple_change/prompt.md +13 -0
  36. data/examples/apply_diff_demo/create_sample_file/prompt.md +11 -0
  37. data/examples/apply_diff_demo/workflow.yml +24 -0
  38. data/examples/available_tools_demo/README.md +42 -0
  39. data/examples/available_tools_demo/analyze_files/prompt.md +6 -0
  40. data/examples/available_tools_demo/explore_directory/prompt.md +6 -0
  41. data/examples/available_tools_demo/workflow.png +0 -0
  42. data/examples/available_tools_demo/workflow.yml +32 -0
  43. data/examples/available_tools_demo/write_summary/prompt.md +6 -0
  44. data/examples/bash_prototyping/api_testing.png +0 -0
  45. data/examples/bash_prototyping/system_analysis.png +0 -0
  46. data/examples/case_when/detect_language/prompt.md +2 -2
  47. data/examples/case_when/workflow.png +0 -0
  48. data/examples/cmd/basic_workflow.png +0 -0
  49. data/examples/cmd/dev_workflow.png +0 -0
  50. data/examples/cmd/explorer_workflow.png +0 -0
  51. data/examples/conditional/simple_workflow.png +0 -0
  52. data/examples/conditional/workflow.png +0 -0
  53. data/examples/context_management_demo/README.md +43 -0
  54. data/examples/context_management_demo/workflow.yml +42 -0
  55. data/examples/direct_coerce_syntax/workflow.png +0 -0
  56. data/examples/dot_notation/workflow.png +0 -0
  57. data/examples/exit_on_error/workflow.png +0 -0
  58. data/examples/grading/run_coverage.rb +0 -2
  59. data/examples/grading/workflow.png +0 -0
  60. data/examples/interpolation/workflow.png +0 -0
  61. data/examples/interpolation/workflow.yml +1 -1
  62. data/examples/iteration/analyze_complexity/prompt.md +2 -2
  63. data/examples/iteration/generate_recommendations/prompt.md +2 -2
  64. data/examples/iteration/implement_fix/prompt.md +2 -2
  65. data/examples/iteration/prioritize_issues/prompt.md +1 -1
  66. data/examples/iteration/prompts/analyze_file.md +2 -2
  67. data/examples/iteration/prompts/generate_summary.md +1 -1
  68. data/examples/iteration/prompts/update_report.md +3 -3
  69. data/examples/iteration/prompts/write_report.md +3 -3
  70. data/examples/iteration/read_file/prompt.md +2 -2
  71. data/examples/iteration/select_next_issue/prompt.md +2 -2
  72. data/examples/iteration/update_fix_count/prompt.md +4 -4
  73. data/examples/iteration/verify_fix/prompt.md +3 -3
  74. data/examples/iteration/workflow.png +0 -0
  75. data/examples/json_handling/workflow.png +0 -0
  76. data/examples/mcp/README.md +3 -3
  77. data/examples/mcp/analyze_changes/prompt.md +1 -1
  78. data/examples/mcp/database_workflow.png +0 -0
  79. data/examples/mcp/database_workflow.yml +1 -1
  80. data/examples/mcp/env_demo/workflow.png +0 -0
  81. data/examples/mcp/fetch_pr_context/prompt.md +1 -1
  82. data/examples/mcp/filesystem_demo/workflow.png +0 -0
  83. data/examples/mcp/github_workflow.png +0 -0
  84. data/examples/mcp/github_workflow.yml +1 -1
  85. data/examples/mcp/multi_mcp_workflow.png +0 -0
  86. data/examples/mcp/post_review/prompt.md +1 -1
  87. data/examples/mcp/workflow.png +0 -0
  88. data/examples/no_model_fallback/README.md +17 -0
  89. data/examples/no_model_fallback/analyze_file/prompt.md +1 -0
  90. data/examples/no_model_fallback/analyze_patterns/prompt.md +27 -0
  91. data/examples/no_model_fallback/generate_report_for_md/prompt.md +10 -0
  92. data/examples/no_model_fallback/generate_report_for_rb/prompt.md +3 -0
  93. data/examples/no_model_fallback/sample.rb +42 -0
  94. data/examples/no_model_fallback/workflow.yml +19 -0
  95. data/examples/openrouter_example/workflow.png +0 -0
  96. data/examples/pre_post_processing/analyze_test_file/prompt.md +1 -1
  97. data/examples/pre_post_processing/improve_test_coverage/prompt.md +1 -1
  98. data/examples/pre_post_processing/optimize_test_performance/prompt.md +1 -1
  99. data/examples/pre_post_processing/post_processing/aggregate_metrics/prompt.md +2 -2
  100. data/examples/pre_post_processing/post_processing/generate_summary_report/prompt.md +1 -1
  101. data/examples/pre_post_processing/pre_processing/setup_test_environment/prompt.md +1 -1
  102. data/examples/pre_post_processing/validate_changes/prompt.md +2 -2
  103. data/examples/pre_post_processing/workflow.png +0 -0
  104. data/examples/rspec_to_minitest/workflow.png +0 -0
  105. data/examples/shared_config/example_with_shared_config/workflow.png +0 -0
  106. data/examples/shared_config/shared.png +0 -0
  107. data/examples/single_target_prepost/workflow.png +0 -0
  108. data/examples/smart_coercion_defaults/workflow.png +0 -0
  109. data/examples/step_configuration/workflow.png +0 -0
  110. data/examples/swarm_example.yml +25 -0
  111. data/examples/tool_config_example/workflow.png +0 -0
  112. data/examples/user_input/README.md +90 -0
  113. data/examples/user_input/funny_name/create_backstory/prompt.md +10 -0
  114. data/examples/user_input/funny_name/workflow.png +0 -0
  115. data/examples/user_input/funny_name/workflow.yml +26 -0
  116. data/examples/user_input/generate_summary/prompt.md +11 -0
  117. data/examples/user_input/simple_input_demo/workflow.png +0 -0
  118. data/examples/user_input/simple_input_demo/workflow.yml +35 -0
  119. data/examples/user_input/survey_workflow.png +0 -0
  120. data/examples/user_input/survey_workflow.yml +71 -0
  121. data/examples/user_input/welcome_message/prompt.md +3 -0
  122. data/examples/user_input/workflow.png +0 -0
  123. data/examples/user_input/workflow.yml +73 -0
  124. data/examples/workflow_generator/create_workflow_files/prompt.md +1 -1
  125. data/examples/workflow_generator/workflow.png +0 -0
  126. data/lib/roast/errors.rb +6 -4
  127. data/lib/roast/helpers/function_caching_interceptor.rb +0 -2
  128. data/lib/roast/helpers/logger.rb +12 -35
  129. data/lib/roast/helpers/minitest_coverage_runner.rb +0 -1
  130. data/lib/roast/helpers/prompt_loader.rb +0 -2
  131. data/lib/roast/helpers/timeout_handler.rb +91 -0
  132. data/lib/roast/resources/api_resource.rb +0 -4
  133. data/lib/roast/resources/url_resource.rb +0 -3
  134. data/lib/roast/resources.rb +0 -8
  135. data/lib/roast/services/context_threshold_checker.rb +42 -0
  136. data/lib/roast/services/token_counting_service.rb +44 -0
  137. data/lib/roast/tools/apply_diff.rb +128 -0
  138. data/lib/roast/tools/ask_user.rb +0 -2
  139. data/lib/roast/tools/bash.rb +12 -9
  140. data/lib/roast/tools/cmd.rb +29 -12
  141. data/lib/roast/tools/coding_agent.rb +65 -17
  142. data/lib/roast/tools/context_summarizer.rb +108 -0
  143. data/lib/roast/tools/grep.rb +0 -3
  144. data/lib/roast/tools/helpers/coding_agent_message_formatter.rb +1 -4
  145. data/lib/roast/tools/read_file.rb +0 -2
  146. data/lib/roast/tools/search_file.rb +0 -2
  147. data/lib/roast/tools/swarm.rb +124 -0
  148. data/lib/roast/tools/update_files.rb +0 -4
  149. data/lib/roast/tools/write_file.rb +0 -3
  150. data/lib/roast/tools.rb +0 -13
  151. data/lib/roast/value_objects/step_name.rb +14 -3
  152. data/lib/roast/value_objects/workflow_path.rb +0 -2
  153. data/lib/roast/value_objects.rb +4 -4
  154. data/lib/roast/version.rb +1 -1
  155. data/lib/roast/workflow/agent_step.rb +33 -0
  156. data/lib/roast/workflow/api_configuration.rb +0 -4
  157. data/lib/roast/workflow/base_iteration_step.rb +3 -6
  158. data/lib/roast/workflow/base_step.rb +54 -28
  159. data/lib/roast/workflow/base_workflow.rb +43 -23
  160. data/lib/roast/workflow/case_executor.rb +0 -1
  161. data/lib/roast/workflow/case_step.rb +0 -4
  162. data/lib/roast/workflow/command_executor.rb +0 -2
  163. data/lib/roast/workflow/conditional_executor.rb +0 -1
  164. data/lib/roast/workflow/conditional_step.rb +0 -4
  165. data/lib/roast/workflow/configuration.rb +5 -67
  166. data/lib/roast/workflow/configuration_loader.rb +63 -3
  167. data/lib/roast/workflow/configuration_parser.rb +1 -7
  168. data/lib/roast/workflow/context_manager.rb +89 -0
  169. data/lib/roast/workflow/dot_access_hash.rb +16 -1
  170. data/lib/roast/workflow/each_step.rb +1 -1
  171. data/lib/roast/workflow/error_handler.rb +0 -3
  172. data/lib/roast/workflow/expression_evaluator.rb +0 -3
  173. data/lib/roast/workflow/file_state_repository.rb +0 -5
  174. data/lib/roast/workflow/input_executor.rb +41 -0
  175. data/lib/roast/workflow/input_step.rb +163 -0
  176. data/lib/roast/workflow/iteration_executor.rb +0 -2
  177. data/lib/roast/workflow/output_handler.rb +1 -3
  178. data/lib/roast/workflow/output_manager.rb +0 -2
  179. data/lib/roast/workflow/repeat_step.rb +1 -1
  180. data/lib/roast/workflow/replay_handler.rb +1 -4
  181. data/lib/roast/workflow/resource_resolver.rb +0 -3
  182. data/lib/roast/workflow/session_manager.rb +0 -3
  183. data/lib/roast/workflow/sqlite_state_repository.rb +342 -0
  184. data/lib/roast/workflow/state_manager.rb +2 -4
  185. data/lib/roast/workflow/state_repository_factory.rb +36 -0
  186. data/lib/roast/workflow/step_completion_reporter.rb +27 -0
  187. data/lib/roast/workflow/step_executor_coordinator.rb +48 -24
  188. data/lib/roast/workflow/step_executor_factory.rb +0 -5
  189. data/lib/roast/workflow/step_executor_registry.rb +1 -4
  190. data/lib/roast/workflow/step_executor_with_reporting.rb +68 -0
  191. data/lib/roast/workflow/step_executors/hash_step_executor.rb +0 -3
  192. data/lib/roast/workflow/step_executors/parallel_step_executor.rb +0 -3
  193. data/lib/roast/workflow/step_executors/string_step_executor.rb +0 -2
  194. data/lib/roast/workflow/step_factory.rb +56 -0
  195. data/lib/roast/workflow/step_loader.rb +31 -17
  196. data/lib/roast/workflow/step_name_extractor.rb +84 -0
  197. data/lib/roast/workflow/step_orchestrator.rb +3 -2
  198. data/lib/roast/workflow/step_type_resolver.rb +28 -1
  199. data/lib/roast/workflow/validation_command.rb +197 -0
  200. data/lib/roast/workflow/validator.rb +0 -4
  201. data/lib/roast/workflow/validators/base_validator.rb +44 -0
  202. data/lib/roast/workflow/validators/dependency_validator.rb +223 -0
  203. data/lib/roast/workflow/validators/linting_validator.rb +113 -0
  204. data/lib/roast/workflow/validators/schema_validator.rb +90 -0
  205. data/lib/roast/workflow/validators/step_collector.rb +57 -0
  206. data/lib/roast/workflow/validators/validation_orchestrator.rb +52 -0
  207. data/lib/roast/workflow/workflow_executor.rb +11 -20
  208. data/lib/roast/workflow/workflow_initializer.rb +1 -8
  209. data/lib/roast/workflow/workflow_runner.rb +6 -7
  210. data/lib/roast/workflow.rb +0 -15
  211. data/lib/roast/workflow_diagram_generator.rb +298 -0
  212. data/lib/roast.rb +212 -10
  213. data/roast.gemspec +4 -2
  214. data/schema/workflow.json +123 -1
  215. metadata +143 -6
  216. 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 "fileutils"
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
- require "roast/errors"
16
- require "roast/helpers"
17
- require "roast/initializers"
18
- require "roast/resources"
19
- require "roast/tools"
20
- require "roast/version"
21
- require "roast/workflow"
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
- "type": "string"
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
  }