claude_console 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/LICENSE.txt +21 -0
- data/README.md +68 -0
- data/lib/claude_console/command.rb +108 -0
- data/lib/claude_console/railtie.rb +12 -0
- data/lib/claude_console/version.rb +5 -0
- data/lib/claude_console.rb +98 -0
- metadata +48 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 37eabc42f77f003757451cc1980f8f36aa8fc79c32f6e8c04dca9db9e3341377
|
|
4
|
+
data.tar.gz: 95581b568ae328d64a0bc889208d7a50a57255caeb44e4a369df47f7f887ae9a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d67329012c2c49d4c7a03c359f32b42cea18f766a76fe529595061c7b31faaa60083f013a70a1745e32838195bb262a075c29f4fa22888f09aedd7581a1e81b8
|
|
7
|
+
data.tar.gz: 023db747e69d21c936b103d18a319cb61567f28a9359379b15e596bd9606616ec8dfcf71f18e4600c22c7091f140d7e7dfb9cd06a65c2cbe3a3d9d913fd4b5e6
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bruno Bornsztein
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Claude Console
|
|
2
|
+
|
|
3
|
+
Pair with [Claude Code](https://claude.ai/claude-code) in your Rails console. No quoting needed.
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
claude help me debug this user's deliverables
|
|
7
|
+
claude write a scope to find users with expired trials
|
|
8
|
+
claude why does User.find(42) have no campaigns
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Claude runs Ruby code directly in your console session and sees the results — just like pair programming.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "claude_console", group: :development
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires [Claude Code](https://claude.ai/claude-code) installed and authenticated on the machine.
|
|
22
|
+
|
|
23
|
+
## How It Works
|
|
24
|
+
|
|
25
|
+
Open `rails console` and type `claude` followed by your request:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
irb(main):001> claude find the user with email "foo@bar.com" and show their campaigns
|
|
29
|
+
Asking Claude...
|
|
30
|
+
|
|
31
|
+
Here's what I found:
|
|
32
|
+
|
|
33
|
+
>> Running:
|
|
34
|
+
user = User.find_by(email: "foo@bar.com")
|
|
35
|
+
|
|
36
|
+
=> #<User id: 42, email: "foo@bar.com">
|
|
37
|
+
|
|
38
|
+
>> Running:
|
|
39
|
+
pp user.campaigns.select(:id, :name, :status)
|
|
40
|
+
|
|
41
|
+
[#<Campaign id: 1, name: "Summer 2026", status: "active">,
|
|
42
|
+
#<Campaign id: 2, name: "Fall Launch", status: "draft">]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Claude sees your console history for context, runs code in your session, and can iterate on results.
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# config/initializers/claude_console.rb
|
|
51
|
+
|
|
52
|
+
# Custom system prompt
|
|
53
|
+
ClaudeConsole.system_prompt = "You are a database expert..."
|
|
54
|
+
|
|
55
|
+
# Path to claude binary
|
|
56
|
+
ClaudeConsole.cli_path = "/usr/local/bin/claude"
|
|
57
|
+
|
|
58
|
+
# Additional env vars to clear (for auth isolation)
|
|
59
|
+
ClaudeConsole.clear_env_vars = ["MY_CUSTOM_API_KEY"]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## How Auth Works
|
|
63
|
+
|
|
64
|
+
Claude Console shells out to `claude -p`. It clears env vars like `ANTHROPIC_API_KEY` and `CLAUDE_CONFIG_DIR` from the subprocess so Claude Code uses its own stored credentials — not keys from your Rails environment.
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module ClaudeConsole
|
|
7
|
+
class Command < IRB::Command::Base
|
|
8
|
+
category "Claude"
|
|
9
|
+
description "Pair with Claude in the console (no quotes needed)"
|
|
10
|
+
help_message <<~HELP
|
|
11
|
+
Usage: claude <your prompt here>
|
|
12
|
+
|
|
13
|
+
Claude can run Ruby code in your session and see the results.
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
claude write a function to fix up this user's deliverables
|
|
17
|
+
claude why does User.find(42) have no campaigns
|
|
18
|
+
claude help me debug the email delivery for this tenant
|
|
19
|
+
HELP
|
|
20
|
+
|
|
21
|
+
def execute(arg)
|
|
22
|
+
prompt = arg.to_s.strip
|
|
23
|
+
if prompt.empty?
|
|
24
|
+
puts "Usage: claude <your prompt here>"
|
|
25
|
+
puts "Example: claude help me debug this user's deliverables"
|
|
26
|
+
return
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
workspace_binding = irb_context.workspace.binding
|
|
30
|
+
|
|
31
|
+
full_prompt = String.new
|
|
32
|
+
context_lines = build_context
|
|
33
|
+
full_prompt << "## Console context\n#{context_lines}\n\n" unless context_lines.empty?
|
|
34
|
+
full_prompt << "## User request\n#{prompt}"
|
|
35
|
+
|
|
36
|
+
puts "Asking Claude..."
|
|
37
|
+
puts
|
|
38
|
+
|
|
39
|
+
stdout, stderr, status = Open3.capture3(
|
|
40
|
+
ClaudeConsole.clean_env,
|
|
41
|
+
*ClaudeConsole.command,
|
|
42
|
+
full_prompt
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
unless status.success?
|
|
46
|
+
warn "Claude error (exit #{status.exitstatus}):"
|
|
47
|
+
warn stderr unless stderr.empty?
|
|
48
|
+
warn stdout unless stdout.empty?
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
text = parse_response(stdout)
|
|
53
|
+
return if text.nil?
|
|
54
|
+
|
|
55
|
+
process_response(text, workspace_binding)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def parse_response(stdout)
|
|
61
|
+
data = JSON.parse(stdout)
|
|
62
|
+
ClaudeConsole.session_id = data["session_id"] if data["session_id"]
|
|
63
|
+
data["result"].to_s
|
|
64
|
+
rescue JSON::ParserError
|
|
65
|
+
warn "Failed to parse Claude response"
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def process_response(response, workspace_binding)
|
|
70
|
+
parts = response.split(/(```ruby\n.*?```)/m)
|
|
71
|
+
|
|
72
|
+
parts.each do |part|
|
|
73
|
+
if part.start_with?("```ruby\n")
|
|
74
|
+
code = part.sub("```ruby\n", "").sub(/\n?```\z/, "")
|
|
75
|
+
puts "\e[36m>> Running:\e[0m"
|
|
76
|
+
puts code.gsub(/^/, " ")
|
|
77
|
+
puts
|
|
78
|
+
begin
|
|
79
|
+
result = workspace_binding.eval(code)
|
|
80
|
+
unless result.nil?
|
|
81
|
+
puts "=> #{result.inspect}"
|
|
82
|
+
puts
|
|
83
|
+
end
|
|
84
|
+
rescue => e
|
|
85
|
+
warn "\e[31mError: #{e.class}: #{e.message}\e[0m"
|
|
86
|
+
puts
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
print part unless part.strip.empty?
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def build_context
|
|
95
|
+
lines = []
|
|
96
|
+
|
|
97
|
+
if defined?(Reline::HISTORY) && Reline::HISTORY.respond_to?(:to_a)
|
|
98
|
+
history = Reline::HISTORY.to_a.last(20)
|
|
99
|
+
unless history.empty?
|
|
100
|
+
lines << "Recent console history:"
|
|
101
|
+
history.each { |h| lines << " >> #{h}" }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
lines.join("\n")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "claude_console/version"
|
|
4
|
+
|
|
5
|
+
require_relative "claude_console/railtie" if defined?(Rails)
|
|
6
|
+
|
|
7
|
+
module ClaudeConsole
|
|
8
|
+
SYSTEM_PROMPT = <<~PROMPT
|
|
9
|
+
You are a senior Rails developer pair-programming inside a live Rails console (IRB).
|
|
10
|
+
You have full access to the application's models, database, and environment.
|
|
11
|
+
|
|
12
|
+
When the user asks you something:
|
|
13
|
+
1. Think about what information you need
|
|
14
|
+
2. Write Ruby code to investigate or solve the problem
|
|
15
|
+
3. Put ALL executable Ruby code in ```ruby fenced code blocks
|
|
16
|
+
4. The code will be eval'd in the console and you'll see the output
|
|
17
|
+
|
|
18
|
+
Keep code blocks focused — one logical step per block so results are clear.
|
|
19
|
+
Use `pp` or `puts` for readable output. Avoid long one-liners.
|
|
20
|
+
You can define methods, assign variables — they persist in the session.
|
|
21
|
+
PROMPT
|
|
22
|
+
|
|
23
|
+
def self.environment_context
|
|
24
|
+
lines = []
|
|
25
|
+
|
|
26
|
+
if defined?(Rails)
|
|
27
|
+
env = Rails.env
|
|
28
|
+
lines << "Rails environment: #{env}"
|
|
29
|
+
lines << "Rails version: #{Rails.version}"
|
|
30
|
+
lines << "Ruby version: #{RUBY_VERSION}"
|
|
31
|
+
lines << "Application: #{Rails.application.class.module_parent_name}" if Rails.application
|
|
32
|
+
|
|
33
|
+
if env.production?
|
|
34
|
+
lines << ""
|
|
35
|
+
lines << "⚠️ PRODUCTION ENVIRONMENT — Exercise extreme caution:"
|
|
36
|
+
lines << " - DO NOT run destructive operations (delete, destroy, update_all) without explicit user confirmation"
|
|
37
|
+
lines << " - Prefer read-only queries unless the user specifically asks to mutate data"
|
|
38
|
+
lines << " - Always scope writes to the smallest possible dataset"
|
|
39
|
+
lines << " - Show the records that would be affected BEFORE making changes"
|
|
40
|
+
elsif env.staging?
|
|
41
|
+
lines << ""
|
|
42
|
+
lines << "⚠️ STAGING ENVIRONMENT — Be careful with data mutations, this may mirror production."
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if defined?(ActiveRecord::Base)
|
|
46
|
+
db_config = ActiveRecord::Base.connection_db_config
|
|
47
|
+
lines << "Database: #{db_config.adapter}#{db_config.database ? " (#{db_config.database})" : ""}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
lines.join("\n")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
attr_accessor :system_prompt, :session_id
|
|
56
|
+
attr_writer :cli_path
|
|
57
|
+
|
|
58
|
+
# Env vars to clear so claude uses its own stored auth
|
|
59
|
+
def clean_env
|
|
60
|
+
env = {}
|
|
61
|
+
%w[
|
|
62
|
+
ANTHROPIC_API_KEY
|
|
63
|
+
CLAUDE_API_KEY
|
|
64
|
+
CLAUDE_CODE_API_KEY
|
|
65
|
+
CLAUDE_CONFIG_DIR
|
|
66
|
+
].each { |k| env[k] = nil }
|
|
67
|
+
Array(clear_env_vars).each { |k| env[k] = nil }
|
|
68
|
+
env
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Additional env vars to clear
|
|
72
|
+
attr_accessor :clear_env_vars
|
|
73
|
+
|
|
74
|
+
def cli_path
|
|
75
|
+
@cli_path || "claude"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def command
|
|
79
|
+
cmd = [cli_path, "-p"]
|
|
80
|
+
|
|
81
|
+
if session_id
|
|
82
|
+
cmd.push("--resume", session_id)
|
|
83
|
+
else
|
|
84
|
+
prompt = system_prompt || SYSTEM_PROMPT
|
|
85
|
+
env_ctx = environment_context
|
|
86
|
+
prompt = "#{prompt}\n## Environment\n#{env_ctx}" unless env_ctx.empty?
|
|
87
|
+
cmd.push("--system-prompt", prompt)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
cmd.push("--output-format", "json")
|
|
91
|
+
cmd
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def reset_session!
|
|
95
|
+
@session_id = nil
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: claude_console
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Bruno Bornsztein
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-02-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- LICENSE.txt
|
|
20
|
+
- README.md
|
|
21
|
+
- lib/claude_console.rb
|
|
22
|
+
- lib/claude_console/command.rb
|
|
23
|
+
- lib/claude_console/railtie.rb
|
|
24
|
+
- lib/claude_console/version.rb
|
|
25
|
+
homepage: https://github.com/bborn/claude_console
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
metadata: {}
|
|
29
|
+
post_install_message:
|
|
30
|
+
rdoc_options: []
|
|
31
|
+
require_paths:
|
|
32
|
+
- lib
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '3.1'
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
requirements: []
|
|
44
|
+
rubygems_version: 3.5.22
|
|
45
|
+
signing_key:
|
|
46
|
+
specification_version: 4
|
|
47
|
+
summary: Pair with Claude Code in your Rails console
|
|
48
|
+
test_files: []
|