rails_console_ai 0.28.0 → 0.30.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +48 -0
  4. data/app/controllers/rails_console_ai/agent_versions_controller.rb +36 -0
  5. data/app/controllers/rails_console_ai/agents_controller.rb +199 -0
  6. data/app/controllers/rails_console_ai/application_controller.rb +5 -0
  7. data/app/controllers/rails_console_ai/memories_controller.rb +159 -0
  8. data/app/controllers/rails_console_ai/memory_versions_controller.rb +33 -0
  9. data/app/controllers/rails_console_ai/skill_versions_controller.rb +35 -0
  10. data/app/controllers/rails_console_ai/skills_controller.rb +200 -0
  11. data/app/helpers/rails_console_ai/diff_helper.rb +114 -0
  12. data/app/models/rails_console_ai/agent.rb +175 -0
  13. data/app/models/rails_console_ai/agent_version.rb +46 -0
  14. data/app/models/rails_console_ai/memory.rb +98 -0
  15. data/app/models/rails_console_ai/memory_version.rb +46 -0
  16. data/app/models/rails_console_ai/session.rb +1 -1
  17. data/app/models/rails_console_ai/skill.rb +198 -0
  18. data/app/models/rails_console_ai/skill_version.rb +54 -0
  19. data/app/views/layouts/rails_console_ai/application.html.erb +78 -1
  20. data/app/views/rails_console_ai/agent_versions/index.html.erb +28 -0
  21. data/app/views/rails_console_ai/agent_versions/show.html.erb +25 -0
  22. data/app/views/rails_console_ai/agents/_form.html.erb +65 -0
  23. data/app/views/rails_console_ai/agents/diff.html.erb +19 -0
  24. data/app/views/rails_console_ai/agents/edit.html.erb +7 -0
  25. data/app/views/rails_console_ai/agents/index.html.erb +80 -0
  26. data/app/views/rails_console_ai/agents/new.html.erb +24 -0
  27. data/app/views/rails_console_ai/agents/show.html.erb +108 -0
  28. data/app/views/rails_console_ai/memories/_form.html.erb +36 -0
  29. data/app/views/rails_console_ai/memories/diff.html.erb +19 -0
  30. data/app/views/rails_console_ai/memories/edit.html.erb +7 -0
  31. data/app/views/rails_console_ai/memories/index.html.erb +67 -0
  32. data/app/views/rails_console_ai/memories/new.html.erb +23 -0
  33. data/app/views/rails_console_ai/memories/show.html.erb +65 -0
  34. data/app/views/rails_console_ai/memory_versions/index.html.erb +26 -0
  35. data/app/views/rails_console_ai/memory_versions/show.html.erb +21 -0
  36. data/app/views/rails_console_ai/skill_versions/index.html.erb +28 -0
  37. data/app/views/rails_console_ai/skill_versions/show.html.erb +23 -0
  38. data/app/views/rails_console_ai/skills/_form.html.erb +65 -0
  39. data/app/views/rails_console_ai/skills/diff.html.erb +22 -0
  40. data/app/views/rails_console_ai/skills/edit.html.erb +7 -0
  41. data/app/views/rails_console_ai/skills/index.html.erb +79 -0
  42. data/app/views/rails_console_ai/skills/new.html.erb +25 -0
  43. data/app/views/rails_console_ai/skills/show.html.erb +94 -0
  44. data/config/routes.rb +42 -0
  45. data/lib/rails_console_ai/agent_loader.rb +131 -43
  46. data/lib/rails_console_ai/agent_runner.rb +158 -0
  47. data/lib/rails_console_ai/channel/api.rb +139 -0
  48. data/lib/rails_console_ai/channel/slack.rb +33 -0
  49. data/lib/rails_console_ai/channel/sub_agent.rb +12 -0
  50. data/lib/rails_console_ai/conversation_engine.rb +50 -13
  51. data/lib/rails_console_ai/session_logger.rb +6 -0
  52. data/lib/rails_console_ai/skill_loader.rb +119 -27
  53. data/lib/rails_console_ai/slack_bot.rb +8 -0
  54. data/lib/rails_console_ai/storage/database_storage.rb +201 -0
  55. data/lib/rails_console_ai/sub_agent.rb +25 -0
  56. data/lib/rails_console_ai/tools/memory_tools.rb +102 -32
  57. data/lib/rails_console_ai/tools/registry.rb +99 -8
  58. data/lib/rails_console_ai/version.rb +1 -1
  59. data/lib/rails_console_ai.rb +256 -0
  60. data/lib/tasks/rails_console_ai.rake +7 -0
  61. metadata +55 -1
