ruby_claude 0.0.0 → 0.0.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 +4 -4
- data/README.md +65 -181
- data/lib/ruby_claude/command.rb +1 -0
- data/lib/ruby_claude/configuration.rb +7 -1
- data/lib/ruby_claude/version.rb +1 -2
- 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: c9e299d43bba4e5a5df1c08f520fe46127d0c7174b9ee4d8875c782dd6a0e0e5
|
|
4
|
+
data.tar.gz: 91c62505b09f35183edb5712da2479a7b23c00afcc4f31a19576cc39f7ffad4a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61eee22fc2cc087cd868088b3d65bb0ee10918491b3a320e0c784f8b9398cff76ec11adf6da8743714bba3a6a112bba7f27ed5283f5f477edc2db9d081b1b958
|
|
7
|
+
data.tar.gz: b39011040b59133357fddb6254f35b6277dc833cf4f46962b9d7521f179d016ec5f2b6f76b39e5d31b0992491292c159b2eab59d48766cb36d079ee6320be193
|
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,77 @@ 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
|
-
| `
|
|
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` |
|
|
89
|
+
| `effort` | `nil` | `--effort` (`low`/`medium`/`high`/`xhigh`/`max`) |
|
|
170
90
|
|
|
171
91
|
## Errors
|
|
172
92
|
|
|
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
|
-
```
|
|
93
|
+
All subclass `RubyClaude::Error`: `BinaryNotFoundError` (no `claude` on PATH),
|
|
94
|
+
`AuthenticationError` (not logged in), `TimeoutError`, `ExecutionError` (non-zero
|
|
95
|
+
exit or an `is_error` result; carries `#status`/`#stderr`), and `ParseError`.
|
|
194
96
|
|
|
195
97
|
## How it works
|
|
196
98
|
|
|
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.
|
|
99
|
+
`Command` builds the argv + child env (pure, no I/O); `Runner` spawns `claude`
|
|
100
|
+
via `Open3` (array form — no shell; prompt on stdin), enforces the timeout, and
|
|
101
|
+
parses output; `Client` builds `Response`/`Event`; `Session` resumes via
|
|
102
|
+
`--resume`. The runner is stateless per call, so a `Client` is safe to share
|
|
103
|
+
across threads.
|
|
212
104
|
|
|
213
105
|
## Development
|
|
214
106
|
|
|
215
107
|
```bash
|
|
216
|
-
bundle
|
|
217
|
-
|
|
218
|
-
rake lint # rubocop
|
|
219
|
-
rake # test + lint
|
|
220
|
-
bin/console # IRB with the gem loaded
|
|
108
|
+
bundle exec rake # tests + lint (hermetic — never spawns the real claude)
|
|
109
|
+
bin/console # IRB with the gem loaded
|
|
221
110
|
```
|
|
222
111
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
`claude` binary. (A handful of `Runner` tests spawn a throwaway local `ruby`
|
|
226
|
-
process to exercise the subprocess plumbing.)
|
|
112
|
+
Contributing or tracking upstream SDK changes? See
|
|
113
|
+
[`doc/DEVELOPMENT.md`](doc/DEVELOPMENT.md).
|
|
227
114
|
|
|
228
115
|
## Building and publishing the gem
|
|
229
116
|
|
|
@@ -233,7 +120,7 @@ Before a release, bump it following [SemVer](https://semver.org).
|
|
|
233
120
|
### Build locally
|
|
234
121
|
|
|
235
122
|
```bash
|
|
236
|
-
gem build ruby_claude.gemspec
|
|
123
|
+
gem build ruby_claude.gemspec # => ruby_claude-<version>.gem
|
|
237
124
|
gem install ./ruby_claude-<version>.gem # try the built gem locally
|
|
238
125
|
```
|
|
239
126
|
|
|
@@ -270,9 +157,6 @@ rake install # build and install locally
|
|
|
270
157
|
gem push ruby_claude-<version>.gem
|
|
271
158
|
```
|
|
272
159
|
|
|
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
160
|
Alternatively, do it all in one step with Bundler's release task, which builds
|
|
277
161
|
the gem, creates and pushes a `v<version>` git tag, and pushes to RubyGems
|
|
278
162
|
(requires a clean, committed tree):
|
data/lib/ruby_claude/command.rb
CHANGED
|
@@ -23,6 +23,7 @@ module RubyClaude
|
|
|
23
23
|
argv = [@config.binary, "-p", "--output-format", stream ? "stream-json" : "json"]
|
|
24
24
|
argv << "--verbose" if stream
|
|
25
25
|
add_flag(argv, "--model", @config.model)
|
|
26
|
+
add_flag(argv, "--effort", @config.effort)
|
|
26
27
|
add_flag(argv, "--append-system-prompt", @config.append_system_prompt)
|
|
27
28
|
add_list(argv, "--allowedTools", @config.allowed_tools)
|
|
28
29
|
add_list(argv, "--disallowedTools", @config.disallowed_tools)
|
|
@@ -40,6 +40,10 @@ module RubyClaude
|
|
|
40
40
|
# @return [Integer, nil] limit for +--max-turns+
|
|
41
41
|
attr_accessor :max_turns
|
|
42
42
|
|
|
43
|
+
# @return [String, nil] reasoning-effort level for +--effort+
|
|
44
|
+
# (+low+, +medium+, +high+, +xhigh+, or +max+); nil uses the CLI default
|
|
45
|
+
attr_accessor :effort
|
|
46
|
+
|
|
43
47
|
def initialize
|
|
44
48
|
@binary = "claude"
|
|
45
49
|
@model = nil
|
|
@@ -52,6 +56,7 @@ module RubyClaude
|
|
|
52
56
|
@add_dirs = []
|
|
53
57
|
@permission_mode = nil
|
|
54
58
|
@max_turns = nil
|
|
59
|
+
@effort = nil
|
|
55
60
|
end
|
|
56
61
|
|
|
57
62
|
# Return a copy with the given overrides applied. The receiver is left
|
|
@@ -77,7 +82,8 @@ module RubyClaude
|
|
|
77
82
|
binary: binary, model: model, cwd: cwd, timeout: timeout,
|
|
78
83
|
use_subscription: use_subscription, append_system_prompt: append_system_prompt,
|
|
79
84
|
allowed_tools: allowed_tools, disallowed_tools: disallowed_tools,
|
|
80
|
-
add_dirs: add_dirs, permission_mode: permission_mode, max_turns: max_turns
|
|
85
|
+
add_dirs: add_dirs, permission_mode: permission_mode, max_turns: max_turns,
|
|
86
|
+
effort: effort
|
|
81
87
|
}
|
|
82
88
|
end
|
|
83
89
|
|
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.2
|
|
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: []
|