ruby_claude 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +64 -181
- data/lib/ruby_claude/version.rb +1 -1
- data/ruby_claude.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ce8e2c7378bd0ed7559c37995d1886fb37367b8e53e29f77a2bc65ffc10344d
|
|
4
|
+
data.tar.gz: a1e7bf9563ed416687cb0b8bac091850b3123b623ecf91fd25bbe31c240be9cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d158e722113e3b3ee48313f3fbb2cf0a9df9d7980b263045a1a89f5ff1c7786e396d4bc8d1195aef7edf256c720b18e5d0c1936253673b0a605c6e790f42b62d
|
|
7
|
+
data.tar.gz: ebac20bcc17499f0b5007c62dba352699d52bb779285b160401daa7a1a08399f67f0df0efcf37bfecbbd0146b99361024f9707b84da17a94b445deb24e409470
|
data/README.md
CHANGED
|
@@ -1,59 +1,36 @@
|
|
|
1
1
|
# Ruby Claude
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Anthropic API key.
|
|
7
|
-
|
|
8
|
-
> **Unofficial.** This is a community gem. It is *not* affiliated with or
|
|
9
|
-
> endorsed by Anthropic. It uses a documented, supported headless feature
|
|
10
|
-
> (`claude -p`) and stays within your subscription's normal rate limits. It
|
|
11
|
-
> does **not** extract or reuse OAuth tokens, and it makes **no** direct HTTP
|
|
12
|
-
> calls to the Anthropic API.
|
|
13
|
-
|
|
14
|
-
## Why a subscription instead of an API key?
|
|
15
|
-
|
|
16
|
-
`claude -p "<prompt>"` runs Claude Code non-interactively and prints the
|
|
17
|
-
result, using whatever credentials the CLI is logged in with. If you logged in
|
|
18
|
-
with a **subscription** (`claude` → `/login` → subscription option), those
|
|
19
|
-
calls draw on your subscription — **no API billing**.
|
|
20
|
-
|
|
21
|
-
The one catch: if `ANTHROPIC_API_KEY` is present in the environment, Claude
|
|
22
|
-
Code may use it and bill the API. **Ruby Claude strips `ANTHROPIC_API_KEY`
|
|
23
|
-
from the child process environment by default** (`use_subscription = true`) so
|
|
24
|
-
the CLI falls back to your logged-in subscription credentials. Set
|
|
25
|
-
`use_subscription = false` only if you *want* API-key billing.
|
|
3
|
+
A tiny, zero-dependency Ruby SDK for Claude that shells out to the **Claude Code
|
|
4
|
+
CLI** (`claude -p`) and authenticates with your **Claude Pro/Max subscription**
|
|
5
|
+
instead of an API key.
|
|
26
6
|
|
|
27
|
-
|
|
7
|
+
> **Unofficial** community gem — not affiliated with Anthropic. It uses the
|
|
8
|
+
> supported `claude -p` headless mode within your subscription's rate limits; no
|
|
9
|
+
> OAuth-token handling and no direct API calls.
|
|
28
10
|
|
|
29
|
-
|
|
11
|
+
## Subscription, not API key
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
2. Log in **once**, choosing the subscription option:
|
|
37
|
-
```bash
|
|
38
|
-
claude # then run /login and pick "Claude account with subscription"
|
|
39
|
-
```
|
|
13
|
+
`claude -p` uses whatever the CLI is logged in with — if that's a subscription,
|
|
14
|
+
calls draw on it with no API billing. Ruby Claude **strips `ANTHROPIC_API_KEY`
|
|
15
|
+
from the child environment by default** so the CLI can't silently fall back to
|
|
16
|
+
API billing; set `use_subscription = false` to opt back in.
|
|
40
17
|
|
|
41
|
-
##
|
|
18
|
+
## Prerequisites
|
|
42
19
|
|
|
43
|
-
|
|
20
|
+
`claude` must be installed and logged in (this gem drives it, it doesn't replace it):
|
|
44
21
|
|
|
45
|
-
```
|
|
46
|
-
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @anthropic-ai/claude-code
|
|
24
|
+
claude # run /login once and choose the subscription option
|
|
47
25
|
```
|
|
48
26
|
|
|
49
|
-
|
|
27
|
+
## Install
|
|
50
28
|
|
|
51
|
-
```
|
|
52
|
-
gem
|
|
29
|
+
```ruby
|
|
30
|
+
gem "ruby_claude" # in your Gemfile
|
|
53
31
|
```
|
|
54
32
|
|
|
55
|
-
|
|
56
|
-
**zero runtime dependencies** — it only uses the standard library.
|
|
33
|
+
…or `gem install ruby_claude`. Requires Ruby 3.2+; zero runtime dependencies.
|
|
57
34
|
|
|
58
35
|
## Quickstart
|
|
59
36
|
|
|
@@ -63,167 +40,76 @@ require "ruby_claude"
|
|
|
63
40
|
puts RubyClaude.query("Summarize lib/foo.rb in two sentences")
|
|
64
41
|
```
|
|
65
42
|
|
|
66
|
-
That's it — if `claude` is installed and logged in, you get an answer back,
|
|
67
|
-
billed against your subscription.
|
|
68
|
-
|
|
69
43
|
## Usage
|
|
70
44
|
|
|
71
|
-
### 1. One-shot convenience
|
|
72
|
-
|
|
73
|
-
Delegates to a memoized, globally-configured default client.
|
|
74
|
-
|
|
75
|
-
```ruby
|
|
76
|
-
puts RubyClaude.query("Summarize lib/foo.rb in two sentences")
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 2. A configured client
|
|
80
|
-
|
|
81
45
|
```ruby
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
append_system_prompt: "Always answer concisely.",
|
|
86
|
-
allowed_tools: ["Read", "Grep"],
|
|
87
|
-
timeout: 180
|
|
88
|
-
)
|
|
46
|
+
# A configured client
|
|
47
|
+
client = RubyClaude::Client.new(model: "claude-sonnet-4-6",
|
|
48
|
+
allowed_tools: ["Read", "Grep"], timeout: 180)
|
|
89
49
|
|
|
90
50
|
res = client.query("What does this project do?")
|
|
91
|
-
res.text
|
|
92
|
-
res.session_id
|
|
93
|
-
res.cost_usd
|
|
94
|
-
res.usage
|
|
95
|
-
res.num_turns # => Integer
|
|
96
|
-
res.duration_ms # => Integer
|
|
97
|
-
res.error? # => false
|
|
98
|
-
res.raw # => parsed Hash of the CLI's final result JSON
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
`Response#to_s` returns `text`, so `puts client.query("...")` prints the answer.
|
|
102
|
-
|
|
103
|
-
### 3. Streaming
|
|
51
|
+
res.text # final text (Response#to_s returns it too, so `puts res` works)
|
|
52
|
+
res.session_id # String
|
|
53
|
+
res.cost_usd # Float (often 0.0 on a subscription)
|
|
54
|
+
res.usage # Hash — also: res.num_turns, res.duration_ms, res.error?, res.raw
|
|
104
55
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
```ruby
|
|
56
|
+
# Streaming — yields typed events, returns the final Response
|
|
108
57
|
client.stream("Write a haiku about Ruby") do |event|
|
|
109
|
-
|
|
110
|
-
when :assistant then print event.text # assistant text for the turn
|
|
111
|
-
when :result then puts "\n[done in #{event.duration_ms}ms]"
|
|
112
|
-
end
|
|
58
|
+
print event.text if event.type == :assistant
|
|
113
59
|
end
|
|
114
|
-
```
|
|
115
60
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
61
|
+
# Multi-turn session — resumes the underlying session_id automatically
|
|
62
|
+
chat = client.session
|
|
63
|
+
chat.query("My favorite number is 7.")
|
|
64
|
+
puts chat.query("What's my favorite number?") # => "...7..."
|
|
119
65
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
A `Session` captures the underlying `session_id` from the first reply and
|
|
123
|
-
transparently resumes it on later calls.
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
session = client.session
|
|
127
|
-
session.query("My favorite number is 7.")
|
|
128
|
-
puts session.query("What's my favorite number?") # => "...7..."
|
|
129
|
-
session.id # => the session_id being resumed
|
|
66
|
+
# Global defaults for RubyClaude.query and new clients
|
|
67
|
+
RubyClaude.configure { |c| c.model = "claude-sonnet-4-6"; c.timeout = 300 }
|
|
130
68
|
```
|
|
131
69
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
### 5. Global configuration
|
|
135
|
-
|
|
136
|
-
```ruby
|
|
137
|
-
RubyClaude.configure do |c|
|
|
138
|
-
c.model = "claude-sonnet-4-6"
|
|
139
|
-
c.timeout = 300
|
|
140
|
-
c.binary = "claude" # path/name of the CLI
|
|
141
|
-
c.cwd = Dir.pwd
|
|
142
|
-
c.use_subscription = true # strips ANTHROPIC_API_KEY from the child env
|
|
143
|
-
end
|
|
144
|
-
```
|
|
70
|
+
A streaming `Event#type` is `:system`, `:assistant`, `:user`, or `:result`. Use
|
|
71
|
+
`#query` (alias `#ask`) — there is intentionally no `#send`.
|
|
145
72
|
|
|
146
|
-
|
|
147
|
-
instances. Per-client options passed to `Client.new(**opts)` override them.
|
|
73
|
+
## Configuration
|
|
148
74
|
|
|
149
|
-
|
|
150
|
-
> `Object#send`). Use `#query`, or its alias `#ask`.
|
|
75
|
+
`Client.new(**opts)` overrides per instance; `RubyClaude.configure` sets globals.
|
|
151
76
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
|
155
|
-
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
| `add_dirs` | `[]` | `--add-dir` (extra readable/writable directories) |
|
|
165
|
-
| `permission_mode` | `nil` | `--permission-mode` (`default` / `acceptEdits` / `plan` / `bypassPermissions`) |
|
|
166
|
-
| `max_turns` | `nil` | `--max-turns` |
|
|
167
|
-
|
|
168
|
-
Tool and directory lists are passed as separate CLI tokens, so permission-rule
|
|
169
|
-
patterns that contain spaces (e.g. `"Bash(git log *)"`) are preserved.
|
|
77
|
+
| Option | Default | Maps to |
|
|
78
|
+
|--------|---------|---------|
|
|
79
|
+
| `binary` | `"claude"` | executable name/path |
|
|
80
|
+
| `model` | `nil` | `--model` |
|
|
81
|
+
| `cwd` | `Dir.pwd` | subprocess working directory |
|
|
82
|
+
| `timeout` | `300` | seconds before the child is killed |
|
|
83
|
+
| `use_subscription` | `true` | strip `ANTHROPIC_API_KEY` from the child env |
|
|
84
|
+
| `append_system_prompt` | `nil` | `--append-system-prompt` |
|
|
85
|
+
| `allowed_tools` / `disallowed_tools` | `nil` | `--allowedTools` / `--disallowedTools` |
|
|
86
|
+
| `add_dirs` | `[]` | `--add-dir` |
|
|
87
|
+
| `permission_mode` | `nil` | `--permission-mode` |
|
|
88
|
+
| `max_turns` | `nil` | `--max-turns` |
|
|
170
89
|
|
|
171
90
|
## Errors
|
|
172
91
|
|
|
173
|
-
All
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|----------------------------------|-----------------------------------------------------------------------------|
|
|
177
|
-
| `RubyClaude::BinaryNotFoundError`| `claude` is not on `PATH` / not executable (message explains how to install)|
|
|
178
|
-
| `RubyClaude::AuthenticationError`| output/exit indicates you are not logged in (suggests `claude` + `/login`) |
|
|
179
|
-
| `RubyClaude::TimeoutError` | the child exceeded `timeout`; the gem killed it |
|
|
180
|
-
| `RubyClaude::ExecutionError` | non-zero exit, or a result with `is_error: true` (carries `#status`, `#stderr`) |
|
|
181
|
-
| `RubyClaude::ParseError` | the CLI output couldn't be parsed as the expected JSON |
|
|
182
|
-
|
|
183
|
-
```ruby
|
|
184
|
-
begin
|
|
185
|
-
RubyClaude.query("hello")
|
|
186
|
-
rescue RubyClaude::BinaryNotFoundError => e
|
|
187
|
-
warn e.message # install + /login instructions
|
|
188
|
-
rescue RubyClaude::AuthenticationError
|
|
189
|
-
warn "Run `claude` and `/login` with your subscription."
|
|
190
|
-
rescue RubyClaude::ExecutionError => e
|
|
191
|
-
warn "claude failed (status #{e.status}): #{e.stderr}"
|
|
192
|
-
end
|
|
193
|
-
```
|
|
92
|
+
All subclass `RubyClaude::Error`: `BinaryNotFoundError` (no `claude` on PATH),
|
|
93
|
+
`AuthenticationError` (not logged in), `TimeoutError`, `ExecutionError` (non-zero
|
|
94
|
+
exit or an `is_error` result; carries `#status`/`#stderr`), and `ParseError`.
|
|
194
95
|
|
|
195
96
|
## How it works
|
|
196
97
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
- **`Runner`** owns all subprocess concerns: it spawns `claude` via `Open3`
|
|
203
|
-
(always the array form — your prompt is **never** shell-interpolated), writes
|
|
204
|
-
the prompt to **stdin** (avoiding `ARG_MAX` and escaping issues), enforces the
|
|
205
|
-
timeout by killing the child, captures output, and — for streaming — reads
|
|
206
|
-
stdout line-by-line as newline-delimited JSON.
|
|
207
|
-
- **`Client`** composes the two and builds `Response` / `Event` objects.
|
|
208
|
-
- **`Session`** remembers the `session_id` and passes `--resume <id>`.
|
|
209
|
-
|
|
210
|
-
The runner is stateless and spawns one subprocess per call, so a `Client` is
|
|
211
|
-
safe to reuse and to call concurrently from multiple threads.
|
|
98
|
+
`Command` builds the argv + child env (pure, no I/O); `Runner` spawns `claude`
|
|
99
|
+
via `Open3` (array form — no shell; prompt on stdin), enforces the timeout, and
|
|
100
|
+
parses output; `Client` builds `Response`/`Event`; `Session` resumes via
|
|
101
|
+
`--resume`. The runner is stateless per call, so a `Client` is safe to share
|
|
102
|
+
across threads.
|
|
212
103
|
|
|
213
104
|
## Development
|
|
214
105
|
|
|
215
106
|
```bash
|
|
216
|
-
bundle
|
|
217
|
-
|
|
218
|
-
rake lint # rubocop
|
|
219
|
-
rake # test + lint
|
|
220
|
-
bin/console # IRB with the gem loaded
|
|
107
|
+
bundle exec rake # tests + lint (hermetic — never spawns the real claude)
|
|
108
|
+
bin/console # IRB with the gem loaded
|
|
221
109
|
```
|
|
222
110
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
`claude` binary. (A handful of `Runner` tests spawn a throwaway local `ruby`
|
|
226
|
-
process to exercise the subprocess plumbing.)
|
|
111
|
+
Contributing or tracking upstream SDK changes? See
|
|
112
|
+
[`doc/DEVELOPMENT.md`](doc/DEVELOPMENT.md).
|
|
227
113
|
|
|
228
114
|
## Building and publishing the gem
|
|
229
115
|
|
|
@@ -233,7 +119,7 @@ Before a release, bump it following [SemVer](https://semver.org).
|
|
|
233
119
|
### Build locally
|
|
234
120
|
|
|
235
121
|
```bash
|
|
236
|
-
gem build ruby_claude.gemspec
|
|
122
|
+
gem build ruby_claude.gemspec # => ruby_claude-<version>.gem
|
|
237
123
|
gem install ./ruby_claude-<version>.gem # try the built gem locally
|
|
238
124
|
```
|
|
239
125
|
|
|
@@ -270,9 +156,6 @@ rake install # build and install locally
|
|
|
270
156
|
gem push ruby_claude-<version>.gem
|
|
271
157
|
```
|
|
272
158
|
|
|
273
|
-
The name `ruby_claude` is currently available on RubyGems. Releasing
|
|
274
|
-
`0.0.0` is unusual — bump to e.g. `0.1.0` for your first real publish.
|
|
275
|
-
|
|
276
159
|
Alternatively, do it all in one step with Bundler's release task, which builds
|
|
277
160
|
the gem, creates and pushes a `v<version>` git tag, and pushes to RubyGems
|
|
278
161
|
(requires a clean, committed tree):
|
data/lib/ruby_claude/version.rb
CHANGED
data/ruby_claude.gemspec
CHANGED
|
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
|
6
6
|
spec.name = "ruby_claude"
|
|
7
7
|
spec.version = RubyClaude::VERSION
|
|
8
8
|
spec.authors = ["Kaíque Kandy Koga"]
|
|
9
|
-
spec.email = ["
|
|
9
|
+
spec.email = ["kaiquekandykoga@gmail.com"]
|
|
10
10
|
|
|
11
11
|
spec.summary = "Subscription-authenticated Ruby SDK for Claude via the Claude Code CLI."
|
|
12
12
|
spec.description = <<~DESC
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_claude
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaíque Kandy Koga
|
|
@@ -74,7 +74,7 @@ description: |
|
|
|
74
74
|
credentials. Unofficial; uses a supported headless feature within the
|
|
75
75
|
subscription's rate limits.
|
|
76
76
|
email:
|
|
77
|
-
-
|
|
77
|
+
- kaiquekandykoga@gmail.com
|
|
78
78
|
executables: []
|
|
79
79
|
extensions: []
|
|
80
80
|
extra_rdoc_files: []
|