@@ -55,6 +55,39 @@ module RailsConsoleAi
55
55
  @current_user = name
56
56
  end
57
57
 
58
+ # Enqueue an agent run. Returns the Integer session id immediately;
59
+ # the actual work is picked up by `rake rails_console_ai:agents`.
60
+ def run_agent(query, name: nil, user_name: nil)
61
+ require 'rails_console_ai/session_logger'
62
+ id = SessionLogger.log(
63
+ query: query,
64
+ conversation: [],
65
+ mode: 'agent_api',
66
+ name: name,
67
+ user_name: user_name,
68
+ status: 'queued',
69
+ executed: false
70
+ )
71
+ raise 'Failed to enqueue agent run (session logging disabled or table missing)' unless id
72
+ id
73
+ end
74
+
75
+ # Returns the current status string for an enqueued agent run, or nil
76
+ # if the session id is not found. Status is one of:
77
+ # 'queued' | 'running' | 'ready' | 'failed'.
78
+ def check_agent(session_id)
79
+ Session.where(id: session_id).pluck(:status).first
80
+ end
81
+
82
+ # Returns a hash describing an agent run:
83
+ # { status:, result:, error: }
84
+ # All three keys are nil when the session id is not found.
85
+ def get_agent_response(session_id)
86
+ row = Session.where(id: session_id).select(:status, :result, :error_message).first
87
+ return { status: nil, result: nil, error: nil } unless row
88
+ { status: row.status, result: row.result, error: row.error_message }
89
+ end
90
+
58
91
  def status
59
92
  c = configuration
60
93
  key = c.resolved_api_key
@@ -123,11 +156,183 @@ module RailsConsoleAi
123
156
  $stdout.puts "\e[32mRailsConsoleAi: created #{table} table.\e[0m"
124
157
  end
125
158
 
159
+ setup_skills_tables!(conn)
160
+ setup_memories_tables!(conn)
161
+ setup_agents_tables!(conn)
162
+
126
163
  migrate!
127
164
  rescue => e
128
165
  $stderr.puts "\e[31mRailsConsoleAi setup failed: #{e.class}: #{e.message}\e[0m"
129
166
  end
130
167
 
