rails-ai-context 4.3.2 → 4.4.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -53
  3. data/CLAUDE.md +3 -1
  4. data/README.md +268 -197
  5. data/demo-trace.gif +0 -0
  6. data/demo-trace.tape +21 -0
  7. data/demo.gif +0 -0
  8. data/demo.tape +33 -0
  9. data/docs/GUIDE.md +9 -9
  10. data/lib/generators/rails_ai_context/install/install_generator.rb +2 -1
  11. data/lib/rails_ai_context/cli/tool_runner.rb +1 -1
  12. data/lib/rails_ai_context/configuration.rb +25 -1
  13. data/lib/rails_ai_context/doctor.rb +4 -2
  14. data/lib/rails_ai_context/fingerprinter.rb +6 -1
  15. data/lib/rails_ai_context/introspectors/accessibility_introspector.rb +52 -1
  16. data/lib/rails_ai_context/introspectors/action_mailbox_introspector.rb +10 -2
  17. data/lib/rails_ai_context/introspectors/action_text_introspector.rb +22 -2
  18. data/lib/rails_ai_context/introspectors/active_storage_introspector.rb +50 -4
  19. data/lib/rails_ai_context/introspectors/api_introspector.rb +41 -5
  20. data/lib/rails_ai_context/introspectors/asset_pipeline_introspector.rb +10 -4
  21. data/lib/rails_ai_context/introspectors/auth_introspector.rb +62 -7
  22. data/lib/rails_ai_context/introspectors/component_introspector.rb +6 -0
  23. data/lib/rails_ai_context/introspectors/config_introspector.rb +59 -9
  24. data/lib/rails_ai_context/introspectors/controller_introspector.rb +45 -13
  25. data/lib/rails_ai_context/introspectors/convention_detector.rb +25 -2
  26. data/lib/rails_ai_context/introspectors/database_stats_introspector.rb +58 -4
  27. data/lib/rails_ai_context/introspectors/design_token_introspector.rb +27 -5
  28. data/lib/rails_ai_context/introspectors/devops_introspector.rb +15 -8
  29. data/lib/rails_ai_context/introspectors/engine_introspector.rb +12 -3
  30. data/lib/rails_ai_context/introspectors/frontend_framework_introspector.rb +36 -1
  31. data/lib/rails_ai_context/introspectors/gem_introspector.rb +47 -1
  32. data/lib/rails_ai_context/introspectors/i18n_introspector.rb +49 -3
  33. data/lib/rails_ai_context/introspectors/job_introspector.rb +48 -5
  34. data/lib/rails_ai_context/introspectors/middleware_introspector.rb +24 -3
  35. data/lib/rails_ai_context/introspectors/migration_introspector.rb +4 -1
  36. data/lib/rails_ai_context/introspectors/model_introspector.rb +108 -11
  37. data/lib/rails_ai_context/introspectors/multi_database_introspector.rb +57 -12
  38. data/lib/rails_ai_context/introspectors/performance_introspector.rb +34 -9
  39. data/lib/rails_ai_context/introspectors/rake_task_introspector.rb +12 -2
  40. data/lib/rails_ai_context/introspectors/route_introspector.rb +25 -8
  41. data/lib/rails_ai_context/introspectors/schema_introspector.rb +45 -7
  42. data/lib/rails_ai_context/introspectors/seeds_introspector.rb +5 -2
  43. data/lib/rails_ai_context/introspectors/stimulus_introspector.rb +59 -6
  44. data/lib/rails_ai_context/introspectors/test_introspector.rb +50 -5
  45. data/lib/rails_ai_context/introspectors/turbo_introspector.rb +44 -13
  46. data/lib/rails_ai_context/introspectors/view_introspector.rb +46 -7
  47. data/lib/rails_ai_context/introspectors/view_template_introspector.rb +25 -7
  48. data/lib/rails_ai_context/resources.rb +1 -1
  49. data/lib/rails_ai_context/server.rb +6 -3
  50. data/lib/rails_ai_context/tasks/rails_ai_context.rake +8 -4
  51. data/lib/rails_ai_context/tools/analyze_feature.rb +66 -19
  52. data/lib/rails_ai_context/tools/base_tool.rb +1 -1
  53. data/lib/rails_ai_context/tools/diagnose.rb +4 -2
  54. data/lib/rails_ai_context/tools/get_callbacks.rb +4 -2
  55. data/lib/rails_ai_context/tools/get_concern.rb +12 -6
  56. data/lib/rails_ai_context/tools/get_controllers.rb +10 -5
  57. data/lib/rails_ai_context/tools/get_conventions.rb +4 -2
  58. data/lib/rails_ai_context/tools/get_design_system.rb +2 -1
  59. data/lib/rails_ai_context/tools/get_env.rb +8 -4
  60. data/lib/rails_ai_context/tools/get_helper_methods.rb +6 -3
  61. data/lib/rails_ai_context/tools/get_job_pattern.rb +2 -1
  62. data/lib/rails_ai_context/tools/get_model_details.rb +10 -5
  63. data/lib/rails_ai_context/tools/get_partial_interface.rb +14 -7
  64. data/lib/rails_ai_context/tools/get_schema.rb +2 -1
  65. data/lib/rails_ai_context/tools/get_service_pattern.rb +2 -1
  66. data/lib/rails_ai_context/tools/get_stimulus.rb +2 -1
  67. data/lib/rails_ai_context/tools/get_test_info.rb +4 -2
  68. data/lib/rails_ai_context/tools/get_turbo_map.rb +22 -11
  69. data/lib/rails_ai_context/tools/get_view.rb +6 -3
  70. data/lib/rails_ai_context/tools/migration_advisor.rb +2 -1
  71. data/lib/rails_ai_context/tools/onboard.rb +2 -1
  72. data/lib/rails_ai_context/tools/performance_check.rb +2 -1
  73. data/lib/rails_ai_context/tools/query.rb +5 -1
  74. data/lib/rails_ai_context/tools/read_logs.rb +3 -0
  75. data/lib/rails_ai_context/tools/runtime_info.rb +10 -5
  76. data/lib/rails_ai_context/tools/search_code.rb +8 -4
  77. data/lib/rails_ai_context/tools/search_docs.rb +2 -1
  78. data/lib/rails_ai_context/tools/session_context.rb +2 -1
  79. data/lib/rails_ai_context/tools/validate.rb +16 -8
  80. data/lib/rails_ai_context/version.rb +1 -1
  81. metadata +5 -1
