rails-ai-bridge 3.3.0 → 3.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.
- checksums.yaml +4 -4
- data/.reek.yml +53 -34
- data/.rubocop.yml +1 -5
- data/.rubocop_todo.yml +7 -0
- data/AGENTS.md +7 -0
- data/CHANGELOG.md +67 -0
- data/README.md +21 -13
- data/docs/GUIDE.md +75 -13
- data/lib/rails_ai_bridge/cache_warmer.rb +42 -0
- data/lib/rails_ai_bridge/config/introspection.rb +46 -13
- data/lib/rails_ai_bridge/config/rubydex.rb +9 -1
- data/lib/rails_ai_bridge/configuration.rb +8 -1
- data/lib/rails_ai_bridge/context_provider.rb +7 -5
- data/lib/rails_ai_bridge/doctor/checkers/bridge_freshness_checker.rb +137 -0
- data/lib/rails_ai_bridge/doctor.rb +1 -0
- data/lib/rails_ai_bridge/engine.rb +24 -1
- data/lib/rails_ai_bridge/fingerprinter/cached_snapshot.rb +78 -0
- data/lib/rails_ai_bridge/fingerprinter.rb +49 -0
- data/lib/rails_ai_bridge/freshness_header.rb +132 -0
- data/lib/rails_ai_bridge/introspector/parallel_runner.rb +136 -0
- data/lib/rails_ai_bridge/introspector/timed_runner.rb +47 -0
- data/lib/rails_ai_bridge/introspector.rb +139 -22
- data/lib/rails_ai_bridge/introspectors/controller_introspector/filter_extractor.rb +62 -0
- data/lib/rails_ai_bridge/introspectors/controller_introspector.rb +2 -25
- data/lib/rails_ai_bridge/introspectors/convention_detector.rb +8 -3
- data/lib/rails_ai_bridge/introspectors/model_introspector/association_extractor.rb +70 -0
- data/lib/rails_ai_bridge/introspectors/model_introspector/callback_extractor.rb +54 -0
- data/lib/rails_ai_bridge/introspectors/model_introspector/method_extractor.rb +44 -0
- data/lib/rails_ai_bridge/introspectors/model_introspector/source_macro_extractor.rb +223 -0
- data/lib/rails_ai_bridge/introspectors/model_introspector.rb +8 -110
- data/lib/rails_ai_bridge/introspectors/model_semantic_enrichment.rb +9 -9
- data/lib/rails_ai_bridge/introspectors/non_ar_models_introspector.rb +3 -2
- data/lib/rails_ai_bridge/introspectors/route_introspector.rb +84 -40
- data/lib/rails_ai_bridge/introspectors/semantic_introspector.rb +2 -2
- data/lib/rails_ai_bridge/rubydex_adapter/incremental_indexer.rb +316 -0
- data/lib/rails_ai_bridge/rubydex_adapter/indexer.rb +9 -3
- data/lib/rails_ai_bridge/rubydex_adapter/method_counter.rb +7 -8
- data/lib/rails_ai_bridge/rubydex_adapter/serializer.rb +79 -34
- data/lib/rails_ai_bridge/rubydex_adapter.rb +110 -14
- data/lib/rails_ai_bridge/serializers/context_file_serializer.rb +99 -21
- data/lib/rails_ai_bridge/serializers/formatters/sections/semantic_formatter.rb +28 -17
- data/lib/rails_ai_bridge/serializers/providers/rules_serializer.rb +2 -83
- data/lib/rails_ai_bridge/tasks/rails_ai_bridge.rake +133 -36
- data/lib/rails_ai_bridge/tools/search_code/ripgrep_search.rb +116 -0
- data/lib/rails_ai_bridge/tools/search_code/ruby_search.rb +100 -0
- data/lib/rails_ai_bridge/tools/search_code/validator.rb +152 -0
- data/lib/rails_ai_bridge/tools/search_code.rb +30 -249
- data/lib/rails_ai_bridge/tools/search_semantic.rb +2 -2
- data/lib/rails_ai_bridge/version.rb +1 -1
- data/rails-ai-bridge.gemspec +3 -4
- metadata +39 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40f60ba64b6bd873a3cd1ebd9935e0c1b8250b9a8311135201bac1b66611f533
|
|
4
|
+
data.tar.gz: '08046f7af401786ad68cb95daaad9647eb2fec306b2dfb0a4b89987ccf8d081f'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ecb06105c4587df9114b465c8db3c88b462182b6462e1f07f841e92093693edeb52720b1fce378191323e38054412fa7637cc6198b91b371911396ebbf2f967
|
|
7
|
+
data.tar.gz: 65ac9a5d50ccf9f89535eaab7e229c90b685f35ef35a85b5edc4afb23047ac22f6ce1bc90398a4c682f99d19611a6678ba24935fccdffdb27af0c49c63265b49
|
data/.reek.yml
CHANGED
|
@@ -19,6 +19,12 @@ detectors:
|
|
|
19
19
|
- RailsAiBridge::Config::Introspection#search_code_allowed_file_types
|
|
20
20
|
- RailsAiBridge::Config::Introspection#search_code_pattern_max_bytes
|
|
21
21
|
- RailsAiBridge::Config::Introspection#search_code_timeout_seconds
|
|
22
|
+
- RailsAiBridge::Config::Introspection#snapshot_ttl
|
|
23
|
+
- RailsAiBridge::Config::Introspection#cache_warm_on_boot
|
|
24
|
+
- RailsAiBridge::Config::Introspection#parallel_introspection
|
|
25
|
+
- RailsAiBridge::Config::Introspection#parallel_pool_size
|
|
26
|
+
- RailsAiBridge::Config::Introspection#parallel_timeout_seconds
|
|
27
|
+
- RailsAiBridge::Fingerprinter::CachedSnapshot#snapshot_ttl
|
|
22
28
|
- RailsAiBridge::Config::Mcp#authorize
|
|
23
29
|
- RailsAiBridge::Config::Mcp#http_log_json
|
|
24
30
|
- RailsAiBridge::Config::Mcp#mode
|
|
@@ -47,6 +53,8 @@ detectors:
|
|
|
47
53
|
- RailsAiBridge::Config::Rubydex#rubydex_index_path
|
|
48
54
|
- RailsAiBridge::Config::Rubydex#semantic_context_depth
|
|
49
55
|
- RailsAiBridge::Config::Rubydex#semantic_introspector_enabled
|
|
56
|
+
- RailsAiBridge::Config::Rubydex#rubydex_incremental_threshold
|
|
57
|
+
- RailsAiBridge::Config::Rubydex#rubydex_persist_index
|
|
50
58
|
- RailsAiBridge#configuration
|
|
51
59
|
BooleanParameter:
|
|
52
60
|
exclude:
|
|
@@ -60,6 +68,7 @@ detectors:
|
|
|
60
68
|
exclude:
|
|
61
69
|
- RailsAiBridge::ContextProvider#rebuild
|
|
62
70
|
- RailsAiBridge::Doctor::Checkers::BaseChecker#check
|
|
71
|
+
- RailsAiBridge::RubydexAdapter#apply_failure_state
|
|
63
72
|
- RailsAiBridge::Doctor#initialize
|
|
64
73
|
- RailsAiBridge::Introspectors::MiddlewareIntrospector#categorize_middleware
|
|
65
74
|
- RailsAiBridge::Mcp::HttpRateLimiter#initialize
|
|
@@ -72,10 +81,12 @@ detectors:
|
|
|
72
81
|
- RailsAiBridge::Watcher#initialize
|
|
73
82
|
- RailsAiBridge::RubydexAdapter#declaration_to_hash
|
|
74
83
|
- RailsAiBridge::Services::FileManagementService#validate_path!
|
|
84
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#build
|
|
75
85
|
DataClump:
|
|
76
86
|
exclude:
|
|
77
87
|
- RailsAiBridge::Serializers::RegenerationFooter
|
|
78
88
|
- RailsAiBridge::Introspectors::SemanticIntrospector
|
|
89
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer
|
|
79
90
|
DuplicateMethodCall:
|
|
80
91
|
exclude:
|
|
81
92
|
- RailsAiBridge::Config::Introspection#excluded_table?
|
|
@@ -90,11 +101,13 @@ detectors:
|
|
|
90
101
|
- RailsAiBridge::HttpTransportApp#build
|
|
91
102
|
- RailsAiBridge::Introspector#app_name
|
|
92
103
|
- RailsAiBridge::Introspector#call
|
|
104
|
+
- RailsAiBridge::Introspector::ParallelRunner#resolve_future
|
|
105
|
+
- RailsAiBridge::Introspector::TimedRunner#self.call
|
|
106
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#reindex
|
|
93
107
|
- RailsAiBridge::Introspectors::ActiveStorageIntrospector#extract_attachments
|
|
94
108
|
- RailsAiBridge::Introspectors::ControllerIntrospector#call
|
|
95
109
|
- RailsAiBridge::Introspectors::ControllerIntrospector#discover_controllers
|
|
96
110
|
- RailsAiBridge::Introspectors::ControllerIntrospector#eager_load_controllers!
|
|
97
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_filters
|
|
98
111
|
- RailsAiBridge::Introspectors::ConventionDetector#detect_architecture
|
|
99
112
|
- RailsAiBridge::Introspectors::DatabaseStatsIntrospector#call
|
|
100
113
|
- RailsAiBridge::Introspectors::EngineIntrospector#discover_mounted_engines
|
|
@@ -110,11 +123,7 @@ detectors:
|
|
|
110
123
|
- RailsAiBridge::Introspectors::ModelIntrospector#call
|
|
111
124
|
- RailsAiBridge::Introspectors::ModelIntrospector#discover_models
|
|
112
125
|
- RailsAiBridge::Introspectors::ModelIntrospector#eager_load_models!
|
|
113
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_associations
|
|
114
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_callbacks
|
|
115
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_source_macros
|
|
116
126
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#extract_semantic_summary
|
|
117
|
-
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#find_similar_models
|
|
118
127
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#detect_model_connections
|
|
119
128
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#discover_databases
|
|
120
129
|
- RailsAiBridge::Introspectors::NonArModelsIntrospector#collect_safe_object_space_entries
|
|
@@ -122,7 +131,6 @@ detectors:
|
|
|
122
131
|
- RailsAiBridge::Introspectors::NonArModelsIntrospector#eager_load_app_models_dir
|
|
123
132
|
- RailsAiBridge::Introspectors::NonArModelsIntrospector#safe_to_process?
|
|
124
133
|
- RailsAiBridge::Introspectors::RakeTaskIntrospector#parse_rake_file
|
|
125
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#detect_api_namespaces
|
|
126
134
|
- RailsAiBridge::Introspectors::RouteIntrospector#detect_mounted_engines
|
|
127
135
|
- RailsAiBridge::Introspectors::RouteIntrospector#extract_routes
|
|
128
136
|
- RailsAiBridge::Introspectors::SemanticIntrospector#detect_code_patterns
|
|
@@ -239,7 +247,6 @@ detectors:
|
|
|
239
247
|
- RailsAiBridge::Introspectors::ConfigIntrospector#detect_cache_store
|
|
240
248
|
- RailsAiBridge::Introspectors::ConfigIntrospector#extract_credentials_keys
|
|
241
249
|
- RailsAiBridge::Introspectors::ConfigIntrospector#extract_middleware
|
|
242
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_filters
|
|
243
250
|
- RailsAiBridge::Introspectors::ConventionDetector#detect_patterns
|
|
244
251
|
- RailsAiBridge::Introspectors::ConventionDetector#scan_directory_structure
|
|
245
252
|
- RailsAiBridge::Introspectors::DevOpsIntrospector#extract_docker_info
|
|
@@ -251,7 +258,6 @@ detectors:
|
|
|
251
258
|
- RailsAiBridge::Introspectors::MiddlewareIntrospector#discover_custom_middleware
|
|
252
259
|
- RailsAiBridge::Introspectors::MigrationIntrospector#pending_migrations
|
|
253
260
|
- RailsAiBridge::Introspectors::ModelIntrospector#discover_models
|
|
254
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_source_macros
|
|
255
261
|
- RailsAiBridge::Introspectors::ModelIntrospector#extract_validations
|
|
256
262
|
- RailsAiBridge::Introspectors::ModelIntrospector#model_table_excluded?
|
|
257
263
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#find_similar_models
|
|
@@ -272,6 +278,8 @@ detectors:
|
|
|
272
278
|
- RailsAiBridge::Introspectors::ViewIntrospector#detect_template_engines
|
|
273
279
|
- RailsAiBridge::RubydexAdapter#constant_references
|
|
274
280
|
- RailsAiBridge::RubydexAdapter#count_methods
|
|
281
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#find_modified_files
|
|
282
|
+
- RailsAiBridge::RubydexAdapter#indexer_options
|
|
275
283
|
- RailsAiBridge::RubydexAdapter#declaration_to_hash
|
|
276
284
|
- RailsAiBridge::RubydexAdapter#definition_to_hash
|
|
277
285
|
- RailsAiBridge::RubydexAdapter#format_location
|
|
@@ -334,6 +342,7 @@ detectors:
|
|
|
334
342
|
- RailsAiBridge::Tools::GetView::FullFormatter
|
|
335
343
|
- RailsAiBridge::Tools::GetView::StandardFormatter
|
|
336
344
|
- RailsAiBridge::Tools::GetView::SummaryFormatter
|
|
345
|
+
- RailsAiBridge::Fingerprinter::CachedSnapshot
|
|
337
346
|
IrresponsibleModule:
|
|
338
347
|
exclude:
|
|
339
348
|
- RailsAiBridge::Generators::InstallGenerator
|
|
@@ -349,6 +358,7 @@ detectors:
|
|
|
349
358
|
- RailsAiBridge::Doctor::Checkers::BaseChecker#check
|
|
350
359
|
- RailsAiBridge::Doctor::Checkers::BaseChecker#new_check
|
|
351
360
|
- RailsAiBridge::Mcp::HttpStructuredLog#emit
|
|
361
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#initialize
|
|
352
362
|
- RailsAiBridge::Serializers::ContextFileSerializer#generate_split_rules
|
|
353
363
|
- RailsAiBridge::Serializers::ProviderDocumentHeader#call
|
|
354
364
|
- RailsAiBridge::Tools::GetRoutes#self.call
|
|
@@ -363,12 +373,13 @@ detectors:
|
|
|
363
373
|
- RailsAiBridge::Tools::SearchCode#self.process_file_for_search
|
|
364
374
|
- RailsAiBridge::Tools::SearchCode#self.search_with_ripgrep
|
|
365
375
|
- RailsAiBridge::Tools::SearchCode#self.search_with_ruby
|
|
376
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#call
|
|
377
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#build
|
|
378
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#reindex
|
|
366
379
|
ManualDispatch:
|
|
367
380
|
exclude:
|
|
368
381
|
- RailsAiBridge::Introspector#app_name
|
|
369
382
|
- RailsAiBridge::Introspectors::ConfigIntrospector#extract_credentials_keys
|
|
370
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_action_condition
|
|
371
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_filters
|
|
372
383
|
- RailsAiBridge::Introspectors::MigrationIntrospector#pending_migrations
|
|
373
384
|
- RailsAiBridge::Introspectors::ModelIntrospector#extract_enums
|
|
374
385
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#discover_databases
|
|
@@ -385,7 +396,14 @@ detectors:
|
|
|
385
396
|
- RailsAiBridge::RubydexAdapter#descendants
|
|
386
397
|
- RailsAiBridge::RubydexAdapter#format_location
|
|
387
398
|
- RailsAiBridge::RubydexAdapter#safe_count
|
|
399
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#patch_graph
|
|
400
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#re_index_file
|
|
401
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#remove_from_graph
|
|
388
402
|
- RailsAiBridge::Services::FileManagementService#default_allowed_base_dir
|
|
403
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#apply_incremental_changes
|
|
404
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#reindex_file
|
|
405
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#remove_file
|
|
406
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#log_error
|
|
389
407
|
MissingSafeMethod:
|
|
390
408
|
exclude:
|
|
391
409
|
- RailsAiBridge::AssistantFormatsPreference
|
|
@@ -397,6 +415,7 @@ detectors:
|
|
|
397
415
|
- RailsAiBridge::Services::FileManagementService
|
|
398
416
|
- RailsAiBridge::Tools::BaseTool
|
|
399
417
|
- RailsAiBridge::Watcher::BridgeRegenerator
|
|
418
|
+
- RailsAiBridge::Fingerprinter::CachedSnapshot
|
|
400
419
|
NestedIterators:
|
|
401
420
|
exclude:
|
|
402
421
|
- RailsAiBridge::Fingerprinter#snapshot
|
|
@@ -405,15 +424,11 @@ detectors:
|
|
|
405
424
|
- RailsAiBridge::Introspectors::ActionTextIntrospector#extract_rich_text_fields
|
|
406
425
|
- RailsAiBridge::Introspectors::ActiveStorageIntrospector#detect_direct_upload
|
|
407
426
|
- RailsAiBridge::Introspectors::ActiveStorageIntrospector#extract_attachments
|
|
408
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_filters
|
|
409
427
|
- RailsAiBridge::Introspectors::DevOpsIntrospector#extract_procfile
|
|
410
428
|
- RailsAiBridge::Introspectors::EngineIntrospector#discover_mounted_engines
|
|
411
429
|
- RailsAiBridge::Introspectors::GemIntrospector#categorize_gems
|
|
412
430
|
- RailsAiBridge::Introspectors::JobIntrospector#extract_channels
|
|
413
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_callbacks
|
|
414
|
-
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#find_similar_models
|
|
415
431
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#detect_model_connections
|
|
416
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#group_by_controller
|
|
417
432
|
- RailsAiBridge::Introspectors::SeedsIntrospector#detect_seeded_models
|
|
418
433
|
- RailsAiBridge::Introspectors::TestIntrospector#detect_test_helpers
|
|
419
434
|
- RailsAiBridge::Introspectors::TurboIntrospector#extract_turbo_frames
|
|
@@ -446,6 +461,7 @@ detectors:
|
|
|
446
461
|
exclude:
|
|
447
462
|
- RailsAiBridge::AssistantFormatsPreference#formats_for_default_bridge_task
|
|
448
463
|
- RailsAiBridge::Config::Introspection#excluded_table?
|
|
464
|
+
- RailsAiBridge::RubydexAdapter#sanitize_index_path
|
|
449
465
|
- RailsAiBridge::Config::Mcp#effective_http_rate_limit_max_requests
|
|
450
466
|
- RailsAiBridge::Generators::InstallGenerator#profile_from_option
|
|
451
467
|
- RailsAiBridge::Introspectors::ControllerIntrospector#discover_controllers
|
|
@@ -485,10 +501,12 @@ detectors:
|
|
|
485
501
|
- RailsAiBridge::Config::Introspection
|
|
486
502
|
- RailsAiBridge::Config::Mcp
|
|
487
503
|
- RailsAiBridge::Config::Output
|
|
504
|
+
- RailsAiBridge::Config::Rubydex
|
|
488
505
|
- RailsAiBridge::Config::Server
|
|
489
506
|
- RailsAiBridge::Configuration
|
|
490
507
|
- RailsAiBridge::Mcp::HttpRateLimiter
|
|
491
508
|
- RailsAiBridge::RubydexAdapter
|
|
509
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer
|
|
492
510
|
- RailsAiBridge::Tools::GetModelDetails::ResponseFormatter
|
|
493
511
|
- RailsAiBridge::Tools::GetRoutes::ResponseFormatter
|
|
494
512
|
- RailsAiBridge::Tools::GetSchema::ResponseFormatter
|
|
@@ -497,6 +515,7 @@ detectors:
|
|
|
497
515
|
- RailsAiBridge::Generators::InstallGenerator
|
|
498
516
|
- RailsAiBridge::Introspectors::ModelIntrospector
|
|
499
517
|
- RailsAiBridge::RubydexAdapter
|
|
518
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer
|
|
500
519
|
- RailsAiBridge::Serializers::Providers::BaseProviderSerializer
|
|
501
520
|
TooManyConstants:
|
|
502
521
|
exclude:
|
|
@@ -507,6 +526,9 @@ detectors:
|
|
|
507
526
|
- RailsAiBridge#validate_auto_mount_configuration!
|
|
508
527
|
- RailsAiBridge::AssistantFormatsPreference#formats_for_default_bridge_task
|
|
509
528
|
- RailsAiBridge::AssistantFormatsPreference#write!
|
|
529
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#call
|
|
530
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#patch_graph
|
|
531
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#run_reindex
|
|
510
532
|
- RailsAiBridge::Config::Mcp#effective_http_rate_limit_max_requests
|
|
511
533
|
- RailsAiBridge::ContextProvider#fetch
|
|
512
534
|
- RailsAiBridge::ContextProvider#fetch_section
|
|
@@ -520,6 +542,10 @@ detectors:
|
|
|
520
542
|
- RailsAiBridge::Generators::InstallGenerator#show_instructions
|
|
521
543
|
- RailsAiBridge::HttpTransportApp#build
|
|
522
544
|
- RailsAiBridge::Introspector#call
|
|
545
|
+
- RailsAiBridge::Introspector#run_single
|
|
546
|
+
- RailsAiBridge::Introspector::ParallelRunner#call
|
|
547
|
+
- RailsAiBridge::Introspector::ParallelRunner#resolve_future
|
|
548
|
+
- RailsAiBridge::Introspector::TimedRunner#self.call
|
|
523
549
|
- RailsAiBridge::Introspectors::ActionMailboxIntrospector#extract_mailboxes
|
|
524
550
|
- RailsAiBridge::Introspectors::ActionTextIntrospector#extract_rich_text_fields
|
|
525
551
|
- RailsAiBridge::Introspectors::ActiveStorageIntrospector#detect_direct_upload
|
|
@@ -536,7 +562,6 @@ detectors:
|
|
|
536
562
|
- RailsAiBridge::Introspectors::AuthIntrospector#scan_models_for
|
|
537
563
|
- RailsAiBridge::Introspectors::ConfigIntrospector#detect_current_attributes
|
|
538
564
|
- RailsAiBridge::Introspectors::ControllerIntrospector#call
|
|
539
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_filters
|
|
540
565
|
- RailsAiBridge::Introspectors::ConventionDetector#detect_architecture
|
|
541
566
|
- RailsAiBridge::Introspectors::ConventionDetector#detect_patterns
|
|
542
567
|
- RailsAiBridge::Introspectors::ConventionDetector#scan_directory_structure
|
|
@@ -559,10 +584,7 @@ detectors:
|
|
|
559
584
|
- RailsAiBridge::Introspectors::MigrationIntrospector#migration_stats
|
|
560
585
|
- RailsAiBridge::Introspectors::MigrationIntrospector#pending_migrations
|
|
561
586
|
- RailsAiBridge::Introspectors::ModelIntrospector#call
|
|
562
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_associations
|
|
563
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_callbacks
|
|
564
587
|
- RailsAiBridge::Introspectors::ModelIntrospector#extract_model_details
|
|
565
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_source_macros
|
|
566
588
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#calculate_complexity_score
|
|
567
589
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#extract_semantic_data
|
|
568
590
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#extract_semantic_summary
|
|
@@ -620,6 +642,11 @@ detectors:
|
|
|
620
642
|
- RailsAiBridge::RubydexAdapter#index!
|
|
621
643
|
- RailsAiBridge::RubydexAdapter#instance
|
|
622
644
|
- RailsAiBridge::RubydexAdapter#search
|
|
645
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#call
|
|
646
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#reindex
|
|
647
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#update_mtimes
|
|
648
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#load_mtimes
|
|
649
|
+
- RailsAiBridge::RubydexAdapter#handle_index_result
|
|
623
650
|
- RailsAiBridge::Serializers::ContextFileSerializer#call
|
|
624
651
|
- RailsAiBridge::Serializers::ContextFileSerializer#generate_split_rules
|
|
625
652
|
- RailsAiBridge::Serializers::ContextFileSerializer#serialize
|
|
@@ -774,7 +801,6 @@ detectors:
|
|
|
774
801
|
- RailsAiBridge::Introspectors::ConfigIntrospector#extract_initializers
|
|
775
802
|
- RailsAiBridge::Introspectors::ConfigIntrospector#extract_middleware
|
|
776
803
|
- RailsAiBridge::Introspectors::ControllerIntrospector#call
|
|
777
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_filters
|
|
778
804
|
- RailsAiBridge::Introspectors::ConventionDetector#detect_config_files
|
|
779
805
|
- RailsAiBridge::Introspectors::ConventionDetector#detect_patterns
|
|
780
806
|
- RailsAiBridge::Introspectors::DatabaseStatsIntrospector#call
|
|
@@ -796,11 +822,6 @@ detectors:
|
|
|
796
822
|
- RailsAiBridge::Introspectors::MigrationIntrospector#migration_stats
|
|
797
823
|
- RailsAiBridge::Introspectors::MigrationIntrospector#pending_migrations
|
|
798
824
|
- RailsAiBridge::Introspectors::ModelIntrospector#call
|
|
799
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_public_class_methods
|
|
800
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_public_instance_methods
|
|
801
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_source_macros
|
|
802
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#sanitize_options
|
|
803
|
-
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#find_similar_models
|
|
804
825
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#call
|
|
805
826
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#detect_model_connections
|
|
806
827
|
- RailsAiBridge::Introspectors::MultiDatabaseIntrospector#discover_replicas
|
|
@@ -809,9 +830,7 @@ detectors:
|
|
|
809
830
|
- RailsAiBridge::Introspectors::NonArModelsIntrospector#safe_to_process?
|
|
810
831
|
- RailsAiBridge::Introspectors::RakeTaskIntrospector#call
|
|
811
832
|
- RailsAiBridge::Introspectors::RakeTaskIntrospector#parse_rake_file
|
|
812
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#detect_api_namespaces
|
|
813
833
|
- RailsAiBridge::Introspectors::RouteIntrospector#detect_mounted_engines
|
|
814
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#group_by_controller
|
|
815
834
|
- RailsAiBridge::Introspectors::Schema::StaticSchemaParser#skip_table?
|
|
816
835
|
- RailsAiBridge::Introspectors::SchemaIntrospector#table_names
|
|
817
836
|
- RailsAiBridge::Introspectors::SeedsIntrospector#analyze_seeds_file
|
|
@@ -937,13 +956,20 @@ detectors:
|
|
|
937
956
|
UtilityFunction:
|
|
938
957
|
exclude:
|
|
939
958
|
- RailsAiBridge::Doctor#compute_score
|
|
959
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#file_mtime
|
|
960
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#current_files
|
|
961
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#reindex_file
|
|
962
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#remove_file
|
|
963
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#threshold_exceeded?
|
|
964
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#mtimes_path
|
|
965
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#serialize_mtimes
|
|
966
|
+
- RailsAiBridge::RubydexAdapter::IncrementalIndexer#deserialize_mtimes
|
|
940
967
|
- RailsAiBridge::Doctor::Checkers::BaseChecker#new_check
|
|
941
968
|
- RailsAiBridge::Doctor::Checkers::StimulusMcpToolChecker#tool_registered?
|
|
942
969
|
- RailsAiBridge::Doctor::Checkers::ViewMcpToolChecker#tool_registered?
|
|
943
970
|
- RailsAiBridge::Introspectors::ControllerIntrospector#api_controller?
|
|
944
971
|
- RailsAiBridge::Introspectors::ControllerIntrospector#discover_controllers
|
|
945
972
|
- RailsAiBridge::Introspectors::ControllerIntrospector#eager_load_controllers!
|
|
946
|
-
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_action_condition
|
|
947
973
|
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_actions
|
|
948
974
|
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_concerns
|
|
949
975
|
- RailsAiBridge::Introspectors::ControllerIntrospector#extract_respond_to
|
|
@@ -957,12 +983,8 @@ detectors:
|
|
|
957
983
|
- RailsAiBridge::Introspectors::JobIntrospector#extract_mailers
|
|
958
984
|
- RailsAiBridge::Introspectors::MigrationIntrospector#detect_migration_actions
|
|
959
985
|
- RailsAiBridge::Introspectors::ModelIntrospector#eager_load_models!
|
|
960
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_associations
|
|
961
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_callbacks
|
|
962
986
|
- RailsAiBridge::Introspectors::ModelIntrospector#extract_concerns
|
|
963
987
|
- RailsAiBridge::Introspectors::ModelIntrospector#extract_enums
|
|
964
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_public_class_methods
|
|
965
|
-
- RailsAiBridge::Introspectors::ModelIntrospector#extract_public_instance_methods
|
|
966
988
|
- RailsAiBridge::Introspectors::ModelIntrospector#sanitize_options
|
|
967
989
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#calculate_complexity_score
|
|
968
990
|
- RailsAiBridge::Introspectors::ModelSemanticEnrichment#extract_semantic_summary
|
|
@@ -974,9 +996,6 @@ detectors:
|
|
|
974
996
|
- RailsAiBridge::Introspectors::NonArModelsIntrospector#safe_to_process?
|
|
975
997
|
- RailsAiBridge::Introspectors::NonArModelsIntrospector#sanitize_error_message
|
|
976
998
|
- RailsAiBridge::Introspectors::RakeTaskIntrospector#parse_rake_file
|
|
977
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#detect_api_namespaces
|
|
978
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#extract_constraints
|
|
979
|
-
- RailsAiBridge::Introspectors::RouteIntrospector#group_by_controller
|
|
980
999
|
- RailsAiBridge::Introspectors::SchemaIntrospector#active_record_connected?
|
|
981
1000
|
- RailsAiBridge::Introspectors::SchemaIntrospector#adapter_name
|
|
982
1001
|
- RailsAiBridge::Introspectors::SchemaIntrospector#connection
|
data/.rubocop.yml
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
plugins:
|
|
2
2
|
- rubocop-rails
|
|
3
3
|
- rubocop-rspec
|
|
4
|
-
|
|
5
4
|
AllCops:
|
|
6
5
|
TargetRubyVersion: 3.2
|
|
7
6
|
NewCops: enable
|
|
@@ -10,19 +9,15 @@ AllCops:
|
|
|
10
9
|
- "vendor/**/*"
|
|
11
10
|
- "spec/internal/**/*"
|
|
12
11
|
- "gemfiles/**/*"
|
|
13
|
-
|
|
14
12
|
Naming/FileName:
|
|
15
13
|
Exclude:
|
|
16
14
|
- "lib/rails-ai-bridge.rb"
|
|
17
15
|
Naming/RescuedExceptionsVariableName:
|
|
18
16
|
PreferredName: error
|
|
19
|
-
|
|
20
17
|
Style/Documentation:
|
|
21
18
|
Enabled: false
|
|
22
|
-
|
|
23
19
|
Metrics/ClassLength:
|
|
24
20
|
Max: 240
|
|
25
|
-
|
|
26
21
|
Metrics/BlockLength:
|
|
27
22
|
Max: 120
|
|
28
23
|
Exclude:
|
|
@@ -73,3 +68,4 @@ Metrics/CyclomaticComplexity:
|
|
|
73
68
|
Max: 35
|
|
74
69
|
Metrics/ParameterLists:
|
|
75
70
|
Max: 6
|
|
71
|
+
inherit_from: .rubocop_todo.yml
|
data/.rubocop_todo.yml
ADDED
data/AGENTS.md
CHANGED
|
@@ -53,3 +53,10 @@ Uses combustion gem for testing Rails engine behavior in isolation.
|
|
|
53
53
|
- All tools prefixed with `rails_` per MCP naming best practices
|
|
54
54
|
- `generate_context` returns `{ written: [], skipped: [] }` hash
|
|
55
55
|
- Zeitwerk autoloads all files — no `require_relative` needed for new classes
|
|
56
|
+
|
|
57
|
+
<!-- lean-ctx -->
|
|
58
|
+
## lean-ctx
|
|
59
|
+
|
|
60
|
+
Prefer lean-ctx MCP tools over native equivalents for token savings.
|
|
61
|
+
Full rules: @LEAN-CTX.md
|
|
62
|
+
<!-- /lean-ctx -->
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,73 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.4.0] - 2026-05-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`TimedRunner` — per-introspector wall-clock timing** (#36) — new `RailsAiBridge::Introspector::TimedRunner.call(klass, app)` value object wraps any introspector class and returns `{ result:, duration_ms: }`. Uses `Process.clock_gettime(CLOCK_MONOTONIC)` for accurate measurement regardless of system clock adjustments. Duration is recorded even when the introspector raises, so you can diagnose slow-then-failing classes. Sequential runs now log duration at `debug` level via `Rails.logger.debug`.
|
|
13
|
+
- **Config-driven `ParallelRunner` pool size** (#36) — `config.parallel_pool_size` (default `4`) sets the upper bound for the `Concurrent::FixedThreadPool`; the actual size is `min(introspector_count, pool_size)` so no idle threads are ever created.
|
|
14
|
+
- **Per-future timeout for parallel introspection** (#36) — `config.parallel_timeout_seconds` (default `10`) is enforced on each `Concurrent::Future` via `future.value(timeout)`. Introspectors that exceed their budget are cancelled and return `{ error: "timed out after Ns" }` without blocking the rest of the pool. The pool's `wait_for_termination` also uses this value.
|
|
15
|
+
- **Rubydex incremental indexing** (#38) — new `RailsAiBridge::RubydexAdapter::IncrementalIndexer` service skips unchanged files on re-index using mtime tracking (integer seconds, no IEEE 754 precision loss). A full rebuild is triggered when the ratio of changed files exceeds `config.rubydex_incremental_threshold` (default `0.3`). The mtime snapshot can optionally survive process restarts via `config.rubydex_persist_index` (default `false`).
|
|
16
|
+
- **`config.rubydex_incremental_threshold`** (#38) (default `0.3`) — ratio of changed-to-total files above which the incremental indexer falls back to a full rebuild.
|
|
17
|
+
- **`config.rubydex_persist_index`** (#38) (default `false`) — when `true`, the rubydex mtime snapshot is written to disk alongside the index so incremental re-indexing survives process restarts.
|
|
18
|
+
- **Path-traversal guard for rubydex index path** (#38) — `RubydexAdapter#indexer_options` now sanitises `config.rubydex_index_path` through a `Pathname#cleanpath` + root-prefix check, returning `nil` (and falling back to the default) for any path that escapes `Rails.root`.
|
|
19
|
+
- **Bridge file freshness stamps** (#37) — generated bridge files (CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules, etc.) now embed a freshness header containing the generation timestamp, a 12-character source fingerprint (SHA-256 of `db/schema.rb` + `config/routes.rb`), and the gem version. Files are skipped on re-generation when their fingerprint matches, eliminating unnecessary timestamps and noisy git diffs.
|
|
20
|
+
- **`Fingerprinter.source_fingerprint`** (#37) — new singleton method that hashes the app's schema and routes files into a compact 12-char hex fingerprint used by the freshness system.
|
|
21
|
+
|
|
22
|
+
### Fixed (Security & Architecture Audit)
|
|
23
|
+
|
|
24
|
+
- **ReDoS Vulnerability in `RubySearch`** — Added a 2-second timeout to the `Regexp.new` engine to prevent catastrophic backtracking denial-of-service on malicious search patterns.
|
|
25
|
+
- **Path Traversal via Symlinks in `RubydexAdapter`** — `sanitize_index_path` now uses `Pathname#realpath` to strictly validate that the configured index path resolves safely inside the `Rails.root` boundary.
|
|
26
|
+
- **TOCTOU Race Condition in `IncrementalIndexer`** — Upgraded mtime tracking from integer seconds (`to_i`) to rational (`to_r`) for precise sub-second caching, preventing scenarios where high-frequency file modifications within the same second bypassed change detection.
|
|
27
|
+
- **Threshold Edge Case in `IncrementalIndexer`** — Changed the rebuild cutoff comparison from `>` to `>=` so that precise boundary thresholds (like 100% of files) trigger full rebuilds correctly.
|
|
28
|
+
- **Memory Leaks & Exhaustion in `ParallelRunner`** — Replaced deprecated `clear_active_connections!` with `connection_pool.release_connection`, and explicitly added `pool.kill` to forcefully shut down long-running threads on timeouts.
|
|
29
|
+
- **State Leakage in Extractors** — Refactored `FilterExtractor`, `AssociationExtractor`, and `SourceMacroExtractor` to eliminate shared mutable state, establishing purely functional object APIs and tightening private encapsulation.
|
|
30
|
+
- **`db/structure.sql` fallback** (#37) — `source_fingerprint` automatically falls back to `db/structure.sql` when `db/schema.rb` is absent (apps using SQL schema format are now supported).
|
|
31
|
+
- **`FreshnessHeader` module** (#37) — centralized utility for embedding and extracting freshness metadata from bridge files. Supports both Markdown (HTML comment header) and JSON (`_meta` object) formats, with backward-compatible parsing of older files that lack the gem-version field.
|
|
32
|
+
- **Bridge freshness Doctor check** (#37) — a new `BridgeFreshnessChecker` is registered with the `Doctor` service. It reports stale bridge files (fingerprint mismatch) or missing bridge files as `:warn`, and fresh files as `:pass`. The Doctor now runs 16 total checks.
|
|
33
|
+
- **`rails ai:check` rake task** (#37) — runs all diagnostic checks and exits with code `1` if any check fails, enabling straightforward CI/CD integration (e.g., `rails ai:check || exit 1`).
|
|
34
|
+
- **`CHECK=1` pre-generation guard** (#37) — pass `CHECK=1` to `rails ai:bridge` (or any bridge sub-task) to run Doctor diagnostics first; generation is aborted if any check fails.
|
|
35
|
+
- **`RailsAiBridge::RakeHelpers` module** (#37) — extracted top-level rake helper methods (`print_result`, `apply_context_mode_override`, `conflict_strategy`, `run_pre_generation_checks`) from global `Object` scope into a properly namespaced module.
|
|
36
|
+
- **`CacheWarmer` & `CachedSnapshot`** (#36) — implemented TTL-based thread-safe caching system with `config.cache_warm_on_boot` to preemptively load context into memory on application start.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **`rubydex` enabled by default** — The `rubydex` gem (v0.2.3) is now a mandatory dependency and semantic analysis is enabled out of the box (`@rubydex_enabled = true`). This provides zero-config code graph and semantic context functionality to all users.
|
|
41
|
+
- **Improved IDE configurations in documentation** — Promoted HTTP/SSE as the primary and highly recommended connection method for `rbenv`/`rvm` users within IDEs (like Antigravity and Cursor) to bypass subprocess ruby environment pathing issues.
|
|
42
|
+
- **`Introspector#run_single`** (#36) — sequential execution is now routed through `TimedRunner` instead of a bare `rescue` block. Error handling behaviour is unchanged (`{ error: message }`), but every introspector call now produces a debug-level duration log entry.
|
|
43
|
+
- **`ParallelRunner#resolve_future`** (#36) — uses `future.value(timeout)` + `future.complete?` check instead of blocking `future.value!`. A `nil` return from a timed-out future is no longer misinterpreted as a successful result.
|
|
44
|
+
- **`ParallelRunner` pool shutdown** (#36) — `wait_for_termination` now uses `config.parallel_timeout_seconds` instead of a hardcoded `10`.
|
|
45
|
+
- **`RubydexAdapter#handle_index_result`** (#38) — on `:reindex!` failure, existing `@graph` and `@indexed` state is preserved rather than reset to `nil`/`false`, preventing a full context blackout on transient indexing errors.
|
|
46
|
+
- **Integer mtimes throughout `IncrementalIndexer`** (#38) — `serialize_mtimes`, `deserialize_mtimes`, and `file_mtime` now all operate in integer seconds (`Time#to_i`) to avoid IEEE 754 floating-point comparison drift.
|
|
47
|
+
- **`FreshnessHeader`** (#37) — expanded API with `embed_for(fmt, ...)`, `extract_metadata_for(fmt, content)`, and `extract_fingerprint_for(fmt, content)` dispatching methods. JSON and Markdown branching is now fully centralized here, removing format-aware `if fmt == :json` conditionals from callers.
|
|
48
|
+
- **`ContextFileSerializer`** (#37) — refactored to use a new `FreshnessWriter` inner class that encapsulates freshness metadata embedding and file write decisions. This eliminates `ControlParameter`, `UtilityFunction`, and `LongParameterList` Reek warnings.
|
|
49
|
+
- **`BridgeFreshnessChecker`** (#37) — refactored with a `ScanResult` struct to eliminate the 6-parameter `check_file` method; introduced `scan_files`, `accumulate_file_result`, `stale?`, and `freshness_check` helpers reducing `TooManyStatements` and `DuplicateMethodCall` Reek warnings.
|
|
50
|
+
- **`Fingerprinter.source_fingerprint`** (#37) — extracted `schema_path(root)` and `read_source_content(paths)` private helpers to reduce method statement count.
|
|
51
|
+
- **`RubySearch`** (#35) — wrapped the 5 search params into a `SearchParams` struct to resolve the `TooManyInstanceVariables` Reek warning; extracted `secret_file?(basename)` from `skip_file?` to fix `FeatureEnvy`; added `SECRET_EXTENSIONS` constant.
|
|
52
|
+
- **`RipgrepSearch::CommandBuilder`** (#35) — moved hardcoded secret file globs to a `SECRET_EXCLUDES` constant; renamed helpers to `excluded_path_flags` / `secret_exclude_flags`; added `# :reek:UtilityFunction` suppressions for intentional stateless helpers.
|
|
53
|
+
- **`Validator`** (#35) — extracted `effective_max_bytes`, `present?`, `normalize_extension`, `safe_extension?`, `build_search_path`, `within_root?`, `path_not_found`, and `pattern_too_long_error` helpers. Fixes `DuplicateMethodCall` on `BaseTool.text_response("Path not found: ...")` in `validate_path_security`.
|
|
54
|
+
- **`SourceMacroExtractor`** (#35) — split `add_attachment_macros` into three single-step helpers (`add_single_attached`, `add_many_attached`, `add_rich_text`) to reduce statement counts.
|
|
55
|
+
- **Rake namespace splitting** (#35) — `namespace :ai` reopened across multiple smaller blocks in `rails_ai_bridge.rake` to comply with `Metrics/BlockLength` RuboCop limit.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- **`ASSISTANT_TABLE` constant redefinition warning** (#35) — wrapped constant definition in `unless defined?` to prevent warnings when Rake tasks are loaded multiple times in test environments.
|
|
60
|
+
|
|
61
|
+
### Tests
|
|
62
|
+
|
|
63
|
+
- Added **68 new examples** covering:
|
|
64
|
+
- `TimedRunner` — result forwarding, error capture, monotonic duration, error-path duration
|
|
65
|
+
- `ParallelRunner` — config-driven pool size, per-future timeout, pool shutdown, mixed success/failure, `available?` with pool-size and missing-constant edge cases
|
|
66
|
+
- `Introspector` — sequential `TimedRunner` wiring (plain result, no `duration_ms` envelope), error capture in sequential mode, debug log assertion
|
|
67
|
+
- `AppOverviewFormatter` — nil/error guards, optional fields, field ordering
|
|
68
|
+
- `GemsFormatter` — nil/error guards, total count, Notable Gems section, category+name sort order
|
|
69
|
+
- `MigrationsFormatter` — nil/error guards, schema version, pending migrations count, recent migrations with and without actions
|
|
70
|
+
- `RubySearch` / `FileProcessor` — pattern matching, max_results cap, secret file skipping (`.env`, `.key`, `.pem`, `.p12`, `.pfx`, `.crt`), excluded paths, file_type filtering, case-insensitive search, relative paths, unreadable file recovery, `:full` return signal
|
|
71
|
+
- `Fingerprinter` — restored `.compute` and `.changed?` unit tests; added `db/structure.sql` fallback and schema.rb-wins-when-both-exist edge cases
|
|
72
|
+
- `FreshnessHeader` — backward-compatible parsing of headers without gem version
|
|
73
|
+
- **Total: 1,745 examples, 0 failures, 94.49% line coverage** (up from 94.04%)
|
|
74
|
+
|
|
8
75
|
## [3.2.0] - 2026-05-04
|
|
9
76
|
|
|
10
77
|
### Added
|
data/README.md
CHANGED
|
@@ -261,7 +261,7 @@ The gem exposes **12 built-in tools** via MCP that AI clients call on-demand (ho
|
|
|
261
261
|
| `rails_get_conventions` | Architecture patterns, directory structure |
|
|
262
262
|
| `rails_search_code` | Ripgrep (or Ruby) search under `Rails.root` with allowlisted extensions, pattern size cap, and optional wall-clock timeout |
|
|
263
263
|
| `rails_get_view` | View layouts, templates, partials; optional per-file detail under the configured `app/views` path |
|
|
264
|
-
| `rails_search_semantic` | Semantic code search using rubydex — find declarations by name with types, locations, and relationships
|
|
264
|
+
| `rails_search_semantic` | Semantic code search using rubydex — find declarations by name with types, locations, and relationships |
|
|
265
265
|
| `rails_get_stimulus` | Stimulus controllers: targets, values, actions, outlets (requires `:stimulus` introspector) |
|
|
266
266
|
|
|
267
267
|
All tools are **read-only** — they never modify your application or database.
|
|
@@ -485,6 +485,9 @@ end
|
|
|
485
485
|
| `http_port` | `6029` | HTTP server port |
|
|
486
486
|
| `cache_ttl` | `30` | Cache TTL in seconds |
|
|
487
487
|
| `watcher_formats` | `:all` | Formats regenerated by `rails ai:watch` (e.g. `%i[claude cursor]` to limit churn) |
|
|
488
|
+
| `parallel_introspection` | `false` | Run introspectors concurrently (requires `concurrent-ruby`, which is already a Rails dependency) |
|
|
489
|
+
| `parallel_pool_size` | `4` | Max threads in the parallel pool; capped at the number of active introspectors so no idle threads are created |
|
|
490
|
+
| `parallel_timeout_seconds` | `10` | Per-introspector future timeout (seconds); timed-out introspectors return `{ error: "timed out after Ns" }` without blocking the others |
|
|
488
491
|
|
|
489
492
|
Other HTTP MCP knobs live only on the nested object, for example `RailsAiBridge.configuration.mcp.authorize`, `mcp.mode`, `mcp.security_profile`, and `mcp.require_auth_in_production` — see [docs/GUIDE.md](docs/GUIDE.md) and [docs/mcp-security.md](docs/mcp-security.md).
|
|
490
493
|
</details>
|
|
@@ -513,19 +516,17 @@ Built-in MCP tools and resources now read through a shared runtime context provi
|
|
|
513
516
|
|
|
514
517
|
### Rubydex Integration (Semantic Code Analysis)
|
|
515
518
|
|
|
516
|
-
[Rubydex](https://github.com/Shopify/rubydex) is Shopify's high-performance Ruby static analysis toolkit.
|
|
519
|
+
[Rubydex](https://github.com/Shopify/rubydex) is Shopify's high-performance Ruby static analysis toolkit. rails-ai-bridge leverages rubydex out-of-the-box for semantic code understanding.
|
|
517
520
|
|
|
518
521
|
**Setup:**
|
|
519
522
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
```
|
|
523
|
+
Rubydex is installed automatically as a core dependency and is enabled by default.
|
|
524
|
+
|
|
525
|
+
To customize it in your initializer (`config/initializers/rails_ai_bridge.rb`):
|
|
524
526
|
|
|
525
|
-
2. Enable in your initializer:
|
|
526
527
|
```ruby
|
|
527
528
|
RailsAiBridge.configure do |config|
|
|
528
|
-
config.rubydex_enabled =
|
|
529
|
+
# config.rubydex_enabled = false # Set to false to disable semantic analysis
|
|
529
530
|
|
|
530
531
|
# Optional: enable the semantic introspector (adds :semantic to introspectors)
|
|
531
532
|
config.semantic_introspector_enabled = true
|
|
@@ -533,6 +534,10 @@ Built-in MCP tools and resources now read through a shared runtime context provi
|
|
|
533
534
|
|
|
534
535
|
# Optional: control semantic context depth in generated files
|
|
535
536
|
# config.semantic_context_depth = :standard # :summary, :standard, or :full
|
|
537
|
+
|
|
538
|
+
# Optional: incremental re-indexing (skip unchanged files on subsequent runs)
|
|
539
|
+
# config.rubydex_incremental_threshold = 0.3 # full rebuild if >30% of files changed
|
|
540
|
+
# config.rubydex_persist_index = true # survive process restarts
|
|
536
541
|
end
|
|
537
542
|
```
|
|
538
543
|
|
|
@@ -545,19 +550,22 @@ Built-in MCP tools and resources now read through a shared runtime context provi
|
|
|
545
550
|
| Semantic introspector | Codebase-wide pattern detection, inheritance analysis, and complexity hotspots |
|
|
546
551
|
| `rails://semantic/analysis` resource | Full semantic analysis accessible via MCP resource read |
|
|
547
552
|
| Context file enrichment | Semantic insights automatically included in generated AI context files |
|
|
553
|
+
| Incremental re-indexing | Only changed files are re-parsed on subsequent calls; unchanged files are skipped |
|
|
548
554
|
|
|
549
|
-
**Internal architecture:** The `RubydexAdapter` wraps the rubydex API as a thin coordinator delegating to three single-responsibility collaborators: `Serializer` (hash conversion), `Indexer` (graph construction + source file scanning), and `MethodCounter` (flat method-counting pipeline). See [CONTRIBUTING.md](CONTRIBUTING.md#project-structure) for the full project layout.
|
|
555
|
+
**Internal architecture:** The `RubydexAdapter` wraps the rubydex API as a thin coordinator delegating to three single-responsibility collaborators: `Serializer` (hash conversion), `Indexer` (graph construction + source file scanning), and `MethodCounter` (flat method-counting pipeline). Incremental re-indexing is handled by the `IncrementalIndexer` service, which tracks file mtimes and triggers a full rebuild when the changed-file ratio exceeds `rubydex_incremental_threshold`. See [CONTRIBUTING.md](CONTRIBUTING.md#project-structure) for the full project layout.
|
|
550
556
|
|
|
551
557
|
**Configuration options:**
|
|
552
558
|
|
|
553
559
|
| Option | Default | Description |
|
|
554
560
|
|--------|---------|-------------|
|
|
555
|
-
| `rubydex_enabled` | `
|
|
556
|
-
| `rubydex_index_path` | `"tmp/rubydex_index"` | Path for the rubydex index |
|
|
561
|
+
| `rubydex_enabled` | `true` | Enable rubydex integration |
|
|
562
|
+
| `rubydex_index_path` | `"tmp/rubydex_index"` | Path for the rubydex index (must stay within `Rails.root`) |
|
|
557
563
|
| `semantic_introspector_enabled` | `false` | Enable the dedicated semantic introspector |
|
|
558
|
-
| `semantic_context_depth` | `:standard` | Depth of semantic context in generated files |
|
|
564
|
+
| `semantic_context_depth` | `:standard` | Depth of semantic context in generated files (`:summary`, `:standard`, `:full`) |
|
|
565
|
+
| `rubydex_incremental_threshold` | `0.3` | Fraction of changed files (0.0–1.0) above which a full rebuild replaces incremental patching |
|
|
566
|
+
| `rubydex_persist_index` | `false` | Persist the mtime snapshot to disk so incremental re-indexing survives process restarts |
|
|
559
567
|
|
|
560
|
-
**Performance considerations:** Rubydex indexes your Ruby source files at startup. For large codebases, the initial indexing may take a few seconds.
|
|
568
|
+
**Performance considerations:** Rubydex indexes your Ruby source files at startup. For large codebases, the initial indexing may take a few seconds. Subsequent calls re-use the in-memory index and only re-parse changed files, keeping re-indexing overhead proportional to churn rather than codebase size.
|
|
561
569
|
|
|
562
570
|
---
|
|
563
571
|
|