roast-ai 0.4.4 → 0.4.5

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 (231) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +2 -0
  3. data/.github/workflows/ci.yaml +1 -0
  4. data/.gitignore +0 -26
  5. data/.rubocop.yml +13 -0
  6. data/.rubocop_todo.yml +21 -0
  7. data/CHANGELOG.md +29 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +43 -1
  10. data/bin/rubocop +27 -0
  11. data/bin/spoom +27 -0
  12. data/bin/srb +27 -0
  13. data/bin/srb-rbi +27 -0
  14. data/bin/tapioca +27 -0
  15. data/docs/INSTRUMENTATION.md +3 -3
  16. data/dsl/simple.rb +10 -0
  17. data/lib/roast/dsl/executor.rb +26 -0
  18. data/lib/roast/dsl.rb +7 -0
  19. data/lib/roast/errors.rb +1 -0
  20. data/lib/roast/factories/api_provider_factory.rb +1 -0
  21. data/lib/roast/helpers/function_caching_interceptor.rb +3 -0
  22. data/lib/roast/helpers/logger.rb +6 -9
  23. data/lib/roast/helpers/metadata_access.rb +1 -0
  24. data/lib/roast/helpers/minitest_coverage_runner.rb +1 -0
  25. data/lib/roast/helpers/path_resolver.rb +8 -18
  26. data/lib/roast/helpers/prompt_loader.rb +3 -2
  27. data/lib/roast/helpers/timeout_handler.rb +3 -5
  28. data/lib/roast/initializers.rb +1 -0
  29. data/lib/roast/resources/api_resource.rb +4 -3
  30. data/lib/roast/resources/base_resource.rb +1 -0
  31. data/lib/roast/resources/directory_resource.rb +1 -0
  32. data/lib/roast/resources/file_resource.rb +1 -0
  33. data/lib/roast/resources/none_resource.rb +1 -0
  34. data/lib/roast/resources/url_resource.rb +2 -1
  35. data/lib/roast/resources.rb +1 -2
  36. data/lib/roast/services/context_threshold_checker.rb +1 -0
  37. data/lib/roast/services/token_counting_service.rb +1 -0
  38. data/lib/roast/tools/apply_diff.rb +1 -0
  39. data/lib/roast/tools/ask_user.rb +1 -0
  40. data/lib/roast/tools/bash.rb +1 -0
  41. data/lib/roast/tools/cmd.rb +1 -0
  42. data/lib/roast/tools/coding_agent.rb +1 -0
  43. data/lib/roast/tools/context_summarizer.rb +1 -0
  44. data/lib/roast/tools/grep.rb +1 -0
  45. data/lib/roast/tools/helpers/coding_agent_message_formatter.rb +1 -0
  46. data/lib/roast/tools/read_file.rb +1 -0
  47. data/lib/roast/tools/search_file.rb +1 -0
  48. data/lib/roast/tools/swarm.rb +1 -0
  49. data/lib/roast/tools/update_files.rb +1 -0
  50. data/lib/roast/tools/write_file.rb +1 -0
  51. data/lib/roast/tools.rb +2 -0
  52. data/lib/roast/value_objects/api_token.rb +1 -0
  53. data/lib/roast/value_objects/step_name.rb +1 -0
  54. data/lib/roast/value_objects/uri_base.rb +1 -0
  55. data/lib/roast/value_objects/workflow_path.rb +1 -0
  56. data/lib/roast/value_objects.rb +1 -0
  57. data/lib/roast/version.rb +2 -1
  58. data/lib/roast/workflow/agent_step.rb +1 -0
  59. data/lib/roast/workflow/api_configuration.rb +1 -0
  60. data/lib/roast/workflow/base_iteration_step.rb +1 -0
  61. data/lib/roast/workflow/base_step.rb +1 -0
  62. data/lib/roast/workflow/base_workflow.rb +1 -0
  63. data/lib/roast/workflow/case_executor.rb +1 -0
  64. data/lib/roast/workflow/case_step.rb +1 -0
  65. data/lib/roast/workflow/command_executor.rb +1 -0
  66. data/lib/roast/workflow/conditional_executor.rb +1 -0
  67. data/lib/roast/workflow/conditional_step.rb +1 -0
  68. data/lib/roast/workflow/configuration.rb +9 -0
  69. data/lib/roast/workflow/configuration_loader.rb +4 -1
  70. data/lib/roast/workflow/context_manager.rb +1 -0
  71. data/lib/roast/workflow/context_path_resolver.rb +1 -0
  72. data/lib/roast/workflow/dot_access_hash.rb +1 -0
  73. data/lib/roast/workflow/each_step.rb +1 -0
  74. data/lib/roast/workflow/error_handler.rb +7 -1
  75. data/lib/roast/workflow/expression_evaluator.rb +1 -0
  76. data/lib/roast/workflow/expression_utils.rb +1 -0
  77. data/lib/roast/workflow/file_state_repository.rb +2 -1
  78. data/lib/roast/workflow/input_executor.rb +1 -0
  79. data/lib/roast/workflow/input_step.rb +1 -0
  80. data/lib/roast/workflow/interpolator.rb +2 -1
  81. data/lib/roast/workflow/iteration_executor.rb +1 -0
  82. data/lib/roast/workflow/llm_boolean_coercer.rb +1 -0
  83. data/lib/roast/workflow/metadata_manager.rb +1 -0
  84. data/lib/roast/workflow/output_handler.rb +1 -0
  85. data/lib/roast/workflow/output_manager.rb +1 -0
  86. data/lib/roast/workflow/parallel_executor.rb +1 -0
  87. data/lib/roast/workflow/prompt_step.rb +1 -0
  88. data/lib/roast/workflow/repeat_step.rb +1 -0
  89. data/lib/roast/workflow/replay_handler.rb +1 -0
  90. data/lib/roast/workflow/resource_resolver.rb +2 -6
  91. data/lib/roast/workflow/session_manager.rb +1 -0
  92. data/lib/roast/workflow/shell_script_step.rb +1 -0
  93. data/lib/roast/workflow/sqlite_state_repository.rb +1 -0
  94. data/lib/roast/workflow/state_manager.rb +1 -0
  95. data/lib/roast/workflow/state_repository.rb +1 -0
  96. data/lib/roast/workflow/state_repository_factory.rb +4 -2
  97. data/lib/roast/workflow/step_completion_reporter.rb +1 -0
  98. data/lib/roast/workflow/step_executor_coordinator.rb +9 -5
  99. data/lib/roast/workflow/step_executor_factory.rb +1 -0
  100. data/lib/roast/workflow/step_executor_registry.rb +2 -3
  101. data/lib/roast/workflow/step_executor_with_reporting.rb +1 -0
  102. data/lib/roast/workflow/step_executors/base_step_executor.rb +1 -0
  103. data/lib/roast/workflow/step_executors/hash_step_executor.rb +1 -0
  104. data/lib/roast/workflow/step_executors/parallel_step_executor.rb +1 -0
  105. data/lib/roast/workflow/step_executors/string_step_executor.rb +1 -0
  106. data/lib/roast/workflow/step_factory.rb +1 -0
  107. data/lib/roast/workflow/step_finder.rb +1 -0
  108. data/lib/roast/workflow/step_loader.rb +1 -0
  109. data/lib/roast/workflow/step_name_extractor.rb +1 -0
  110. data/lib/roast/workflow/step_runner.rb +1 -0
  111. data/lib/roast/workflow/step_type_resolver.rb +1 -0
  112. data/lib/roast/workflow/validation_command.rb +1 -0
  113. data/lib/roast/workflow/validator.rb +1 -0
  114. data/lib/roast/workflow/validators/base_validator.rb +1 -0
  115. data/lib/roast/workflow/validators/dependency_validator.rb +1 -0
  116. data/lib/roast/workflow/validators/linting_validator.rb +1 -0
  117. data/lib/roast/workflow/validators/schema_validator.rb +1 -0
  118. data/lib/roast/workflow/validators/step_collector.rb +1 -0
  119. data/lib/roast/workflow/validators/validation_orchestrator.rb +1 -0
  120. data/lib/roast/workflow/workflow_context.rb +1 -0
  121. data/lib/roast/workflow/workflow_execution_context.rb +1 -0
  122. data/lib/roast/workflow/workflow_executor.rb +2 -1
  123. data/lib/roast/workflow/workflow_initializer.rb +1 -0
  124. data/lib/roast/workflow/workflow_runner.rb +66 -55
  125. data/lib/roast/workflow.rb +1 -0
  126. data/lib/roast/workflow_diagram_generator.rb +3 -2
  127. data/lib/roast.rb +23 -8
  128. data/sorbet/config +8 -0
  129. data/sorbet/rbi/dsl/.gitattributes +1 -0
  130. data/sorbet/rbi/dsl/active_support/callbacks.rbi +21 -0
  131. data/sorbet/rbi/gems/.gitattributes +1 -0
  132. data/sorbet/rbi/gems/activesupport@8.0.2.rbi +19107 -0
  133. data/sorbet/rbi/gems/addressable@2.8.7.rbi +1994 -0
  134. data/sorbet/rbi/gems/ast@2.4.3.rbi +585 -0
  135. data/sorbet/rbi/gems/base64@0.3.0.rbi +545 -0
  136. data/sorbet/rbi/gems/benchmark@0.4.1.rbi +619 -0
  137. data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +62 -0
  138. data/sorbet/rbi/gems/cgi@0.5.0.rbi +2961 -0
  139. data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +568 -0
  140. data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +1991 -0
  141. data/sorbet/rbi/gems/cli-ui@2.3.0.rbi +3181 -0
  142. data/sorbet/rbi/gems/coderay@1.1.3.rbi +9 -0
  143. data/sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi +11657 -0
  144. data/sorbet/rbi/gems/connection_pool@2.5.3.rbi +9 -0
  145. data/sorbet/rbi/gems/crack@1.0.0.rbi +145 -0
  146. data/sorbet/rbi/gems/diff-lcs@1.6.2.rbi +972 -0
  147. data/sorbet/rbi/gems/dotenv@3.1.8.rbi +295 -0
  148. data/sorbet/rbi/gems/drb@2.2.3.rbi +1661 -0
  149. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +672 -0
  150. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +1894 -0
  151. data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +659 -0
  152. data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +781 -0
  153. data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +1127 -0
  154. data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +3727 -0
  155. data/sorbet/rbi/gems/dry-types@1.8.3.rbi +3969 -0
  156. data/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
  157. data/sorbet/rbi/gems/event_stream_parser@1.0.0.rbi +49 -0
  158. data/sorbet/rbi/gems/faraday-multipart@1.1.1.rbi +283 -0
  159. data/sorbet/rbi/gems/faraday-net_http@3.4.1.rbi +147 -0
  160. data/sorbet/rbi/gems/faraday-retry@2.3.2.rbi +9 -0
  161. data/sorbet/rbi/gems/faraday@2.13.1.rbi +2977 -0
  162. data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +1588 -0
  163. data/sorbet/rbi/gems/ffi@1.17.2.rbi +9 -0
  164. data/sorbet/rbi/gems/formatador@1.1.0.rbi +9 -0
  165. data/sorbet/rbi/gems/guard-compat@1.2.1.rbi +102 -0
  166. data/sorbet/rbi/gems/guard-minitest@2.4.6.rbi +402 -0
  167. data/sorbet/rbi/gems/guard@2.19.1.rbi +2283 -0
  168. data/sorbet/rbi/gems/hashdiff@1.2.0.rbi +355 -0
  169. data/sorbet/rbi/gems/i18n@1.14.7.rbi +2359 -0
  170. data/sorbet/rbi/gems/json-schema@5.1.1.rbi +1466 -0
  171. data/sorbet/rbi/gems/json@2.12.2.rbi +2051 -0
  172. data/sorbet/rbi/gems/language_server-protocol@3.17.0.5.rbi +9 -0
  173. data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +9 -0
  174. data/sorbet/rbi/gems/listen@3.9.0.rbi +1206 -0
  175. data/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
  176. data/sorbet/rbi/gems/lumberjack@1.2.10.rbi +1830 -0
  177. data/sorbet/rbi/gems/method_source@1.1.0.rbi +9 -0
  178. data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +136 -0
  179. data/sorbet/rbi/gems/mime-types@3.7.0.rbi +1342 -0
  180. data/sorbet/rbi/gems/minitest-rg@5.3.0.rbi +160 -0
  181. data/sorbet/rbi/gems/minitest@5.25.5.rbi +1640 -0
  182. data/sorbet/rbi/gems/mocha@2.7.1.rbi +12 -0
  183. data/sorbet/rbi/gems/multipart-post@2.4.1.rbi +244 -0
  184. data/sorbet/rbi/gems/nenv@0.3.0.rbi +147 -0
  185. data/sorbet/rbi/gems/net-http@0.6.0.rbi +4247 -0
  186. data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
  187. data/sorbet/rbi/gems/notiffany@0.1.3.rbi +1079 -0
  188. data/sorbet/rbi/gems/open_router@0.3.3.rbi +230 -0
  189. data/sorbet/rbi/gems/ostruct@0.6.2.rbi +354 -0
  190. data/sorbet/rbi/gems/parallel@1.27.0.rbi +291 -0
  191. data/sorbet/rbi/gems/parser@3.3.8.0.rbi +5535 -0
  192. data/sorbet/rbi/gems/prism@1.4.0.rbi +41732 -0
  193. data/sorbet/rbi/gems/pry@0.15.2.rbi +9 -0
  194. data/sorbet/rbi/gems/public_suffix@6.0.2.rbi +936 -0
  195. data/sorbet/rbi/gems/racc@1.8.1.rbi +158 -0
  196. data/sorbet/rbi/gems/rack@2.2.17.rbi +5659 -0
  197. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +9 -0
  198. data/sorbet/rbi/gems/raix@1.0.2.rbi +1104 -0
  199. data/sorbet/rbi/gems/rake@13.3.0.rbi +3036 -0
  200. data/sorbet/rbi/gems/rb-fsevent@0.11.2.rbi +9 -0
  201. data/sorbet/rbi/gems/rb-inotify@0.11.1.rbi +9 -0
  202. data/sorbet/rbi/gems/rbi@0.3.6.rbi +6893 -0
  203. data/sorbet/rbi/gems/rbs@3.9.4.rbi +6978 -0
  204. data/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +9 -0
  205. data/sorbet/rbi/gems/rexml@3.4.1.rbi +5346 -0
  206. data/sorbet/rbi/gems/rubocop-ast@1.45.1.rbi +9 -0
  207. data/sorbet/rbi/gems/rubocop-shopify@2.17.1.rbi +9 -0
  208. data/sorbet/rbi/gems/rubocop@1.77.0.rbi +9 -0
  209. data/sorbet/rbi/gems/ruby-graphviz@1.2.5.rbi +1333 -0
  210. data/sorbet/rbi/gems/ruby-openai@8.1.0.rbi +758 -0
  211. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +9 -0
  212. data/sorbet/rbi/gems/ruby2_keywords@0.0.5.rbi +9 -0
  213. data/sorbet/rbi/gems/securerandom@0.4.1.rbi +75 -0
  214. data/sorbet/rbi/gems/shellany@0.0.1.rbi +102 -0
  215. data/sorbet/rbi/gems/spoom@1.6.3.rbi +6985 -0
  216. data/sorbet/rbi/gems/sqlite3@2.7.0.rbi +1900 -0
  217. data/sorbet/rbi/gems/tapioca@0.16.11.rbi +3628 -0
  218. data/sorbet/rbi/gems/thor@1.4.0.rbi +4399 -0
  219. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5918 -0
  220. data/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +9 -0
  221. data/sorbet/rbi/gems/unicode-emoji@4.0.4.rbi +9 -0
  222. data/sorbet/rbi/gems/uri@1.0.3.rbi +2349 -0
  223. data/sorbet/rbi/gems/vcr@6.3.1.rbi +3040 -0
  224. data/sorbet/rbi/gems/webmock@3.25.1.rbi +1792 -0
  225. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
  226. data/sorbet/rbi/gems/yard@0.9.37.rbi +18492 -0
  227. data/sorbet/rbi/gems/zeitwerk@2.7.3.rbi +1429 -0
  228. data/sorbet/tapioca/config.yml +13 -0
  229. data/sorbet/tapioca/require.rb +36 -0
  230. metadata +113 -2
  231. data/lib/roast/workflow/configuration_parser.rb +0 -54
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90d715c7be58b0ec9d6325f0eafd0b234d8bcf51fa3151c5830717dac034fdb5
4
- data.tar.gz: d52615bc873667aaf3dc22a29c59b4ae0f6f1dd25af3addaaa4c8ad23eaa7cb4
3
+ metadata.gz: 70a8b9b5eb5427205d65f9e2d3eb989c53a0cd449aa83d2e8df712f3b06ae203
4
+ data.tar.gz: 29d8236ed59630399b69ddc7286d9f80428620158117a68bc14048c8ac0a1e78
5
5
  SHA512:
