girb 0.3.0 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3fb74de93bd206f42859682f712f57bf0603c2348e665775b6c19833af65fd1f
4
- data.tar.gz: af6f430afff9ca6123bddfc4a87820956cbff3fda65721d86f39f5ad688cdea5
3
+ metadata.gz: cdae8b7956a4924603ee3edfcebdf3835ed6d994037847eed0d432d078dec400
4
+ data.tar.gz: 270cf95c4e39728de8cfb5ca5153139951c94b4f94d9255d250d6a47e527813e
5
5
  SHA512:
6
- metadata.gz: 8998f2942d8cf0ed23012c65dbede91b88231a0e26e31ed2b808c8cc513dc69bf162fb709320768880f59536ab5a5e1031fac0f8ac5cfdb096281d0abcae3b49
7
- data.tar.gz: 1fda60427ec07042e05d2840f4ef99ba0740552e3735b10012d46c990ba62deeb653c21fc19c5653ae03e60150af9083fa3869deace3c7b3e0780632a0cbaa59
6
+ metadata.gz: ea1a35c4aaf70ce1a4a8a28ad9a1b12fc33ef8b15e01e257ac9a94b2cbbef60d290e0d6aff426bc95b88da9257c1f0b6d76549be350bfe92bb81bd3398e349c8
7
+ data.tar.gz: 4c0738e997b5796c26d331c38ec60b782186c41803f1858ec1553d7a577d226ec38d3a794f1bfef008b8aa1a5c452494428d4bf55ad38b08c123fcf144e9b723
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.2] - 2026-02-08
4
+
5
+ ### Added
6
+
7
+ - Show real-time progress feedback during tool execution (AI now explains each step as it works)
8
+ - Generic `metadata` field for tool calls to support provider-specific data pass-through (e.g. Gemini 3 `thought_signature`)
9
+
10
+ ### Changed
11
+
12
+ - Improved breakpoint tracking pattern: self-initializing `$tracked ||= []` in conditions to prevent nil errors
13
+
14
+ ### Fixed
15
+
16
+ - Fix process crash when pressing Ctrl+C during AI API call in debug mode
17
+ - SIGINT now sets interrupt flag instead of propagating to main thread's `Queue.pop`
18
+ - Pending debug commands are properly discarded on interrupt
19
+ - Original SIGINT handler is always restored via `ensure` block
20
+ - Save session history on every AI turn instead of only at exit, preventing data loss on crash or unexpected exit
21
+
3
22
  ## [0.3.0] - 2026-02-07
4
23
 
5
24
  ### Added
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "auto_continue"
4
4
  require_relative "conversation_history"
5
+ require_relative "session_persistence"
5
6
  require_relative "providers/base"
6
7
  require_relative "debug_context_builder"
7
8
  require_relative "debug_prompt_builder"
@@ -88,6 +89,9 @@ module Girb
88
89
  Girb::AutoContinue.clear_interrupt!
89
90
  end
90
91
  end
92
+ ensure
93
+ # 毎ターン会話履歴を保存(クラッシュやexitでの消失を防止)
94
+ SessionPersistence.save_session
91
95
  end
92
96
 
93
97
  private
@@ -177,9 +181,9 @@ module Girb
177
181
  end
178
182
 
179
183
  if response.function_call?
180
- # Accumulate text that comes with function calls
184
+ # Print progress text immediately (don't accumulate for re-display at the end)
181
185
  if response.text && !response.text.empty?
182
- accumulated_text << response.text
186
+ puts response.text
183
187
  end
184
188
 
185
189
  debug_command_called = false
@@ -201,7 +205,7 @@ module Girb
201
205
  result: result
202
206
  }
203
207
 
204
- ConversationHistory.add_tool_call(tool_name, tool_args, result, id: tool_id)
208
+ ConversationHistory.add_tool_call(tool_name, tool_args, result, id: tool_id, metadata: function_call[:metadata])
205
209
 
206
210
  if Girb.configuration.debug && result.is_a?(Hash) && result[:error]
207
211
  puts "[girb] Tool error: #{result[:error]}"
@@ -22,8 +22,8 @@ module Girb
22
22
  instance.add_assistant_message(content)
23
23
  end
24
24
 
25
- def add_tool_call(tool_name, args, result, id: nil)
26
- instance.add_tool_call(tool_name, args, result, id: id)
25
+ def add_tool_call(tool_name, args, result, id: nil, metadata: nil)
26
+ instance.add_tool_call(tool_name, args, result, id: id, metadata: metadata)
27
27
  end
28
28
 
29
29
  def to_contents
@@ -72,13 +72,14 @@ module Girb
72
72
  end
73
73
  end
74
74
 