@@ -294,7 +294,8 @@ module RailsAiContext
294
294
  end
295
295
 
296
296
  methods
297
- rescue
297
+ rescue => e
298
+ $stderr.puts "[rails-ai-context] parse_public_methods failed: #{e.message}" if ENV["DEBUG"]
298
299
  []
299
300
  end
300
301
 
@@ -332,7 +333,8 @@ module RailsAiContext
332
333
  end
333
334
 
334
335
  methods
335
- rescue
336
+ rescue => e
337
+ $stderr.puts "[rails-ai-context] parse_class_methods failed: #{e.message}" if ENV["DEBUG"]
336
338
  []
337
339
  end
338
340
 
@@ -364,7 +366,8 @@ module RailsAiContext
364
366
  end
365
367
 
366
368
  macros
367
- rescue
369
+ rescue => e
370
+ $stderr.puts "[rails-ai-context] parse_concern_macros failed: #{e.message}" if ENV["DEBUG"]
368
371
  []
369
372
  end
370
373
 
@@ -379,7 +382,8 @@ module RailsAiContext
379
382
  end
380
383
 
381
384
  callbacks
382
- rescue
385
+ rescue => e
386
+ $stderr.puts "[rails-ai-context] parse_concern_callbacks failed: #{e.message}" if ENV["DEBUG"]
383
387
  []
384
388
  end
385
389
 
@@ -405,7 +409,8 @@ module RailsAiContext
405
409
  end
406
410
 
407
411
  result.join("\n")
408
- rescue
412
+ rescue => e
413
+ $stderr.puts "[rails-ai-context] extract_method_source failed: #{e.message}" if ENV["DEBUG"]
409
414
  nil
410
415
  end
411
416
 
@@ -448,7 +453,8 @@ module RailsAiContext
448
453
  end
449
454
 
450
455
  includers.sort
451
- rescue
456
+ rescue => e
457
+ $stderr.puts "[rails-ai-context] find_includers failed: #{e.message}" if ENV["DEBUG"]
452
458
  []