6
- metadata.gz: 80796feab187127b4815af1d6120f5370724e817c55ecc240b2f5c58c39faf63c2e7ccb65ebc58c427a09debea7c26fc2ba26a044450e8696901a5da9aaba4b9
7
- data.tar.gz: fa9791f7148cad5177f756705eab8778f3925183f734537b5732d712513be494c7124a530e065b5ed40765c967361fd6d4a1ee62897b0ab34db334588f2dfb78
6
+ metadata.gz: 48fcaf3988124ce0d627ff22f11fc563a990b96eb92afa27b75294f4c21ed2341c240f5ecf1fcf2b618f87078b19876adaa5cc493419a6be3b05c6b2137abd58
7
+ data.tar.gz: 4df2dfb16ff49b3bfe53102d878de8c356330621e4267a07fbfd35642251b512eab974cbe4c3377fbdaf29933b4a36c91010abc741ccb6b11321fcc4decf191a
@@ -0,0 +1,2 @@
1
+ * @nfgrep @dersam @obie @juniper-shopify
2
+
@@ -30,4 +30,5 @@ jobs:
30
30
  ruby-version: ${{ matrix.ruby }}
31
31
  bundler-cache: true
32
32
  - run: bundle exec rake ci
33
+ - run: bin/srb tc
33
34
 
