rails_console_ai 0.23.0 → 0.24.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/generators/rails_console_ai/templates/initializer.rb +1 -1
- data/lib/rails_console_ai/channel/base.rb +2 -1
- data/lib/rails_console_ai/channel/console.rb +5 -1
- data/lib/rails_console_ai/channel/slack.rb +14 -19
- data/lib/rails_console_ai/context_builder.rb +2 -1
- data/lib/rails_console_ai/conversation_engine.rb +18 -12
- data/lib/rails_console_ai/executor.rb +16 -3
- data/lib/rails_console_ai/providers/bedrock.rb +3 -3
- data/lib/rails_console_ai/slack_bot.rb +2 -2
- data/lib/rails_console_ai/tools/registry.rb +2 -13
- data/lib/rails_console_ai/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: 41b94c2b3a01cba1210242c6c8752510fc1976bf9b7195272f4a109dceadcdab
|
|
4
|
+
data.tar.gz: 6c813abfeeda804b115dfd2a385bc67e1796b0af80cc438e5f41d66886fa519e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 52281835764610528b039d12ac786ec7056d035e9055d5574bee3a3143c9833e8fa5943a5932980c7ee28ae77351a178186dc15b3afb2f0e9acb7c71b1104c5f
|
|
7
|
+
data.tar.gz: d1f56cd511ff3411b6e2783c0790bbc3d012a420971fe2cd400f7f573ffc168f5b6bca5c692618bf5c4ef2eaa9d0d0283958afcf1b7ac71d426dd4f2f4377425
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.24.0]
|
|
6
|
+
|
|
7
|
+
- Refactor thinking text display and include in Slack with more technical detail
|
|
8
|
+
- Add `a` command to trigger auto-accept mode
|
|
9
|
+
- Include code output in Slack server logs
|
|
10
|
+
- Fix Bedrock issue after declining code execution
|
|
11
|
+
- Fix `allow_code_execution` configuration
|
|
12
|
+
|
|
5
13
|
## [0.23.0]
|
|
6
14
|
|
|
7
15
|
- Add `save_skill` tool
|
|
@@ -86,7 +86,7 @@ RailsConsoleAi.configure do |config|
|
|
|
86
86
|
# config.channels = {
|
|
87
87
|
# 'slack' => {
|
|
88
88
|
# 'allowed_usernames' => ['alice', 'bob'], # who can use the bot (or 'ALL')
|
|
89
|
-
# 'allow_code_execution' => ['alice'], # who can run code (nil = everyone)
|
|
89
|
+
# 'allow_code_execution' => ['alice'], # who can run code directly via ``` (nil = everyone)
|
|
90
90
|
# 'pinned_memory_tags' => ['sharding'],
|
|
91
91
|
# 'bypass_guards_for_methods' => ['ChangeApproval#approve_by!']
|
|
92
92
|
# },
|
|
@@ -2,7 +2,8 @@ module RailsConsoleAi
|
|
|
2
2
|
module Channel
|
|
3
3
|
class Base
|
|
4
4
|
def display(text); raise NotImplementedError; end
|
|
5
|
-
def
|
|
5
|
+
def display_thinking(text); raise NotImplementedError; end
|
|
6
|
+
def display_status(text); raise NotImplementedError; end
|
|
6
7
|
def display_warning(text); raise NotImplementedError; end
|
|
7
8
|
def display_error(text); raise NotImplementedError; end
|
|
8
9
|
def display_code(code); raise NotImplementedError; end
|
|
@@ -28,23 +28,21 @@ module RailsConsoleAi
|
|
|
28
28
|
post(strip_ansi(text))
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
def display_thinking(text)
|
|
32
|
+
stripped = strip_ansi(text).strip
|
|
33
|
+
return if stripped.empty?
|
|
34
|
+
post(stripped)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def display_status(text)
|
|
38
|
+
stripped = strip_ansi(text).strip
|
|
39
|
+
return if stripped.empty?
|
|
34
40
|
|
|
35
41
|
if stripped =~ /\AThinking\.\.\.|\AAttempting to fix|\ACancelled|\A_session:/
|
|
36
42
|
post(stripped)
|
|
37
|
-
elsif stripped =~ /\ACalling LLM/
|
|
38
|
-
# Technical LLM round status — suppress in Slack
|
|
39
|
-
@output_log.write("#{stripped}\n")
|
|
40
|
-
STDOUT.puts "#{@log_prefix} (dim) #{stripped}"
|
|
41
|
-
elsif raw =~ /\A {2,4}\S/ && stripped.length > 10
|
|
42
|
-
# LLM thinking text (2-space indent from conversation engine) — show as status
|
|
43
|
-
post(stripped)
|
|
44
43
|
else
|
|
45
|
-
# Tool result previews (5+ space indent) and other technical noise — log only
|
|
46
44
|
@output_log.write("#{stripped}\n")
|
|
47
|
-
STDOUT.puts "#{@log_prefix} (
|
|
45
|
+
STDOUT.puts "#{@log_prefix} (status) #{stripped}"
|
|
48
46
|
end
|
|
49
47
|
end
|
|
50
48
|
|
|
@@ -112,9 +110,9 @@ module RailsConsoleAi
|
|
|
112
110
|
|
|
113
111
|
def system_instructions
|
|
114
112
|
<<~INSTRUCTIONS.strip
|
|
115
|
-
##
|
|
113
|
+
## Slack Channel
|
|
116
114
|
|
|
117
|
-
You are responding
|
|
115
|
+
You are responding in a Slack thread.
|
|
118
116
|
|
|
119
117
|
## Code Execution
|
|
120
118
|
- ALWAYS use the `execute_code` tool to run Ruby code. Do NOT put code in markdown
|
|
@@ -132,11 +130,8 @@ module RailsConsoleAi
|
|
|
132
130
|
123 John Smith john@example.com
|
|
133
131
|
456 Jane Doe jane@example.com
|
|
134
132
|
```
|
|
135
|
-
- Use `puts` with formatted output instead of returning arrays or hashes
|
|
136
|
-
-
|
|
137
|
-
- Do NOT show technical details like SQL queries, token counts, or class names
|
|
138
|
-
- Keep explanations simple and jargon-free
|
|
139
|
-
- Never return raw Ruby objects — always present data in a human-readable way
|
|
133
|
+
- Use `puts` with formatted output instead of returning arrays or hashes.
|
|
134
|
+
- Never return raw Ruby objects — always present data in a human-readable way.
|
|
140
135
|
- The output of `puts` in your code is automatically shown to the user. Do NOT
|
|
141
136
|
repeat or re-display data that your code already printed via `puts`.
|
|
142
137
|
Just add a brief summary after (e.g. "10 events found" or "Let me know if you need more detail").
|
|
@@ -97,7 +97,8 @@ module RailsConsoleAi
|
|
|
97
97
|
- Give ONE concise answer. Do not offer multiple alternatives or variations.
|
|
98
98
|
- For multi-step tasks, use execute_plan to break the work into small, clear steps.
|
|
99
99
|
- For simple queries, use the execute_code tool.
|
|
100
|
-
-
|
|
100
|
+
- Before calling tools, briefly state what you're about to do (e.g., "Let me check the
|
|
101
|
+
user's migration status." or "I'll look up the table structure."). Keep it to one sentence.
|
|
101
102
|
- Use the app's actual model names, associations, and schema.
|
|
102
103
|
- Prefer ActiveRecord query interface over raw SQL.
|
|
103
104
|
- For destructive operations, add a comment warning.
|
|
@@ -49,7 +49,7 @@ module RailsConsoleAi
|
|
|
49
49
|
conversation << { role: :assistant, content: @_last_result_text }
|
|
50
50
|
conversation << { role: :user, content: error_msg }
|
|
51
51
|
|
|
52
|
-
@channel.
|
|
52
|
+
@channel.display_status(" Ran into an issue, trying a different approach...")
|
|
53
53
|
exec_result, code, executed = one_shot_round(conversation)
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -114,7 +114,7 @@ module RailsConsoleAi
|
|
|
114
114
|
|
|
115
115
|
status = send_and_execute
|
|
116
116
|
if status == :error
|
|
117
|
-
@channel.
|
|
117
|
+
@channel.display_status(" Ran into an issue, trying a different approach...")
|
|
118
118
|
send_and_execute
|
|
119
119
|
end
|
|
120
120
|
end
|
|
@@ -232,7 +232,7 @@ module RailsConsoleAi
|
|
|
232
232
|
@channel.display_warning("No code to retry.")
|
|
233
233
|
return
|
|
234
234
|
end
|
|
235
|
-
@channel.
|
|
235
|
+
@channel.display_status(" Retrying last code...")
|
|
236
236
|
execute_direct(code)
|
|
237
237
|
end
|
|
238
238
|
|
|
@@ -790,19 +790,19 @@ module RailsConsoleAi
|
|
|
790
790
|
|
|
791
791
|
max_rounds.times do |round|
|
|
792
792
|
if @channel.cancelled?
|
|
793
|
-
@channel.
|
|
793
|
+
@channel.display_status(" Cancelled.")
|
|
794
794
|
break
|
|
795
795
|
end
|
|
796
796
|
|
|
797
797
|
if round == 0
|
|
798
|
-
@channel.
|
|
798
|
+
@channel.display_status(" Thinking...")
|
|
799
799
|
else
|
|
800
800
|
if last_thinking
|
|
801
801
|
last_thinking.split("\n").each do |line|
|
|
802
|
-
@channel.
|
|
802
|
+
@channel.display_thinking(" #{line}")
|
|
803
803
|
end
|
|
804
804
|
end
|
|
805
|
-
@channel.
|
|
805
|
+
@channel.display_status(" #{llm_status(round, messages, total_input, last_thinking, last_tool_names)}")
|
|
806
806
|
end
|
|
807
807
|
|
|
808
808
|
# Trim large tool outputs between rounds to prevent context explosion.
|
|
@@ -856,7 +856,7 @@ module RailsConsoleAi
|
|
|
856
856
|
else
|
|
857
857
|
"No matching outputs found with id(s) #{ids.join(', ')}."
|
|
858
858
|
end
|
|
859
|
-
@channel.
|
|
859
|
+
@channel.display_status(" #{tool_result}")
|
|
860
860
|
tool_msg = provider.format_tool_result(tc[:id], tool_result)
|
|
861
861
|
messages << tool_msg
|
|
862
862
|
new_messages << tool_msg
|
|
@@ -865,7 +865,7 @@ module RailsConsoleAi
|
|
|
865
865
|
|
|
866
866
|
# Display any pending LLM text before executing the tool
|
|
867
867
|
if last_thinking
|
|
868
|
-
last_thinking.split("\n").each { |line| @channel.
|
|
868
|
+
last_thinking.split("\n").each { |line| @channel.display_thinking(" #{line}") }
|
|
869
869
|
last_thinking = nil
|
|
870
870
|
end
|
|
871
871
|
|
|
@@ -879,7 +879,7 @@ module RailsConsoleAi
|
|
|
879
879
|
|
|
880
880
|
preview = compact_tool_result(tc[:name], tool_result)
|
|
881
881
|
cached_tag = tools.last_cached? ? " (cached)" : ""
|
|
882
|
-
@channel.
|
|
882
|
+
@channel.display_status(" #{preview}#{cached_tag}")
|
|
883
883
|
end
|
|
884
884
|
|
|
885
885
|
if RailsConsoleAi.configuration.debug
|
|
@@ -908,10 +908,10 @@ module RailsConsoleAi
|
|
|
908
908
|
tool_call_counts[key] += 1
|
|
909
909
|
|
|
910
910
|
if tool_call_counts[key] >= LOOP_BREAK_THRESHOLD
|
|
911
|
-
@channel.
|
|
911
|
+
@channel.display_status(" Loop detected: #{tc[:name]} called #{tool_call_counts[key]} times with same args — stopping.")
|
|
912
912
|
exhausted = true
|
|
913
913
|
elsif tool_call_counts[key] >= LOOP_WARN_THRESHOLD
|
|
914
|
-
@channel.
|
|
914
|
+
@channel.display_status(" Warning: #{tc[:name]} called #{tool_call_counts[key]} times with same args — consider a different approach.")
|
|
915
915
|
messages << { role: :user, content: "You are repeating the same tool call (#{tc[:name]}) with the same arguments. This is not making progress. Try a different approach or provide your answer now." }
|
|
916
916
|
end
|
|
917
917
|
end
|
|
@@ -1095,6 +1095,12 @@ module RailsConsoleAi
|
|
|
1095
1095
|
else
|
|
1096
1096
|
truncate(result, 80)
|
|
1097
1097
|
end
|
|
1098
|
+
when 'execute_code'
|
|
1099
|
+
lines = result.split("\n").reject { |l| l.strip.empty? }
|
|
1100
|
+
output_lines = lines.select { |l| !l.start_with?('Output:') && !l.start_with?('Return value:') }
|
|
1101
|
+
summary = output_lines.first(2).map { |l| l.strip }.join('; ')
|
|
1102
|
+
summary = truncate(summary, 70) if summary.length > 70
|
|
1103
|
+
"#{output_lines.length} lines: #{summary}"
|
|
1098
1104
|
when 'execute_plan'
|
|
1099
1105
|
steps_done = result.scan(/^Step \d+/).length
|
|
1100
1106
|
steps_done > 0 ? "#{steps_done} steps executed" : truncate(result, 80)
|
|
@@ -216,7 +216,20 @@ module RailsConsoleAi
|
|
|
216
216
|
|
|
217
217
|
loop do
|
|
218
218
|
case answer
|
|
219
|
-
when '
|
|
219
|
+
when 'a', 'auto'
|
|
220
|
+
RailsConsoleAi.configuration.auto_execute = true
|
|
221
|
+
if @channel
|
|
222
|
+
@channel.display_status("Auto-execute: ON")
|
|
223
|
+
else
|
|
224
|
+
$stdout.puts colorize("Auto-execute: ON", :cyan)
|
|
225
|
+
end
|
|
226
|
+
result = execute(code)
|
|
227
|
+
if @last_safety_error
|
|
228
|
+
return nil unless danger_allowed?
|
|
229
|
+
return offer_danger_retry(code)
|
|
230
|
+
end
|
|
231
|
+
return result
|
|
232
|
+
when 'y', 'yes'
|
|
220
233
|
result = execute(code)
|
|
221
234
|
if @last_safety_error
|
|
222
235
|
return nil unless danger_allowed?
|
|
@@ -355,9 +368,9 @@ module RailsConsoleAi
|
|
|
355
368
|
def execute_prompt
|
|
356
369
|
guards = RailsConsoleAi.configuration.safety_guards
|
|
357
370
|
if !guards.empty? && guards.enabled? && danger_allowed?
|
|
358
|
-
"Execute? [y/N/danger] "
|
|
371
|
+
"Execute? [y/N/a/danger] "
|
|
359
372
|
else
|
|
360
|
-
"Execute? [y/N] "
|
|
373
|
+
"Execute? [y/N/a] "
|
|
361
374
|
end
|
|
362
375
|
end
|
|
363
376
|
|
|
@@ -121,10 +121,10 @@ module RailsConsoleAi
|
|
|
121
121
|
else
|
|
122
122
|
[{ text: msg[:content].to_s }]
|
|
123
123
|
end
|
|
124
|
-
# Bedrock rejects empty text blocks in content arrays
|
|
125
|
-
content.reject! { |block| block.is_a?(Hash) && block.key?(:text) && !block.key?(:tool_use) && !block.key?(:tool_result) && block[:text].to_s.empty? }
|
|
124
|
+
# Bedrock rejects empty or whitespace-only text blocks in content arrays
|
|
125
|
+
content.reject! { |block| block.is_a?(Hash) && block.key?(:text) && !block.key?(:tool_use) && !block.key?(:tool_result) && block[:text].to_s.strip.empty? }
|
|
126
126
|
# Bedrock also rejects messages with completely empty content arrays
|
|
127
|
-
content << { text: '
|
|
127
|
+
content << { text: '.' } if content.empty?
|
|
128
128
|
{ role: msg[:role].to_s, content: content }
|
|
129
129
|
end
|
|
130
130
|
|
|
@@ -443,10 +443,10 @@ module RailsConsoleAi
|
|
|
443
443
|
Thread.current.report_on_exception = false
|
|
444
444
|
Thread.current[:log_prefix] = "[#{channel_id}/#{thread_ts}]#{channel_log_tag(channel_id)} @#{user_name}"
|
|
445
445
|
begin
|
|
446
|
-
channel.
|
|
446
|
+
channel.display_status("_session: #{channel_id}/#{thread_ts}_")
|
|
447
447
|
if restored
|
|
448
448
|
puts "Restored session for thread #{thread_ts} (#{engine.history.length} messages)"
|
|
449
|
-
channel.
|
|
449
|
+
channel.display_status("_(session restored — continuing from previous conversation)_")
|
|
450
450
|
end
|
|
451
451
|
engine.process_message(text)
|
|
452
452
|
rescue => e
|
|
@@ -368,13 +368,6 @@ module RailsConsoleAi
|
|
|
368
368
|
def register_execute_plan
|
|
369
369
|
return unless @executor
|
|
370
370
|
|
|
371
|
-
# Check per-channel code execution permission
|
|
372
|
-
if @channel
|
|
373
|
-
unless RailsConsoleAi.configuration.username_allowed?(@channel.mode, 'allow_code_execution', @channel.user_identity)
|
|
374
|
-
return
|
|
375
|
-
end
|
|
376
|
-
end
|
|
377
|
-
|
|
378
371
|
register(
|
|
379
372
|
name: 'execute_code',
|
|
380
373
|
description: 'Execute Ruby code in the Rails console and return the result. Use this for all code execution — simple queries, data lookups, reports, etc. The output of puts/print statements is automatically shown to the user. The return value is sent back to you so you can summarize the findings.',
|
|
@@ -419,12 +412,8 @@ module RailsConsoleAi
|
|
|
419
412
|
# Show the code to the user
|
|
420
413
|
@executor.display_code_block(code)
|
|
421
414
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
exec_result = if @channel&.mode == 'slack'
|
|
425
|
-
@executor.execute(code, display: false)
|
|
426
|
-
elsif RailsConsoleAi.configuration.auto_execute
|
|
427
|
-
@executor.execute(code, display: false)
|
|
415
|
+
exec_result = if @channel&.mode == 'slack' || RailsConsoleAi.configuration.auto_execute
|
|
416
|
+
@executor.execute(code)
|
|
428
417
|
else
|
|
429
418
|
@executor.confirm_and_execute(code)
|
|
430
419
|
end
|