168
+ def setup_skills_tables!(conn)
169
+ skills_table = 'rails_console_ai_skills'
170
+ versions_table = 'rails_console_ai_skill_versions'
171
+
172
+ unless conn.table_exists?(skills_table)
173
+ conn.create_table(skills_table) do |t|
174
+ t.string :name, limit: 255, null: false
175
+ t.text :description
176
+ t.text :body
177
+ t.text :tags
178
+ t.text :bypass_guards_for_methods
179
+ t.string :status, limit: 20, default: 'proposed', null: false
180
+ t.string :approved_by, limit: 255
181
+ t.datetime :approved_at
182
+ t.integer :use_count, default: 0, null: false
183
+ t.datetime :last_used_at
184
+ t.datetime :created_at, null: false
185
+ t.datetime :updated_at, null: false
186
+ end
187
+ conn.add_index(skills_table, :name, unique: true)
188
+ conn.add_index(skills_table, :status)
189
+ $stdout.puts "\e[32mRailsConsoleAi: created #{skills_table} table.\e[0m"
190
+ else
191
+ # Idempotent column-add probes for existing installs.
192
+ unless conn.column_exists?(skills_table, :status)
193
+ conn.add_column(skills_table, :status, :string, limit: 20, default: 'proposed', null: false)
194
+ conn.add_index(skills_table, :status) unless conn.index_exists?(skills_table, :status)
195
+ end
196
+ unless conn.column_exists?(skills_table, :approved_by)
197
+ conn.add_column(skills_table, :approved_by, :string, limit: 255)
198
+ end
199
+ unless conn.column_exists?(skills_table, :approved_at)
200
+ conn.add_column(skills_table, :approved_at, :datetime)
201
+ end
202
+ unless conn.column_exists?(skills_table, :use_count)
203
+ conn.add_column(skills_table, :use_count, :integer, default: 0, null: false)
204
+ end
205
+ unless conn.column_exists?(skills_table, :last_used_at)
206
+ conn.add_column(skills_table, :last_used_at, :datetime)
207
+ end
208
+ end
209
+
210
+ unless conn.table_exists?(versions_table)
211
+ conn.create_table(versions_table) do |t|
212
+ t.integer :skill_id
213
+ t.string :name, limit: 255
214
+ t.text :description
215
+ t.text :body
216
+ t.text :tags
217
+ t.text :bypass_guards_for_methods
218
+ t.string :status, limit: 20
219
+ t.string :edited_by, limit: 255
220
+ t.text :change_note
221
+ t.datetime :created_at, null: false
222
+ end
223
+ conn.add_index(versions_table, :skill_id)
224
+ conn.add_index(versions_table, :created_at)
225
+ $stdout.puts "\e[32mRailsConsoleAi: created #{versions_table} table.\e[0m"
226
+ else
227
+ unless conn.column_exists?(versions_table, :status)
228
+ conn.add_column(versions_table, :status, :string, limit: 20)
229
+ end
230
+ end
231
+ end
232
+
233
+ def setup_memories_tables!(conn)
234
+ memories_table = 'rails_console_ai_memories'
235
+ versions_table = 'rails_console_ai_memory_versions'
236
+
237
+ unless conn.table_exists?(memories_table)
238
+ conn.create_table(memories_table) do |t|
239
+ t.string :name, limit: 255, null: false
240
+ t.text :description
241
+ t.text :tags
242
+ t.integer :use_count, default: 0, null: false
243
+ t.datetime :last_used_at
244
+ t.datetime :created_at, null: false
245
+ t.datetime :updated_at, null: false
246
+ end
247
+ conn.add_index(memories_table, :name, unique: true)
248
+ $stdout.puts "\e[32mRailsConsoleAi: created #{memories_table} table.\e[0m"
249
+ else
250
+ unless conn.column_exists?(memories_table, :use_count)
251
+ conn.add_column(memories_table, :use_count, :integer, default: 0, null: false)
252
+ end
253
+ unless conn.column_exists?(memories_table, :last_used_at)
254
+ conn.add_column(memories_table, :last_used_at, :datetime)
255
+ end
256
+ end
257
+
258
+ unless conn.table_exists?(versions_table)
259
+ conn.create_table(versions_table) do |t|
260
+ t.integer :memory_id
261
+ t.string :name, limit: 255
262
+ t.text :description
263
+ t.text :tags
264
+ t.string :edited_by, limit: 255
265
+ t.text :change_note
266
+ t.datetime :created_at, null: false
267
+ end
268
+ conn.add_index(versions_table, :memory_id)
269
+ conn.add_index(versions_table, :created_at)
270
+ $stdout.puts "\e[32mRailsConsoleAi: created #{versions_table} table.\e[0m"
271
+ end
272
+ end
273
+
274
+ def setup_agents_tables!(conn)
275
+ agents_table = 'rails_console_ai_agents'
276
+ versions_table = 'rails_console_ai_agent_versions'
277
+
278
+ unless conn.table_exists?(agents_table)
279
+ conn.create_table(agents_table) do |t|
280
+ t.string :name, limit: 255, null: false
281
+ t.text :description
282
+ t.text :body
283
+ t.integer :max_rounds
284
+ t.string :model, limit: 100
285
+ t.text :tools
286
+ t.string :status, limit: 20, default: 'proposed', null: false
287
+ t.string :approved_by, limit: 255
288
+ t.datetime :approved_at
289
+ t.integer :use_count, default: 0, null: false
290
+ t.datetime :last_used_at
291
+ t.datetime :created_at, null: false
292
+ t.datetime :updated_at, null: false
293
+ end
294
+ conn.add_index(agents_table, :name, unique: true)
295
+ conn.add_index(agents_table, :status)
296
+ $stdout.puts "\e[32mRailsConsoleAi: created #{agents_table} table.\e[0m"
297
+ else
298
+ unless conn.column_exists?(agents_table, :status)
299
+ conn.add_column(agents_table, :status, :string, limit: 20, default: 'proposed', null: false)
300
+ conn.add_index(agents_table, :status) unless conn.index_exists?(agents_table, :status)
301
+ end
302
+ unless conn.column_exists?(agents_table, :approved_by)
303
+ conn.add_column(agents_table, :approved_by, :string, limit: 255)
304
+ end
305
+ unless conn.column_exists?(agents_table, :approved_at)
306
+ conn.add_column(agents_table, :approved_at, :datetime)
307
+ end
308
+ unless conn.column_exists?(agents_table, :use_count)
309
+ conn.add_column(agents_table, :use_count, :integer, default: 0, null: false)
310
+ end
311
+ unless conn.column_exists?(agents_table, :last_used_at)
312
+ conn.add_column(agents_table, :last_used_at, :datetime)
313
+ end
314
+ end
315
+
316
+ unless conn.table_exists?(versions_table)
317
+ conn.create_table(versions_table) do |t|
318
+ t.integer :agent_id
319
+ t.string :name, limit: 255
320
+ t.text :description
321
+ t.text :body
322
+ t.integer :max_rounds
323
+ t.string :model, limit: 100
324
+ t.text :tools
325
+ t.string :status, limit: 20
326
+ t.string :edited_by, limit: 255
327
+ t.text :change_note
328
+ t.datetime :created_at, null: false
329
+ end
330
+ conn.add_index(versions_table, :agent_id)
331
+ conn.add_index(versions_table, :created_at)
332
+ $stdout.puts "\e[32mRailsConsoleAi: created #{versions_table} table.\e[0m"
333
+ end
334
+ end
335
+
131
336
  def migrate!
