claude_code 0.0.14

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.
data/USAGE_EXAMPLES.md ADDED
@@ -0,0 +1,428 @@
1
+ # Ruby Claude Code SDK - Usage Examples
2
+
3
+ ## Directory Structure
4
+
5
+ ```
6
+ ruby/
7
+ ├── lib/claude_code_sdk.rb # Main SDK entry point
8
+ ├── docs/ # Comprehensive documentation
9
+ ├── examples/ # Working code examples
10
+ ├── spec/ # RSpec tests
11
+ └── README.md # Quick start guide
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ### 1. From the ruby directory:
17
+
18
+ ```bash
19
+ cd ruby
20
+
21
+ # Install dependencies
22
+ bundle install
23
+
24
+ # Try basic examples
25
+ ruby examples/basic_usage.rb
26
+ ruby examples/streaming_examples.rb
27
+ ruby examples/mcp_examples.rb
28
+ ruby examples/conversation_resuming.rb
29
+ ruby examples/authentication_examples.rb
30
+ ruby examples/streaming_json_input.rb
31
+ ruby examples/jsonl_cli_equivalent.rb
32
+ ```
33
+
34
+ ### 2. In IRB/console:
35
+
36
+ ```ruby
37
+ # Load from ruby directory
38
+ require_relative 'lib/claude_code_sdk'
39
+
40
+ # Or load with helpers
41
+ require_relative 'examples/irb_helpers'
42
+
43
+ # Then use:
44
+ quick_claude("What is Ruby?")
45
+ stream_claude("Explain blocks")
46
+ ninja_test("Tell me about yourself")
47
+
48
+ # Conversation helpers:
49
+ continue_chat("Follow up question")
50
+ resume_chat("session-id", "New prompt")
51
+ save_session("session-id")
52
+ resume_last("Continue with last session")
53
+ ```
54
+
55
+ ### 3. In a Ruby project:
56
+
57
+ ```ruby
58
+ # Add to your project
59
+ require_relative 'path/to/ruby/lib/claude_code_sdk'
60
+
61
+ # Use normally
62
+ ClaudeCodeSDK.query(prompt: "Hello").each { |msg| puts msg }
63
+ ```
64
+
65
+ ## Rails + Sidekiq Streaming Example
66
+
67
+ Here's how to integrate the SDK with Rails and Sidekiq for real-time streaming:
68
+
69
+ ### 1. Gemfile
70
+
71
+ ```ruby
72
+ # Gemfile
73
+ gem 'sidekiq'
74
+ gem 'redis'
75
+
76
+ # Add the Claude SDK (assuming it's in your project)
77
+ # gem 'claude_code_sdk', path: 'vendor/claude-code-sdk-ruby'
78
+ ```
79
+
80
+ ### 2. Sidekiq Job
81
+
82
+ ```ruby
83
+ # app/jobs/claude_streaming_job.rb
84
+ class ClaudeStreamingJob
85
+ include Sidekiq::Job
86
+
87
+ def perform(user_id, query_id, prompt, options = {})
88
+ require_relative '../vendor/claude-code-sdk-ruby/lib/claude_code_sdk'
89
+
90
+ channel = "claude_stream_#{user_id}_#{query_id}"
91
+
92
+ # Parse options
93
+ claude_options = ClaudeCodeSDK::ClaudeCodeOptions.new(
94
+ model: options['model'],
95
+ max_turns: options['max_turns'] || 1,
96
+ system_prompt: options['system_prompt'],
97
+ allowed_tools: options['allowed_tools'] || []
98
+ )
99
+
100
+ # Stream Claude responses
101
+ message_count = 0
102
+ ClaudeCodeSDK.query(
103
+ prompt: prompt,
104
+ options: claude_options,
105
+ cli_path: ENV['CLAUDE_CLI_PATH'] || '/usr/local/bin/claude'
106
+ ).each do |message|
107
+ message_count += 1
108
+
109
+ # Broadcast each message via ActionCable
110
+ ActionCable.server.broadcast(channel, {
111
+ type: 'claude_message',
112
+ query_id: query_id,
113
+ message_index: message_count,
114
+ timestamp: Time.current.iso8601,
115
+ data: serialize_claude_message(message)
116
+ })
117
+
118
+ # Optional: Save to database
119
+ save_message_to_db(user_id, query_id, message_count, message)
120
+ end
121
+
122
+ # Broadcast completion
123
+ ActionCable.server.broadcast(channel, {
124
+ type: 'complete',
125
+ query_id: query_id,
126
+ total_messages: message_count,
127
+ timestamp: Time.current.iso8601
128
+ })
129
+ end
130
+
131
+ private
132
+
133
+ def serialize_claude_message(message)
134
+ case message
135
+ when ClaudeCodeSDK::SystemMessage
136
+ {
137
+ message_type: 'system',
138
+ subtype: message.subtype,
139
+ data: message.data
140
+ }
141
+ when ClaudeCodeSDK::AssistantMessage
142
+ {
143
+ message_type: 'assistant',
144
+ content: message.content.map { |block| serialize_content_block(block) }
145
+ }
146
+ when ClaudeCodeSDK::ResultMessage
147
+ {
148
+ message_type: 'result',
149
+ subtype: message.subtype,
150
+ duration_ms: message.duration_ms,
151
+ cost_usd: message.total_cost_usd,
152
+ session_id: message.session_id
153
+ }
154
+ end
155
+ end
156
+
157
+ def serialize_content_block(block)
158
+ case block
159
+ when ClaudeCodeSDK::TextBlock
160
+ { type: 'text', text: block.text }
161
+ when ClaudeCodeSDK::ToolUseBlock
162
+ { type: 'tool_use', id: block.id, name: block.name, input: block.input }
163
+ when ClaudeCodeSDK::ToolResultBlock
164
+ { type: 'tool_result', tool_use_id: block.tool_use_id, content: block.content, is_error: block.is_error }
165
+ end
166
+ end
167
+
168
+ def save_message_to_db(user_id, query_id, message_index, message)
169
+ # Example database save
170
+ # ClaudeMessage.create!(
171
+ # user_id: user_id,
172
+ # query_id: query_id,
173
+ # message_index: message_index,
174
+ # message_data: serialize_claude_message(message),
175
+ # created_at: Time.current
176
+ # )
177
+ end
178
+ end
179
+ ```
180
+
181
+ ### 3. ActionCable Channel
182
+
183
+ ```ruby
184
+ # app/channels/claude_stream_channel.rb
185
+ class ClaudeStreamChannel < ApplicationCable::Channel
186
+ def subscribed
187
+ # Authorize user access
188
+ if params[:user_id].to_i == current_user.id
189
+ stream_from "claude_stream_#{params[:user_id]}_#{params[:query_id]}"
190
+ else
191
+ reject
192
+ end
193
+ end
194
+
195
+ def unsubscribed
196
+ # Cleanup when channel is unsubscribed
197
+ end
198
+ end
199
+ ```
200
+
201
+ ### 4. Controller
202
+
203
+ ```ruby
204
+ # app/controllers/claude_controller.rb
205
+ class ClaudeController < ApplicationController
206
+ before_action :authenticate_user!
207
+
208
+ def create_stream_query
209
+ query_id = SecureRandom.uuid
210
+
211
+ # Validate input
212
+ prompt = params.require(:prompt)
213
+ options = {
214
+ 'model' => params[:model],
215
+ 'max_turns' => params[:max_turns]&.to_i,
216
+ 'system_prompt' => params[:system_prompt],
217
+ 'allowed_tools' => params[:allowed_tools] || []
218
+ }
219
+
220
+ # Start background job
221
+ ClaudeStreamingJob.perform_async(
222
+ current_user.id,
223
+ query_id,
224
+ prompt,
225
+ options
226
+ )
227
+
228
+ render json: {
229
+ query_id: query_id,
230
+ channel: "claude_stream_#{current_user.id}_#{query_id}",
231
+ status: 'started'
232
+ }
233
+ end
234
+ end
235
+ ```
236
+
237
+ ### 5. Frontend JavaScript
238
+
239
+ ```javascript
240
+ // app/javascript/claude_streaming.js
241
+ class ClaudeStreaming {
242
+ constructor(userId) {
243
+ this.userId = userId;
244
+ this.activeSubscriptions = new Map();
245
+ }
246
+
247
+ startQuery(prompt, options = {}) {
248
+ return fetch('/claude/stream_query', {
249
+ method: 'POST',
250
+ headers: {
251
+ 'Content-Type': 'application/json',
252
+ 'X-CSRF-Token': document.querySelector('[name="csrf-token"]').content
253
+ },
254
+ body: JSON.stringify({
255
+ prompt: prompt,
256
+ model: options.model,
257
+ max_turns: options.maxTurns,
258
+ system_prompt: options.systemPrompt,
259
+ allowed_tools: options.allowedTools
260
+ })
261
+ })
262
+ .then(response => response.json())
263
+ .then(data => {
264
+ this.subscribeToQuery(data.query_id, options.onMessage, options.onComplete);
265
+ return data;
266
+ });
267
+ }
268
+
269
+ subscribeToQuery(queryId, onMessage, onComplete) {
270
+ const subscription = App.cable.subscriptions.create(
271
+ {
272
+ channel: "ClaudeStreamChannel",
273
+ user_id: this.userId,
274
+ query_id: queryId
275
+ },
276
+ {
277
+ received: (data) => {
278
+ switch(data.type) {
279
+ case 'claude_message':
280
+ this.handleClaudeMessage(data, onMessage);
281
+ break;
282
+ case 'complete':
283
+ this.handleComplete(data, onComplete);
284
+ this.unsubscribeFromQuery(queryId);
285
+ break;
286
+ }
287
+ }
288
+ }
289
+ );
290
+
291
+ this.activeSubscriptions.set(queryId, subscription);
292
+ }
293
+
294
+ handleClaudeMessage(data, onMessage) {
295
+ const message = data.data;
296
+
297
+ if (message.message_type === 'assistant') {
298
+ message.content.forEach(block => {
299
+ if (block.type === 'text') {
300
+ onMessage({
301
+ type: 'text',
302
+ content: block.text,
303
+ timestamp: data.timestamp
304
+ });
305
+ } else if (block.type === 'tool_use') {
306
+ onMessage({
307
+ type: 'tool_use',
308
+ tool: block.name,
309
+ input: block.input,
310
+ timestamp: data.timestamp
311
+ });
312
+ }
313
+ });
314
+ } else if (message.message_type === 'result') {
315
+ onMessage({
316
+ type: 'result',
317
+ cost: message.cost_usd,
318
+ duration: message.duration_ms,
319
+ timestamp: data.timestamp
320
+ });
321
+ }
322
+ }
323
+
324
+ handleComplete(data, onComplete) {
325
+ if (onComplete) {
326
+ onComplete({
327
+ queryId: data.query_id,
328
+ totalMessages: data.total_messages,
329
+ timestamp: data.timestamp
330
+ });
331
+ }
332
+ }
333
+
334
+ unsubscribeFromQuery(queryId) {
335
+ const subscription = this.activeSubscriptions.get(queryId);
336
+ if (subscription) {
337
+ subscription.unsubscribe();
338
+ this.activeSubscriptions.delete(queryId);
339
+ }
340
+ }
341
+ }
342
+
343
+ // Usage
344
+ const claudeStreaming = new ClaudeStreaming(currentUserId);
345
+
346
+ claudeStreaming.startQuery("Explain Ruby on Rails", {
347
+ model: "sonnet",
348
+ maxTurns: 1,
349
+ onMessage: (message) => {
350
+ console.log('Received:', message);
351
+ if (message.type === 'text') {
352
+ appendToChat(message.content);
353
+ } else if (message.type === 'tool_use') {
354
+ showToolUsage(message.tool, message.input);
355
+ }
356
+ },
357
+ onComplete: (summary) => {
358
+ console.log('Query completed:', summary);
359
+ showCompletionMessage(summary);
360
+ }
361
+ });
362
+ ```
363
+
364
+ ### 6. Environment Configuration
365
+
366
+ ```bash
367
+ # .env
368
+ CLAUDE_CLI_PATH=/usr/local/bin/claude
369
+ ANTHROPIC_API_KEY=your_api_key_here
370
+ REDIS_URL=redis://localhost:6379/0
371
+ ```
372
+
373
+ ### 7. Rails Routes
374
+
375
+ ```ruby
376
+ # config/routes.rb
377
+ Rails.application.routes.draw do
378
+ mount ActionCable.server => '/cable'
379
+
380
+ resources :claude, only: [] do
381
+ collection do
382
+ post 'stream_query'
383
+ end
384
+ end
385
+ end
386
+ ```
387
+
388
+ ## Usage in Rails Console
389
+
390
+ ```ruby
391
+ # Start a streaming query
392
+ ClaudeStreamingJob.perform_async(
393
+ 1, # user_id
394
+ SecureRandom.uuid, # query_id
395
+ "Explain Ruby metaprogramming", # prompt
396
+ {
397
+ 'model' => 'sonnet',
398
+ 'max_turns' => 1,
399
+ 'system_prompt' => 'You are a Ruby expert'
400
+ }
401
+ )
402
+
403
+ # With MCP servers
404
+ ClaudeStreamingJob.perform_async(
405
+ 1,
406
+ SecureRandom.uuid,
407
+ "Use the about tool",
408
+ {
409
+ 'model' => 'sonnet',
410
+ 'allowed_tools' => ['mcp__ninja__about'],
411
+ 'mcp_servers' => {
412
+ 'ninja' => 'https://mcp-creator-ninja-v1-4-0.mcp.soy/'
413
+ }
414
+ }
415
+ )
416
+ ```
417
+
418
+ This setup provides:
419
+ - ✅ Real-time streaming via WebSockets
420
+ - ✅ Background processing with Sidekiq
421
+ - ✅ Error handling and retry logic
422
+ - ✅ Message persistence (optional)
423
+ - ✅ User authentication and authorization
424
+ - ✅ Frontend integration with ActionCable
425
+ - ✅ MCP server support
426
+ - ✅ Cost tracking and monitoring
427
+
428
+ The streaming happens in real-time, so users see Claude's responses as they're generated, providing an excellent user experience for AI-powered Rails applications.
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/claude_code/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'claude_code'
7
+ spec.version = ClaudeCode::VERSION
8
+ spec.authors = ['Anthropic']
9
+ spec.email = ['support@anthropic.com']
10
+
11
+ spec.summary = 'Ruby SDK for Claude Code with streaming support and ergonomic MCP integration'
12
+ spec.description = 'Official Ruby SDK for Claude Code. See the Claude Code SDK documentation for more information.'
13
+ spec.homepage = 'https://github.com/anthropics/claude-code-sdk-python'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.0.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+ spec.metadata['documentation_uri'] = 'https://docs.anthropic.com/en/docs/claude-code/sdk'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/anthropics/claude-code-sdk-python/blob/main/ruby/CHANGELOG.md'
21
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (File.expand_path(f) == __FILE__) ||
27
+ f.start_with?('bin/', 'test/', 'spec/', 'features/', '.git', '.github', 'appveyor', 'Gemfile')
28
+ end
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ # Runtime dependencies
35
+ spec.add_dependency 'json', '~> 2.0'
36
+
37
+ # Development dependencies
38
+ spec.add_development_dependency 'bundler', '~> 2.0'
39
+ spec.add_development_dependency 'rake', '~> 13.0'
40
+ spec.add_development_dependency 'rspec', '~> 3.0'
41
+ spec.add_development_dependency 'rubocop', '~> 1.50'
42
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.20'
43
+ spec.add_development_dependency 'rubocop-performance', '~> 1.0'
44
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
45
+ spec.add_development_dependency 'simplecov', '~> 0.22'
46
+ spec.add_development_dependency 'yard', '~> 0.9'
47
+ end