ruby_agent 0.2.1 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '039e385b0c47944839ae33f1b8e4ed75e499803ae06538e9da4c3a9899dbe247'
4
- data.tar.gz: 6726c84e2a9fbb9f795300dbb8995fba831bb989540681ce35c3b6604c9c6b35
3
+ metadata.gz: 4f16c4c8fdacecaee67784197c3a8fe8b0e00adc248115c3d8c8f873d954fafd
4
+ data.tar.gz: c94d916ea812575d2227a822df90cdb5dbdfe9ee41794af1be0375cc4aa4d9ee
5
5
  SHA512:
6
- metadata.gz: 787bdc5703131d4169ca2fb53175ce3cc920330fd398e34c881ab2fbf4bfdf73a360a3459a60891fbdd05bb4891a7ff41f74efca715ef126164e63a128daf5ef
7
- data.tar.gz: 0c273eb33000ddc93e065a3b1d43483012012fa350e67bcae2f96def1d882d3f7e2846659fbca4287b5b0e280201023c0469cccfeec72b8282a0d218d831854c
6
+ metadata.gz: 46cf5e8405ce2661346832274d770c2ccdc19c55e53797553a7f9794f402fe654134b44908c2b46a0bf02bda13dc73d96b688a6f8a7de300bdd7ae85fd228700
7
+ data.tar.gz: 8fb706b0a8f0b88eb50fdea84081aa2ef8b3e581bdbf6de4f70d230c92ca99103f123a3781e8be7ff9ecd791977b42043a8eb04b05fffede4a1645914a074c45
data/.rubocop.yml ADDED
@@ -0,0 +1,58 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.4
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+ - 'tmp/**/*'
8
+ - 'bin/**/*'
9
+
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ Style/StringLiterals:
14
+ EnforcedStyle: double_quotes
15
+
16
+ Style/FrozenStringLiteralComment:
17
+ Enabled: false
18
+
19
+ Layout/LineLength:
20
+ Max: 125
21
+
22
+ Metrics/MethodLength:
23
+ Max: 50
24
+
25
+ Metrics/AbcSize:
26
+ Max: 40
27
+
28
+ Metrics/CyclomaticComplexity:
29
+ Max: 25
30
+
31
+ Metrics/PerceivedComplexity:
32
+ Max: 25
33
+
34
+ Metrics/BlockNesting:
35
+ Max: 5
36
+
37
+ Metrics/BlockLength:
38
+ Exclude:
39
+ - 'test/**/*'
40
+ - 'Rakefile'
41
+
42
+ Metrics/ClassLength:
43
+ Max: 350
44
+
45
+ Metrics/ParameterLists:
46
+ Max: 10
47
+
48
+ Lint/UnusedMethodArgument:
49
+ AllowUnusedKeywordArguments: true
50
+
51
+ Lint/EmptyBlock:
52
+ Enabled: false
53
+
54
+ Lint/SuppressedException:
55
+ AllowComments: true
56
+
57
+ Gemspec/RequiredRubyVersion:
58
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "minitest", "~> 5.0"
6
+ gem "minitest-reporters", "~> 1.6"
7
+ gem "rake", "~> 13.0"
8
+
9
+ group :development do
10
+ gem "bundler-audit", "~> 0.9", require: false
11
+ gem "rubocop", "~> 1.50", require: false
12
+ end
data/README.md CHANGED
@@ -205,6 +205,64 @@ rescue RubyAgent::AgentError => e
205
205
  end
