rails_console_ai 0.15.0 → 0.17.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 +16 -0
- data/lib/rails_console_ai/channel/console.rb +9 -0
- data/lib/rails_console_ai/channel/slack.rb +15 -29
- data/lib/rails_console_ai/conversation_engine.rb +10 -0
- data/lib/rails_console_ai/executor.rb +17 -2
- data/lib/rails_console_ai/prefixed_io.rb +50 -0
- data/lib/rails_console_ai/providers/bedrock.rb +13 -1
- data/lib/rails_console_ai/safety_guards.rb +11 -0
- data/lib/rails_console_ai/slack_bot.rb +43 -1
- data/lib/rails_console_ai/version.rb +1 -1
- data/lib/rails_console_ai.rb +3 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 98abad8ed9e3d5d510ffc055352687156ffd4a5c02fbde2610f8ecd46cf2c263
|
|
4
|
+
data.tar.gz: 2979eeb2d6f7e3f8df58a4cd25666307cd4e108dfec379020e3ad6422bbba911
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc6695f1321d2c5b39b14e08e676b354f89eb62f29718244a0a25580a5d13250c7581ccfdd52552489f7a53c252433be465fb6b95c9de981825317680f575f5e
|
|
7
|
+
data.tar.gz: 35ff574a1c8404cc43c308f5c5bd13bcc1acda339c9543026a7ac216e4756988629088c436a38ab9dac2a34dc2e4a994168a4f8c70a57fbf7262135db8ac8b7d
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.17.0]
|
|
6
|
+
|
|
7
|
+
- Add `/retry` command
|
|
8
|
+
- Print provider information when `ai!` starts
|
|
9
|
+
- Keep Slack bot alive during long-running sessions
|
|
10
|
+
- Improve Slack bot log prefixes for production log search
|
|
11
|
+
- Catch safety errors even when swallowed by executed code
|
|
12
|
+
- Fix Bedrock handling of multiple tool results
|
|
13
|
+
|
|
14
|
+
## [0.16.0]
|
|
15
|
+
|
|
16
|
+
- Run migrations during setup
|
|
17
|
+
- Clarify LLM activity messages in Slack channel
|
|
18
|
+
- Clean up error messages
|
|
19
|
+
- Rotate thinking messages in Slack bot
|
|
20
|
+
|
|
5
21
|
## [0.15.0]
|
|
6
22
|
|
|
7
23
|
- Add `config.code_search_paths` to configure searchable code directories
|
|
@@ -158,6 +158,8 @@ module RailsConsoleAi
|
|
|
158
158
|
guards = RailsConsoleAi.configuration.safety_guards
|
|
159
159
|
name_display = @engine.session_name ? " (#{@engine.session_name})" : ""
|
|
160
160
|
@real_stdout.puts "\e[36mRailsConsoleAi interactive mode#{name_display}. Type 'exit' or 'quit' to leave.\e[0m"
|
|
161
|
+
config = RailsConsoleAi.configuration
|
|
162
|
+
@real_stdout.puts "\e[2m Provider: #{config.provider} | Model: #{config.resolved_model}\e[0m"
|
|
161
163
|
safe_info = guards.empty? ? '' : " | Safe mode: #{guards.enabled? ? 'ON' : 'OFF'} (/danger to toggle)"
|
|
162
164
|
@real_stdout.puts "\e[2m Auto-execute: #{auto ? 'ON' : 'OFF'} (Shift-Tab or /auto to toggle)#{safe_info} | > code | /usage | /cost | /compact | /think | /name <label>\e[0m"
|
|
163
165
|
|
|
@@ -259,6 +261,8 @@ module RailsConsoleAi
|
|
|
259
261
|
else
|
|
260
262
|
@real_stdout.puts "\e[33mNo omitted output with id #{expand_id}\e[0m"
|
|
261
263
|
end
|
|
264
|
+
when '/retry'
|
|
265
|
+
retry_last_code
|
|
262
266
|
when /\A\/name/
|
|
263
267
|
handle_name_command(input)
|
|
264
268
|
else
|
|
@@ -267,6 +271,10 @@ module RailsConsoleAi
|
|
|
267
271
|
true
|
|
268
272
|
end
|
|
269
273
|
|
|
274
|
+
def retry_last_code
|
|
275
|
+
@engine.retry_last_code
|
|
276
|
+
end
|
|
277
|
+
|
|
270
278
|
def handle_direct_execution(input)
|
|
271
279
|
raw_code = input.sub(/\A>\s?/, '')
|
|
272
280
|
Readline::HISTORY.push(input) unless input == Readline::HISTORY.to_a.last
|
|
@@ -341,6 +349,7 @@ module RailsConsoleAi
|
|
|
341
349
|
@real_stdout.puts "\e[2m /system Show the system prompt\e[0m"
|
|
342
350
|
@real_stdout.puts "\e[2m /expand <id> Show full omitted output\e[0m"
|
|
343
351
|
@real_stdout.puts "\e[2m /debug Toggle debug summaries (context stats, cost per call)\e[0m"
|
|
352
|
+
@real_stdout.puts "\e[2m /retry Re-execute the last code block\e[0m"
|
|
344
353
|
@real_stdout.puts "\e[2m > code Execute Ruby directly (skip LLM)\e[0m"
|
|
345
354
|
@real_stdout.puts "\e[2m exit/quit Leave interactive mode\e[0m"
|
|
346
355
|
end
|
|
@@ -5,24 +5,6 @@ module RailsConsoleAi
|
|
|
5
5
|
class Slack < Base
|
|
6
6
|
ANSI_REGEX = /\e\[[0-9;]*m/
|
|
7
7
|
|
|
8
|
-
THINKING_MESSAGES = [
|
|
9
|
-
"Thinking...",
|
|
10
|
-
"Reticulating splines...",
|
|
11
|
-
"Scrubbing encryption bits...",
|
|
12
|
-
"Consulting the oracle...",
|
|
13
|
-
"Rummaging through the database...",
|
|
14
|
-
"Warming up the hamster wheel...",
|
|
15
|
-
"Polishing the pixels...",
|
|
16
|
-
"Untangling the spaghetti code...",
|
|
17
|
-
"Asking the magic 8-ball...",
|
|
18
|
-
"Counting all the things...",
|
|
19
|
-
"Herding the electrons...",
|
|
20
|
-
"Dusting off the old records...",
|
|
21
|
-
"Feeding the algorithms...",
|
|
22
|
-
"Shaking the data tree...",
|
|
23
|
-
"Bribing the servers...",
|
|
24
|
-
].freeze
|
|
25
|
-
|
|
26
8
|
def initialize(slack_bot:, channel_id:, thread_ts:, user_name: nil)
|
|
27
9
|
@slack_bot = slack_bot
|
|
28
10
|
@channel_id = channel_id
|
|
@@ -47,15 +29,22 @@ module RailsConsoleAi
|
|
|
47
29
|
end
|
|
48
30
|
|
|
49
31
|
def display_dim(text)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
32
|
+
raw = strip_ansi(text)
|
|
33
|
+
stripped = raw.strip
|
|
34
|
+
|
|
35
|
+
if stripped =~ /\AThinking\.\.\.|\AAttempting to fix|\ACancelled|\A_session:/
|
|
36
|
+
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
|
|
54
43
|
post(stripped)
|
|
55
44
|
else
|
|
56
|
-
#
|
|
45
|
+
# Tool result previews (5+ space indent) and other technical noise — log only
|
|
57
46
|
@output_log.write("#{stripped}\n")
|
|
58
|
-
|
|
47
|
+
STDOUT.puts "#{@log_prefix} (dim) #{stripped}"
|
|
59
48
|
end
|
|
60
49
|
end
|
|
61
50
|
|
|
@@ -135,6 +124,7 @@ module RailsConsoleAi
|
|
|
135
124
|
- The output of `puts` in your code is automatically shown to the user. Do NOT
|
|
136
125
|
repeat or re-display data that your code already printed via `puts`.
|
|
137
126
|
Just add a brief summary after (e.g. "10 events found" or "Let me know if you need more detail").
|
|
127
|
+
- Do not offer to make changes or take actions on behalf of the user. Only report findings.
|
|
138
128
|
- This is a live production database — other processes, users, and background jobs are
|
|
139
129
|
constantly changing data. Never assume results will be the same as a previous query.
|
|
140
130
|
Always re-run queries when asked, even if you just ran the same one.
|
|
@@ -160,7 +150,7 @@ module RailsConsoleAi
|
|
|
160
150
|
def post(text)
|
|
161
151
|
return if text.nil? || text.strip.empty?
|
|
162
152
|
@output_log.write("#{text}\n")
|
|
163
|
-
|
|
153
|
+
STDOUT.puts "#{@log_prefix} >> #{text}"
|
|
164
154
|
@slack_bot.send(:post_message,
|
|
165
155
|
channel: @channel_id,
|
|
166
156
|
thread_ts: @thread_ts,
|
|
@@ -170,10 +160,6 @@ module RailsConsoleAi
|
|
|
170
160
|
RailsConsoleAi.logger.error("Slack post failed: #{e.message}")
|
|
171
161
|
end
|
|
172
162
|
|
|
173
|
-
def random_thinking_message
|
|
174
|
-
THINKING_MESSAGES.sample
|
|
175
|
-
end
|
|
176
|
-
|
|
177
163
|
def strip_ansi(text)
|
|
178
164
|
text.to_s.gsub(ANSI_REGEX, '')
|
|
179
165
|
end
|
|
@@ -221,6 +221,16 @@ module RailsConsoleAi
|
|
|
221
221
|
end
|
|
222
222
|
end
|
|
223
223
|
|
|
224
|
+
def retry_last_code
|
|
225
|
+
code = @last_interactive_code
|
|
226
|
+
unless code
|
|
227
|
+
@channel.display_warning("No code to retry.")
|
|
228
|
+
return
|
|
229
|
+
end
|
|
230
|
+
@channel.display_dim(" Retrying last code...")
|
|
231
|
+
execute_direct(code)
|
|
232
|
+
end
|
|
233
|
+
|
|
224
234
|
def execute_direct(raw_code)
|
|
225
235
|
exec_result = @executor.execute(raw_code)
|
|
226
236
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'stringio'
|
|
2
|
+
require_relative 'safety_guards'
|
|
2
3
|
|
|
3
4
|
module RailsConsoleAi
|
|
4
5
|
# Writes to two IO streams simultaneously
|
|
@@ -97,12 +98,25 @@ module RailsConsoleAi
|
|
|
97
98
|
# Tee output: capture it and also print to the real stdout
|
|
98
99
|
$stdout = TeeIO.new(old_stdout, captured_output)
|
|
99
100
|
|
|
101
|
+
RailsConsoleAi::SafetyError.clear!
|
|
102
|
+
|
|
100
103
|
result = with_safety_guards do
|
|
101
104
|
binding_context.eval(code, "(rails_console_ai)", 1)
|
|
102
105
|
end
|
|
103
106
|
|
|
104
107
|
$stdout = old_stdout
|
|
105
108
|
|
|
109
|
+
# Check if a SafetyError was raised but swallowed by a rescue inside the eval'd code
|
|
110
|
+
if (swallowed = RailsConsoleAi::SafetyError.last_raised)
|
|
111
|
+
RailsConsoleAi::SafetyError.clear!
|
|
112
|
+
@last_error = "SafetyError: #{swallowed.message}"
|
|
113
|
+
@last_safety_error = true
|
|
114
|
+
@last_safety_exception = swallowed
|
|
115
|
+
display_error("Blocked: #{swallowed.message}")
|
|
116
|
+
@last_output = captured_output&.string
|
|
117
|
+
return nil
|
|
118
|
+
end
|
|
119
|
+
|
|
106
120
|
# Send captured puts output through channel before the return value
|
|
107
121
|
if @channel && !captured_output.string.empty?
|
|
108
122
|
@channel.display_result_output(captured_output.string)
|
|
@@ -114,6 +128,7 @@ module RailsConsoleAi
|
|
|
114
128
|
result
|
|
115
129
|
rescue RailsConsoleAi::SafetyError => e
|
|
116
130
|
$stdout = old_stdout if old_stdout
|
|
131
|
+
RailsConsoleAi::SafetyError.clear!
|
|
117
132
|
@last_error = "SafetyError: #{e.message}"
|
|
118
133
|
@last_safety_error = true
|
|
119
134
|
@last_safety_exception = e
|
|
@@ -140,8 +155,8 @@ module RailsConsoleAi
|
|
|
140
155
|
return nil
|
|
141
156
|
end
|
|
142
157
|
@last_error = "#{e.class}: #{e.message}"
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
backtrace = e.backtrace.first(3).map { |line| " #{line}" }.join("\n")
|
|
159
|
+
display_error("Error: #{@last_error}\n#{backtrace}")
|
|
145
160
|
@last_output = captured_output&.string
|
|
146
161
|
nil
|
|
147
162
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module RailsConsoleAi
|
|
2
|
+
class PrefixedIO
|
|
3
|
+
def initialize(io)
|
|
4
|
+
@io = io
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def write(str)
|
|
8
|
+
prefix = Thread.current[:log_prefix]
|
|
9
|
+
if prefix && str.is_a?(String) && !str.strip.empty?
|
|
10
|
+
prefixed = str.gsub(/^(?=.)/, "#{prefix} ")
|
|
11
|
+
@io.write(prefixed)
|
|
12
|
+
else
|
|
13
|
+
@io.write(str)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def puts(*args)
|
|
18
|
+
prefix = Thread.current[:log_prefix]
|
|
19
|
+
if prefix
|
|
20
|
+
args = [""] if args.empty?
|
|
21
|
+
args.each do |a|
|
|
22
|
+
line = a.to_s
|
|
23
|
+
if line.strip.empty?
|
|
24
|
+
@io.write("\n")
|
|
25
|
+
else
|
|
26
|
+
@io.write("#{prefix} #{line}\n")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
@io.puts(*args)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def print(*args)
|
|
35
|
+
@io.print(*args)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def flush
|
|
39
|
+
@io.flush
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def respond_to_missing?(method, include_private = false)
|
|
43
|
+
@io.respond_to?(method, include_private) || super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def method_missing(method, *args, &block)
|
|
47
|
+
@io.send(method, *args, &block)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -115,7 +115,7 @@ module RailsConsoleAi
|
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
def format_messages(messages)
|
|
118
|
-
messages.map do |msg|
|
|
118
|
+
formatted = messages.map do |msg|
|
|
119
119
|
content = if msg[:content].is_a?(Array)
|
|
120
120
|
msg[:content]
|
|
121
121
|
else
|
|
@@ -123,6 +123,18 @@ module RailsConsoleAi
|
|
|
123
123
|
end
|
|
124
124
|
{ role: msg[:role].to_s, content: content }
|
|
125
125
|
end
|
|
126
|
+
|
|
127
|
+
# Bedrock requires all tool_result blocks for a single assistant turn
|
|
128
|
+
# to be in one user message. Merge consecutive same-role messages.
|
|
129
|
+
merged = []
|
|
130
|
+
formatted.each do |msg|
|
|
131
|
+
if merged.last && merged.last[:role] == msg[:role]
|
|
132
|
+
merged.last[:content].concat(msg[:content])
|
|
133
|
+
else
|
|
134
|
+
merged << msg
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
merged
|
|
126
138
|
end
|
|
127
139
|
|
|
128
140
|
def extract_text(response)
|
|
@@ -5,10 +5,21 @@ module RailsConsoleAi
|
|
|
5
5
|
class SafetyError < StandardError
|
|
6
6
|
attr_reader :guard, :blocked_key
|
|
7
7
|
|
|
8
|
+
# Thread-local tracking so the executor can detect safety errors
|
|
9
|
+
# even when swallowed by a rescue inside eval'd code.
|
|
10
|
+
def self.last_raised
|
|
11
|
+
Thread.current[:rails_console_ai_last_safety_error]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.clear!
|
|
15
|
+
Thread.current[:rails_console_ai_last_safety_error] = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
8
18
|
def initialize(message, guard: nil, blocked_key: nil)
|
|
9
19
|
super(message)
|
|
10
20
|
@guard = guard
|
|
11
21
|
@blocked_key = blocked_key
|
|
22
|
+
Thread.current[:rails_console_ai_last_safety_error] = self
|
|
12
23
|
end
|
|
13
24
|
end
|
|
14
25
|
|
|
@@ -2,6 +2,7 @@ require 'json'
|
|
|
2
2
|
require 'uri'
|
|
3
3
|
require 'net/http'
|
|
4
4
|
require 'openssl'
|
|
5
|
+
require 'rails_console_ai/prefixed_io'
|
|
5
6
|
require 'rails_console_ai/channel/slack'
|
|
6
7
|
require 'rails_console_ai/conversation_engine'
|
|
7
8
|
require 'rails_console_ai/context_builder'
|
|
@@ -10,6 +11,9 @@ require 'rails_console_ai/executor'
|
|
|
10
11
|
|
|
11
12
|
module RailsConsoleAi
|
|
12
13
|
class SlackBot
|
|
14
|
+
PING_INTERVAL = 30 # seconds — send ping if no data received
|
|
15
|
+
PONG_TIMEOUT = 60 # seconds — reconnect if no pong after ping
|
|
16
|
+
|
|
13
17
|
def initialize
|
|
14
18
|
@bot_token = RailsConsoleAi.configuration.slack_bot_token || ENV['SLACK_BOT_TOKEN']
|
|
15
19
|
@app_token = RailsConsoleAi.configuration.slack_app_token || ENV['SLACK_APP_TOKEN']
|
|
@@ -26,6 +30,11 @@ module RailsConsoleAi
|
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
def start
|
|
33
|
+
$stdout.sync = true
|
|
34
|
+
$stderr.sync = true
|
|
35
|
+
$stdout = RailsConsoleAi::PrefixedIO.new($stdout) unless $stdout.is_a?(RailsConsoleAi::PrefixedIO)
|
|
36
|
+
$stderr = RailsConsoleAi::PrefixedIO.new($stderr) unless $stderr.is_a?(RailsConsoleAi::PrefixedIO)
|
|
37
|
+
|
|
29
38
|
@bot_user_id = slack_api("auth.test", token: @bot_token).dig("user_id")
|
|
30
39
|
log_startup
|
|
31
40
|
|
|
@@ -78,9 +87,27 @@ module RailsConsoleAi
|
|
|
78
87
|
|
|
79
88
|
puts "Connected to Slack Socket Mode."
|
|
80
89
|
|
|
81
|
-
# Main read loop
|
|
90
|
+
# Main read loop with keepalive
|
|
91
|
+
last_activity = Time.now
|
|
92
|
+
ping_sent = false
|
|
93
|
+
|
|
82
94
|
loop do
|
|
95
|
+
ready = IO.select([ssl.to_io], nil, nil, PING_INTERVAL)
|
|
96
|
+
|
|
97
|
+
if ready.nil?
|
|
98
|
+
# Timeout — no data received
|
|
99
|
+
if ping_sent && (Time.now - last_activity) > PONG_TIMEOUT
|
|
100
|
+
puts "Slack connection timed out (no pong received). Reconnecting..."
|
|
101
|
+
break
|
|
102
|
+
end
|
|
103
|
+
send_ws_ping(ssl)
|
|
104
|
+
ping_sent = true
|
|
105
|
+
next
|
|
106
|
+
end
|
|
107
|
+
|
|
83
108
|
data = read_ws_frame(ssl)
|
|
109
|
+
last_activity = Time.now
|
|
110
|
+
ping_sent = false
|
|
84
111
|
next unless data
|
|
85
112
|
|
|
86
113
|
begin
|
|
@@ -130,6 +157,11 @@ module RailsConsoleAi
|
|
|
130
157
|
send_ws_pong(ssl, payload)
|
|
131
158
|
return nil
|
|
132
159
|
end
|
|
160
|
+
# Handle pong (opcode 10) — response to our keepalive ping
|
|
161
|
+
if opcode == 0xA
|
|
162
|
+
read_ws_payload(ssl) # consume payload
|
|
163
|
+
return nil
|
|
164
|
+
end
|
|
133
165
|
# Close frame (opcode 8)
|
|
134
166
|
return nil if opcode == 8
|
|
135
167
|
# Only process text frames (opcode 1)
|
|
@@ -194,6 +226,14 @@ module RailsConsoleAi
|
|
|
194
226
|
ssl.write(frame)
|
|
195
227
|
end
|
|
196
228
|
|
|
229
|
+
def send_ws_ping(ssl)
|
|
230
|
+
mask_key = 4.times.map { rand(256) }
|
|
231
|
+
frame = [0x89].pack("C") # FIN + ping opcode
|
|
232
|
+
frame << [0x80].pack("C") # masked, zero-length payload
|
|
233
|
+
frame << mask_key.pack("C*")
|
|
234
|
+
ssl.write(frame)
|
|
235
|
+
end
|
|
236
|
+
|
|
197
237
|
# --- Slack Web API (minimal, uses Net::HTTP) ---
|
|
198
238
|
|
|
199
239
|
def slack_api(method, token: @bot_token, **params)
|
|
@@ -309,6 +349,7 @@ module RailsConsoleAi
|
|
|
309
349
|
|
|
310
350
|
session[:thread] = Thread.new do
|
|
311
351
|
Thread.current.report_on_exception = false
|
|
352
|
+
Thread.current[:log_prefix] = "[#{channel_id}/#{thread_ts}] @#{user_name}"
|
|
312
353
|
begin
|
|
313
354
|
channel.display_dim("_session: #{channel_id}/#{thread_ts}_")
|
|
314
355
|
if restored
|
|
@@ -351,6 +392,7 @@ module RailsConsoleAi
|
|
|
351
392
|
# Otherwise treat as a new message in the conversation
|
|
352
393
|
session[:thread] = Thread.new do
|
|
353
394
|
Thread.current.report_on_exception = false
|
|
395
|
+
Thread.current[:log_prefix] = channel.instance_variable_get(:@log_prefix)
|
|
354
396
|
begin
|
|
355
397
|
engine.process_message(text)
|
|
356
398
|
rescue => e
|
data/lib/rails_console_ai.rb
CHANGED
|
@@ -94,7 +94,8 @@ module RailsConsoleAi
|
|
|
94
94
|
table = 'rails_console_ai_sessions'
|
|
95
95
|
|
|
96
96
|
if conn.table_exists?(table)
|
|
97
|
-
$stdout.puts "\e[32mRailsConsoleAi: #{table} already exists
|
|
97
|
+
$stdout.puts "\e[32mRailsConsoleAi: #{table} already exists — checking for pending migrations.\e[0m"
|
|
98
|
+
migrate!
|
|
98
99
|
else
|
|
99
100
|
conn.create_table(table) do |t|
|
|
100
101
|
t.text :query, null: false
|
|
@@ -151,6 +152,7 @@ module RailsConsoleAi
|
|
|
151
152
|
if migrations.empty?
|
|
152
153
|
$stdout.puts "\e[32mRailsConsoleAi: #{table} is up to date.\e[0m"
|
|
153
154
|
else
|
|
155
|
+
RailsConsoleAi::Session.reset_column_information if defined?(RailsConsoleAi::Session)
|
|
154
156
|
$stdout.puts "\e[32mRailsConsoleAi: added columns: #{migrations.join(', ')}.\e[0m"
|
|
155
157
|
end
|
|
156
158
|
rescue => e
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_console_ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cortfr
|
|
@@ -109,6 +109,7 @@ files:
|
|
|
109
109
|
- lib/rails_console_ai/conversation_engine.rb
|
|
110
110
|
- lib/rails_console_ai/engine.rb
|
|
111
111
|
- lib/rails_console_ai/executor.rb
|
|
112
|
+
- lib/rails_console_ai/prefixed_io.rb
|
|
112
113
|
- lib/rails_console_ai/providers/anthropic.rb
|
|
113
114
|
- lib/rails_console_ai/providers/base.rb
|
|
114
115
|
- lib/rails_console_ai/providers/bedrock.rb
|