75
- def add_tool_call(tool_name, args, result, id: nil)
75
+ def add_tool_call(tool_name, args, result, id: nil, metadata: nil)
76
76
  @pending_tool_calls << {
77
77
  id: id || "call_#{SecureRandom.hex(12)}",
78
78
  name: tool_name,
79
79
  args: args,
80
- result: result
81
- }
80
+ result: result,
81
+ metadata: metadata
82
+ }.compact
82
83
  end
83
84
 
84
85
  def clear!
@@ -106,14 +107,18 @@ module Girb
106
107
 
107
108
  # Add tool calls and results if present
108
109
  msg.tool_calls&.each do |tc|
109
- result << { role: :tool_call, id: tc[:id], name: tc[:name], args: tc[:args] }
110
+ tool_call = { role: :tool_call, id: tc[:id], name: tc[:name], args: tc[:args] }
111
+ tool_call[:metadata] = tc[:metadata] if tc[:metadata]
112
+ result << tool_call
110
113
  result << { role: :tool_result, id: tc[:id], name: tc[:name], result: tc[:result] }
111
114
  end
112
115
  end
113
116
 
114
117
  # Add pending tool calls
115
118
  @pending_tool_calls.each do |tc|
116
- result << { role: :tool_call, id: tc[:id], name: tc[:name], args: tc[:args] }
119
+ tool_call = { role: :tool_call, id: tc[:id], name: tc[:name], args: tc[:args] }
120
+ tool_call[:metadata] = tc[:metadata] if tc[:metadata]
121
+ result << tool_call
117
122
  result << { role: :tool_result, id: tc[:id], name: tc[:name], result: tc[:result] }
118
123
  end
119
124
 
@@ -313,17 +313,37 @@ module Girb
313
313
  # 初回のAI質問時にセッションを開始
314
314
  Girb::DebugIntegration.start_session!
315
315
 
316
+ # Ctrl+Cでプロセスがクラッシュするのを防ぐ
317
+ # trapハンドラを設置し、SIGINTをフラグ設定のみに抑える
318
+ original_handler = trap("INT") do
319
+ Girb::DebugIntegration.interrupt!
320
+ end
321
+
316
322
  context = Girb::DebugContextBuilder.new(current_binding).build
317
323
  client = Girb::AiClient.new
318
324
  # Disable Ruby's Timeout during API call to avoid deadlock with debug gem's threading
319
325
  with_timeout_disabled do
320
326
  client.ask(question, context, binding: current_binding, debug_mode: true)
321
327
  end
328
+
329
+ # API呼び出し後にinterruptチェック
330
+ if Girb::DebugIntegration.interrupted?
331
+ Girb::DebugIntegration.clear_interrupt!
332
+ Girb::DebugIntegration.auto_continue = false
333
+ Girb::DebugIntegration.take_pending_debug_commands
334
+ puts "\n[girb] Interrupted by user (Ctrl+C)"
335
+ end
322
336
  rescue Girb::ConfigurationError => e
323
337
  puts "[girb] #{e.message}"
324
338
  rescue StandardError => e
325
339
  puts "[girb] Error: #{e.message}"
326
340
  puts e.backtrace.first(3).join("\n") if Girb.configuration.debug
341
+ ensure
342
+ if original_handler
343
+ trap("INT", original_handler)
344
+ else
345
+ trap("INT", "DEFAULT")
346
+ end
327
347
  end
328
348
 
329
349
  # Temporarily disable Ruby's Timeout module to avoid deadlock with debug gem
@@ -46,15 +46,56 @@ module Girb
46
46
  When tracking variables through many iterations (loops, recursion), avoid repeated `next`/`step`
47
47
  commands. Each step requires an API call, which is slow. Use conditional breakpoints instead:
48
48
 
49
- **Efficient approach for loops with many iterations:**
50
- 1. `evaluate_code("$tracked = []")` - initialize tracking array
51
- 2. Use a conditional breakpoint that records AND stops on condition:
52
- `break file.rb:10 if: ($tracked << x; x == 1)`
53
- This appends x to $tracked on EVERY hit, but only stops when x == 1.
54
- 3. `continue` - run through all iterations at full speed
55
- 4. When stopped (or at end): `evaluate_code("$tracked")` to see all collected values
49
+ **CRITICAL: Breakpoint Line Placement Rules**
50
+ Before setting a breakpoint, use `read_file` to verify the target line.
51
+ - NEVER place a breakpoint on a block header line (a line containing `do |...|`, `.each`, `.map`, `.times`, etc.).
52
+ Block header lines execute only ONCE when the method is called, so the breakpoint will only hit once.
53
+ - ALWAYS place breakpoints on a line INSIDE the block body. Block body lines execute on every iteration.
54
+ - Example:
55
+ ```
56
+ 10: data.each_with_index do |val, i| # BAD: this line hits only once
57
+ 11: x = (x * val + i * 3) % 100 # GOOD: this line hits every iteration
58
+ 12: end
59
+ ```
60
+ Use `break file.rb:11` (body line), NOT `break file.rb:10` (header line).
56
61
 