206
206
  ```
207
207
 
208
+ ## Development
209
+
210
+ ### Contributing
211
+
212
+ 1. Fork the repository: https://github.com/AllYourBot/ruby-agent
213
+ 2. Create a feature branch: `git checkout -b my-new-feature`
214
+ 3. Make your changes
215
+ 4. Run the CI suite locally to ensure everything passes:
216
+
217
+ ```bash
218
+ # Run all CI tasks (linting + tests)
219
+ rake ci
220
+
221
+ # Or run tasks individually:
222
+ rake ci:test # Run test suite
223
+ rake ci:lint # Run RuboCop linter
224
+ rake ci:scan # Run security audit
225
+ ```
226
+
227
+ 5. Commit your changes: `git commit -am 'Add some feature'`
228
+ 6. Push to your fork: `git push origin my-new-feature`
229
+ 7. Create a Pull Request against the `main` branch
230
+
231
+ ### Running Tests Locally
232
+
233
+ The test suite includes an integration test that runs Claude Code CLI locally:
234
+
235
+ ```bash
236
+ # Run all tests
237
+ rake test
238
+
239
+ # Run a specific test
240
+ ruby test/ruby_agent_test.rb --name test_simple_agent_query
241
+ ```
242
+
243
+ **Note**: Tests require Claude Code CLI to be installed on your machine (see Prerequisites section).
244
+
245
+ ### Linting
246
+
247
+ We use RuboCop for code linting:
248
+
249
+ ```bash
250
+ # Check for linting issues
251
+ rake ci:lint
252
+
253
+ # Auto-fix linting issues
254
+ bundle exec rubocop -a
255
+ ```
256
+
257
+ ### Publishing
258
+
259
+ Publishing to RubyGems happens automatically via GitHub Actions when code is merged to `main`. The version number is read from `lib/ruby_agent/version.rb`.
260
+
261
+ **Before merging a PR**, make sure to bump the version number appropriately:
262
+ - Patch version (0.2.1 → 0.2.2) for bug fixes
263
+ - Minor version (0.2.1 → 0.3.0) for new features
264
+ - Major version (0.2.1 → 1.0.0) for breaking changes
265
+
208
266
  ## License
209
267
 
210
268
  MIT
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task default: :test
11
+
12
+ namespace :ci do
13
+ desc "Run tests"
14
+ task :test do
15
+ sh "bundle exec rake test"
16
+ end
17
+
18
+ desc "Run linter"
19
+ task :lint do
20
+ sh "bundle exec rubocop"
21
+ rescue StandardError
22
+ puts "Rubocop not configured yet, skipping..."
23
+ end
24
+
25
+ desc "Run security scan"
26
+ task :scan do
27
+ sh "bundle exec bundler-audit check --update"
28
+ rescue StandardError
29
+ puts "Bundler-audit not installed, skipping..."
30
+ end
31
+ end
32
+
33
+ desc "Run all CI tasks"
34
+ task ci: ["ci:lint", "ci:test"]
@@ -1,3 +1,3 @@
1
1
  class RubyAgent
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2".freeze
3
3
  end
data/lib/ruby_agent.rb CHANGED
@@ -46,15 +46,15 @@ class RubyAgent
46
46
  @pending_interrupt_request_id = nil
47
47
  @deferred_exit = false
48
48
 
49
- unless @session_key
50
- inject_streaming_response({
51
- type: "system",
52
- subtype: "prompt",
53
- system_prompt: @system_prompt,
54
- timestamp: Time.now.utc.iso8601(6),
55
- received_at: Time.now.utc.iso8601(6)
56
- })
57
- end
49
+ return if @session_key
50
+
51
+ inject_streaming_response({
52
+ type: "system",
53
+ subtype: "prompt",
54
+ system_prompt: @system_prompt,
55
+ timestamp: Time.now.utc.iso8601(6),
56
+ received_at: Time.now.utc.iso8601(6)
57
+ })
58
58
  end
59
59
 
60
60
  def create_message_callback(name, &processor)
@@ -68,7 +68,7 @@ class RubyAgent
68
68
  @on_message_callback = block
69
69
  end
70
70
 
71
- alias_method :on_event, :on_message
71
+ alias on_event on_message
72
72
 
73
73
  def on_error(&block)
74
74
  @on_error_callback = block
@@ -111,37 +111,37 @@ class RubyAgent
111
111
  @wait_thr = nil
112
112
  end
113
113
  end
114
- rescue => e
114
+ rescue StandardError => e
115
115
  trigger_error(e)
116
116
  raise
117
117
  end
118
118
 
119
119
  def ask(text, sender_name: "User", additional: [])
120
120
  formatted_text = if sender_name.downcase == "system"
121
- <<~TEXT.strip
122
- <system>
123
- #{text}
124
- </system>
125
- TEXT
126
- else
127
- "#{sender_name}: #{text}"
128
- end
121
+ <<~TEXT.strip
122
+ <system>
123
+ #{text}
124
+ </system>
125
+ TEXT
126
+ else
127
+ "#{sender_name}: #{text}"
128
+ end
129
129
  formatted_text += extra_context(additional, sender_name:)
130
130
 
131
131
  inject_streaming_response({
132
- type: "user",
133
- subtype: "new_message",
134
- sender_name:,
135
- text:,
136
- formatted_text:,
137
- timestamp: Time.now.utc.iso8601(6)
138
- })
132
+ type: "user",
133
+ subtype: "new_message",
134
+ sender_name:,
135
+ text:,
136
+ formatted_text:,
137
+ timestamp: Time.now.utc.iso8601(6)
138
+ })
139
139
 
140
140
  send_message(formatted_text)
141
141
  end
142
142
 
143
143
  def ask_after_interrupt(text, sender_name: "User", additional: [])
144
- @pending_ask_after_interrupt = {text:, sender_name:, additional:}
144
+ @pending_ask_after_interrupt = { text:, sender_name:, additional: }
145
145
  end
146
146
 
147
147
  def send_system_message(text)
@@ -171,7 +171,7 @@ class RubyAgent
171
171
 
172
172
  puts "→ stdout closed, waiting for process to exit..." if DEBUG
173
173
  exit_status = @wait_thr.value
174
- puts "→ Process exited with status: #{exit_status.success? ? "success" : "failure"}" if DEBUG
174
+ puts "→ Process exited with status: #{exit_status.success? ? 'success' : 'failure'}" if DEBUG
175
175
  unless exit_status.success?
176
176
  stderr_output = @stderr.read
177
177
  raise ConnectionError, "Claude command failed: #{stderr_output}"
@@ -202,7 +202,9 @@ class RubyAgent
202
202
  request_id = "req_#{@request_counter}_#{SecureRandom.hex(4)}"
203
203
 
204
204
  @pending_interrupt_request_id = request_id if @pending_ask_after_interrupt
205
- puts "→ Sending interrupt with request_id: #{request_id}, pending_ask: #{@pending_ask_after_interrupt ? true : false}" if DEBUG
205
+ if DEBUG
206
+ puts "→ Sending interrupt with request_id: #{request_id}, pending_ask: #{@pending_ask_after_interrupt ? true : false}"
207
+ end
206
208
 
207
209
  control_request = {
208
210
  type: "control_request",
@@ -213,14 +215,14 @@ class RubyAgent
213
215
  }
214
216
 
215
217
  inject_streaming_response({
216
- type: "control",
217
- subtype: "interrupt",
218
- timestamp: Time.now.utc.iso8601(6)
219
- })
218
+ type: "control",
219
+ subtype: "interrupt",
220
+ timestamp: Time.now.utc.iso8601(6)
221
+ })
220
222
 
221
223
  @stdin.puts JSON.generate(control_request)
222
224
  @stdin.flush
223
- rescue => e
225
+ rescue StandardError => e
224
226
  warn "Failed to send interrupt signal: #{e.message}"
225
227
  raise
226
228
  end
@@ -239,7 +241,7 @@ class RubyAgent
239
241
  begin
240
242
  @stdin.close unless @stdin.closed?
241
243
  puts "→ stdin closed" if DEBUG
242
- rescue => e
244
+ rescue StandardError => e
243
245
  warn "Error closing stdin during exit: #{e.message}"
244
246
  end
245
247
  end
@@ -267,7 +269,7 @@ class RubyAgent
267
269
 
268
270
  def build_mcp_config(mcp_servers)
269
271
  servers = mcp_servers.transform_keys { |k| k.to_s.gsub("_", "-") }
270
- {mcpServers: servers}
272
+ { mcpServers: servers }
271
273
  end
272
274
 
273
275
  def parse_system_prompt(template_content, context_vars)
@@ -285,9 +287,7 @@ class RubyAgent
285
287
  binding_context = create_binding_context(**context_vars)
286
288
  result = erb.result(binding_context)
287
289
 
288
- if result.include?("<%=") || result.include?("%>")
289
- raise ParseError, "There was an error parsing the system prompt."
290
- end
290
+ raise ParseError, "There was an error parsing the system prompt." if result.include?("<%=") || result.include?("%>")
291
291
 
292
292
  result
293
293
  end
@@ -319,13 +319,13 @@ class RubyAgent
319
319
 
320
320
  message_json = {
321
321
  type: "user",
322
- message: {role: "user", content: content},
322
+ message: { role: "user", content: content },
323
323
  session_id: session_id
324
324
  }.compact
325
325
 
326
326
  @stdin.puts JSON.generate(message_json)
327
327
  @stdin.flush
328
- rescue => e
328
+ rescue StandardError => e
329
329
  trigger_error(e)
330
330
  raise
331
331
  end
@@ -388,6 +388,7 @@ class RubyAgent
388
388
 
389
389
  def check_nested_content_types(message, all_messages)
390
390
  return unless message["message"].is_a?(Hash)
391
+
391
392
  content = message.dig("message", "content")
392
393
  return unless content.is_a?(Array)
393
394
 
@@ -406,7 +407,7 @@ class RubyAgent
406
407
  end
407
408
 
408
409
  def trigger_custom_message_callbacks(message, all_messages)
409
- @custom_message_callbacks.each do |name, config|
410
+ @custom_message_callbacks.each_value do |config|
410
411
  processor = config[:processor]
411
412
  callback = config[:callback]
412
413
 
metadata CHANGED
@@ -1,31 +1,36 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Schacht
8
+ - Matt Lindsey
8
9
  bindir: exe
9
10
  cert_chain: []
10
11
  date: 1980-01-02 00:00:00.000000000 Z
11
12
  dependencies: []
12
13
  description: A framework for building AI agents in Ruby
13
14
  email:
14
- - keith@keithschacht.com
15
+ - krschacht@gmail.com
15
16
  executables: []
16
17
  extensions: []
17
18
  extra_rdoc_files: []
18
19
  files:
20
+ - ".rubocop.yml"
21
+ - Gemfile
19
22
  - LICENSE
20
23
  - README.md
24
+ - Rakefile
21
25
  - lib/ruby_agent.rb
22
26
  - lib/ruby_agent/version.rb
23
- homepage: https://github.com/keithschacht/ruby-agent
27
+ homepage: https://github.com/AllYourBot/ruby-agent
24
28
  licenses:
25
29
  - MIT
26
30
  metadata:
27
- homepage_uri: https://github.com/keithschacht/ruby-agent
28
- source_code_uri: https://github.com/keithschacht/ruby-agent
31
+ homepage_uri: https://github.com/AllYourBot/ruby-agent
32
+ source_code_uri: https://github.com/AllYourBot/ruby-agent
33
+ rubygems_mfa_required: 'true'
29
34
  rdoc_options: []
30
35
  require_paths:
31
36
  - lib
@@ -33,14 +38,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
33
38
  requirements:
34
39
  - - ">="
35
40
  - !ruby/object:Gem::Version
36
- version: 2.7.0
41
+ version: 3.2.0
37
42
  required_rubygems_version: !ruby/object:Gem::Requirement
38
43
  requirements:
39
44
  - - ">="
40
45
  - !ruby/object:Gem::Version
41
46
  version: '0'
42
47
  requirements: []
43
- rubygems_version: 3.6.7
48
+ rubygems_version: 3.6.9
44
49
  specification_version: 4
45
50
  summary: Ruby agent framework
46
51
  test_files: []