data/.gitignore CHANGED
@@ -13,32 +13,6 @@
13
13
  **/CLAUDE.local.md
14
14
  .roast/
15
15
 
16
- bin/_guard-core
17
- bin/bundle
18
- bin/coderay
19
- bin/dotenv
20
- bin/erb
21
- bin/guard
22
- bin/htmldiff
23
- bin/irb
24
- bin/ldiff
25
- bin/listen
26
- bin/pry
27
- bin/racc
28
- bin/rake
29
- bin/rbs
30
- bin/rdbg
31
- bin/rdoc
32
- bin/ri
33
- bin/rubocop
34
- bin/ruby-lsp
35
- bin/ruby-lsp-check
36
- bin/ruby-lsp-launcher
37
- bin/ruby-lsp-test-exec
38
- bin/ruby-parse
39
- bin/ruby-rewrite
40
- bin/thor
41
-
42
16
  gemfiles/*.lock
43
17
  bin/claude-swarm
44
18
  *.gem
data/.rubocop.yml CHANGED
@@ -1,3 +1,8 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ plugins:
4
+ - rubocop-sorbet
5
+
1
6
  inherit_gem:
2
7
  rubocop-shopify: rubocop.yml
3
8
 
@@ -10,3 +15,11 @@ AllCops:
10
15
  SuggestExtensions: false
11
16
  TargetRubyVersion: 3.4
12
17
  UseCache: true
18
+
19
+ Sorbet/FalseSigil:
20
+ Enabled: true
21
+ Include:
22
+ - "lib/**/*.rb"
23
+ Exclude:
24
+ - "test/**/*"
25
+ - "examples/**/*"
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,21 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2025-08-17 17:40:01 UTC using RuboCop version 1.77.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 4
10
+ Sorbet/ConstantsFromStrings:
11
+ Exclude:
12
+ - 'lib/roast/helpers/logger.rb'
13
+ - 'lib/roast/workflow/step_loader.rb'
14
+ - 'lib/roast/workflow/validators/dependency_validator.rb'
15
+ - 'lib/roast/workflow/workflow_initializer.rb'
16
+
17
+ # Offense count: 1
18
+ # This cop supports safe autocorrection (--autocorrect).
19
+ Sorbet/SelectByIsA:
20
+ Exclude:
21
+ - 'lib/roast/workflow/configuration_loader.rb'
data/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.5]
9
+
10
+ ### Added
11
+ - **Sorbet static type checking** (#359, #360, #362)
12
+ - Initial setup of Sorbet for static type analysis
13
+ - Added `bin/srb tc` command for type checking
14
+ - Gradually adding type signatures to improve code safety and navigation
15
+ - Fixed invalid constants that were undefined and never reached
16
+ - Added to CI pipeline for continuous type checking
17
+ - **Workflow name in step event payloads** (#333, #351)
18
+ - Added `workflow_name` field to all step-related ActiveSupport::Notification events
19
+ - Enables better tracking of which workflow a step belongs to in monitoring systems
20
+ - Updated events: `roast.step.start`, `roast.step.complete`, `roast.step.error`
21
+
22
+ ### Changed
23
+ - **Improved error output formatting** (#358)
24
+ - Error messages now show concise output by default (just the error message)
25
+ - Full exception details and stack traces only shown in verbose mode (`-v`)
26
+ - Makes error output cleaner and more user-friendly
27
+ - **Consolidated workflow runner architecture** (#355)
28
+ - Merged ConfigurationParser functionality into WorkflowRunner for better cohesion
29
+ - Simplified the codebase by removing redundant abstraction layers
30
+
31
+ ### Fixed
32
+ - **Removed duplicated pre/post processing code** (#357)
33
+ - Extracted common pre/post processing logic from individual step executors
34
+ - Eliminated code duplication across different step types
35
+ - Improved maintainability and consistency
36
+
8
37
  ## [0.4.3] - 2025-07-10
9
38
 
10
39
  ### Changed
data/Gemfile CHANGED
@@ -15,8 +15,12 @@ gem "guard-minitest"
15
15
  gem "mocha"
16
16
  gem "rake", require: false
17
17
  gem "rubocop-shopify", require: false
18
+ gem "rubocop-sorbet", require: false
18
19
  gem "vcr", require: false
19
20
  gem "webmock", require: false
20
21
  gem "minitest-rg"
21
22
 
23
+ gem "sorbet", require: false
24
+ gem "tapioca", require: false
25
+
22
26
  gem "claude_swarm"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (0.4.4)
4
+ roast-ai (0.4.5)
5
5
  activesupport (>= 7.0)
6
6
  cli-kit (~> 5.0)
7
7
  cli-ui (= 2.3.0)
@@ -81,6 +81,7 @@ GEM
81
81
  dry-inflector (~> 1.0)
82
82
  dry-logic (~> 1.4)
83
83
  zeitwerk (~> 2.6)
84
+ erubi (1.13.1)
84
85
  event_stream_parser (1.0.0)
85
86
  faraday (2.13.1)
86
87
  faraday-net_http (>= 2.0, < 3.5)
@@ -145,6 +146,7 @@ GEM
145
146
  nenv (0.3.0)
146
147
  net-http (0.6.0)
147
148
  uri
149
+ netrc (0.11.0)
148
150
  notiffany (0.1.3)
149
151
  nenv (~> 0.1)
150
152
  shellany (~> 0.0)
@@ -176,6 +178,11 @@ GEM
176
178
  rb-fsevent (0.11.2)
177
179
  rb-inotify (0.11.1)
178
180
  ffi (~> 1.0)
181
+ rbi (0.3.6)
182
+ prism (~> 1.0)
183
+ rbs (>= 3.4.4)
184
+ rbs (3.9.4)
185
+ logger
179
186
  regexp_parser (2.10.0)
180
187
  rexml (3.4.1)
181
188
  rubocop (1.77.0)
@@ -194,6 +201,9 @@ GEM
194
201
  prism (~> 1.4)
195
202
  rubocop-shopify (2.17.1)
196
203
  rubocop (~> 1.62)
204
+ rubocop-sorbet (0.10.5)
205
+ lint_roller
206
+ rubocop (>= 1.75.2)
197
207
  ruby-graphviz (1.2.5)
198
208
  rexml
199
209
  ruby-openai (8.1.0)
@@ -204,8 +214,33 @@ GEM
204
214
  ruby2_keywords (0.0.5)
205
215
  securerandom (0.4.1)
206
216
  shellany (0.0.1)
217
+ sorbet (0.5.12414)
218
+ sorbet-static (= 0.5.12414)
219
+ sorbet-runtime (0.5.12414)
220
+ sorbet-static (0.5.12414-universal-darwin)
221
+ sorbet-static (0.5.12414-x86_64-linux)
222
+ sorbet-static-and-runtime (0.5.12414)
223
+ sorbet (= 0.5.12414)
224
+ sorbet-runtime (= 0.5.12414)
225
+ spoom (1.6.3)
226
+ erubi (>= 1.10.0)
227
+ prism (>= 0.28.0)
228
+ rbi (>= 0.3.3)
229
+ rexml (>= 3.2.6)
230
+ sorbet-static-and-runtime (>= 0.5.10187)
231
+ thor (>= 0.19.2)
207
232
  sqlite3 (2.7.0-arm64-darwin)
208
233
  sqlite3 (2.7.0-x86_64-linux-gnu)
234
+ tapioca (0.16.11)
235
+ benchmark
236
+ bundler (>= 2.2.25)
237
+ netrc (>= 0.11.0)
238
+ parallel (>= 1.21.0)
239
+ rbi (~> 0.2)
240
+ sorbet-static-and-runtime (>= 0.5.11087)
241
+ spoom (>= 1.2.0)
242
+ thor (>= 1.2.0)
243
+ yard-sorbet
209
244
  thor (1.4.0)
210
245
  tzinfo (2.0.6)
211
246
  concurrent-ruby (~> 1.0)
@@ -219,6 +254,10 @@ GEM
219
254
  addressable (>= 2.8.0)
220
255
  crack (>= 0.3.2)
221
256
  hashdiff (>= 0.4.0, < 2.0.0)
257
+ yard (0.9.37)
258
+ yard-sorbet (0.9.0)
259
+ sorbet-runtime
260
+ yard
222
261
  zeitwerk (2.7.3)
223
262
 
224
263
  PLATFORMS
@@ -237,6 +276,9 @@ DEPENDENCIES
237
276
  rake
238
277
  roast-ai!
239
278
  rubocop-shopify
279
+ rubocop-sorbet
280
+ sorbet
281
+ tapioca
240
282
  vcr
241
283
  webmock
242
284
 
data/bin/rubocop ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rubocop", "rubocop")
data/bin/spoom ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'spoom' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("spoom", "spoom")
data/bin/srb ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'srb' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("sorbet", "srb")
data/bin/srb-rbi ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'srb-rbi' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("sorbet", "srb-rbi")
data/bin/tapioca ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'tapioca' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("tapioca", "tapioca")
@@ -39,13 +39,13 @@ your-project/
39
39
  ### Step Events
40
40
 
41
41
  - `roast.step.start` - Emitted when a step begins execution
42
- - Payload: `{ step_name: }`
42
+ - Payload: `{ step_name:, resource_type:, workflow_name: }`
43
43
 
44
44
  - `roast.step.complete` - Emitted when a step completes successfully
45
- - Payload: `{ step_name:, success: true, execution_time:, result_size: }`
45
+ - Payload: `{ step_name:, resource_type:, workflow_name:, success: true, execution_time:, result_size: }`
46
46
 
47
47
  - `roast.step.error` - Emitted when a step encounters an error
48
- - Payload: `{ step_name:, error:, message:, execution_time: }`
48
+ - Payload: `{ step_name:, resource_type:, workflow_name:, error:, message:, execution_time: }`
49
49
 
50
50
  ### AI/Chat Completion Events
51
51
 
data/dsl/simple.rb ADDED
@@ -0,0 +1,10 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Executor
5
+
6
+ # This is a dead simple workflow that calls two shell scripts
7
+ shell <<~SHELLSTEP
8
+ echo "I have no idea what's going on"
9
+ SHELLSTEP
10
+ shell "pwd"
@@ -0,0 +1,26 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ class Executor
7
+ class << self
8
+ def from_file(workflow_path)
9
+ execute(File.read(workflow_path))
10
+ end
11
+
12
+ private
13
+
14
+ def execute(input)
15
+ new.instance_eval(input)
16
+ end
17
+ end
18
+
19
+ # Define methods to be used in workflows below.
20
+
21
+ def shell(command_string)
22
+ puts %x(#{command_string})
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/roast/dsl.rb ADDED
@@ -0,0 +1,7 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Roast
5
+ module DSL
6
+ end
7
+ end
data/lib/roast/errors.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -1,9 +1,12 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
4
5
  module Helpers
5
6
  # Intercepts function dispatching to add caching capabilities
6
7
  # This module wraps around Raix::FunctionDispatch to provide caching for tool functions
8
+ # @requires_ancestor: Kernel
9
+ # @requires_ancestor: Raix::ChatCompletion
7
10
  module FunctionCachingInterceptor
8
11
  def dispatch_tool_function(function_name, params)
9
12
  start_time = Time.now
@@ -1,28 +1,25 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
4
5
  module Helpers
5
6
  # Central logger for the Roast application
6
7
  class Logger
7
- VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"].freeze
8
+ VALID_LOG_LEVELS = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"].freeze #: Array[String]
8
9
 
9
- delegate_missing_to :@logger
10
+ delegate :debug, :info, :warn, :error, :fatal, to: :@logger
10
11
 
11
12
  attr_reader :logger
12
13
 
13
14
  class << self
14
- delegate_missing_to :instance
15
+ delegate :debug, :info, :warn, :error, :fatal, to: :instance
15
16
 
17
+ #: -> Roast::Helpers::Logger
16
18
  def instance
17
19
  @instance ||= new
18
20
  end
19
21
 
20
- # Override Kernel#warn to ensure proper delegation
21
- def warn(*args)
22
- instance.warn(*args)
23
- end
24
-
25
- # For testing purposes
22
+ #: -> void
26
23
  def reset
27
24
  @instance = nil
28
25
  end
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "coverage"
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -38,17 +39,14 @@ module Roast
38
39
  next if i == 0 # Skip the first segment
39
40
 
40
41
  # Check if this segment appears earlier in the path
41
- next unless path_parts[0...i].include?(part)
42
+ next unless path_parts[0...i]&.include?(part)
42
43
 
43
44
  # Create a new path without this segment
44
45
  test_parts = path_parts.dup
45
46
  test_parts.delete_at(i)
47
+ test_parts.prepend("/") if original_path.start_with?("/")
46
48
 
47
- test_path = if original_path.start_with?("/")
48
- File.join("/", *test_parts)
49
- else
50
- File.join(test_parts)
51
- end
49
+ test_path = File.join(test_parts)
52
50
 
53
51
  # If this path exists, return it
54
52
  return test_path if File.exist?(test_path)
@@ -66,12 +64,8 @@ module Roast
66
64
  filtered_parts = path_parts.dup
67
65
  # Remove from end to beginning to keep indices valid
68
66
  duplicate_indices.reverse_each { |idx| filtered_parts.delete_at(idx) }
69
-
70
- test_path = if original_path.start_with?("/")
71
- File.join("/", *filtered_parts)
72
- else
73
- File.join(filtered_parts)
74
- end
67
+ filtered_parts.prepend("/") if original_path.start_with?("/")
68
+ test_path = File.join(filtered_parts)
75
69
 
76
70
  return test_path if File.exist?(test_path)
77
71
  end
@@ -93,12 +87,8 @@ module Roast
93
87
  unique_parts = path_parts.dup
94
88
  # Remove from end to beginning to keep indices valid
95
89
  duplicate_indices.reverse_each { |i| unique_parts.delete_at(i) }
96
-
97
- test_path = if original_path.start_with?("/")
98
- File.join("/", *unique_parts)
99
- else
100
- File.join(unique_parts)
101
- end
90
+ unique_parts.prepend("/") if original_path.start_with?("/")
91
+ test_path = File.join(unique_parts)
102
92
 
103
93
  return test_path if File.exist?(test_path)
104
94
  end
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -58,7 +59,7 @@ module Roast
58
59
  glob_pattern = File.join(context_dir, "{#{base_name},prompt}.*+*.md")
59
60
  Dir.glob(glob_pattern).each do |combined_path|
60
61
  basename = File.basename(combined_path, ".md")
61
- combined_exts = basename.split(".", 2)[1].split("+")
62
+ combined_exts = basename.split(".", 2)[1]&.split("+")
62
63
 
63
64
  # Return the first matching combined format
64
65
  return combined_path if extensions.intersect?(combined_exts)
@@ -77,7 +78,7 @@ module Roast
77
78
 
78
79
  if file_basename.end_with?(".md") && file_basename.count(".") > 1
79
80
  without_md = file_basename[0...-3] # Remove .md
80
- without_md.split(".", 2)[1]&.split("+") || []
81
+ without_md&.split(".", 2)&.[](1)&.split("+") || []
81
82
  else
82
83
  ext = File.extname(target_file)[1..]
83
84
  ext&.empty? ? [] : [ext]
@@ -1,8 +1,6 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
- require "timeout"
4
- require "open3"
5
-
6
4
  module Roast
7
5
  module Helpers
8
6
  # Shared timeout handling logic for command-based tools
@@ -29,8 +27,8 @@ module Roast
29
27
  def call(command, timeout: DEFAULT_TIMEOUT, working_directory: Dir.pwd)
30
28
  timeout = validate_timeout(timeout)
31
29
  output = ""
32
- exit_status = nil
33
- wait_thr = nil
30
+ exit_status = nil #: Integer?
31
+ wait_thr = nil #: Process::Waiter?
34
32
 
35
33
  begin
36
34
  Timeout.timeout(timeout) do
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -57,7 +58,7 @@ module Roast
57
58
  http.use_ssl = (uri.scheme == "https")
58
59
 
59
60
  # Use HEAD request to check existence
60
- request = Net::HTTP::Head.new(uri.path.empty? ? "/" : uri.path)
61
+ request = Net::HTTP::Head.new(uri.path&.empty? ? "/" : uri.path)
61
62
 
62
63
  # Add headers if present in options
63
64
  if options["headers"].is_a?(Hash)
@@ -124,8 +125,8 @@ module Roast
124
125
  # Replace environment variables in the format ${VAR_NAME}
125
126
  def process_env_vars(text)
126
127
  text.gsub(/\${([^}]+)}/) do |_match|
127
- var_name = ::Regexp.last_match(1).strip
128
- ENV.fetch(var_name, "${#{var_name}}")
128
+ var_name = ::Regexp.last_match(1)&.strip
129
+ ENV.fetch(var_name, "${#{var_name}}") unless var_name.nil?
129
130
  end
130
131
  end
131
132
  end
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Roast