57
- This completes in 2-3 API turns instead of many turns with repeated stepping.
62
+ **Efficient approach for loops with many iterations:**
63
+ 1. `read_file` to check the source and identify the correct body line (not a block header)
64
+ 2. `evaluate_code("$tracked = []")` - initialize tracking array
65
+ 3. Use a conditional breakpoint on a block BODY line that records AND stops on condition:
66
+ `break file.rb:11 if: ($tracked << x; x == 1)`
67
+ This appends x to $tracked on EVERY iteration, but only stops when x == 1.
68
+ 4. CRITICAL: In the SAME tool call batch, call `run_debug_command("c")` with `auto_continue: true`.
69
+ You MUST continue immediately after setting the breakpoint — do NOT stop and wait for user input.
70
+ When the breakpoint hits, you will be re-invoked with the new context.
71
+ 5. When re-invoked after the breakpoint hits: `evaluate_code("$tracked")` to retrieve results,
72
+ then report the findings to the user.
73
+
74
+ Steps 3 and 4 MUST happen in the same turn. Example tool calls in one response:
75
+ - `run_debug_command("break file.rb:11 if: ($tracked << x; x == 1)")`
76
+ - `run_debug_command("c", auto_continue: true)`
77
+
78
+ This completes in 3-4 API turns instead of many turns with repeated stepping.
79
+
80
+ **Alternative: evaluate_code for pure tracking scenarios**
81
+ When the goal is purely to collect variable values and stop on a condition (without needing
82
+ to interact at the breakpoint), `evaluate_code` can run the loop directly. This is simpler
83
+ and avoids breakpoint line selection issues entirely:
84
+ ```ruby
85
+ evaluate_code <<~RUBY
86
+ $tracked = [x]
87
+ catch(:girb_stop) do
88
+ data.each_with_index do |val, i|
89
+ x = (x * val + i * 3) % 100
90
+ $tracked << x
91
+ throw(:girb_stop) if x == 1
92
+ end
93
+ end
94
+ { tracked_values: $tracked, stopped: (x == 1) }
95
+ RUBY
96
+ ```
97
+ Use this when the user wants to collect values and find when a condition is met,
98
+ and you can reconstruct the loop logic from the source code.
58
99
 
59
100
  **When to use repeated stepping (next/step):**
60
101
  - Understanding complex logic flow (few lines)
@@ -68,6 +109,11 @@ module Girb
68
109
  - "Find when X becomes Y" requests
69
110
  - Collecting history of values
70
111
 
112
+ **When to use evaluate_code loop:**
113
+ - Pure value tracking without needing to stop and interact
114
+ - When you can reconstruct the loop logic from source code
115
+ - When breakpoint placement is complex (nested blocks, etc.)
116
+
71
117
  ## CRITICAL: Executing Debugger Commands
72
118
  When the user asks you to perform a debugging action (e.g., "go to the next line", "step into",
73
119
  "continue", "advance to line N", "set a breakpoint"), you MUST use the `run_debug_command` tool.
@@ -24,6 +24,9 @@ module Girb
24
24
  - Keep responses concise and practical
25
25
  - Read patterns and intentions; handle hypothetical questions
26
26
  - Code examples should use variables and objects from the current context and be directly executable
27
+ - When calling tools, ALWAYS include a brief one-line comment explaining what you're about to do
28
+ (e.g., "ソースファイルを確認します", "ループを実行してxの値を追跡します")
29
+ This gives users real-time visibility into your investigation process.
27
30
 
28
31
  ## Debugging Support on Errors
29
32
  When users encounter errors, actively support debugging.
@@ -59,11 +62,6 @@ module Girb
59
62
  - When asked to track variables, run the actual code and report real results
60
63
  - NEVER invent or substitute code - always use what's in the file
61
64
 
62
- ### Example: User says "run this loop and track x"
63
- 1. Read the source file to see the actual loop code
64
- 2. Execute that exact code using evaluate_code
65
- 3. Report the actual results
66
-
67
65
  ### WRONG approach:
68
66
  - Guessing what the code might do
69
67
  - Writing your own version of the code
@@ -76,11 +74,72 @@ module Girb
76
74
  - `continue` / `c`: Continue execution
77
75
  - `finish`: Run until current method returns
78
76
  - `break <file>:<line>`: Set a breakpoint
