Ruby Claude
A tiny, zero-dependency Ruby SDK for Claude that shells out to the Claude Code
CLI (claude -p) and authenticates with your Claude Pro/Max subscription
instead of an API key.
Unofficial community gem — not affiliated with Anthropic. It uses the supported
claude -pheadless mode within your subscription's rate limits; no OAuth-token handling and no direct API calls.
Subscription, not API key
claude -p uses whatever the CLI is logged in with — if that's a subscription,
calls draw on it with no API billing. Ruby Claude strips ANTHROPIC_API_KEY
from the child environment by default so the CLI can't silently fall back to
API billing; set use_subscription = false to opt back in.
Prerequisites
claude must be installed and logged in (this gem drives it, it doesn't replace it):
npm install -g @anthropic-ai/claude-code
claude # run /login once and choose the subscription option
Install
gem "ruby_claude" # in your Gemfile
…or gem install ruby_claude. Requires Ruby 3.2+; zero runtime dependencies.
Quickstart
require "ruby_claude"
puts RubyClaude.query("Summarize lib/foo.rb in two sentences")
Usage
# A configured client
client = RubyClaude::Client.new(model: "claude-sonnet-4-6",
allowed_tools: ["Read", "Grep"], timeout: 180)
res = client.query("What does this project do?")
res.text # final text (Response#to_s returns it too, so `puts res` works)
res.session_id # String
res.cost_usd # Float (often 0.0 on a subscription)
res.usage # Hash — also: res.num_turns, res.duration_ms, res.error?, res.raw
# Streaming — yields typed events, returns the final Response
client.stream("Write a haiku about Ruby") do |event|
print event.text if event.type == :assistant
end
# Multi-turn session — resumes the underlying session_id automatically
chat = client.session
chat.query("My favorite number is 7.")
puts chat.query("What's my favorite number?") # => "...7..."
# Global defaults for RubyClaude.query and new clients
RubyClaude.configure { |c| c.model = "claude-sonnet-4-6"; c.timeout = 300 }
A streaming Event#type is :system, :assistant, :user, or :result. Use
#query (alias #ask) — there is intentionally no #send.
Configuration
Client.new(**opts) overrides per instance; RubyClaude.configure sets globals.
| Option | Default | Maps to |
|---|---|---|
binary |
"claude" |
executable name/path |
model |
nil |
--model |
cwd |
Dir.pwd |
subprocess working directory |
timeout |
300 |
seconds before the child is killed |
use_subscription |
true |
strip ANTHROPIC_API_KEY from the child env |
append_system_prompt |
nil |
--append-system-prompt |
allowed_tools / disallowed_tools |
nil |
--allowedTools / --disallowedTools |
add_dirs |
[] |
--add-dir |
permission_mode |
nil |
--permission-mode |
max_turns |
nil |
--max-turns |
Errors
All subclass RubyClaude::Error: BinaryNotFoundError (no claude on PATH),
AuthenticationError (not logged in), TimeoutError, ExecutionError (non-zero
exit or an is_error result; carries #status/#stderr), and ParseError.
How it works
Command builds the argv + child env (pure, no I/O); Runner spawns claude
via Open3 (array form — no shell; prompt on stdin), enforces the timeout, and
parses output; Client builds Response/Event; Session resumes via
--resume. The runner is stateless per call, so a Client is safe to share
across threads.
Development
bundle exec rake # tests + lint (hermetic — never spawns the real claude)
bin/console # IRB with the gem loaded
Contributing or tracking upstream SDK changes? See
doc/DEVELOPMENT.md.
Building and publishing the gem
The version lives in lib/ruby_claude/version.rb.
Before a release, bump it following SemVer.
Build locally
gem build ruby_claude.gemspec # => ruby_claude-<version>.gem
gem install ./ruby_claude-<version>.gem # try the built gem locally
spec.files is derived from git ls-files, so only tracked files are
packaged — commit (or at least stage) your changes before building, or the gem
will be missing files. Bundler's gem tasks do the same and drop the artifact in
pkg/:
rake build # build into pkg/
rake install # build and install locally
Publish to RubyGems
- Create a RubyGems.org account and sign in once
(credentials are stored in
~/.gem/credentials):
gem signin
- Make sure the tree is green and committed:
rake # tests + lint
git status # nothing uncommitted
- Build and push:
gem build ruby_claude.gemspec
gem push ruby_claude-<version>.gem
Alternatively, do it all in one step with Bundler's release task, which builds
the gem, creates and pushes a v<version> git tag, and pushes to RubyGems
(requires a clean, committed tree):
rake release
The gemspec sets
rubygems_mfa_required, so enable MFA on your RubyGems account; pushes and yanks will then prompt for a one-time code.
License
BSD-3-Clause. See LICENSE.