453
459
  end
454
460
  end
@@ -301,7 +301,8 @@ module RailsAiContext
301
301
  next unless body
302
302
  { name: method_name, code: body[:code], start_line: body[:start_line], end_line: body[:end_line] }
303
303
  end.first(5) # Limit to 5 to avoid overwhelming response
304
- rescue
304
+ rescue => e
305
+ $stderr.puts "[rails-ai-context] detect_called_private_methods failed: #{e.message}" if ENV["DEBUG"]
305
306
  []
306
307
  end
307
308
 
@@ -332,7 +333,8 @@ module RailsAiContext
332
333
  end
333
334
  end
334
335
  filters
335
- rescue
336
+ rescue => e
337
+ $stderr.puts "[rails-ai-context] detect_parent_filters failed: #{e.message}" if ENV["DEBUG"]
336
338
  []
337
339
  end
338
340
 
@@ -359,7 +361,8 @@ module RailsAiContext
359
361
  end
360
362
  end
361
363
  skipped
362
- rescue
364
+ rescue => e
365
+ $stderr.puts "[rails-ai-context] detect_skipped_filters failed: #{e.message}" if ENV["DEBUG"]
363
366
  []
364
367
  end
365
368
 
@@ -415,7 +418,8 @@ module RailsAiContext
415
418
  end
416
419
 
417
420
  { redirects: redirects.uniq, renders: renders.uniq, side_effects: side_effects.uniq }
418
- rescue
421
+ rescue => e
422
+ $stderr.puts "[rails-ai-context] extract_render_map failed: #{e.message}" if ENV["DEBUG"]
419
423
  { redirects: [], renders: [], side_effects: [] }
420
424
  end
421
425
 
@@ -443,7 +447,8 @@ module RailsAiContext
443
447
  start_line: start_idx + 1,
444
448
  end_line: end_idx + 1
445
449
  }
446
- rescue
450
+ rescue => e
451
+ $stderr.puts "[rails-ai-context] extract_method_with_lines failed: #{e.message}" if ENV["DEBUG"]
447
452
  nil
448
453
  end
449
454
 
@@ -374,7 +374,8 @@ module RailsAiContext
374
374
  end
375
375
 
376
376
  info
377
- rescue
377
+ rescue => e
378
+ $stderr.puts "[rails-ai-context] detect_locale_info failed: #{e.message}" if ENV["DEBUG"]
378
379
  []
379
380
  end
380
381
 
@@ -431,7 +432,8 @@ module RailsAiContext
431
432
  sections << "Detected from: #{detected_in.first(3).join(', ')}"
432
433
 
433
434
  sections
434
- rescue
435
+ rescue => e
436
+ $stderr.puts "[rails-ai-context] detect_test_pattern failed: #{e.message}" if ENV["DEBUG"]
435
437
  []
436
438
  end
437
439
  end
@@ -370,7 +370,8 @@ module RailsAiContext
370
370
  end
371
371
 
372
372
  lines
373
- rescue
373
+ rescue => e
374
+ $stderr.puts "[rails-ai-context] render_layout_patterns failed: #{e.message}" if ENV["DEBUG"]
374
375
  []
375
376
  end
376
377
 
@@ -442,7 +442,8 @@ module RailsAiContext
442
442
  end
443
443
 
444
444
  services.uniq { |s| "#{s[:name]}:#{s[:file]}" }
445
- rescue
445
+ rescue => e
446
+ $stderr.puts "[rails-ai-context] detect_http_clients failed: #{e.message}" if ENV["DEBUG"]
446
447
  []
447
448
  end
448
449
 
@@ -459,7 +460,8 @@ module RailsAiContext
459
460
  return nil if parts.size < 2
460
461
  # Use the main domain part
461
462
  parts[-2]&.capitalize
462
- rescue
463
+ rescue => e
464
+ $stderr.puts "[rails-ai-context] extract_service_name_from_url failed: #{e.message}" if ENV["DEBUG"]
463
465
  nil
464
466
  end
465
467
  end
@@ -484,7 +486,8 @@ module RailsAiContext
484
486
  end
485
487
 
486
488
  vars.to_a.sort
487
- rescue
489
+ rescue => e
490
+ $stderr.puts "[rails-ai-context] find_env_vars_with_prefix failed: #{e.message}" if ENV["DEBUG"]
488
491
  []