77
+ - `break <file>:<line> if: <condition>`: Conditional breakpoint (use `if:` with colon)
79
78
  - `backtrace` / `bt`: Show call stack
80
79
  - `info`: Show local variables
81
80
 
82
- When the user asks for step-by-step execution, use `run_debug_command` with `auto_continue: true`
83
- to step through the code and be re-invoked to see the results.
81
+ ### CRITICAL: Autonomous Debugging Act, Don't Ask
82
+ When the user asks to track variables, find conditions, or debug behavior, you MUST:
83
+ 1. Read the source file with `read_file` to understand the code
84
+ 2. Set up tracking and breakpoints using tools
85
+ 3. Execute — don't ask the user for file names, line numbers, or code
86
+
87
+ ### Variable Persistence Across Frames
88
+ Local variables created via `evaluate_code` do NOT persist after `step`, `next`, or `continue`.
89
+ To track values across frames, use global variables: `$tracked = []`
90
+
91
+ ### Breakpoint Line Placement Rules
92
+ - NEVER place a breakpoint on a block header line (`do |...|`, `.each`, `.map`, etc.) — it only hits once
93
+ - ALWAYS place breakpoints on a line INSIDE the block body
94
+ - Example:
95
+ ```
96
+ 10: data.each_with_index do |val, i| # BAD: hits only once
97
+ 11: x = (x * val + i * 3) % 100 # GOOD: hits every iteration
98
+ 12: end
99
+ ```
100
+
101
+ ### Efficient Variable Tracking Patterns
102
+ **Pattern A: evaluate_code loop (PREFERRED — safe and reliable)**
103
+ Read the source file, reconstruct the loop, and execute it directly.
104
+ This always returns results even if the condition is never met.
105
+ ```ruby
106
+ evaluate_code <<~RUBY
107
+ $tracked = [x]
108
+ catch(:girb_stop) do
109
+ data.each_with_index do |val, i|
110
+ x = (x * val + i * 3) % 100
111
+ $tracked << x
112
+ throw(:girb_stop) if x == 1
113
+ end
114
+ end
115
+ { tracked_values: $tracked, final_x: x, stopped: (x == 1) }
116
+ RUBY
117
+ ```
118
+ IMPORTANT: Always report whether the condition was met or not.
119
+
120
+ **Pattern B: Conditional breakpoint with tracking**
121
+ Use ONLY when you need to interact with the actual stopped program state
122
+ (e.g., inspect complex objects, check call stack at the breakpoint).
123
+ WARNING: If the condition is never met, the program runs to completion and ALL tracked data is lost.
124
+ 1. `run_debug_command("break file.rb:11 if: ($tracked ||= []; $tracked << x; x == 1)")` — self-initializing tracking, stop on condition
125
+ 2. `run_debug_command("c", auto_continue: true)` — continue and be re-invoked when it stops
126
+ 3. When re-invoked: `evaluate_code("$tracked")` — retrieve and report results
127
+
128
+ Steps 1 and 2 MUST happen in the same turn.
129
+
130
+ ### Interactive Debugging with auto_continue
131
+ Use `run_debug_command` with `auto_continue: true` when you need to execute a command
132
+ AND see the result before deciding your next action. After the command executes, you will be
133
+ automatically re-invoked with the updated context.
134
+
135
+ Use `auto_continue: true` when:
136
+ - Stepping through code to find where a variable changes
137
+ - Continuing to a breakpoint and then analyzing the state
138
+ - Any scenario where you need to see the result of a navigation command
139
+
140
+ ### Response Guidelines
141
+ - When a task is complete, ALWAYS report the results — don't just execute and stop
142
+ - NEVER repeat the same failed action. Analyze the error and try a different approach
84
143
  PROMPT
85
144
 
86
145
  # Prompt specific to interactive IRB mode (girb command)
@@ -66,12 +66,11 @@ module Girb
66
66
 
67
67
  data = {
68
68
  session_id: @current_session_id,
69
- saved_at: Time.now.iso8601,
69
+ saved_at: Time.now.to_s,
70
70
  messages: serialize_messages
71
71
  }
72
72
 
73
73
  File.write(file_path, JSON.pretty_generate(data))
74
- puts "[girb] Session saved: #{@current_session_id}"
75
74
  rescue => e
76
75
  puts "[girb] Failed to save session: #{e.message}"
77
76
  end
@@ -157,7 +156,8 @@ module Girb
157
156
  tc[:name],
158
157
  tc[:args],
159
158
  tc[:result],
160
- id: tc[:id]
159
+ id: tc[:id],
160
+ metadata: tc[:metadata]
161
161
  )
162
162
  end
163
163
  end
data/lib/girb/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Girb
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: girb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - rira100000000