132
337
  conn = session_connection
133
338
  table = 'rails_console_ai_sessions'
@@ -156,6 +361,50 @@ module RailsConsoleAi
156
361
  migrations << 'slack_channel_name'
157
362
  end
158
363
 
364
+ unless conn.column_exists?(table, :status)
365
+ conn.add_column(table, :status, :string, limit: 20)
366
+ migrations << 'status'
367
+ end
368
+
369
+ unless conn.column_exists?(table, :result)
370
+ conn.add_column(table, :result, :text)
371
+ migrations << 'result'
372
+ end
373
+
374
+ unless conn.column_exists?(table, :error_message)
375
+ conn.add_column(table, :error_message, :text)
376
+ migrations << 'error_message'
377
+ end
378
+
379
+ unless conn.index_exists?(table, [:mode, :status], name: 'idx_rca_sessions_mode_status')
380
+ conn.add_index(table, [:mode, :status], name: 'idx_rca_sessions_mode_status')
381
+ migrations << 'idx_rca_sessions_mode_status'
382
+ end
383
+
384
+ # Bring skills/memories/agents tables fully up to date. Each setup_* method is
385
+ # internally idempotent (guards both `create_table` and every `add_column` /
386
+ # `add_index`), so running it on an existing install adds any missing columns
387
+ # (e.g. `status`, `approved_by`, `approved_at`) and indexes without disturbing
388
+ # data. Note: we always call these — the previous version skipped them when
389
+ # the base table already existed, which meant column probes never ran on
390
+ # upgrade and methods like Skill#status hit NameError. See:
391
+ # https://github.com/cortfr/rails_console_ai/issues (whichever issue you file)
392
+ pre_columns = {
393
+ skills: table_columns(conn, 'rails_console_ai_skills'),
394
+ memories: table_columns(conn, 'rails_console_ai_memories'),
395
+ agents: table_columns(conn, 'rails_console_ai_agents')
396
+ }
397
+
398
+ setup_skills_tables!(conn)
399
+ setup_memories_tables!(conn)
400
+ setup_agents_tables!(conn)
401
+
402
+ [[:skills, 'rails_console_ai_skills'], [:memories, 'rails_console_ai_memories'], [:agents, 'rails_console_ai_agents']].each do |key, name|
403
+ post = table_columns(conn, name)
404
+ added = post - pre_columns[key]
405
+ migrations.concat(added.map { |c| "#{name}.#{c}" }) unless added.empty?
406
+ end
407
+
159
408
  if migrations.empty?
160
409
  $stdout.puts "\e[32mRailsConsoleAi: #{table} is up to date.\e[0m"
161
410
  else
@@ -214,6 +463,13 @@ module RailsConsoleAi
214
463
  ActiveRecord::Base.connection
215
464
  end
216
465
  end
466
+
467
+ def table_columns(conn, table_name)
468
+ return [] unless conn.table_exists?(table_name)
469
+ conn.columns(table_name).map { |c| c.name }
470
+ rescue
471
+ []
472
+ end
217
473
  end
218
474
  end
219
475
 
@@ -4,4 +4,11 @@ namespace :rails_console_ai do
4
4
  require 'rails_console_ai/slack_bot'
5
5
  RailsConsoleAi::SlackBot.new.start
6
6
  end
7
+
8
+ desc "Run the RailsConsoleAi agent runner (polls DB for queued agent runs)"
9
+ task agents: :environment do
10
+ require 'rails_console_ai/agent_runner'
11
+ concurrency = Integer(ENV['AGENT_CONCURRENCY'] || RailsConsoleAi::AgentRunner::DEFAULT_CONCURRENCY)
12
+ RailsConsoleAi::AgentRunner.new(concurrency: concurrency).start
13
+ end
7
14
  end
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.28.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cortfr
@@ -79,6 +79,20 @@ dependencies:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
81
  version: '12.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: sqlite3
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '1.4'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '1.4'
82
96
  description: An LLM-powered agent for your Rails console. Ask questions in natural