489
492
  end
490
493
 
@@ -639,7 +642,8 @@ module RailsAiContext
639
642
 
640
643
  private_class_method def self.safe_read(path)
641
644
  File.read(path, encoding: "UTF-8", invalid: :replace, undef: :replace)
642
- rescue
645
+ rescue => e
646
+ $stderr.puts "[rails-ai-context] safe_read failed: #{e.message}" if ENV["DEBUG"]
643
647
  nil
644
648
  end
645
649
 
@@ -220,7 +220,8 @@ module RailsAiContext
220
220
  end
221
221
 
222
222
  methods
223
- rescue
223
+ rescue => e
224
+ $stderr.puts "[rails-ai-context] parse_helper_methods failed: #{e.message}" if ENV["DEBUG"]
224
225
  []
225
226
  end
226
227
 
@@ -250,7 +251,8 @@ module RailsAiContext
250
251
  end
251
252
 
252
253
  references
253
- rescue
254
+ rescue => e
255
+ $stderr.puts "[rails-ai-context] find_view_references failed: #{e.message}" if ENV["DEBUG"]
254
256
  {}
255
257
  end
256
258
 
@@ -303,7 +305,8 @@ module RailsAiContext
303
305
  end
304
306
 
305
307
  detected
306
- rescue
308
+ rescue => e
309
+ $stderr.puts "[rails-ai-context] detect_framework_helpers failed: #{e.message}" if ENV["DEBUG"]
307
310
  {}
308
311
  end
309
312
  end
@@ -407,7 +407,8 @@ module RailsAiContext
407
407
 
408
408
  private_class_method def self.safe_read(path)
409
409
  File.read(path, encoding: "UTF-8", invalid: :replace, undef: :replace)
410
- rescue
410
+ rescue => e
411
+ $stderr.puts "[rails-ai-context] safe_read failed: #{e.message}" if ENV["DEBUG"]
411
412
  nil
412
413
  end
413
414
 
@@ -393,7 +393,8 @@ module RailsAiContext
393
393
  end
394
394
  end
395
395
  bodies
396
- rescue
396
+ rescue => e
397
+ $stderr.puts "[rails-ai-context] extract_custom_validate_bodies failed: #{e.message}" if ENV["DEBUG"]
397
398
  {}
398
399
  end
399
400
 
@@ -414,7 +415,8 @@ module RailsAiContext
414
415
  end
415
416
 
416
417
  methods.empty? ? nil : methods
417
- rescue
418
+ rescue => e
419
+ $stderr.puts "[rails-ai-context] extract_source_defined_methods failed: #{e.message}" if ENV["DEBUG"]
418
420
  nil
419
421
  end
420
422
 
@@ -439,7 +441,8 @@ module RailsAiContext
439
441
  end
440
442
 
441
443
  signatures
442
- rescue
444
+ rescue => e
445
+ $stderr.puts "[rails-ai-context] extract_method_signatures failed: #{e.message}" if ENV["DEBUG"]
443
446
  nil
444
447
  end
445
448
 
@@ -469,7 +472,8 @@ module RailsAiContext
469
472
  end
470
473
 
471
474
  methods.empty? ? nil : methods
472
- rescue
475
+ rescue => e
476
+ $stderr.puts "[rails-ai-context] extract_concern_methods failed: #{e.message}" if ENV["DEBUG"]
473
477
  nil
474
478
  end
475
479
 
@@ -512,7 +516,8 @@ module RailsAiContext
512
516
  sections << { start: current_start, end: source_lines.size, label: current_section } if current_section
513
517
 
514
518
  { path: path, total_lines: source_lines.size, sections: sections }
515
- rescue
519
+ rescue => e
520
+ $stderr.puts "[rails-ai-context] extract_model_structure failed: #{e.message}" if ENV["DEBUG"]
516
521
  nil
517
522
  end
518
523
  end
@@ -267,7 +267,8 @@ module RailsAiContext
267
267
  end
268
268
 
269
269
  locals.uniq
270
- rescue
270
+ rescue => e
271
+ $stderr.puts "[rails-ai-context] extract_magic_comment_locals failed: #{e.message}" if ENV["DEBUG"]
271
272
  []
