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 +4 -4
- data/CHANGELOG.md +19 -0
- data/lib/girb/ai_client.rb +7 -3
- data/lib/girb/conversation_history.rb +12 -7
- data/lib/girb/debug_integration.rb +20 -0
- data/lib/girb/debug_prompt_builder.rb +54 -8
- data/lib/girb/prompt_builder.rb +66 -7
- data/lib/girb/session_persistence.rb +3 -3
- data/lib/girb/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cdae8b7956a4924603ee3edfcebdf3835ed6d994037847eed0d432d078dec400
|
|
4
|
+
data.tar.gz: 270cf95c4e39728de8cfb5ca5153139951c94b4f94d9255d250d6a47e527813e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
data/lib/girb/ai_client.rb
CHANGED
|
@@ -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
|
-
#
|
|
184
|
+
# Print progress text immediately (don't accumulate for re-display at the end)
|
|
181
185
|
if response.text && !response.text.empty?
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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.
|
data/lib/girb/prompt_builder.rb
CHANGED
|
@@ -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
|
-
|
|
83
|
-
|
|
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.
|
|
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