83
97
  language, get executable Ruby code.
84
98
  email: cortfr@gmail.com
@@ -89,20 +103,59 @@ files:
89
103
  - CHANGELOG.md
90
104
  - LICENSE
91
105
  - README.md
106
+ - app/controllers/rails_console_ai/agent_versions_controller.rb
107
+ - app/controllers/rails_console_ai/agents_controller.rb
92
108
  - app/controllers/rails_console_ai/application_controller.rb
109
+ - app/controllers/rails_console_ai/memories_controller.rb
110
+ - app/controllers/rails_console_ai/memory_versions_controller.rb
93
111
  - app/controllers/rails_console_ai/sessions_controller.rb
112
+ - app/controllers/rails_console_ai/skill_versions_controller.rb
113
+ - app/controllers/rails_console_ai/skills_controller.rb
114
+ - app/helpers/rails_console_ai/diff_helper.rb
94
115
  - app/helpers/rails_console_ai/sessions_helper.rb
116
+ - app/models/rails_console_ai/agent.rb
117
+ - app/models/rails_console_ai/agent_version.rb
118
+ - app/models/rails_console_ai/memory.rb
119
+ - app/models/rails_console_ai/memory_version.rb
95
120
  - app/models/rails_console_ai/session.rb
121
+ - app/models/rails_console_ai/skill.rb
122
+ - app/models/rails_console_ai/skill_version.rb
96
123
  - app/views/layouts/rails_console_ai/application.html.erb
124
+ - app/views/rails_console_ai/agent_versions/index.html.erb
125
+ - app/views/rails_console_ai/agent_versions/show.html.erb
126
+ - app/views/rails_console_ai/agents/_form.html.erb
127
+ - app/views/rails_console_ai/agents/diff.html.erb
128
+ - app/views/rails_console_ai/agents/edit.html.erb
129
+ - app/views/rails_console_ai/agents/index.html.erb
130
+ - app/views/rails_console_ai/agents/new.html.erb
131
+ - app/views/rails_console_ai/agents/show.html.erb
132
+ - app/views/rails_console_ai/memories/_form.html.erb
133
+ - app/views/rails_console_ai/memories/diff.html.erb
134
+ - app/views/rails_console_ai/memories/edit.html.erb
135
+ - app/views/rails_console_ai/memories/index.html.erb
136
+ - app/views/rails_console_ai/memories/new.html.erb
137
+ - app/views/rails_console_ai/memories/show.html.erb
138
+ - app/views/rails_console_ai/memory_versions/index.html.erb
139
+ - app/views/rails_console_ai/memory_versions/show.html.erb
97
140
  - app/views/rails_console_ai/sessions/index.html.erb
98
141
  - app/views/rails_console_ai/sessions/show.html.erb
142
+ - app/views/rails_console_ai/skill_versions/index.html.erb
143
+ - app/views/rails_console_ai/skill_versions/show.html.erb
144
+ - app/views/rails_console_ai/skills/_form.html.erb
145
+ - app/views/rails_console_ai/skills/diff.html.erb
146
+ - app/views/rails_console_ai/skills/edit.html.erb
147
+ - app/views/rails_console_ai/skills/index.html.erb
148
+ - app/views/rails_console_ai/skills/new.html.erb
149
+ - app/views/rails_console_ai/skills/show.html.erb
99
150
  - config/routes.rb
100
151
  - lib/generators/rails_console_ai/install_generator.rb
101
152
  - lib/generators/rails_console_ai/templates/initializer.rb
102
153
  - lib/rails_console_ai.rb
103
154
  - lib/rails_console_ai/agent_loader.rb
155
+ - lib/rails_console_ai/agent_runner.rb
104
156
  - lib/rails_console_ai/agents/explore-data.md
105
157
  - lib/rails_console_ai/agents/investigate-code.md
158
+ - lib/rails_console_ai/channel/api.rb
106
159
  - lib/rails_console_ai/channel/base.rb
107
160
  - lib/rails_console_ai/channel/console.rb
108
161
  - lib/rails_console_ai/channel/slack.rb
@@ -126,6 +179,7 @@ files:
126
179
  - lib/rails_console_ai/skill_loader.rb
127
180
  - lib/rails_console_ai/slack_bot.rb
128
181
  - lib/rails_console_ai/storage/base.rb
182
+ - lib/rails_console_ai/storage/database_storage.rb
129
183
  - lib/rails_console_ai/storage/file_storage.rb
130
184
  - lib/rails_console_ai/sub_agent.rb
131
185
  - lib/rails_console_ai/tools/code_tools.rb