272
273
  end
273
274
 
@@ -324,7 +325,8 @@ module RailsAiContext
324
325
 
325
326
  # Filter out things that are clearly method definitions or blocks
326
327
  locals.reject { |l| l.match?(/\A(each|map|select|reject|find|collect|do|end)\z/) }.to_a.sort
327
- rescue
328
+ rescue => e
329
+ $stderr.puts "[rails-ai-context] extract_local_variable_references failed: #{e.message}" if ENV["DEBUG"]
328
330
  []
329
331
  end
330
332
 
@@ -387,7 +389,8 @@ module RailsAiContext
387
389
  end
388
390
 
389
391
  sites
390
- rescue
392
+ rescue => e
393
+ $stderr.puts "[rails-ai-context] find_render_sites failed: #{e.message}" if ENV["DEBUG"]
391
394
  []
392
395
  end
393
396
 
@@ -414,7 +417,8 @@ module RailsAiContext
414
417
  end
415
418
 
416
419
  locals.uniq
417
- rescue
420
+ rescue => e
421
+ $stderr.puts "[rails-ai-context] extract_locals_from_render failed: #{e.message}" if ENV["DEBUG"]
418
422
  []
419
423
  end
420
424
 
@@ -443,7 +447,8 @@ module RailsAiContext
443
447
  end
444
448
 
445
449
  calls
446
- rescue
450
+ rescue => e
451
+ $stderr.puts "[rails-ai-context] extract_method_calls_on_locals failed: #{e.message}" if ENV["DEBUG"]
447
452
  {}
448
453
  end
449
454
 
@@ -456,13 +461,15 @@ module RailsAiContext
456
461
  parts[-1] = parts[-1].delete_prefix("_").sub(/\..*\z/, "")
457
462
  parts.join("/")
458
463
  end.sort.first(30)
459
- rescue
464
+ rescue => e
465
+ $stderr.puts "[rails-ai-context] find_available_partials failed: #{e.message}" if ENV["DEBUG"]
460
466
  []
461
467
  end
462
468
 
463
469
  private_class_method def self.safe_read(path)
464
470
  File.read(path, encoding: "UTF-8", invalid: :replace, undef: :replace)
465
- rescue
471
+ rescue => e
472
+ $stderr.puts "[rails-ai-context] safe_read failed: #{e.message}" if ENV["DEBUG"]
466
473
  nil
467
474
  end
468
475
 
@@ -205,7 +205,8 @@ module RailsAiContext
205
205
  return [] unless models.is_a?(Hash)
206
206
 
207
207
  models.select { |_, d| d.is_a?(Hash) && d[:table_name] == table_name }.keys
208
- rescue
208
+ rescue => e
209
+ $stderr.puts "[rails-ai-context] models_for_table failed: #{e.message}" if ENV["DEBUG"]
209
210
  []
210
211
  end
211
212
 
@@ -319,7 +319,8 @@ module RailsAiContext
319
319
 
320
320
  private_class_method def self.safe_read(path)
321
321
  File.read(path, encoding: "UTF-8", invalid: :replace, undef: :replace)
322
- rescue
322
+ rescue => e
323
+ $stderr.puts "[rails-ai-context] safe_read failed: #{e.message}" if ENV["DEBUG"]
323
324
  nil
324
325
  end
325
326
 
@@ -236,7 +236,8 @@ module RailsAiContext
236
236
  next unless content.include?(alt_pattern)
237
237
  path.sub("#{Rails.root}/app/views/", "")
238
238
  end.first(10)
239
- rescue
239
+ rescue => e
240
+ $stderr.puts "[rails-ai-context] find_views_using failed: #{e.message}" if ENV["DEBUG"]
240
241
  []
241
242
  end
242
243
 
@@ -333,7 +333,8 @@ module RailsAiContext
333
333
  end
334
334
 
335
335
  lines
336
- rescue
336
+ rescue => e
337
+ $stderr.puts "[rails-ai-context] generate_test_template failed: #{e.message}" if ENV["DEBUG"]
337
338
  []
338
339
  end
339
340
 
@@ -367,7 +368,8 @@ module RailsAiContext
367
368
  end
368
369
 
369
370
  lines.any? ? lines.join("\n") : nil
