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 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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeConsole
4
+ class Railtie < Rails::Railtie
5
+ console do
6
+ require "irb"
7
+ require "irb/command"
8
+ require "claude_console/command"
9
+ IRB::Command.register(:claude, ClaudeConsole::Command)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeConsole
4
+ VERSION = "0.1.0"
5
+ 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: []