roast-ai 0.1.3 → 0.1.6
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 +41 -1
- data/CLAUDE.md +118 -8
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -22
- data/README.md +43 -7
- data/Rakefile +5 -1
- data/examples/interpolation/README.md +50 -0
- data/examples/interpolation/analyze_file/prompt.md +1 -0
- data/examples/interpolation/analyze_patterns/prompt.md +27 -0
- data/examples/interpolation/generate_report_for_js/prompt.md +3 -0
- data/examples/interpolation/generate_report_for_rb/prompt.md +3 -0
- data/examples/interpolation/sample.js +48 -0
- data/examples/interpolation/sample.rb +42 -0
- data/examples/interpolation/workflow.md +1 -0
- data/examples/interpolation/workflow.yml +21 -0
- data/examples/openrouter_example/README.md +48 -0
- data/examples/openrouter_example/analyze_input/prompt.md +16 -0
- data/examples/openrouter_example/generate_response/prompt.md +9 -0
- data/examples/openrouter_example/workflow.yml +12 -0
- data/lib/roast/initializers.rb +39 -0
- data/lib/roast/tools/search_file.rb +22 -15
- data/lib/roast/tools/write_file.rb +8 -6
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_workflow.rb +4 -0
- data/lib/roast/workflow/configuration.rb +28 -1
- data/lib/roast/workflow/configuration_parser.rb +10 -21
- data/lib/roast/workflow/workflow_executor.rb +33 -2
- data/lib/roast.rb +6 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bdaada020320af4c7fead86184a7cd0b1c06a89e88bc15ac424b31f4b449fb6
|
4
|
+
data.tar.gz: 3f83f410a1f21992de59b19b8e2f0ea402b9fe50b314b82514723cf20f9a599f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6dfdd57104e363735a6e24bf5beb43a042340f582f94d8126c3f8a0d30dd92b031c5201a7d3d136bfcd3b7095a51c49329b08c07c5c19544240b18e20ad9948
|
7
|
+
data.tar.gz: 02aec8d620c3fe0c57d0d4e06790a7513a4a1aa44f68462721b6aa7da3f22f377598b4c00453e4ec825b0adcac3c335d470f945599d39571edb67e8a380597ac
|
data/.github/workflows/ci.yaml
CHANGED
@@ -16,7 +16,7 @@ jobs:
|
|
16
16
|
fail-fast: false
|
17
17
|
matrix:
|
18
18
|
os: [ubuntu]
|
19
|
-
ruby: ['3.4'
|
19
|
+
ruby: ['3.4']
|
20
20
|
runs-on: ${{ matrix.os }}-latest
|
21
21
|
continue-on-error: ${{ matrix.ruby == 'ruby-head' }}
|
22
22
|
steps:
|
@@ -25,5 +25,5 @@ jobs:
|
|
25
25
|
with:
|
26
26
|
ruby-version: ${{ matrix.ruby }}
|
27
27
|
bundler-cache: true
|
28
|
-
- run: bundle exec rake
|
28
|
+
- run: bundle exec rake ci
|
29
29
|
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,47 @@ 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.1.
|
8
|
+
## [0.1.6] - 2024-05-15
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Support for OpenRouter as an API provider
|
12
|
+
- `api_provider` configuration option allowing choice between OpenAI and OpenRouter
|
13
|
+
- Added separate CI rake task for improved build pipeline
|
14
|
+
- Version command to check current Roast version
|
15
|
+
- Walking up to home folder for config root
|
16
|
+
- Improved initializer support for better project configuration
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Enhanced search tool to work with globs for more flexible searches
|
20
|
+
- Improved error handling in configuration and initializers
|
21
|
+
- Fixed and simplified interpolation examples
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- Better error messages for search file tool
|
25
|
+
- Improved initializer loading and error handling
|
26
|
+
- Fixed tests for nested .roast folders
|
27
|
+
|
28
|
+
[0.1.6]: https://github.com/Shopify/roast/compare/v0.1.5...v0.1.6
|
29
|
+
|
30
|
+
## [0.1.5] - 2024-05-13
|
31
|
+
|
32
|
+
### Added
|
33
|
+
- Interpolation feature for dynamic workflows using `{{}}` syntax
|
34
|
+
- Support for injecting values from workflow context into step names and commands
|
35
|
+
- Ability to access file metadata and step outputs using interpolation expressions
|
36
|
+
- Examples demonstrating interpolation usage with different file types
|
37
|
+
|
38
|
+
[0.1.5]: https://github.com/Shopify/roast/releases/tag/v0.1.5
|
39
|
+
|
40
|
+
## [0.1.4] - 2024-05-13
|
41
|
+
|
42
|
+
### Fixed
|
43
|
+
- Remove test directory restriction from WriteTool. (Thank you @endoze)
|
44
|
+
|
45
|
+
[0.1.4]: https://github.com/Shopify/roast/releases/tag/v0.1.4
|
46
|
+
|
47
|
+
|
48
|
+
## [0.1.3] - 2024-05-12
|
9
49
|
|
10
50
|
### Fixed
|
11
51
|
- ReadFile tool now handles absolute and relative paths correctly
|
data/CLAUDE.md
CHANGED
@@ -7,16 +7,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
7
7
|
|
8
8
|
## Commands
|
9
9
|
|
10
|
-
- Build: `bundle exec rake build`
|
11
|
-
- Test all: `bundle exec rspec`
|
12
|
-
- Run single test: `bundle exec rspec spec/path/to/test_file.rb`
|
13
|
-
- Lint: `bundle exec rubocop -A`
|
14
10
|
- Default (tests + lint): `bundle exec rake`
|
15
|
-
-
|
11
|
+
- Test all: `bundle exec test`
|
12
|
+
- Run single test: `bundle exec ruby -Itest test/path/to/test_file.rb`
|
13
|
+
- Lint: `bundle exec rubocop`
|
14
|
+
- Lint (with autocorrect, preferred): `bundle exec rubocop -A`
|
16
15
|
|
17
16
|
## Tech stack
|
18
|
-
- `
|
19
|
-
- Testing: Use
|
17
|
+
- `thor` and `cli-ui` for the CLI tool
|
18
|
+
- Testing: Use Minitest, VCR for HTTP mocking, test files named with `_test.rb` suffix
|
20
19
|
|
21
20
|
## Code Style Guidelines
|
22
21
|
|
@@ -30,4 +29,115 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
30
29
|
- Define class methods inside `class << self; end` declarations.
|
31
30
|
- Add runtime dependencies to `roast.gemspec`.
|
32
31
|
- Add development dependencies to `Gemfile`.
|
33
|
-
- Don't ever test private methods directly. Specs should test behavior, not implementation.
|
32
|
+
- Don't ever test private methods directly. Specs should test behavior, not implementation.
|
33
|
+
|
34
|
+
## Git Workflow Practices
|
35
|
+
|
36
|
+
1. **Amending Commits**:
|
37
|
+
- Use `git commit --amend --no-edit` to add staged changes to the last commit without changing the commit message
|
38
|
+
- This is useful for incorporating small fixes or changes that belong with the previous commit
|
39
|
+
- Be careful when amending commits that have already been pushed, as it will require a force push
|
40
|
+
|
41
|
+
2. **Force Pushing Safety**:
|
42
|
+
- Always use `git push --force-with-lease` rather than `git push --force` when pushing amended commits
|
43
|
+
- This prevents accidentally overwriting remote changes made by others that you haven't pulled yet
|
44
|
+
- It's a safer alternative that respects collaborative work environments
|
45
|
+
|
46
|
+
4. **PR Management**:
|
47
|
+
- Pay attention to linting results before pushing to avoid CI failures
|
48
|
+
|
49
|
+
## GitHub API Commands
|
50
|
+
To get comments from a Pull Request using the GitHub CLI:
|
51
|
+
|
52
|
+
```bash
|
53
|
+
# Get review comments from a PR
|
54
|
+
gh api repos/Shopify/roast/pulls/{pr_number}/comments
|
55
|
+
|
56
|
+
# Get issue-style comments
|
57
|
+
gh api repos/Shopify/roast/issues/{pr_number}/comments
|
58
|
+
|
59
|
+
# Filter comments from a specific user using jq
|
60
|
+
gh api repos/Shopify/roast/pulls/{pr_number}/comments | jq '.[] | select(.user.login == "username")'
|
61
|
+
|
62
|
+
# Get only the comment content
|
63
|
+
gh api repos/Shopify/roast/pulls/{pr_number}/comments | jq '.[].body'
|
64
|
+
```
|
65
|
+
|
66
|
+
### Creating and Managing Issues via API
|
67
|
+
|
68
|
+
```bash
|
69
|
+
# Create a new issue
|
70
|
+
gh api repos/Shopify/roast/issues -X POST -F title="Issue Title" -F body="Issue description"
|
71
|
+
|
72
|
+
# Update an existing issue
|
73
|
+
gh api repos/Shopify/roast/issues/{issue_number} -X PATCH -F body="Updated description"
|
74
|
+
|
75
|
+
# Add a comment to an issue
|
76
|
+
gh api repos/Shopify/roast/issues/{issue_number}/comments -X POST -F body="Comment text"
|
77
|
+
```
|
78
|
+
|
79
|
+
### Creating and Managing Pull Requests
|
80
|
+
|
81
|
+
```bash
|
82
|
+
# Create a new PR with a detailed description using heredoc
|
83
|
+
gh pr create --title "PR Title" --body "$(cat <<'EOF'
|
84
|
+
## Summary
|
85
|
+
|
86
|
+
Detailed description here...
|
87
|
+
|
88
|
+
## Testing
|
89
|
+
|
90
|
+
Testing instructions here...
|
91
|
+
EOF
|
92
|
+
)"
|
93
|
+
|
94
|
+
# Update an existing PR description
|
95
|
+
gh pr edit {pr_number} --body "$(cat <<'EOF'
|
96
|
+
Updated PR description...
|
97
|
+
EOF
|
98
|
+
)"
|
99
|
+
|
100
|
+
# Check PR details
|
101
|
+
gh pr view {pr_number}
|
102
|
+
|
103
|
+
# View PR diff
|
104
|
+
gh pr diff {pr_number}
|
105
|
+
```
|
106
|
+
|
107
|
+
#### Formatting Tips for GitHub API
|
108
|
+
1. Use literal newlines in the body text instead of `\n` escape sequences
|
109
|
+
2. When formatting is stripped (like backticks), use alternatives:
|
110
|
+
- **Bold text** instead of `code formatting`
|
111
|
+
- Add a follow-up comment with proper formatting
|
112
|
+
3. For complex issues, create the basic issue first, then enhance with formatted comments
|
113
|
+
4. Always verify the formatting in the created content
|
114
|
+
5. Use raw JSON for complex formatting requirements:
|
115
|
+
```bash
|
116
|
+
gh api repos/Shopify/roast/issues -X POST --raw-field '{"title":"Issue Title","body":"Complex **formatting** with `code` and lists:\n\n1. Item one\n2. Item two"}'
|
117
|
+
```
|
118
|
+
|
119
|
+
## PR Review Best Practices
|
120
|
+
1. **Always provide your honest opinion about the PR** - be candid about both strengths and concerns
|
121
|
+
2. Give a clear assessment of risks, architectural implications, and potential future issues
|
122
|
+
3. Don't be afraid to point out potential problems even in otherwise good PRs
|
123
|
+
4. When reviewing feature flag removal PRs, carefully inspect control flow changes, not just code branch removals
|
124
|
+
5. Pay special attention to control flow modifiers like `next`, `return`, and `break` which affect iteration behavior
|
125
|
+
6. Look for variable scope issues, especially for variables that persist across loop iterations
|
126
|
+
7. Analyze how code behavior changes in all cases, not just how code structure changes
|
127
|
+
8. Be skeptical of seemingly simple changes that simply remove conditional branches
|
128
|
+
9. When CI checks fail, look for subtle logic inversions or control flow changes
|
129
|
+
10. Examine every file changed in a PR with local code for context, focusing on both what's removed and what remains
|
130
|
+
11. Verify variable initialization, modification, and usage patterns remain consistent after refactoring
|
131
|
+
12. **Never try to directly check out PR branches** - instead, compare PR changes against the existing local codebase
|
132
|
+
13. Understand the broader system architecture to identify potential impacts beyond the changed files
|
133
|
+
14. Look at both the "before" and "after" state of the code when evaluating changes, not just the diff itself
|
134
|
+
15. Consider how the changes will interact with other components that depend on the modified code
|
135
|
+
16. Run searches or examine related files even if they're not directly modified by the PR
|
136
|
+
17. Look for optimization opportunities, especially in frequently-called methods:
|
137
|
+
- Unnecessary object creation in loops
|
138
|
+
- Redundant collection transformations
|
139
|
+
- Inefficient filtering methods that create temporary collections
|
140
|
+
18. Prioritize code readability while encouraging performance optimizations:
|
141
|
+
- Avoid premature optimization outside of hot paths
|
142
|
+
- Consider the tradeoff between readability and performance
|
143
|
+
- Suggest optimizations that improve both clarity and performance
|
data/Gemfile
CHANGED
@@ -7,11 +7,11 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
7
7
|
# Specify your gem's dependencies in roast.gemspec
|
8
8
|
gemspec
|
9
9
|
|
10
|
+
gem "cgi"
|
10
11
|
gem "dotenv"
|
11
|
-
gem "guard-rspec"
|
12
12
|
gem "guard"
|
13
|
+
gem "guard-minitest"
|
13
14
|
gem "mocha"
|
14
|
-
gem "pry"
|
15
15
|
gem "rake", require: false
|
16
16
|
gem "rubocop-shopify", require: false
|
17
17
|
gem "vcr", require: false
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
roast-ai (0.1.
|
4
|
+
roast-ai (0.1.6)
|
5
5
|
activesupport (~> 8.0)
|
6
6
|
faraday-retry
|
7
7
|
json-schema
|
@@ -30,13 +30,13 @@ GEM
|
|
30
30
|
base64 (0.2.0)
|
31
31
|
benchmark (0.4.0)
|
32
32
|
bigdecimal (3.1.9)
|
33
|
+
cgi (0.4.2)
|
33
34
|
coderay (1.1.3)
|
34
35
|
concurrent-ruby (1.3.5)
|
35
36
|
connection_pool (2.5.1)
|
36
37
|
crack (1.0.0)
|
37
38
|
bigdecimal
|
38
39
|
rexml
|
39
|
-
diff-lcs (1.6.1)
|
40
40
|
dotenv (3.1.8)
|
41
41
|
drb (2.2.1)
|
42
42
|
event_stream_parser (1.0.0)
|
@@ -52,6 +52,7 @@ GEM
|
|
52
52
|
faraday (~> 2.0)
|
53
53
|
ffi (1.17.2)
|
54
54
|
ffi (1.17.2-arm64-darwin)
|
55
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
55
56
|
formatador (1.1.0)
|
56
57
|
guard (2.19.1)
|
57
58
|
formatador (>= 0.2.4)
|
@@ -65,10 +66,9 @@ GEM
|
|
65
66
|
shellany (~> 0.0)
|
66
67
|
thor (>= 0.18.1)
|
67
68
|
guard-compat (1.2.1)
|
68
|
-
guard-
|
69
|
-
guard (~> 2
|
70
|
-
|
71
|
-
rspec (>= 2.99.0, < 4.0)
|
69
|
+
guard-minitest (2.4.6)
|
70
|
+
guard-compat (~> 1.2)
|
71
|
+
minitest (>= 3.0)
|
72
72
|
hashdiff (1.1.2)
|
73
73
|
i18n (1.14.7)
|
74
74
|
concurrent-ruby (~> 1.0)
|
@@ -122,19 +122,6 @@ GEM
|
|
122
122
|
ffi (~> 1.0)
|
123
123
|
regexp_parser (2.10.0)
|
124
124
|
rexml (3.4.1)
|
125
|
-
rspec (3.13.0)
|
126
|
-
rspec-core (~> 3.13.0)
|
127
|
-
rspec-expectations (~> 3.13.0)
|
128
|
-
rspec-mocks (~> 3.13.0)
|
129
|
-
rspec-core (3.13.3)
|
130
|
-
rspec-support (~> 3.13.0)
|
131
|
-
rspec-expectations (3.13.3)
|
132
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
133
|
-
rspec-support (~> 3.13.0)
|
134
|
-
rspec-mocks (3.13.2)
|
135
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
136
|
-
rspec-support (~> 3.13.0)
|
137
|
-
rspec-support (3.13.2)
|
138
125
|
rubocop (1.75.3)
|
139
126
|
json (~> 2.3)
|
140
127
|
language_server-protocol (~> 3.17.0.2)
|
@@ -175,14 +162,14 @@ GEM
|
|
175
162
|
|
176
163
|
PLATFORMS
|
177
164
|
arm64-darwin-23
|
178
|
-
|
165
|
+
x86_64-linux
|
179
166
|
|
180
167
|
DEPENDENCIES
|
168
|
+
cgi
|
181
169
|
dotenv
|
182
170
|
guard
|
183
|
-
guard-
|
171
|
+
guard-minitest
|
184
172
|
mocha
|
185
|
-
pry
|
186
173
|
rake
|
187
174
|
roast-ai!
|
188
175
|
rubocop-shopify
|
data/README.md
CHANGED
@@ -53,15 +53,15 @@ steps:
|
|
53
53
|
- generate_final_report
|
54
54
|
```
|
55
55
|
|
56
|
-
Workflows can include steps that run bash commands (wrap in `$()`) and even simple inlined prompts as a natural language string.
|
56
|
+
Workflows can include steps that run bash commands (wrap in `$()`), use interpolation with `{{}}` syntax, and even simple inlined prompts as a natural language string.
|
57
57
|
|
58
58
|
```yaml
|
59
59
|
steps:
|
60
60
|
- analyze_spec
|
61
61
|
- create_minitest
|
62
62
|
- run_and_improve
|
63
|
-
- $(bundle exec rubocop -A)
|
64
|
-
- Summarize the changes made to
|
63
|
+
- $(bundle exec rubocop -A {{file}})
|
64
|
+
- Summarize the changes made to {{File.basename(file)}}.
|
65
65
|
```
|
66
66
|
|
67
67
|
## How to use Roast
|
@@ -122,7 +122,7 @@ Roast supports several types of steps:
|
|
122
122
|
|
123
123
|
#### Data Flow Between Steps
|
124
124
|
|
125
|
-
Roast handles data flow between steps in
|
125
|
+
Roast handles data flow between steps in three primary ways:
|
126
126
|
|
127
127
|
1. **Conversation Context (Implicit)**: The LLM naturally remembers the entire conversation history, including all previous prompts and responses. In most cases, this is all you need for a step to reference and build upon previous results. This is the preferred approach for most prompt-oriented workflows.
|
128
128
|
|
@@ -131,7 +131,21 @@ Roast handles data flow between steps in two primary ways:
|
|
131
131
|
- You're writing custom output logic
|
132
132
|
- You need to access specific values for presentation or logging
|
133
133
|
|
134
|
-
|
134
|
+
3. **Interpolation (Dynamic)**: You can use `{{expression}}` syntax to inject values from the workflow context directly into step names, commands, or prompt text. For example:
|
135
|
+
```yaml
|
136
|
+
steps:
|
137
|
+
- analyze_file
|
138
|
+
- $(rubocop -A {{file}})
|
139
|
+
- Generate a summary for {{file}}
|
140
|
+
- result_for_{{file}}: store_results
|
141
|
+
```
|
142
|
+
|
143
|
+
Interpolation supports:
|
144
|
+
- Simple variable access: `{{file}}`, `{{resource.target}}`
|
145
|
+
- Access to step outputs: `{{output['previous_step']}}`
|
146
|
+
- Any valid Ruby expression evaluated in the workflow context: `{{File.basename(file)}}`
|
147
|
+
|
148
|
+
For typical AI workflows, the continuous conversation history provides seamless data flow without requiring explicit access to the output hash. Steps can simply refer to previous information in their prompts, and the AI model will use its memory of the conversation to provide context-aware responses. For more dynamic requirements, the interpolation syntax provides a convenient way to inject context-specific values into steps.
|
135
149
|
|
136
150
|
### Command Line Options
|
137
151
|
|
@@ -298,9 +312,27 @@ Individual steps can override this setting with their own model parameter:
|
|
298
312
|
|
299
313
|
```yaml
|
300
314
|
analyze_data:
|
301
|
-
model: anthropic
|
315
|
+
model: anthropic/claude-3-haiku # Takes precedence over the global model
|
302
316
|
```
|
303
317
|
|
318
|
+
#### API Provider Configuration
|
319
|
+
|
320
|
+
Roast supports both OpenAI and OpenRouter as API providers. By default, Roast uses OpenAI, but you can specify OpenRouter:
|
321
|
+
|
322
|
+
```yaml
|
323
|
+
name: My Workflow
|
324
|
+
api_provider: openrouter
|
325
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
326
|
+
model: anthropic/claude-3-opus-20240229
|
327
|
+
```
|
328
|
+
|
329
|
+
Benefits of using OpenRouter:
|
330
|
+
- Access to multiple model providers through a single API
|
331
|
+
- Support for models from Anthropic, Meta, Mistral, and more
|
332
|
+
- Consistent API interface across different model providers
|
333
|
+
|
334
|
+
When using OpenRouter, specify fully qualified model names including the provider prefix (e.g., `anthropic/claude-3-opus-20240229`).
|
335
|
+
|
304
336
|
#### Dynamic API Tokens
|
305
337
|
|
306
338
|
Roast allows you to dynamically fetch API tokens using shell commands directly in your workflow configuration:
|
@@ -309,8 +341,12 @@ Roast allows you to dynamically fetch API tokens using shell commands directly i
|
|
309
341
|
# This will execute the shell command and use the result as the API token
|
310
342
|
api_token: $(print-token --key)
|
311
343
|
|
312
|
-
#
|
344
|
+
# For OpenAI (default)
|
313
345
|
api_token: $(echo $OPENAI_API_KEY)
|
346
|
+
|
347
|
+
# For OpenRouter (requires api_provider setting)
|
348
|
+
api_provider: openrouter
|
349
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
314
350
|
```
|
315
351
|
|
316
352
|
This makes it easy to use environment-specific tokens without hardcoding credentials, especially useful in development environments or CI/CD pipelines.
|
data/Rakefile
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Interpolation Example
|
2
|
+
|
3
|
+
This example demonstrates how to use Roast's interpolation feature to create dynamic workflows.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The workflow in this example:
|
8
|
+
1. Analyzes a file and extracts its metadata
|
9
|
+
2. Extracts patterns based on the file type
|
10
|
+
3. Dynamically selects a report generation step based on the file extension
|
11
|
+
4. Outputs a completion message using the file's basename
|
12
|
+
|
13
|
+
## Interpolation Examples
|
14
|
+
|
15
|
+
The workflow demonstrates several types of interpolation:
|
16
|
+
|
17
|
+
- `{{ }}` syntax for embedding dynamic values
|
18
|
+
- Access to file metadata via expressions like `{{file_basename}}` and `{{file_ext}}`
|
19
|
+
- Dynamic step selection with `generate_report_for_{{file_ext}}`
|
20
|
+
- Shell command interpolation with `$(echo "Processing completed for file: {{file_basename}}")`
|
21
|
+
|
22
|
+
## Running the Example
|
23
|
+
|
24
|
+
To run this example with a Ruby file:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
roast execute workflow.yml /path/to/some_file.rb
|
28
|
+
```
|
29
|
+
|
30
|
+
Or with a JavaScript file:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
roast execute workflow.yml /path/to/some_file.js
|
34
|
+
```
|
35
|
+
|
36
|
+
The workflow will:
|
37
|
+
1. Extract the file's basename and extension
|
38
|
+
2. Store these in the workflow context
|
39
|
+
3. Dynamically choose a report generator based on file extension
|
40
|
+
4. Create a markdown report file
|
41
|
+
5. Output a completion message with the filename
|
42
|
+
|
43
|
+
## How Interpolation Works
|
44
|
+
|
45
|
+
1. When Roast processes a step name or shell command, it looks for `{{ }}` patterns
|
46
|
+
2. Expressions inside `{{ }}` are evaluated in the workflow's context using Ruby's `instance_eval`
|
47
|
+
3. This allows access to the workflow's variables, methods, and output hash
|
48
|
+
4. The evaluated expressions replace the `{{ }}` patterns in the step name or command
|
49
|
+
|
50
|
+
This makes workflows dynamic and able to respond to different inputs without code changes.
|
@@ -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,48 @@
|
|
1
|
+
// Sample JavaScript file for testing interpolation in workflows
|
2
|
+
|
3
|
+
class Calculator {
|
4
|
+
constructor() {
|
5
|
+
this.memory = 0;
|
6
|
+
}
|
7
|
+
|
8
|
+
add(number) {
|
9
|
+
this.memory += number;
|
10
|
+
return this.memory;
|
11
|
+
}
|
12
|
+
|
13
|
+
subtract(number) {
|
14
|
+
this.memory -= number;
|
15
|
+
return this.memory;
|
16
|
+
}
|
17
|
+
|
18
|
+
multiply(number) {
|
19
|
+
this.memory *= number;
|
20
|
+
return this.memory;
|
21
|
+
}
|
22
|
+
|
23
|
+
divide(number) {
|
24
|
+
if (number === 0) {
|
25
|
+
throw new Error("Division by zero!");
|
26
|
+
}
|
27
|
+
this.memory /= number;
|
28
|
+
return this.memory;
|
29
|
+
}
|
30
|
+
|
31
|
+
getMemory() {
|
32
|
+
return this.memory;
|
33
|
+
}
|
34
|
+
|
35
|
+
clear() {
|
36
|
+
this.memory = 0;
|
37
|
+
return this.memory;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
// Example usage
|
42
|
+
if (require.main === module) {
|
43
|
+
const calc = new Calculator();
|
44
|
+
calc.add(10);
|
45
|
+
calc.multiply(2);
|
46
|
+
calc.subtract(5);
|
47
|
+
console.log(`Result: ${calc.getMemory()}`);
|
48
|
+
}
|
@@ -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 @@
|
|
1
|
+
You are a good robot.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
name: interpolation_example
|
2
|
+
model: anthropic:claude-3-7-sonnet
|
3
|
+
|
4
|
+
tools:
|
5
|
+
- Roast::Tools::ReadFile
|
6
|
+
|
7
|
+
steps:
|
8
|
+
- analyze_file
|
9
|
+
- analyze_patterns
|
10
|
+
- generate_report_for_{{File.extname(workflow.file).sub('.', '')}}
|
11
|
+
- '$(echo "Processing completed for file: {{File.basename(workflow.file)}}")'
|
12
|
+
|
13
|
+
analyze_patterns:
|
14
|
+
json: true
|
15
|
+
|
16
|
+
generate_report_for_rb:
|
17
|
+
print_response: true
|
18
|
+
|
19
|
+
generate_report_for_md:
|
20
|
+
print_response: true
|
21
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# OpenRouter Example
|
2
|
+
|
3
|
+
This example demonstrates how to use OpenRouter with Roast to access models from different providers through a single API.
|
4
|
+
|
5
|
+
## Setup
|
6
|
+
|
7
|
+
1. Sign up for an account at [OpenRouter](https://openrouter.ai/)
|
8
|
+
2. Get your API key from the OpenRouter dashboard
|
9
|
+
3. Set the API key as an environment variable:
|
10
|
+
```bash
|
11
|
+
export OPENROUTER_API_KEY=your_api_key_here
|
12
|
+
```
|
13
|
+
|
14
|
+
## Running the Example
|
15
|
+
|
16
|
+
```bash
|
17
|
+
# Run without a specific target (general analysis)
|
18
|
+
roast execute workflow.yml
|
19
|
+
|
20
|
+
# Run with a specific file to analyze
|
21
|
+
roast execute workflow.yml path/to/your/file.txt
|
22
|
+
```
|
23
|
+
|
24
|
+
## How it Works
|
25
|
+
|
26
|
+
This example configures Roast to use OpenRouter as the API provider:
|
27
|
+
|
28
|
+
```yaml
|
29
|
+
api_provider: openrouter
|
30
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
31
|
+
model: anthropic/claude-3-haiku-20240307
|
32
|
+
```
|
33
|
+
|
34
|
+
The workflow consists of two steps:
|
35
|
+
1. `analyze_input`: Analyzes the provided content (or generates general insights if no target is provided)
|
36
|
+
2. `generate_response`: Creates a structured response based on the analysis
|
37
|
+
|
38
|
+
## Available Models
|
39
|
+
|
40
|
+
When using OpenRouter, you can access models from multiple providers by specifying the fully qualified model name, including the provider prefix. Some examples:
|
41
|
+
|
42
|
+
- `anthropic/claude-3-opus-20240229`
|
43
|
+
- `anthropic/claude-3-sonnet-20240229`
|
44
|
+
- `meta/llama-3-70b-instruct`
|
45
|
+
- `google/gemini-1.5-pro-latest`
|
46
|
+
- `mistral/mistral-large-latest`
|
47
|
+
|
48
|
+
Check the [OpenRouter documentation](https://openrouter.ai/docs) for the complete list of supported models.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
I'd like you to analyze the following input and provide your insights.
|
2
|
+
|
3
|
+
<% if workflow.file && workflow.resource.content %>
|
4
|
+
Here is the content to analyze:
|
5
|
+
|
6
|
+
```
|
7
|
+
<%= workflow.resource.content %>
|
8
|
+
```
|
9
|
+
<% else %>
|
10
|
+
The workflow is running without a specific file target. Please provide general insights based on the context.
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
Please provide:
|
14
|
+
1. A summary of the key points
|
15
|
+
2. Any notable patterns or issues
|
16
|
+
3. Recommendations based on your analysis
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Based on the analysis from the previous step, please generate a response that addresses the findings.
|
2
|
+
|
3
|
+
Your response should be well-structured and include:
|
4
|
+
1. An introduction summarizing the analysis
|
5
|
+
2. Detailed discussion of key points
|
6
|
+
3. Clear recommendations for improvements
|
7
|
+
4. A conclusion
|
8
|
+
|
9
|
+
Format the response in markdown for better readability.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Roast
|
4
|
+
class Initializers
|
5
|
+
class << self
|
6
|
+
def config_root(starting_path = Dir.pwd, ending_path = File.dirname(Dir.home))
|
7
|
+
paths = []
|
8
|
+
candidate = starting_path
|
9
|
+
while candidate != ending_path
|
10
|
+
paths << File.join(candidate, ".roast")
|
11
|
+
candidate = File.dirname(candidate)
|
12
|
+
end
|
13
|
+
|
14
|
+
first_existing = paths.find { |path| Dir.exist?(path) }
|
15
|
+
first_existing || paths.first
|
16
|
+
end
|
17
|
+
|
18
|
+
def initializers_path
|
19
|
+
File.join(Roast::Initializers.config_root, "initializers")
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_all
|
23
|
+
project_initializers = Roast::Initializers.initializers_path
|
24
|
+
return unless Dir.exist?(project_initializers)
|
25
|
+
|
26
|
+
$stderr.puts "Loading project initializers from #{project_initializers}"
|
27
|
+
pattern = File.join(project_initializers, "**/*.rb")
|
28
|
+
Dir.glob(pattern, sort: true).each do |file|
|
29
|
+
$stderr.puts "Loading initializer: #{file}"
|
30
|
+
require file
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
puts "ERROR: Error loading initializers: #{e.message}"
|
34
|
+
Roast::Helpers::Logger.error("Error loading initializers: #{e.message}")
|
35
|
+
# Don't fail the workflow if initializers can't be loaded
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -13,10 +13,11 @@ module Roast
|
|
13
13
|
base.class_eval do
|
14
14
|
function(
|
15
15
|
:search_for_file,
|
16
|
-
|
17
|
-
|
16
|
+
"Search for a file in the project using a glob pattern.",
|
17
|
+
glob_pattern: { type: "string", description: "A glob pattern to search for. Example: 'test/**/*_test.rb'" },
|
18
|
+
path: { type: "string", description: "path to search from" },
|
18
19
|
) do |params|
|
19
|
-
Roast::Tools::SearchFile.call(params[:
|
20
|
+
Roast::Tools::SearchFile.call(params[:glob_pattern], params[:path]).tap do |result|
|
20
21
|
Roast::Helpers::Logger.debug(result) if ENV["DEBUG"]
|
21
22
|
end
|
22
23
|
end
|
@@ -24,27 +25,33 @@ module Roast
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
def call(
|
28
|
-
Roast::Helpers::Logger.info("🔍 Searching for
|
29
|
-
search_for(
|
30
|
-
return "No results found for #{
|
31
|
-
return
|
28
|
+
def call(glob_pattern, path = ".")
|
29
|
+
Roast::Helpers::Logger.info("🔍 Searching for: '#{glob_pattern}' in '#{path}'\n")
|
30
|
+
search_for(glob_pattern, path).then do |results|
|
31
|
+
return "No results found for #{glob_pattern} in #{path}" if results.empty?
|
32
|
+
return read_contents(results.first) if results.size == 1
|
32
33
|
|
33
|
-
results.
|
34
|
+
results.join("\n") # purposely give the AI list of actual paths so that it can read without searching first
|
34
35
|
end
|
35
36
|
rescue StandardError => e
|
36
|
-
"Error searching for
|
37
|
+
"Error searching for '#{glob_pattern}' in '#{path}': #{e.message}".tap do |error_message|
|
37
38
|
Roast::Helpers::Logger.error(error_message + "\n")
|
38
39
|
Roast::Helpers::Logger.debug(e.backtrace.join("\n") + "\n") if ENV["DEBUG"]
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
43
|
+
def read_contents(path)
|
44
|
+
contents = File.read(path)
|
45
|
+
token_count = contents.size / 4
|
46
|
+
if token_count > 25_000
|
47
|
+
path
|
48
|
+
else
|
49
|
+
contents
|
50
|
+
end
|
51
|
+
end
|
45
52
|
|
46
|
-
|
47
|
-
|
53
|
+
def search_for(pattern, path)
|
54
|
+
Dir.glob(pattern, base: path)
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
@@ -21,7 +21,9 @@ module Roast
|
|
21
21
|
},
|
22
22
|
content: { type: "string", description: "The content to write to the file" },
|
23
23
|
) do |params|
|
24
|
-
|
24
|
+
restrict_path = params[:params]&.dig("restrict")
|
25
|
+
|
26
|
+
Roast::Tools::WriteFile.call(params[:path], params[:content], restrict_path).tap do |_result|
|
25
27
|
Roast::Helpers::Logger.info(params[:content])
|
26
28
|
end
|
27
29
|
end
|
@@ -29,9 +31,8 @@ module Roast
|
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
def call(path, content)
|
33
|
-
if path.start_with?(
|
34
|
-
|
34
|
+
def call(path, content, restrict_path = nil)
|
35
|
+
if restrict_path.nil? || restrict_path.empty? || path.start_with?(restrict_path)
|
35
36
|
Roast::Helpers::Logger.info("📝 Writing to file: #{path}\n")
|
36
37
|
|
37
38
|
# Ensure the directory exists
|
@@ -46,8 +47,9 @@ module Roast
|
|
46
47
|
|
47
48
|
"Successfully wrote #{content.lines.count} lines to #{path}"
|
48
49
|
else
|
49
|
-
|
50
|
-
|
50
|
+
restriction_message = "😳 Path must start with '#{restrict_path}' to use the write_file tool\n"
|
51
|
+
Roast::Helpers::Logger.error(restriction_message)
|
52
|
+
"Error: Path must start with '#{restrict_path}' to use the write_file tool, try again."
|
51
53
|
end
|
52
54
|
rescue StandardError => e
|
53
55
|
"Error writing file: #{e.message}".tap do |error_message|
|
data/lib/roast/version.rb
CHANGED
@@ -8,7 +8,7 @@ module Roast
|
|
8
8
|
# Encapsulates workflow configuration data and provides structured access
|
9
9
|
# to the configuration settings
|
10
10
|
class Configuration
|
11
|
-
attr_reader :config_hash, :workflow_path, :name, :steps, :tools, :function_configs, :api_token, :model, :resource
|
11
|
+
attr_reader :config_hash, :workflow_path, :name, :steps, :tools, :function_configs, :api_token, :api_provider, :model, :resource
|
12
12
|
attr_accessor :target
|
13
13
|
|
14
14
|
def initialize(workflow_path, options = {})
|
@@ -45,6 +45,9 @@ module Roast
|
|
45
45
|
@api_token = process_shell_command(@config_hash["api_token"])
|
46
46
|
end
|
47
47
|
|
48
|
+
# Determine API provider (defaults to OpenAI if not specified)
|
49
|
+
@api_provider = determine_api_provider
|
50
|
+
|
48
51
|
# Extract default model if provided
|
49
52
|
@model = @config_hash["model"]
|
50
53
|
end
|
@@ -138,8 +141,32 @@ module Roast
|
|
138
141
|
@function_configs[function_name.to_s] || {}
|
139
142
|
end
|
140
143
|
|
144
|
+
def openrouter?
|
145
|
+
@api_provider == :openrouter
|
146
|
+
end
|
147
|
+
|
148
|
+
def openai?
|
149
|
+
@api_provider == :openai
|
150
|
+
end
|
151
|
+
|
141
152
|
private
|
142
153
|
|
154
|
+
def determine_api_provider
|
155
|
+
return :openai unless @config_hash["api_provider"]
|
156
|
+
|
157
|
+
provider = @config_hash["api_provider"].to_s.downcase
|
158
|
+
|
159
|
+
case provider
|
160
|
+
when "openai"
|
161
|
+
:openai
|
162
|
+
when "openrouter"
|
163
|
+
:openrouter
|
164
|
+
else
|
165
|
+
Roast::Helpers::Logger.warn("Unknown API provider '#{provider}', defaulting to OpenAI")
|
166
|
+
:openai
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
143
170
|
def process_shell_command(command)
|
144
171
|
# If it's a bash command with the $(command) syntax
|
145
172
|
if command =~ /^\$\((.*)\)$/
|
@@ -6,6 +6,7 @@ require_relative "../helpers/function_caching_interceptor"
|
|
6
6
|
require "active_support"
|
7
7
|
require "active_support/isolated_execution_state"
|
8
8
|
require "active_support/notifications"
|
9
|
+
require "raix"
|
9
10
|
|
10
11
|
module Roast
|
11
12
|
module Workflow
|
@@ -98,35 +99,23 @@ module Roast
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def load_roast_initializers
|
101
|
-
|
102
|
-
project_initializers = File.join(Dir.pwd, ".roast", "initializers")
|
103
|
-
|
104
|
-
if Dir.exist?(project_initializers)
|
105
|
-
$stderr.puts "Loading project initializers from #{project_initializers}"
|
106
|
-
Dir.glob(File.join(project_initializers, "**/*.rb")).sort.each do |file|
|
107
|
-
$stderr.puts "Loading initializer: #{file}"
|
108
|
-
require file
|
109
|
-
end
|
110
|
-
end
|
111
|
-
rescue => e
|
112
|
-
Roast::Helpers::Logger.error("Error loading initializers: #{e.message}")
|
113
|
-
# Don't fail the workflow if initializers can't be loaded
|
102
|
+
Roast::Initializers.load_all
|
114
103
|
end
|
115
104
|
|
116
105
|
def configure_api_client
|
117
106
|
return unless configuration.api_token
|
118
107
|
|
119
108
|
begin
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
109
|
+
case configuration.api_provider
|
110
|
+
when :openrouter
|
111
|
+
$stderr.puts "Configuring OpenRouter client with token from workflow"
|
112
|
+
require "open_router"
|
124
113
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
Raix.configuration.openai_client = OpenAI::Client.new(access_token: configuration.api_token)
|
114
|
+
Raix.configure do |config|
|
115
|
+
config.openrouter_client = OpenRouter::Client.new(api_key: configuration.api_token)
|
116
|
+
end
|
129
117
|
else
|
118
|
+
$stderr.puts "Configuring OpenAI client with token from workflow"
|
130
119
|
require "openai"
|
131
120
|
|
132
121
|
Raix.configure do |config|
|
@@ -33,6 +33,25 @@ module Roast
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
# Interpolates {{expression}} in a string with values from the workflow context
|
37
|
+
def interpolate(text)
|
38
|
+
return text unless text.is_a?(String) && text.include?("{{") && text.include?("}}")
|
39
|
+
|
40
|
+
# Replace all {{expression}} with their evaluated values
|
41
|
+
text.gsub(/\{\{([^}]+)\}\}/) do |match|
|
42
|
+
expression = Regexp.last_match(1).strip
|
43
|
+
begin
|
44
|
+
# Evaluate the expression in the workflow's context
|
45
|
+
workflow.instance_eval(expression).to_s
|
46
|
+
rescue => e
|
47
|
+
# If evaluation fails, provide a more detailed error message but preserve the original expression
|
48
|
+
error_msg = "Error interpolating {{#{expression}}}: #{e.message}. This variable is not defined in the workflow context. Please define it before using it in a step name."
|
49
|
+
$stderr.puts "ERROR: #{error_msg}"
|
50
|
+
match # Return the original match to preserve it in the string
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
36
55
|
def execute_step(name)
|
37
56
|
start_time = Time.now
|
38
57
|
# For tests, make sure that we handle this gracefully
|
@@ -95,10 +114,16 @@ module Roast
|
|
95
114
|
def execute_hash_step(step)
|
96
115
|
# execute a command and store the output in a variable
|
97
116
|
name, command = step.to_a.flatten
|
117
|
+
|
118
|
+
# Interpolate variable name if it contains {{}}
|
119
|
+
interpolated_name = interpolate(name)
|
120
|
+
|
98
121
|
if command.is_a?(Hash)
|
99
122
|
execute_steps([command])
|
100
123
|
else
|
101
|
-
|
124
|
+
# Interpolate command value
|
125
|
+
interpolated_command = interpolate(command)
|
126
|
+
workflow.output[interpolated_name] = execute_step(interpolated_command)
|
102
127
|
end
|
103
128
|
end
|
104
129
|
|
@@ -110,7 +135,9 @@ module Roast
|
|
110
135
|
end
|
111
136
|
|
112
137
|
def execute_string_step(step)
|
113
|
-
|
138
|
+
# Interpolate any {{}} expressions before executing the step
|
139
|
+
interpolated_step = interpolate(step)
|
140
|
+
execute_step(interpolated_step)
|
114
141
|
end
|
115
142
|
|
116
143
|
def find_and_load_step(step_name)
|
@@ -172,7 +199,11 @@ module Roast
|
|
172
199
|
|
173
200
|
def strip_and_execute(step)
|
174
201
|
if step.match?(/^\$\((.*)\)$/)
|
202
|
+
# Extract the command from the $(command) syntax
|
175
203
|
command = step.strip.match(/^\$\((.*)\)$/)[1]
|
204
|
+
|
205
|
+
# NOTE: We don't need to call interpolate here as it's already been done
|
206
|
+
# in execute_string_step before this method is called
|
176
207
|
%x(#{command})
|
177
208
|
else
|
178
209
|
raise "Missing closing parentheses: #{step}"
|
data/lib/roast.rb
CHANGED
@@ -7,6 +7,7 @@ require "roast/tools"
|
|
7
7
|
require "roast/helpers"
|
8
8
|
require "roast/resources"
|
9
9
|
require "roast/workflow"
|
10
|
+
require "roast/initializers"
|
10
11
|
|
11
12
|
module Roast
|
12
13
|
ROOT = File.expand_path("../..", __FILE__)
|
@@ -28,6 +29,11 @@ module Roast
|
|
28
29
|
Roast::Workflow::ConfigurationParser.new(expanded_workflow_path, files, options.transform_keys(&:to_sym)).begin!
|
29
30
|
end
|
30
31
|
|
32
|
+
desc "version", "Display the current version of Roast"
|
33
|
+
def version
|
34
|
+
puts "Roast version #{Roast::VERSION}"
|
35
|
+
end
|
36
|
+
|
31
37
|
class << self
|
32
38
|
def exit_on_failure?
|
33
39
|
true
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roast-ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
@@ -128,6 +128,19 @@ files:
|
|
128
128
|
- examples/grading/workflow.ts+tsx.md
|
129
129
|
- examples/grading/workflow.yml
|
130
130
|
- examples/instrumentation.rb
|
131
|
+
- examples/interpolation/README.md
|
132
|
+
- examples/interpolation/analyze_file/prompt.md
|
133
|
+
- examples/interpolation/analyze_patterns/prompt.md
|
134
|
+
- examples/interpolation/generate_report_for_js/prompt.md
|
135
|
+
- examples/interpolation/generate_report_for_rb/prompt.md
|
136
|
+
- examples/interpolation/sample.js
|
137
|
+
- examples/interpolation/sample.rb
|
138
|
+
- examples/interpolation/workflow.md
|
139
|
+
- examples/interpolation/workflow.yml
|
140
|
+
- examples/openrouter_example/README.md
|
141
|
+
- examples/openrouter_example/analyze_input/prompt.md
|
142
|
+
- examples/openrouter_example/generate_response/prompt.md
|
143
|
+
- examples/openrouter_example/workflow.yml
|
131
144
|
- examples/rspec_to_minitest/README.md
|
132
145
|
- examples/rspec_to_minitest/analyze_spec/prompt.md
|
133
146
|
- examples/rspec_to_minitest/create_minitest/prompt.md
|
@@ -142,6 +155,7 @@ files:
|
|
142
155
|
- lib/roast/helpers/minitest_coverage_runner.rb
|
143
156
|
- lib/roast/helpers/path_resolver.rb
|
144
157
|
- lib/roast/helpers/prompt_loader.rb
|
158
|
+
- lib/roast/initializers.rb
|
145
159
|
- lib/roast/resources.rb
|
146
160
|
- lib/roast/resources/api_resource.rb
|
147
161
|
- lib/roast/resources/base_resource.rb
|