370
- rescue
371
+ rescue => e
372
+ $stderr.puts "[rails-ai-context] parse_factory_details failed: #{e.message}" if ENV["DEBUG"]
371
373
  nil
372
374
  end
373
375
 
@@ -490,7 +490,8 @@ module RailsAiContext
490
490
  match = line.match(/broadcasts_refreshes_to\s+:?(\w+)/)
491
491
  match ? match[1] : nil
492
492
  end
493
- rescue
493
+ rescue => e
494
+ $stderr.puts "[rails-ai-context] extract_stream_name_from_macro failed: #{e.message}" if ENV["DEBUG"]
494
495
  nil
495
496
  end
496
497
 
@@ -515,7 +516,8 @@ module RailsAiContext
515
516
  pattern = /#{Regexp.escape(method)}\s*\(?\s*:?["']?(\w+)["']?/
516
517
  match = line.match(pattern)
517
518
  match ? match[1] : "(dynamic)"
518
- rescue
519
+ rescue => e
520
+ $stderr.puts "[rails-ai-context] extract_stream_from_broadcast failed: #{e.message}" if ENV["DEBUG"]
519
521
  "(dynamic)"
520
522
  end
521
523
 
@@ -523,7 +525,8 @@ module RailsAiContext
523
525
  private_class_method def self.extract_target_from_broadcast(line)
