girb 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.
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "../session_history"
5
+
6
+ module Girb
7
+ module Tools
8
+ class SessionHistoryTool < Base
9
+ class << self
10
+ def name
11
+ "get_session_history"
12
+ end
13
+
14
+ def description
15
+ "Get IRB session history. Can retrieve specific lines, line ranges, method definitions, AI conversation details, or full history."
16
+ end
17
+
18
+ def parameters
19
+ {
20
+ type: "object",
21
+ properties: {
22
+ action: {
23
+ type: "string",
24
+ enum: %w[get_line get_range get_method list_methods full_history list_ai_conversations get_ai_detail],
25
+ description: "Action to perform: get_line (single line), get_range (line range), get_method (method source), list_methods (list defined methods), full_history (all history), list_ai_conversations (list AI Q&A), get_ai_detail (get AI response with reasoning)"
26
+ },
27
+ line: {
28
+ type: "integer",
29
+ description: "Line number for get_line action"
30
+ },
31
+ start_line: {
32
+ type: "integer",
33
+ description: "Start line for get_range action"
34
+ },
35
+ end_line: {
36
+ type: "integer",
37
+ description: "End line for get_range action"
38
+ },
39
+ method_name: {
40
+ type: "string",
41
+ description: "Method name for get_method action"
42
+ }
43
+ },
44
+ required: ["action"]
45
+ }
46
+ end
47
+ end
48
+
49
+ def execute(_binding, action:, line: nil, start_line: nil, end_line: nil, method_name: nil)
50
+ case action
51
+ when "get_line"
52
+ get_single_line(line)
53
+ when "get_range"
54
+ get_line_range(start_line, end_line)
55
+ when "get_method"
56
+ get_method_source(method_name)
57
+ when "list_methods"
58
+ list_defined_methods
59
+ when "full_history"
60
+ get_full_history
61
+ when "list_ai_conversations"
62
+ list_ai_conversations
63
+ when "get_ai_detail"
64
+ get_ai_detail(line)
65
+ else
66
+ { error: "Unknown action: #{action}" }
67
+ end
68
+ rescue StandardError => e
69
+ { error: "#{e.class}: #{e.message}" }
70
+ end
71
+
72
+ private
73
+
74
+ def get_single_line(line)
75
+ return { error: "line parameter is required" } unless line
76
+
77
+ entry = SessionHistory.find_by_line(line)
78
+ if entry
79
+ {
80
+ line: entry.line_no,
81
+ code: entry.code,
82
+ method_definition: entry.method_definition&.name
83
+ }
84
+ else
85
+ { error: "Line #{line} not found in session history" }
86
+ end
87
+ end
88
+
89
+ def get_line_range(start_line, end_line)
90
+ return { error: "start_line and end_line parameters are required" } unless start_line && end_line
91
+
92
+ entries = SessionHistory.find_by_line_range(start_line, end_line)
93
+ if entries.any?
94
+ {
95
+ range: "#{start_line}-#{end_line}",
96
+ entries: entries.map { |e| { line: e.line_no, code: e.code } }
97
+ }
98
+ else
99
+ { error: "No entries found in range #{start_line}-#{end_line}" }
100
+ end
101
+ end
102
+
103
+ def get_method_source(method_name)
104
+ return { error: "method_name parameter is required" } unless method_name
105
+
106
+ method_def = SessionHistory.find_method(method_name)
107
+ if method_def
108
+ {
109
+ method_name: method_def.name,
110
+ start_line: method_def.start_line,
111
+ end_line: method_def.end_line,
112
+ source: method_def.code
113
+ }
114
+ else
115
+ { error: "Method '#{method_name}' not found in session history" }
116
+ end
117
+ end
118
+
119
+ def list_defined_methods
120
+ methods = SessionHistory.method_definitions
121
+ if methods.any?
122
+ {
123
+ count: methods.size,
124
+ methods: methods.map do |m|
125
+ {
126
+ name: m.name,
127
+ lines: "#{m.start_line}-#{m.end_line}"
128
+ }
129
+ end
130
+ }
131
+ else
132
+ { message: "No methods defined in this session" }
133
+ end
134
+ end
135
+
136
+ def get_full_history
137
+ history = SessionHistory.all_with_line_numbers
138
+ if history.any?
139
+ {
140
+ count: history.size,
141
+ history: history
142
+ }
143
+ else
144
+ { message: "No history in this session" }
145
+ end
146
+ end
147
+
148
+ def list_ai_conversations
149
+ conversations = SessionHistory.ai_conversations
150
+ if conversations.any?
151
+ {
152
+ count: conversations.size,
153
+ conversations: conversations.map do |c|
154
+ {
155
+ line: c[:line_no],
156
+ question: c[:question],
157
+ response_preview: c[:response][0, 200] + (c[:response].length > 200 ? "..." : "")
158
+ }
159
+ end
160
+ }
161
+ else
162
+ { message: "No AI conversations in this session" }
163
+ end
164
+ end
165
+
166
+ def get_ai_detail(line)
167
+ return { error: "line parameter is required" } unless line
168
+
169
+ detail = SessionHistory.get_ai_detail(line)
170
+ if detail
171
+ {
172
+ line: detail[:line_no],
173
+ question: detail[:question],
174
+ response: detail[:response],
175
+ reasoning: detail[:reasoning]
176
+ }
177
+ else
178
+ { error: "No AI conversation found at line #{line}" }
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
data/lib/girb/tools.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tools/base"
4
+ require_relative "tools/inspect_object"
5
+ require_relative "tools/get_source"
6
+ require_relative "tools/list_methods"
7
+ require_relative "tools/evaluate_code"
8
+ require_relative "tools/read_file"
9
+ require_relative "tools/find_file"
10
+ require_relative "tools/session_history_tool"
11
+
12
+ module Girb
13
+ module Tools
14
+ CORE_TOOLS = [InspectObject, GetSource, ListMethods, EvaluateCode, ReadFile, FindFile, SessionHistoryTool].freeze
15
+
16
+ class << self
17
+ def available_tools
18
+ tools = CORE_TOOLS.dup
19
+
20
+ # Rails tools are loaded conditionally
21
+ if defined?(ActiveRecord::Base)
22
+ require_relative "tools/rails_tools"
23
+ tools << RailsModelInfo
24
+ end
25
+
26
+ tools.select { |t| !t.respond_to?(:available?) || t.available? }
27
+ end
28
+
29
+ def find_tool(name)
30
+ available_tools.find { |t| t.tool_name == name }
31
+ end
32
+
33
+ def to_gemini_tools
34
+ available_tools.map(&:to_gemini_tool)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Girb
4
+ VERSION = "0.1.0"
5
+ end
data/lib/girb.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "girb/version"
4
+ require_relative "girb/configuration"
5
+ require_relative "girb/providers/base"
6
+ require_relative "girb/exception_capture"
7
+ require_relative "girb/context_builder"
8
+ require_relative "girb/prompt_builder"
9
+ require_relative "girb/conversation_history"
10
+ require_relative "girb/tools"
11
+ require_relative "girb/ai_client"
12
+
13
+ module Girb
14
+ class Error < StandardError; end
15
+ class ConfigurationError < Error; end
16
+ class ApiError < Error; end
17
+
18
+ class << self
19
+ attr_accessor :configuration
20
+
21
+ def configure
22
+ self.configuration ||= Configuration.new
23
+ yield(configuration) if block_given?
24
+ configuration
25
+ end
26
+
27
+ def setup!
28
+ configure unless configuration
29
+ require_relative "girb/irb_integration"
30
+ IrbIntegration.setup
31
+ end
32
+ end
33
+ end
34
+
35
+ # IRB がロードされていたら自動で組み込む
36
+ if defined?(IRB)
37
+ Girb.setup!
38
+ end
39
+
40
+ # binding.girb サポート
41
+ class Binding
42
+ def girb
43
+ require "irb"
44
+ Girb.setup! unless defined?(IRB::Command::Qq)
45
+
46
+ # IRB.start with this binding
47
+ IRB.setup(source_location[0], argv: [])
48
+ workspace = IRB::WorkSpace.new(self)
49
+ irb = IRB::Irb.new(workspace)
50
+ IRB.conf[:MAIN_CONTEXT] = irb.context
51
+ irb.run(IRB.conf)
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "irb/command"
4
+
5
+ module IRB
6
+ module Command
7
+ class Qq < Base
8
+ category "AI Assistant"
9
+ description "Ask AI a question about your code"
10
+
11
+ def execute(question)
12
+ question = question.to_s.strip
13
+ if question.empty?
14
+ puts "[girb] Usage: qq \"your question here\""
15
+ return
16
+ end
17
+
18
+ unless Girb.configuration&.gemini_api_key
19
+ puts "[girb] Error: GEMINI_API_KEY not set"
20
+ puts "[girb] Run: export GEMINI_API_KEY=your-key"
21
+ puts "[girb] Or configure in your .irbrc:"
22
+ puts "[girb] Girb.configure { |c| c.gemini_api_key = 'your-key' }"
23
+ return
24
+ end
25
+
26
+ current_binding = irb_context.workspace.binding
27
+
28
+ # AI質問を履歴に記録
29
+ line_no = irb_context.line_no rescue 0
30
+ Girb::SessionHistory.record(line_no, question, is_ai_question: true)
31
+
32
+ context = Girb::ContextBuilder.new(
33
+ current_binding,
34
+ irb_context
35
+ ).build
36
+
37
+ if Girb.configuration.debug
38
+ puts "[girb] Context collected:"
39
+ require "yaml"
40
+ puts context.to_yaml
41
+ end
42
+
43
+ client = Girb::AiClient.new
44
+ client.ask(question, context, binding: current_binding, line_no: line_no)
45
+ rescue Girb::Error => e
46
+ puts "[girb] Error: #{e.message}"
47
+ rescue StandardError => e
48
+ puts "[girb] Error: #{e.message}"
49
+ puts e.backtrace.first(5).join("\n") if Girb.configuration&.debug
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ IRB::Command.register(:qq, IRB::Command::Qq)
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: girb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - rira100000000
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: irb
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 1.6.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 1.6.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '13.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: webmock
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ description: Ask questions in IRB and get AI-powered answers based on your runtime
69
+ context. Access local variables, exception info, and Rails model data while debugging.
70
+ Requires an LLM provider gem (e.g., girb-ruby_llm) or custom provider implementation.
71
+ email:
72
+ - 101010hayakawa@gmail.com
73
+ executables:
74
+ - girb
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".rspec"
79
+ - CHANGELOG.md
80
+ - LICENSE
81
+ - README.md
82
+ - README_ja.md
83
+ - Rakefile
84
+ - exe/girb
85
+ - lib/girb.rb
86
+ - lib/girb/ai_client.rb
87
+ - lib/girb/configuration.rb
88
+ - lib/girb/context_builder.rb
89
+ - lib/girb/conversation_history.rb
90
+ - lib/girb/exception_capture.rb
91
+ - lib/girb/irb_integration.rb
92
+ - lib/girb/prompt_builder.rb
93
+ - lib/girb/providers/base.rb
94
+ - lib/girb/session_history.rb
95
+ - lib/girb/tools.rb
96
+ - lib/girb/tools/base.rb
97
+ - lib/girb/tools/evaluate_code.rb
98
+ - lib/girb/tools/find_file.rb
99
+ - lib/girb/tools/get_source.rb
100
+ - lib/girb/tools/inspect_object.rb
101
+ - lib/girb/tools/list_methods.rb
102
+ - lib/girb/tools/rails_tools.rb
103
+ - lib/girb/tools/read_file.rb
104
+ - lib/girb/tools/session_history_tool.rb
105
+ - lib/girb/version.rb
106
+ - lib/irb/command/qq.rb
107
+ homepage: https://github.com/rira100000000/girb
108
+ licenses:
109
+ - MIT
110
+ metadata:
111
+ homepage_uri: https://github.com/rira100000000/girb
112
+ source_code_uri: https://github.com/rira100000000/girb
113
+ changelog_uri: https://github.com/rira100000000/girb/blob/main/CHANGELOG.md
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 3.2.0
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.7.2
129
+ specification_version: 4
130
+ summary: AI-powered IRB assistant
131
+ test_files: []