claude_agent 0.1.0
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 +7 -0
- data/.claude/commands/spec/complete.md +105 -0
- data/.claude/commands/spec/update.md +95 -0
- data/.claude/rules/conventions.md +622 -0
- data/.claude/rules/git.md +86 -0
- data/.claude/rules/pull-requests.md +31 -0
- data/.claude/rules/releases.md +177 -0
- data/.claude/rules/testing.md +267 -0
- data/.claude/settings.json +49 -0
- data/CHANGELOG.md +13 -0
- data/CLAUDE.md +94 -0
- data/LICENSE.txt +21 -0
- data/README.md +679 -0
- data/Rakefile +63 -0
- data/SPEC.md +558 -0
- data/lib/claude_agent/abort_controller.rb +113 -0
- data/lib/claude_agent/client.rb +298 -0
- data/lib/claude_agent/content_blocks.rb +163 -0
- data/lib/claude_agent/control_protocol.rb +717 -0
- data/lib/claude_agent/errors.rb +103 -0
- data/lib/claude_agent/hooks.rb +228 -0
- data/lib/claude_agent/mcp/server.rb +166 -0
- data/lib/claude_agent/mcp/tool.rb +137 -0
- data/lib/claude_agent/message_parser.rb +262 -0
- data/lib/claude_agent/messages.rb +421 -0
- data/lib/claude_agent/options.rb +264 -0
- data/lib/claude_agent/permissions.rb +164 -0
- data/lib/claude_agent/query.rb +90 -0
- data/lib/claude_agent/sandbox_settings.rb +139 -0
- data/lib/claude_agent/spawn.rb +235 -0
- data/lib/claude_agent/transport/base.rb +61 -0
- data/lib/claude_agent/transport/subprocess.rb +432 -0
- data/lib/claude_agent/types.rb +193 -0
- data/lib/claude_agent/version.rb +5 -0
- data/lib/claude_agent.rb +28 -0
- data/sig/claude_agent.rbs +912 -0
- data/sig/manifest.yaml +5 -0
- metadata +97 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Release Conventions
|
|
2
|
+
|
|
3
|
+
Guidelines for versioning and releasing the claude_agent gem.
|
|
4
|
+
|
|
5
|
+
## Semantic Versioning
|
|
6
|
+
|
|
7
|
+
Follow [Semantic Versioning 2.0.0](https://semver.org/):
|
|
8
|
+
|
|
9
|
+
| Version Part | When to Increment | Example |
|
|
10
|
+
|--------------|------------------------------------|-------------------|
|
|
11
|
+
| **MAJOR** | Breaking API changes | `1.0.0` → `2.0.0` |
|
|
12
|
+
| **MINOR** | New features (backward compatible) | `1.0.0` → `1.1.0` |
|
|
13
|
+
| **PATCH** | Bug fixes (backward compatible) | `1.0.0` → `1.0.1` |
|
|
14
|
+
|
|
15
|
+
### Pre-release Versions
|
|
16
|
+
|
|
17
|
+
For beta/alpha releases, append a pre-release identifier:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
1.0.0-alpha.1
|
|
21
|
+
1.0.0-beta.1
|
|
22
|
+
1.0.0-rc.1
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Changelog Format
|
|
26
|
+
|
|
27
|
+
Follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format:
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
## [Unreleased]
|
|
31
|
+
|
|
32
|
+
## [1.2.0] - 2025-03-15
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- New feature description
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- Modified behavior description
|
|
39
|
+
|
|
40
|
+
### Deprecated
|
|
41
|
+
- Feature scheduled for removal
|
|
42
|
+
|
|
43
|
+
### Removed
|
|
44
|
+
- Deleted feature description
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
- Bug fix description
|
|
48
|
+
|
|
49
|
+
### Security
|
|
50
|
+
- Security patch description
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Changelog Guidelines
|
|
54
|
+
|
|
55
|
+
1. **Maintain [Unreleased]** - Always keep an Unreleased section at the top
|
|
56
|
+
2. **Add entries as you work** - Don't wait until release time
|
|
57
|
+
3. **User-focused language** - Write for gem users, not developers
|
|
58
|
+
4. **Link to issues/PRs** - Reference GitHub issues when relevant
|
|
59
|
+
5. **Newest first** - Most recent version at top
|
|
60
|
+
|
|
61
|
+
### What to Include
|
|
62
|
+
|
|
63
|
+
| Include | Exclude |
|
|
64
|
+
|---------|---------|
|
|
65
|
+
| API additions/changes | Internal refactors |
|
|
66
|
+
| Bug fixes users might hit | Code style changes |
|
|
67
|
+
| Deprecation notices | Test-only changes |
|
|
68
|
+
| Breaking changes (prominent) | Documentation typos |
|
|
69
|
+
| Security fixes | Dependency updates (minor) |
|
|
70
|
+
|
|
71
|
+
## Release Process
|
|
72
|
+
|
|
73
|
+
### Prerequisites
|
|
74
|
+
|
|
75
|
+
Before releasing:
|
|
76
|
+
|
|
77
|
+
1. All tests pass (`bundle exec rake`)
|
|
78
|
+
2. CHANGELOG.md has entry for new version
|
|
79
|
+
3. No uncommitted changes
|
|
80
|
+
4. On `main` branch (or confirm if not)
|
|
81
|
+
|
|
82
|
+
### Release Command
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
bin/release VERSION
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
```bash
|
|
90
|
+
bin/release 1.2.0
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### What the Script Does
|
|
94
|
+
|
|
95
|
+
1. Validates version format (semantic versioning)
|
|
96
|
+
2. Checks CHANGELOG.md has entry for version
|
|
97
|
+
3. Updates `lib/claude_agent/version.rb`
|
|
98
|
+
4. Updates `Gemfile.lock`
|
|
99
|
+
5. Commits with message "Release vX.Y.Z"
|
|
100
|
+
6. Creates annotated tag `vX.Y.Z`
|
|
101
|
+
7. Pushes commit and tag to remote
|
|
102
|
+
8. Builds and publishes gem to RubyGems
|
|
103
|
+
|
|
104
|
+
### Post-Release
|
|
105
|
+
|
|
106
|
+
After running `bin/release`:
|
|
107
|
+
|
|
108
|
+
1. Create GitHub release at the new tag
|
|
109
|
+
2. Add `## [Unreleased]` section to CHANGELOG.md
|
|
110
|
+
|
|
111
|
+
## Version Bumping Guidelines
|
|
112
|
+
|
|
113
|
+
### When to Bump MAJOR (Breaking)
|
|
114
|
+
|
|
115
|
+
- Removing public methods/classes
|
|
116
|
+
- Changing method signatures (required params)
|
|
117
|
+
- Changing return types
|
|
118
|
+
- Renaming public constants
|
|
119
|
+
- Dropping Ruby version support
|
|
120
|
+
|
|
121
|
+
### When to Bump MINOR (Feature)
|
|
122
|
+
|
|
123
|
+
- Adding new public methods/classes
|
|
124
|
+
- Adding optional parameters
|
|
125
|
+
- New configuration options
|
|
126
|
+
- New message types or content blocks
|
|
127
|
+
|
|
128
|
+
### When to Bump PATCH (Fix)
|
|
129
|
+
|
|
130
|
+
- Bug fixes
|
|
131
|
+
- Documentation corrections
|
|
132
|
+
- Performance improvements (no API change)
|
|
133
|
+
- Internal refactoring (no API change)
|
|
134
|
+
|
|
135
|
+
## Example Release Workflow
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# 1. Ensure tests pass
|
|
139
|
+
bundle exec rake
|
|
140
|
+
|
|
141
|
+
# 2. Update CHANGELOG.md
|
|
142
|
+
# Add entry under [Unreleased], then rename to version:
|
|
143
|
+
## [1.2.0] - 2025-03-15
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
- New `Client#foo` method for bar functionality
|
|
147
|
+
|
|
148
|
+
### Fixed
|
|
149
|
+
- Resolved timeout issue in subprocess transport
|
|
150
|
+
|
|
151
|
+
# 3. Commit changelog
|
|
152
|
+
git add CHANGELOG.md
|
|
153
|
+
git commit -m "docs: update changelog for 1.2.0"
|
|
154
|
+
|
|
155
|
+
# 4. Release
|
|
156
|
+
bin/release 1.2.0
|
|
157
|
+
|
|
158
|
+
# 5. Add new Unreleased section
|
|
159
|
+
# Edit CHANGELOG.md to add:
|
|
160
|
+
## [Unreleased]
|
|
161
|
+
|
|
162
|
+
# 6. Commit
|
|
163
|
+
git add CHANGELOG.md
|
|
164
|
+
git commit -m "docs: add unreleased section"
|
|
165
|
+
git push
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Gem Metadata
|
|
169
|
+
|
|
170
|
+
The gemspec includes these URIs for RubyGems.org:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
174
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
This enables the "Changelog" link on the RubyGems.org gem page.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Testing Conventions
|
|
2
|
+
|
|
3
|
+
SDK-specific testing guidance. For general patterns (base classes, mocking, structure), see `conventions.md`.
|
|
4
|
+
|
|
5
|
+
## Running Tests
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bundle exec rake test # Unit tests only
|
|
9
|
+
bundle exec rake test_integration # Integration tests (requires CLI v2.0.0+)
|
|
10
|
+
bundle exec rake test_all # All tests
|
|
11
|
+
bundle exec ruby -Itest test/claude_agent/test_foo.rb # Single file
|
|
12
|
+
|
|
13
|
+
# Binstubs
|
|
14
|
+
bin/test # Unit tests only
|
|
15
|
+
bin/test-integration # Integration tests
|
|
16
|
+
bin/test-all # All tests
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Directory Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
test/
|
|
23
|
+
├── test_helper.rb # Central setup, requires, base class
|
|
24
|
+
├── integration_helper.rb # Base class for integration tests
|
|
25
|
+
├── support/ # Shared mocks, test transports, helpers
|
|
26
|
+
│ └── mock_transport.rb
|
|
27
|
+
├── claude_agent/ # Unit tests (mirrors lib/claude_agent/)
|
|
28
|
+
│ ├── test_client.rb
|
|
29
|
+
│ ├── test_options.rb
|
|
30
|
+
│ └── mcp/
|
|
31
|
+
│ └── test_tool.rb
|
|
32
|
+
├── integration/ # Integration tests (require Claude CLI)
|
|
33
|
+
│ ├── test_query.rb
|
|
34
|
+
│ ├── test_client.rb
|
|
35
|
+
│ └── ...
|
|
36
|
+
└── fixtures/ # JSON fixtures for parser tests
|
|
37
|
+
├── assistant_message.json
|
|
38
|
+
└── tool_use_response.json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## File Naming
|
|
42
|
+
|
|
43
|
+
| Component | Convention |
|
|
44
|
+
|---------------|------------------------------------|
|
|
45
|
+
| Test files | `test_{module}.rb` |
|
|
46
|
+
| Test classes | `TestClaudeAgent{Module}` |
|
|
47
|
+
| Support files | `mock_{name}.rb`, `fake_{name}.rb` |
|
|
48
|
+
|
|
49
|
+
## Extracting Test Support
|
|
50
|
+
|
|
51
|
+
Large mocks and fakes belong in `test/support/`, not inline:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
# test/support/mock_transport.rb
|
|
55
|
+
class MockTransport < ClaudeAgent::Transport::Base
|
|
56
|
+
attr_reader :written_messages
|
|
57
|
+
|
|
58
|
+
def initialize(responses: [])
|
|
59
|
+
super()
|
|
60
|
+
@responses = responses
|
|
61
|
+
@written_messages = []
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def write(data)
|
|
65
|
+
@written_messages << JSON.parse(data)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def read_messages
|
|
69
|
+
@responses.each { |r| yield r }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# ... minimal interface implementation
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
# test/test_helper.rb
|
|
78
|
+
require "claude_agent"
|
|
79
|
+
require "minitest/autorun"
|
|
80
|
+
require "mocha/minitest"
|
|
81
|
+
|
|
82
|
+
Dir[File.join(__dir__, "support", "**", "*.rb")].each { |f| require f }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Testing Data.define Types
|
|
86
|
+
|
|
87
|
+
This SDK uses `Data.define` for immutable message types:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
test "data type attributes" do
|
|
91
|
+
block = ClaudeAgent::TextBlock.new(text: "hello")
|
|
92
|
+
|
|
93
|
+
assert_equal "hello", block.text
|
|
94
|
+
assert_equal :text, block.type
|
|
95
|
+
assert block.frozen?
|
|
96
|
+
assert_equal({ type: "text", text: "hello" }, block.to_h)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
test "optional fields default to nil" do
|
|
100
|
+
block = ClaudeAgent::ToolResultBlock.new(tool_use_id: "123")
|
|
101
|
+
assert_nil block.content
|
|
102
|
+
assert_nil block.is_error
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Testing String and Symbol Keys
|
|
107
|
+
|
|
108
|
+
JSON parses to string keys, Ruby prefers symbols. Always test both:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
test "accepts symbol keys" do
|
|
112
|
+
block = ClaudeAgent::ImageContentBlock.new(
|
|
113
|
+
source: { type: "base64", media_type: "image/png", data: "..." }
|
|
114
|
+
)
|
|
115
|
+
assert_equal "base64", block.source_type
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
test "accepts string keys" do
|
|
119
|
+
block = ClaudeAgent::ImageContentBlock.new(
|
|
120
|
+
source: { "type" => "base64", "media_type" => "image/png", "data" => "..." }
|
|
121
|
+
)
|
|
122
|
+
assert_equal "base64", block.source_type
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Fixtures vs Inline Data
|
|
127
|
+
|
|
128
|
+
**Use fixtures** for:
|
|
129
|
+
- Complex JSON structures reused across tests
|
|
130
|
+
- Large response payloads
|
|
131
|
+
- Realistic CLI output samples
|
|
132
|
+
|
|
133
|
+
**Use inline data** for:
|
|
134
|
+
- Simple, single-use test cases
|
|
135
|
+
- When the data structure IS the test (e.g., edge cases)
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# test/fixtures/assistant_with_tool_use.json
|
|
139
|
+
{
|
|
140
|
+
"type": "assistant",
|
|
141
|
+
"message": {
|
|
142
|
+
"model": "claude",
|
|
143
|
+
"content": [
|
|
144
|
+
{ "type": "text", "text": "Let me read that" },
|
|
145
|
+
{ "type": "tool_use", "id": "tool_123", "name": "Read", "input": {} }
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
# Usage
|
|
153
|
+
test "parses complex message" do
|
|
154
|
+
data = json_fixture("assistant_with_tool_use")
|
|
155
|
+
message = parser.parse(data)
|
|
156
|
+
|
|
157
|
+
assert message.has_tool_use?
|
|
158
|
+
end
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Testing CLI Interaction
|
|
162
|
+
|
|
163
|
+
### Prefer Mocha over manual mocks
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
test "spawns subprocess with correct args" do
|
|
167
|
+
Process.expects(:spawn).with(
|
|
168
|
+
"claude", "--print", "--output-format", "json",
|
|
169
|
+
has_entries(chdir: Dir.pwd)
|
|
170
|
+
).returns(123)
|
|
171
|
+
|
|
172
|
+
transport.start
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Use MockTransport for Client tests
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
test "sends user message" do
|
|
180
|
+
transport = MockTransport.new(responses: [result_message])
|
|
181
|
+
client = ClaudeAgent::Client.new(transport: transport)
|
|
182
|
+
client.connect
|
|
183
|
+
|
|
184
|
+
client.send_message("Hello")
|
|
185
|
+
|
|
186
|
+
assert_equal 1, transport.written_messages.size
|
|
187
|
+
assert_equal "user", transport.written_messages.first["type"]
|
|
188
|
+
end
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Capturing I/O
|
|
192
|
+
|
|
193
|
+
Use Minitest's built-in `capture_io`:
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
test "logs to stderr" do
|
|
197
|
+
_, err = capture_io { client.send_message("test") }
|
|
198
|
+
assert_match(/sending message/, err)
|
|
199
|
+
end
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Error Testing
|
|
203
|
+
|
|
204
|
+
Test error hierarchy and context fields:
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
test "error includes context" do
|
|
208
|
+
error = ClaudeAgent::ProcessError.new(
|
|
209
|
+
"CLI failed",
|
|
210
|
+
exit_code: 1,
|
|
211
|
+
stderr: "error details"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
assert_equal 1, error.exit_code
|
|
215
|
+
assert_equal "error details", error.stderr
|
|
216
|
+
assert_match(/CLI failed/, error.message)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
test "error inheritance" do
|
|
220
|
+
error = ClaudeAgent::ProcessError.new("test")
|
|
221
|
+
assert_kind_of ClaudeAgent::Error, error
|
|
222
|
+
assert_kind_of StandardError, error
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Integration Tests
|
|
227
|
+
|
|
228
|
+
Integration tests live in `test/integration/` and inherit from `IntegrationTestCase`:
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
# test/integration/test_query.rb
|
|
232
|
+
require_relative "../integration_helper"
|
|
233
|
+
|
|
234
|
+
class TestIntegrationQuery < IntegrationTestCase
|
|
235
|
+
test "real query returns result" do
|
|
236
|
+
messages = ClaudeAgent.query(prompt: "Say hello", options: test_options).to_a
|
|
237
|
+
result = messages.find { |m| m.is_a?(ClaudeAgent::ResultMessage) }
|
|
238
|
+
|
|
239
|
+
assert_not_nil result
|
|
240
|
+
assert result.success?
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The `IntegrationTestCase` base class:
|
|
246
|
+
- Skips tests unless `INTEGRATION=true` is set (automatic with `rake test_integration`)
|
|
247
|
+
- Skips if Claude CLI is not installed
|
|
248
|
+
- Provides `test_options` helper with sensible defaults
|
|
249
|
+
|
|
250
|
+
## What to Test
|
|
251
|
+
|
|
252
|
+
| Component | Focus Areas |
|
|
253
|
+
|----------------|---------------------------------------------|
|
|
254
|
+
| Options | Default values, validation, CLI arg mapping |
|
|
255
|
+
| Messages | Parsing, field access, type discrimination |
|
|
256
|
+
| Content Blocks | All block types, to_h serialization |
|
|
257
|
+
| Client | Message sending, streaming, error handling |
|
|
258
|
+
| Transport | Process lifecycle, I/O handling |
|
|
259
|
+
| Errors | Hierarchy, context fields, messages |
|
|
260
|
+
| MCP | Tool definition, server config |
|
|
261
|
+
|
|
262
|
+
## Test Coverage Guidelines
|
|
263
|
+
|
|
264
|
+
- High coverage on public API surface
|
|
265
|
+
- Don't test private methods directly—test through public interface
|
|
266
|
+
- Cover edge cases: nil values, empty collections, invalid input
|
|
267
|
+
- Test both success and failure paths
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(bundle:*)",
|
|
5
|
+
"Bash(gem:*)",
|
|
6
|
+
"Bash(rbs:*)",
|
|
7
|
+
"Bash(git log:*)",
|
|
8
|
+
"Bash(git diff:*)",
|
|
9
|
+
"Bash(git show:*)",
|
|
10
|
+
"Bash(git status:*)",
|
|
11
|
+
"Bash(git rev-parse:*)",
|
|
12
|
+
"Bash(git ls-tree:*)",
|
|
13
|
+
"Bash(git branch:*)",
|
|
14
|
+
"Bash(git fetch:*)",
|
|
15
|
+
"Bash(git worktree:*)",
|
|
16
|
+
"Bash(git mv:*)",
|
|
17
|
+
"Bash(gh pr view:*)",
|
|
18
|
+
"Bash(gh pr diff:*)",
|
|
19
|
+
"Bash(gh pr list:*)",
|
|
20
|
+
"Bash(gh pr checks:*)",
|
|
21
|
+
"Bash(gh run view:*)",
|
|
22
|
+
"Bash(ls:*)",
|
|
23
|
+
"Bash(find:*)",
|
|
24
|
+
"Bash(grep:*)",
|
|
25
|
+
"Bash(ps:*)",
|
|
26
|
+
"Bash(lsof:*)",
|
|
27
|
+
"Bash(wc:*)",
|
|
28
|
+
"Bash(mkdir:*)",
|
|
29
|
+
"Bash(bin/rbs-validate:*)",
|
|
30
|
+
"Bash(bin/setup:*)",
|
|
31
|
+
"Bash(bin/test:*)",
|
|
32
|
+
"Bash(bin/test-integration:*)",
|
|
33
|
+
"Bash(bin/test-all:*)",
|
|
34
|
+
"Bash(bin/update-reference-sdks:*)",
|
|
35
|
+
"WebFetch(domain:docs.anthropic.com)",
|
|
36
|
+
"WebFetch(domain:docs.claude.com)"
|
|
37
|
+
],
|
|
38
|
+
"deny": [],
|
|
39
|
+
"ask": [],
|
|
40
|
+
"additionalDirectories": [],
|
|
41
|
+
"defaultMode": "default"
|
|
42
|
+
},
|
|
43
|
+
"hooks": {},
|
|
44
|
+
"env": {},
|
|
45
|
+
"attribution": {
|
|
46
|
+
"commit": "",
|
|
47
|
+
"pr": ""
|
|
48
|
+
}
|
|
49
|
+
}
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-01-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- MVP implementation of the Claude Agent SDK for Ruby
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# ClaudeAgent Ruby SDK
|
|
2
|
+
|
|
3
|
+
Ruby SDK for building autonomous AI agents that interact with Claude Code CLI.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- **Ruby** 3.2+ (uses `Data.define` for immutable types)
|
|
8
|
+
- **Minitest** for testing
|
|
9
|
+
- **RuboCop** with `rubocop-rails-omakase` for linting
|
|
10
|
+
- **RBS** for type signatures (in `sig/`)
|
|
11
|
+
- **No external API clients** - communicates with Claude Code CLI via JSON Lines protocol
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bin/setup # Install dependencies
|
|
17
|
+
bundle exec rake # Run unit tests + rbs + rubocop (default)
|
|
18
|
+
bundle exec rake test # Unit tests only
|
|
19
|
+
bundle exec rake test_integration # Integration tests (requires CLI v2.0.0+)
|
|
20
|
+
bundle exec rake test_all # All tests (requires CLI v2.0.0+)
|
|
21
|
+
bundle exec rake rbs # Validate RBS signatures
|
|
22
|
+
bundle exec rake rbs:parse # RBS syntax check only (faster)
|
|
23
|
+
bundle exec rake rbs:prototype # Generate RBS from lib/ (for new code)
|
|
24
|
+
bundle exec rubocop # Lint only
|
|
25
|
+
bin/console # IRB with gem loaded
|
|
26
|
+
|
|
27
|
+
# Binstubs
|
|
28
|
+
bin/test # Unit tests only
|
|
29
|
+
bin/test-integration # Integration tests
|
|
30
|
+
bin/test-all # All tests
|
|
31
|
+
bin/rbs-validate # Validate RBS signatures
|
|
32
|
+
bin/release VERSION # Release gem (e.g., bin/release 1.2.0)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Architecture
|
|
36
|
+
|
|
37
|
+
| Component | Purpose |
|
|
38
|
+
|-----------------------------|----------------------------------------------------|
|
|
39
|
+
| `Query` | One-shot stateless prompts |
|
|
40
|
+
| `Client` | Multi-turn bidirectional conversations |
|
|
41
|
+
| `ControlProtocol` | Handles handshake, hooks, permissions, MCP routing |
|
|
42
|
+
| `Transport::Subprocess` | Spawns CLI, manages stdin/stdout |
|
|
43
|
+
| `MCP::Tool` / `MCP::Server` | Custom tool definitions |
|
|
44
|
+
|
|
45
|
+
## Conventions
|
|
46
|
+
|
|
47
|
+
- **Immutable data types**: All messages and options use `Data.define`
|
|
48
|
+
- **Frozen string literals**: Every file starts with `# frozen_string_literal: true`
|
|
49
|
+
- **Message polymorphism**: Use `case` statements or `is_a?()` for content block types
|
|
50
|
+
- **Error hierarchy**: All errors inherit from `ClaudeAgent::Error` with context (exit code, stderr, etc.)
|
|
51
|
+
- **Protocol flow**: Transport → ControlProtocol → MessageParser → typed message objects
|
|
52
|
+
|
|
53
|
+
## Key Patterns
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
# One-shot query
|
|
57
|
+
result = ClaudeAgent.query("prompt", model: "sonnet")
|
|
58
|
+
|
|
59
|
+
# Interactive client
|
|
60
|
+
client = ClaudeAgent::Client.new(options)
|
|
61
|
+
client.send_message("prompt") { |msg| handle(msg) }
|
|
62
|
+
|
|
63
|
+
# Content blocks are polymorphic
|
|
64
|
+
message.content.each do |block|
|
|
65
|
+
case block
|
|
66
|
+
when ClaudeAgent::TextBlock then ...
|
|
67
|
+
when ClaudeAgent::ToolUseBlock then ...
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Testing Notes
|
|
73
|
+
|
|
74
|
+
- Unit tests in `test/claude_agent/` - run without Claude CLI
|
|
75
|
+
- Integration tests in `test/integration/` - require Claude Code CLI v2.0.0+
|
|
76
|
+
- Integration tests are skipped by default; set `INTEGRATION=true` to run them
|
|
77
|
+
- Skip CLI version check with `CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK=true`
|
|
78
|
+
|
|
79
|
+
## Releasing
|
|
80
|
+
|
|
81
|
+
Uses [Semantic Versioning](https://semver.org/) and [Keep a Changelog](https://keepachangelog.com/) format.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# 1. Update CHANGELOG.md with version entry
|
|
85
|
+
# 2. Commit the changelog
|
|
86
|
+
git commit -am "docs: update changelog for X.Y.Z"
|
|
87
|
+
|
|
88
|
+
# 3. Run the release script
|
|
89
|
+
bin/release X.Y.Z
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The release script validates the changelog, updates version.rb, commits, tags, and publishes to RubyGems.
|
|
93
|
+
|
|
94
|
+
See `.claude/rules/releases.md` for detailed conventions.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Carr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|