524
526
  match = line.match(/target:\s*["'](\w+)["']/)
525
527
  match ? match[1] : nil
526
- rescue
528
+ rescue => e
529
+ $stderr.puts "[rails-ai-context] extract_target_from_broadcast failed: #{e.message}" if ENV["DEBUG"]
527
530
  nil
528
531
  end
529
532
 
@@ -531,7 +534,8 @@ module RailsAiContext
531
534
  private_class_method def self.extract_partial_from_broadcast(line)
532
535
  match = line.match(/partial:\s*["']([^"']+)["']/)
533
536
  match ? match[1] : nil
534
- rescue
537
+ rescue => e
538
+ $stderr.puts "[rails-ai-context] extract_partial_from_broadcast failed: #{e.message}" if ENV["DEBUG"]
535
539
  nil
536
540
  end
537
541
 
@@ -559,7 +563,8 @@ module RailsAiContext
559
563
 
560
564
  # Clean up and return meaningful stream name
561
565
  args.gsub(/["']/, "").gsub(/\s*,\s*/, ", ").strip
562
- rescue
566
+ rescue => e
567
+ $stderr.puts "[rails-ai-context] extract_stream_from_subscription failed: #{e.message}" if ENV["DEBUG"]
563
568
  "(dynamic)"
564
569
  end
565
570
 
@@ -570,7 +575,8 @@ module RailsAiContext
570
575
  # turbo_frame_tag dom_id(@model)
571
576
  match = line.match(/turbo_frame_tag\s+["':]*([^"',\s)]+)/)
572
577
  match ? match[1] : "(dynamic)"
573
- rescue
578
+ rescue => e
579
+ $stderr.puts "[rails-ai-context] extract_frame_id failed: #{e.message}" if ENV["DEBUG"]
574
580
  "(dynamic)"
575
581
  end
576
582
 
@@ -578,7 +584,8 @@ module RailsAiContext
578
584
  private_class_method def self.extract_frame_src(line)
579
585
  match = line.match(/src:\s*["']?([^"',\s)]+)["']?/)
580
586
  match ? match[1] : nil
581
- rescue
587
+ rescue => e
588
+ $stderr.puts "[rails-ai-context] extract_frame_src failed: #{e.message}" if ENV["DEBUG"]
582
589
  nil
583
590
  end
584
591
 
@@ -618,7 +625,8 @@ module RailsAiContext
618
625
  end
619
626
 
620
627
  warnings.sort
621
- rescue
628
+ rescue => e
629
+ $stderr.puts "[rails-ai-context] detect_mismatches failed: #{e.message}" if ENV["DEBUG"]
622
630
  []
623
631
  end
624
632
 
@@ -660,20 +668,23 @@ module RailsAiContext
660
668
  end
661
669
 
662
670
  wiring.sort_by { |k, _| k }.to_h
663
- rescue
671
+ rescue => e
672
+ $stderr.puts "[rails-ai-context] build_stream_wiring failed: #{e.message}" if ENV["DEBUG"]
664
673
  {}
665
674
  end
666
675
 
667
676
  private_class_method def self.extract_class_name(source)
668
677
  match = source.match(/class\s+([\w:]+)/)
669
678
  match[1] if match
670
- rescue
679
+ rescue => e
680
+ $stderr.puts "[rails-ai-context] extract_class_name failed: #{e.message}" if ENV["DEBUG"]
671
681
  nil
672
682
  end
673
683
 
674
684
  private_class_method def self.safe_read(path)
675
685
  File.read(path, encoding: "UTF-8", invalid: :replace, undef: :replace)
676
- rescue
686
+ rescue => e
687
+ $stderr.puts "[rails-ai-context] safe_read failed: #{e.message}" if ENV["DEBUG"]
677
688
  nil
678
689
  end
679
690
 
@@ -280,7 +280,8 @@ module RailsAiContext
280
280
  private_class_method def self.read_view_content(relative_path)
281
281
  full_path = Rails.root.join("app", "views", relative_path)
282
282
  File.exist?(full_path) ? File.read(full_path) : "(file not found)"
283
- rescue
283
+ rescue => e
284
+ $stderr.puts "[rails-ai-context] read_view_content failed: #{e.message}" if ENV["DEBUG"]
284
285
  "(error reading file)"
285
286
  end
286
287
 
@@ -311,7 +312,8 @@ module RailsAiContext
311
312
  end
312
313
 
313
314
  result
314
- rescue
315
+ rescue => e
316
+ $stderr.puts "[rails-ai-context] extract_view_metadata failed: #{e.message}" if ENV["DEBUG"]
315
317
  { ivars: [], turbo: [], components: [], helpers: [] }
316
318
  end
317
319
 
@@ -361,7 +363,8 @@ module RailsAiContext
361
363
  end
362
364
 
363
365
  locals.to_a.sort
364
- rescue
366
+ rescue => e
367
+ $stderr.puts "[rails-ai-context] extract_partial_locals failed: #{e.message}" if ENV["DEBUG"]
365
368
  []
366
369
  end
367
370
 
@@ -401,7 +401,8 @@ module RailsAiContext
401
401
 
402
402
  def rails_version
403
403
  Rails.version.split(".").first(2).join(".")
404
- rescue
404
+ rescue => e
405
+ $stderr.puts "[rails-ai-context] rails_version failed: #{e.message}" if ENV["DEBUG"]
405
406
  "7.1"
406
407
  end
407
408
  end
@@ -638,7 +638,8 @@ module RailsAiContext
638
638
  name = File.basename(path, ".rb").camelize
639
639
  name unless name == "ApplicationService" || name == "BaseService"
640
640
  end
641
- rescue
641
+ rescue => e
642
+ $stderr.puts "[rails-ai-context] extract_service_names failed: #{e.message}" if ENV["DEBUG"]
642
643
  []
643
644
  end
644
645
 
@@ -115,7 +115,8 @@ module RailsAiContext
115
115
  filter_lower = model_filter.downcase
116
116
  table_form = begin
117
117
  model_filter.underscore.pluralize.downcase
118
- rescue
118
+ rescue => e
119
+ $stderr.puts "[rails-ai-context] filter_items failed: #{e.message}" if ENV["DEBUG"]
119
120
  filter_lower
120
121
  end
121
122
  items.select { |i|
@@ -249,8 +249,12 @@ module RailsAiContext
249
249
  columns = result.columns
250
250
  rows = result.rows
251
251
 
252
+ # Match both real column names and aliases that end with sensitive suffixes
253
+ sensitive_suffixes = %w[password secret token key digest hash].freeze
252
254
  redacted_indices = columns.each_with_index.filter_map { |col, i|
253
- i if redacted_cols.include?(col.downcase)
255
+ col_down = col.downcase
256
+ i if redacted_cols.include?(col_down) ||
257
+ sensitive_suffixes.any? { |suffix| col_down.end_with?(suffix) || col_down.include?("password") || col_down.include?("secret") || col_down.include?("token") }
254
258
  }
255
259
 
256
260
  return result if redacted_indices.empty?
@@ -55,6 +55,9 @@ module RailsAiContext
55
55
  /(?<=cookie:\s)\S+/i,
56
56
  /(?<=session_id=)\S+/i,
57
57
  /(?<=_session=)\S+/i,
58
+ /\bAKIA[0-9A-Z]{16}\b/, # AWS access key IDs
59
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/, # JWT tokens
60
+ /-----BEGIN\s+(RSA|DSA|EC|OPENSSH)?\s*PRIVATE KEY-----/, # SSH/TLS private keys
58
61
  /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}\b/i
59
62
  ].freeze
60
63
 
@@ -144,7 +144,8 @@ module RailsAiContext
144
144
  begin
145
145
  result = conn.select_all("SELECT name, SUM(pgsize) AS bytes FROM dbstat GROUP BY name ORDER BY bytes DESC")
146
146
  return result.map { |r| { name: r["name"], bytes: r["bytes"].to_i } } if result.any?
147
- rescue
147
+ rescue => e
148
+ $stderr.puts "[rails-ai-context] gather_table_sizes failed: #{e.message}" if ENV["DEBUG"]
148
149
  nil
149
150
  end
150
151
  # Fallback: whole database size
@@ -156,7 +157,8 @@ module RailsAiContext
156
157
  else
157
158
  nil
158
159
  end
159
- rescue
160
+ rescue => e
161
+ $stderr.puts "[rails-ai-context] gather_table_sizes failed: #{e.message}" if ENV["DEBUG"]
160
162
  nil
161
163
  end
162
164
 
@@ -171,7 +173,8 @@ module RailsAiContext
171
173
  ActiveRecord::Migrator.new(:up, context.migrations).pending_migrations
172
174
  end
173
175
  pending.map { |m| "#{m.version} — #{m.name}" }
174
- rescue
176
+ rescue => e
177
+ $stderr.puts "[rails-ai-context] gather_pending_migrations failed: #{e.message}" if ENV["DEBUG"]
175
178
  nil
176
179
  end
177
180
 
@@ -183,7 +186,8 @@ module RailsAiContext
183
186
  else
184
187
  nil
185
188
  end
186
- rescue
189
+ rescue => e
190
+ $stderr.puts "[rails-ai-context] gather_index_usage failed: #{e.message}" if ENV["DEBUG"]
187
191
  nil
188
192
  end
189
193
 
@@ -228,7 +232,8 @@ module RailsAiContext
228
232
  adapter_name = begin
229
233
  name = ActiveJob::Base.queue_adapter_name
230
234
  name.empty? ? "not configured" : name
231
- rescue
235
+ rescue => e
236
+ $stderr.puts "[rails-ai-context] gather_jobs failed: #{e.message}" if ENV["DEBUG"]
232
237
  "not available"
233
238
  end
234
239
  lines << "**Adapter:** #{adapter_name}"
@@ -454,7 +454,8 @@ module RailsAiContext
454
454
  end
455
455
  end
456
456
  nil
457
- rescue
457
+ rescue => e
458
+ $stderr.puts "[rails-ai-context] extract_class_context failed: #{e.message}" if ENV["DEBUG"]
458
459
  nil
459
460
  end
460
461
 
@@ -474,7 +475,8 @@ module RailsAiContext
474
475
  end
475
476
  end
476
477
  methods
477
- rescue
478
+ rescue => e
479
+ $stderr.puts "[rails-ai-context] extract_sibling_methods failed: #{e.message}" if ENV["DEBUG"]
478
480
  []
479
481
  end
480
482
 
@@ -499,7 +501,8 @@ module RailsAiContext
499
501
  return nil unless ctrl_routes&.any?
500
502
  # Show the first 2 routes as hints
501
503
  ctrl_routes.first(2).map { |r| "`#{r[:verb]} #{r[:path]}`" }.join(", ")
502
- rescue
504
+ rescue => e
505
+ $stderr.puts "[rails-ai-context] find_routes_for_controller failed: #{e.message}" if ENV["DEBUG"]
503
506
  nil
504
507
  end
505
508
 
@@ -521,7 +524,8 @@ module RailsAiContext
521
524
  end
522
525
 
523
526
  result.join("\n")
524
- rescue
527
+ rescue => e
528
+ $stderr.puts "[rails-ai-context] extract_method_body failed: #{e.message}" if ENV["DEBUG"]
525
529
  nil
526
530
  end
527
531
  end