codebase_index 0.1.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 (171) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +29 -0
  3. data/CODE_OF_CONDUCT.md +83 -0
  4. data/CONTRIBUTING.md +65 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +481 -0
  7. data/exe/codebase-console-mcp +22 -0
  8. data/exe/codebase-index-mcp +61 -0
  9. data/exe/codebase-index-mcp-http +64 -0
  10. data/exe/codebase-index-mcp-start +58 -0
  11. data/lib/codebase_index/ast/call_site_extractor.rb +106 -0
  12. data/lib/codebase_index/ast/method_extractor.rb +76 -0
  13. data/lib/codebase_index/ast/node.rb +88 -0
  14. data/lib/codebase_index/ast/parser.rb +653 -0
  15. data/lib/codebase_index/ast.rb +6 -0
  16. data/lib/codebase_index/builder.rb +137 -0
  17. data/lib/codebase_index/chunking/chunk.rb +84 -0
  18. data/lib/codebase_index/chunking/semantic_chunker.rb +290 -0
  19. data/lib/codebase_index/console/adapters/cache_adapter.rb +58 -0
  20. data/lib/codebase_index/console/adapters/good_job_adapter.rb +66 -0
  21. data/lib/codebase_index/console/adapters/sidekiq_adapter.rb +66 -0
  22. data/lib/codebase_index/console/adapters/solid_queue_adapter.rb +66 -0
  23. data/lib/codebase_index/console/audit_logger.rb +75 -0
  24. data/lib/codebase_index/console/bridge.rb +170 -0
  25. data/lib/codebase_index/console/confirmation.rb +90 -0
  26. data/lib/codebase_index/console/connection_manager.rb +173 -0
  27. data/lib/codebase_index/console/console_response_renderer.rb +78 -0
  28. data/lib/codebase_index/console/model_validator.rb +81 -0
  29. data/lib/codebase_index/console/safe_context.rb +82 -0
  30. data/lib/codebase_index/console/server.rb +557 -0
  31. data/lib/codebase_index/console/sql_validator.rb +172 -0
  32. data/lib/codebase_index/console/tools/tier1.rb +118 -0
  33. data/lib/codebase_index/console/tools/tier2.rb +117 -0
  34. data/lib/codebase_index/console/tools/tier3.rb +110 -0
  35. data/lib/codebase_index/console/tools/tier4.rb +79 -0
  36. data/lib/codebase_index/coordination/pipeline_lock.rb +109 -0
  37. data/lib/codebase_index/cost_model/embedding_cost.rb +88 -0
  38. data/lib/codebase_index/cost_model/estimator.rb +128 -0
  39. data/lib/codebase_index/cost_model/provider_pricing.rb +67 -0
  40. data/lib/codebase_index/cost_model/storage_cost.rb +52 -0
  41. data/lib/codebase_index/cost_model.rb +22 -0
  42. data/lib/codebase_index/db/migrations/001_create_units.rb +38 -0
  43. data/lib/codebase_index/db/migrations/002_create_edges.rb +35 -0
  44. data/lib/codebase_index/db/migrations/003_create_embeddings.rb +37 -0
  45. data/lib/codebase_index/db/migrations/004_create_snapshots.rb +45 -0
  46. data/lib/codebase_index/db/migrations/005_create_snapshot_units.rb +40 -0
  47. data/lib/codebase_index/db/migrator.rb +71 -0
  48. data/lib/codebase_index/db/schema_version.rb +73 -0
  49. data/lib/codebase_index/dependency_graph.rb +227 -0
  50. data/lib/codebase_index/embedding/indexer.rb +130 -0
  51. data/lib/codebase_index/embedding/openai.rb +105 -0
  52. data/lib/codebase_index/embedding/provider.rb +135 -0
  53. data/lib/codebase_index/embedding/text_preparer.rb +112 -0
  54. data/lib/codebase_index/evaluation/baseline_runner.rb +115 -0
  55. data/lib/codebase_index/evaluation/evaluator.rb +146 -0
  56. data/lib/codebase_index/evaluation/metrics.rb +79 -0
  57. data/lib/codebase_index/evaluation/query_set.rb +148 -0
  58. data/lib/codebase_index/evaluation/report_generator.rb +90 -0
  59. data/lib/codebase_index/extracted_unit.rb +145 -0
  60. data/lib/codebase_index/extractor.rb +956 -0
  61. data/lib/codebase_index/extractors/action_cable_extractor.rb +228 -0
  62. data/lib/codebase_index/extractors/ast_source_extraction.rb +46 -0
  63. data/lib/codebase_index/extractors/behavioral_profile.rb +309 -0
  64. data/lib/codebase_index/extractors/caching_extractor.rb +261 -0
  65. data/lib/codebase_index/extractors/callback_analyzer.rb +232 -0
  66. data/lib/codebase_index/extractors/concern_extractor.rb +253 -0
  67. data/lib/codebase_index/extractors/configuration_extractor.rb +219 -0
  68. data/lib/codebase_index/extractors/controller_extractor.rb +494 -0
  69. data/lib/codebase_index/extractors/database_view_extractor.rb +278 -0
  70. data/lib/codebase_index/extractors/decorator_extractor.rb +260 -0
  71. data/lib/codebase_index/extractors/engine_extractor.rb +204 -0
  72. data/lib/codebase_index/extractors/event_extractor.rb +211 -0
  73. data/lib/codebase_index/extractors/factory_extractor.rb +289 -0
  74. data/lib/codebase_index/extractors/graphql_extractor.rb +917 -0
  75. data/lib/codebase_index/extractors/i18n_extractor.rb +117 -0
  76. data/lib/codebase_index/extractors/job_extractor.rb +369 -0
  77. data/lib/codebase_index/extractors/lib_extractor.rb +249 -0
  78. data/lib/codebase_index/extractors/mailer_extractor.rb +339 -0
  79. data/lib/codebase_index/extractors/manager_extractor.rb +202 -0
  80. data/lib/codebase_index/extractors/middleware_extractor.rb +133 -0
  81. data/lib/codebase_index/extractors/migration_extractor.rb +469 -0
  82. data/lib/codebase_index/extractors/model_extractor.rb +960 -0
  83. data/lib/codebase_index/extractors/phlex_extractor.rb +252 -0
  84. data/lib/codebase_index/extractors/policy_extractor.rb +214 -0
  85. data/lib/codebase_index/extractors/poro_extractor.rb +246 -0
  86. data/lib/codebase_index/extractors/pundit_extractor.rb +223 -0
  87. data/lib/codebase_index/extractors/rails_source_extractor.rb +473 -0
  88. data/lib/codebase_index/extractors/rake_task_extractor.rb +343 -0
  89. data/lib/codebase_index/extractors/route_extractor.rb +181 -0
  90. data/lib/codebase_index/extractors/scheduled_job_extractor.rb +331 -0
  91. data/lib/codebase_index/extractors/serializer_extractor.rb +334 -0
  92. data/lib/codebase_index/extractors/service_extractor.rb +254 -0
  93. data/lib/codebase_index/extractors/shared_dependency_scanner.rb +91 -0
  94. data/lib/codebase_index/extractors/shared_utility_methods.rb +99 -0
  95. data/lib/codebase_index/extractors/state_machine_extractor.rb +398 -0
  96. data/lib/codebase_index/extractors/test_mapping_extractor.rb +225 -0
  97. data/lib/codebase_index/extractors/validator_extractor.rb +225 -0
  98. data/lib/codebase_index/extractors/view_component_extractor.rb +310 -0
  99. data/lib/codebase_index/extractors/view_template_extractor.rb +261 -0
  100. data/lib/codebase_index/feedback/gap_detector.rb +89 -0
  101. data/lib/codebase_index/feedback/store.rb +119 -0
  102. data/lib/codebase_index/flow_analysis/operation_extractor.rb +209 -0
  103. data/lib/codebase_index/flow_analysis/response_code_mapper.rb +154 -0
  104. data/lib/codebase_index/flow_assembler.rb +290 -0
  105. data/lib/codebase_index/flow_document.rb +191 -0
  106. data/lib/codebase_index/flow_precomputer.rb +102 -0
  107. data/lib/codebase_index/formatting/base.rb +40 -0
  108. data/lib/codebase_index/formatting/claude_adapter.rb +98 -0
  109. data/lib/codebase_index/formatting/generic_adapter.rb +56 -0
  110. data/lib/codebase_index/formatting/gpt_adapter.rb +64 -0
  111. data/lib/codebase_index/formatting/human_adapter.rb +78 -0
  112. data/lib/codebase_index/graph_analyzer.rb +374 -0
  113. data/lib/codebase_index/mcp/index_reader.rb +394 -0
  114. data/lib/codebase_index/mcp/renderers/claude_renderer.rb +81 -0
  115. data/lib/codebase_index/mcp/renderers/json_renderer.rb +17 -0
  116. data/lib/codebase_index/mcp/renderers/markdown_renderer.rb +352 -0
  117. data/lib/codebase_index/mcp/renderers/plain_renderer.rb +240 -0
  118. data/lib/codebase_index/mcp/server.rb +935 -0
  119. data/lib/codebase_index/mcp/tool_response_renderer.rb +62 -0
  120. data/lib/codebase_index/model_name_cache.rb +51 -0
  121. data/lib/codebase_index/notion/client.rb +217 -0
  122. data/lib/codebase_index/notion/exporter.rb +219 -0
  123. data/lib/codebase_index/notion/mapper.rb +39 -0
  124. data/lib/codebase_index/notion/mappers/column_mapper.rb +65 -0
  125. data/lib/codebase_index/notion/mappers/migration_mapper.rb +39 -0
  126. data/lib/codebase_index/notion/mappers/model_mapper.rb +164 -0
  127. data/lib/codebase_index/notion/rate_limiter.rb +68 -0
  128. data/lib/codebase_index/observability/health_check.rb +81 -0
  129. data/lib/codebase_index/observability/instrumentation.rb +34 -0
  130. data/lib/codebase_index/observability/structured_logger.rb +75 -0
  131. data/lib/codebase_index/operator/error_escalator.rb +81 -0
  132. data/lib/codebase_index/operator/pipeline_guard.rb +99 -0
  133. data/lib/codebase_index/operator/status_reporter.rb +80 -0
  134. data/lib/codebase_index/railtie.rb +26 -0
  135. data/lib/codebase_index/resilience/circuit_breaker.rb +99 -0
  136. data/lib/codebase_index/resilience/index_validator.rb +185 -0
  137. data/lib/codebase_index/resilience/retryable_provider.rb +108 -0
  138. data/lib/codebase_index/retrieval/context_assembler.rb +249 -0
  139. data/lib/codebase_index/retrieval/query_classifier.rb +131 -0
  140. data/lib/codebase_index/retrieval/ranker.rb +273 -0
  141. data/lib/codebase_index/retrieval/search_executor.rb +327 -0
  142. data/lib/codebase_index/retriever.rb +160 -0
  143. data/lib/codebase_index/ruby_analyzer/class_analyzer.rb +190 -0
  144. data/lib/codebase_index/ruby_analyzer/dataflow_analyzer.rb +78 -0
  145. data/lib/codebase_index/ruby_analyzer/fqn_builder.rb +18 -0
  146. data/lib/codebase_index/ruby_analyzer/mermaid_renderer.rb +275 -0
  147. data/lib/codebase_index/ruby_analyzer/method_analyzer.rb +143 -0
  148. data/lib/codebase_index/ruby_analyzer/trace_enricher.rb +139 -0
  149. data/lib/codebase_index/ruby_analyzer.rb +87 -0
  150. data/lib/codebase_index/session_tracer/file_store.rb +111 -0
  151. data/lib/codebase_index/session_tracer/middleware.rb +143 -0
  152. data/lib/codebase_index/session_tracer/redis_store.rb +112 -0
  153. data/lib/codebase_index/session_tracer/session_flow_assembler.rb +263 -0
  154. data/lib/codebase_index/session_tracer/session_flow_document.rb +223 -0
  155. data/lib/codebase_index/session_tracer/solid_cache_store.rb +145 -0
  156. data/lib/codebase_index/session_tracer/store.rb +67 -0
  157. data/lib/codebase_index/storage/graph_store.rb +120 -0
  158. data/lib/codebase_index/storage/metadata_store.rb +169 -0
  159. data/lib/codebase_index/storage/pgvector.rb +163 -0
  160. data/lib/codebase_index/storage/qdrant.rb +172 -0
  161. data/lib/codebase_index/storage/vector_store.rb +156 -0
  162. data/lib/codebase_index/temporal/snapshot_store.rb +341 -0
  163. data/lib/codebase_index/version.rb +5 -0
  164. data/lib/codebase_index.rb +223 -0
  165. data/lib/generators/codebase_index/install_generator.rb +32 -0
  166. data/lib/generators/codebase_index/pgvector_generator.rb +37 -0
  167. data/lib/generators/codebase_index/templates/add_pgvector_to_codebase_index.rb.erb +15 -0
  168. data/lib/generators/codebase_index/templates/create_codebase_index_tables.rb.erb +43 -0
  169. data/lib/tasks/codebase_index.rake +583 -0
  170. data/lib/tasks/codebase_index_evaluation.rake +115 -0
  171. metadata +252 -0
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @see CodebaseIndex
4
+ module CodebaseIndex
5
+ class Error < StandardError; end unless defined?(CodebaseIndex::Error)
6
+
7
+ module Console
8
+ class SqlValidationError < CodebaseIndex::Error; end
9
+
10
+ # Validates SQL strings for read-only safety.
11
+ #
12
+ # Allows only SELECT and WITH...SELECT statements. Rejects DML (INSERT,
13
+ # UPDATE, DELETE), DDL (CREATE, DROP, ALTER, TRUNCATE), and administrative
14
+ # commands (GRANT, REVOKE). Also rejects multiple statements (semicolons).
15
+ #
16
+ # Uses pattern-based validation, not full SQL parsing.
17
+ #
18
+ # @example
19
+ # validator = SqlValidator.new
20
+ # validator.validate!('SELECT * FROM users') # => true
21
+ # validator.validate!('DELETE FROM users') # => raises SqlValidationError
22
+ # validator.valid?('SELECT 1') # => true
23
+ #
24
+ class SqlValidator
25
+ # Forbidden statement prefixes (case-insensitive).
26
+ FORBIDDEN_KEYWORDS = %w[
27
+ INSERT UPDATE DELETE DROP ALTER TRUNCATE CREATE GRANT REVOKE
28
+ ].freeze
29
+
30
+ # Keywords that are forbidden anywhere in the SQL (not just at start).
31
+ BODY_FORBIDDEN_KEYWORDS = %w[UNION INTO COPY].freeze
32
+
33
+ # Dangerous functions that can be used for DoS or file access.
34
+ DANGEROUS_FUNCTIONS = %w[
35
+ pg_sleep lo_import lo_export pg_read_file pg_write_file
36
+ load_file sleep benchmark
37
+ ].freeze
38
+
39
+ # Allowed statement prefixes (case-insensitive).
40
+ ALLOWED_PREFIXES = /\A\s*(SELECT|WITH|EXPLAIN)\b/i
41
+
42
+ # @return [true]
43
+ # @raise [SqlValidationError] if the SQL is not a safe read-only statement
44
+ def validate!(sql) # rubocop:disable Naming/PredicateMethod
45
+ raise SqlValidationError, 'SQL is empty' if sql.nil? || sql.strip.empty?
46
+
47
+ normalized = sql.strip
48
+
49
+ # Reject multiple statements (semicolons not inside string literals)
50
+ if contains_multiple_statements?(normalized)
51
+ raise SqlValidationError, 'Rejected: multiple statements are not allowed'
52
+ end
53
+
54
+ # Check for forbidden keywords at statement start
55
+ check_forbidden_keywords!(normalized)
56
+
57
+ # Check for writable CTEs (before body keywords to give better error messages)
58
+ check_writable_ctes!(normalized)
59
+
60
+ # Check for forbidden keywords anywhere in the SQL body
61
+ check_body_forbidden_keywords!(normalized)
62
+
63
+ # Check for dangerous functions
64
+ check_dangerous_functions!(normalized)
65
+
66
+ # After stripping comments, check again for forbidden keywords that might have been hidden
67
+ check_forbidden_keywords_in_body!(normalized)
68
+
69
+ # Must start with an allowed prefix
70
+ unless normalized.match?(ALLOWED_PREFIXES)
71
+ raise SqlValidationError, 'Rejected: SQL must start with SELECT, WITH, or EXPLAIN'
72
+ end
73
+
74
+ true
75
+ end
76
+
77
+ # Check if SQL is valid without raising.
78
+ #
79
+ # @param sql [String] SQL string to validate
80
+ # @return [Boolean]
81
+ def valid?(sql)
82
+ validate!(sql)
83
+ true
84
+ rescue SqlValidationError
85
+ false
86
+ end
87
+
88
+ private
89
+
90
+ # Check if the SQL contains multiple statements separated by semicolons.
91
+ # Strips SQL comments and string literals before checking.
92
+ #
93
+ # @param sql [String]
94
+ # @return [Boolean]
95
+ def contains_multiple_statements?(sql)
96
+ # Strip SQL comments before checking
97
+ stripped = sql.gsub(/--[^\n]*/, '') # line comments
98
+ stripped = stripped.gsub(%r{/\*.*?\*/}m, '') # block comments
99
+ # Strip single-quoted strings to avoid false positives
100
+ stripped = stripped.gsub(/'[^']*'/, '')
101
+ stripped.include?(';')
102
+ end
103
+
104
+ # Check if the SQL starts with a forbidden keyword.
105
+ #
106
+ # @param sql [String]
107
+ # @raise [SqlValidationError] if a forbidden keyword is found
108
+ def check_forbidden_keywords!(sql)
109
+ FORBIDDEN_KEYWORDS.each do |keyword|
110
+ if sql.match?(/\A\s*#{keyword}\b/i)
111
+ raise SqlValidationError, "Rejected: #{keyword} statements are not allowed"
112
+ end
113
+ end
114
+ end
115
+
116
+ # Check if the SQL contains forbidden keywords anywhere in the body.
117
+ #
118
+ # @param sql [String]
119
+ # @raise [SqlValidationError] if a forbidden keyword is found
120
+ def check_body_forbidden_keywords!(sql)
121
+ BODY_FORBIDDEN_KEYWORDS.each do |keyword|
122
+ raise SqlValidationError, "Rejected: #{keyword} is not allowed" if sql.match?(/\b#{keyword}\b/i)
123
+ end
124
+ end
125
+
126
+ # Check if the SQL contains writable CTEs (WITH...DELETE/UPDATE/INSERT).
127
+ #
128
+ # @param sql [String]
129
+ # @raise [SqlValidationError] if a writable CTE is found
130
+ def check_writable_ctes!(sql)
131
+ return unless sql.match?(/WITH\s+\w+\s+AS\s*\(\s*(DELETE|UPDATE|INSERT)\b/i)
132
+
133
+ raise SqlValidationError, 'Rejected: writable CTEs are not allowed'
134
+ end
135
+
136
+ # Check if the SQL calls dangerous functions.
137
+ #
138
+ # @param sql [String]
139
+ # @raise [SqlValidationError] if a dangerous function is found
140
+ def check_dangerous_functions!(sql)
141
+ DANGEROUS_FUNCTIONS.each do |func|
142
+ if sql.match?(/\b#{func}\s*\(/i)
143
+ raise SqlValidationError, "Rejected: dangerous function #{func} is not allowed"
144
+ end
145
+ end
146
+ end
147
+
148
+ # Check if the SQL contains forbidden keywords anywhere in the body after stripping comments.
149
+ # This catches comment-hidden injections like "SELECT 1 --;\nDELETE FROM users".
150
+ #
151
+ # @param sql [String]
152
+ # @raise [SqlValidationError] if a forbidden keyword is found
153
+ def check_forbidden_keywords_in_body!(sql)
154
+ # Strip comments to reveal hidden statements
155
+ stripped = sql.gsub(/--[^\n]*/, '') # line comments
156
+ stripped = stripped.gsub(%r{/\*.*?\*/}m, '') # block comments
157
+
158
+ # Check if any forbidden keyword appears anywhere (not just at start)
159
+ FORBIDDEN_KEYWORDS.each do |keyword|
160
+ # Look for keyword as a whole word anywhere in the stripped SQL
161
+ next unless stripped.match?(/\b#{keyword}\b/i)
162
+
163
+ # Make sure it's not at the very start (already checked)
164
+ unless stripped.match?(/\A\s*#{keyword}\b/i)
165
+ raise SqlValidationError,
166
+ "Rejected: #{keyword} statements are not allowed (found in SQL body)"
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CodebaseIndex
4
+ module Console
5
+ module Tools
6
+ # Tier 1: MVP read-only tools for querying live Rails data.
7
+ #
8
+ # Each method builds a bridge request hash from validated parameters.
9
+ # The bridge executes the query against the Rails database.
10
+ #
11
+ module Tier1
12
+ module_function
13
+
14
+ # Count records matching scope conditions.
15
+ #
16
+ # @param model [String] Model name
17
+ # @param scope [Hash, nil] Filter conditions
18
+ # @return [Hash] Bridge request
19
+ def console_count(model:, scope: nil)
20
+ { tool: 'count', params: { model: model, scope: scope }.compact }
21
+ end
22
+
23
+ # Random sample of records.
24
+ #
25
+ # @param model [String] Model name
26
+ # @param scope [Hash, nil] Filter conditions
27
+ # @param limit [Integer] Max records (default: 5, max: 25)
28
+ # @param columns [Array<String>, nil] Columns to include
29
+ # @return [Hash] Bridge request
30
+ def console_sample(model:, scope: nil, limit: 5, columns: nil)
31
+ limit = [limit, 25].min
32
+ { tool: 'sample', params: { model: model, scope: scope, limit: limit, columns: columns }.compact }
33
+ end
34
+
35
+ # Find a single record by primary key or unique column.
36
+ #
37
+ # @param model [String] Model name
38
+ # @param id [Integer, nil] Primary key value
39
+ # @param by [Hash, nil] Unique column lookup (e.g., { email: "x@y.com" })
40
+ # @param columns [Array<String>, nil] Columns to include
41
+ # @return [Hash] Bridge request
42
+ def console_find(model:, id: nil, by: nil, columns: nil)
43
+ { tool: 'find', params: { model: model, id: id, by: by, columns: columns }.compact }
44
+ end
45
+
46
+ # Extract column values.
47
+ #
48
+ # @param model [String] Model name
49
+ # @param columns [Array<String>] Column names to pluck
50
+ # @param scope [Hash, nil] Filter conditions
51
+ # @param limit [Integer] Max records (default: 100, max: 1000)
52
+ # @param distinct [Boolean] Return unique values only
53
+ # @return [Hash] Bridge request
54
+ def console_pluck(model:, columns:, scope: nil, limit: 100, distinct: false)
55
+ limit = [limit, 1000].min
56
+ { tool: 'pluck', params: { model: model, columns: columns, scope: scope,
57
+ limit: limit, distinct: distinct }.compact }
58
+ end
59
+
60
+ # Run aggregate function on a column.
61
+ #
62
+ # @param model [String] Model name
63
+ # @param function [String] One of: sum, avg, minimum, maximum
64
+ # @param column [String] Column to aggregate
65
+ # @param scope [Hash, nil] Filter conditions
66
+ # @return [Hash] Bridge request
67
+ def console_aggregate(model:, function:, column:, scope: nil)
68
+ { tool: 'aggregate', params: { model: model, function: function, column: column, scope: scope }.compact }
69
+ end
70
+
71
+ # Count associated records.
72
+ #
73
+ # @param model [String] Model name
74
+ # @param id [Integer] Record primary key
75
+ # @param association [String] Association name
76
+ # @param scope [Hash, nil] Filter on the association
77
+ # @return [Hash] Bridge request
78
+ def console_association_count(model:, id:, association:, scope: nil)
79
+ { tool: 'association_count',
80
+ params: { model: model, id: id, association: association, scope: scope }.compact }
81
+ end
82
+
83
+ # Get database schema for a model.
84
+ #
85
+ # @param model [String] Model name
86
+ # @param include_indexes [Boolean] Include index information
87
+ # @return [Hash] Bridge request
88
+ def console_schema(model:, include_indexes: false)
89
+ { tool: 'schema', params: { model: model, include_indexes: include_indexes } }
90
+ end
91
+
92
+ # Recently created/updated records.
93
+ #
94
+ # @param model [String] Model name
95
+ # @param order_by [String] Column to sort by (default: created_at)
96
+ # @param direction [String] Sort direction (default: desc)
97
+ # @param limit [Integer] Max records (default: 10, max: 50)
98
+ # @param scope [Hash, nil] Filter conditions
99
+ # @param columns [Array<String>, nil] Columns to include
100
+ # @return [Hash] Bridge request
101
+ # rubocop:disable Metrics/ParameterLists
102
+ def console_recent(model:, order_by: 'created_at', direction: 'desc', limit: 10, scope: nil, columns: nil)
103
+ limit = [limit, 50].min
104
+ { tool: 'recent', params: { model: model, order_by: order_by, direction: direction,
105
+ limit: limit, scope: scope, columns: columns }.compact }
106
+ end
107
+ # rubocop:enable Metrics/ParameterLists
108
+
109
+ # System health check.
110
+ #
111
+ # @return [Hash] Bridge request
112
+ def console_status
113
+ { tool: 'status', params: {} }
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CodebaseIndex
4
+ module Console
5
+ module Tools
6
+ # Tier 2: Domain-aware tools for querying live Rails data.
7
+ #
8
+ # These tools build on Tier 1 primitives to provide higher-level
9
+ # domain operations: model diagnostics, data snapshots, validation,
10
+ # settings management, policy checks, and decorator invocation.
11
+ #
12
+ # Each method builds a bridge request hash from validated parameters.
13
+ # The bridge executes the operation against the Rails environment.
14
+ #
15
+ module Tier2
16
+ module_function
17
+
18
+ # Diagnose a model by composing multiple queries: count, recent records, and aggregates.
19
+ #
20
+ # @param model [String] Model name
21
+ # @param scope [Hash, nil] Filter conditions
22
+ # @param sample_size [Integer] Number of sample records (default: 5, max: 25)
23
+ # @return [Hash] Bridge request
24
+ def console_diagnose_model(model:, scope: nil, sample_size: 5)
25
+ sample_size = [sample_size, 25].min
26
+ { tool: 'diagnose_model', params: { model: model, scope: scope, sample_size: sample_size }.compact }
27
+ end
28
+
29
+ # Snapshot a record with its associations for debugging.
30
+ #
31
+ # @param model [String] Model name
32
+ # @param id [Integer] Record primary key
33
+ # @param associations [Array<String>, nil] Association names to include
34
+ # @param depth [Integer] Association traversal depth (default: 1, max: 3)
35
+ # @return [Hash] Bridge request
36
+ def console_data_snapshot(model:, id:, associations: nil, depth: 1)
37
+ depth = [depth, 3].min
38
+ { tool: 'data_snapshot',
39
+ params: { model: model, id: id, associations: associations, depth: depth }.compact }
40
+ end
41
+
42
+ # Run validations on an existing record, optionally with changed attributes.
43
+ #
44
+ # @param model [String] Model name
45
+ # @param id [Integer] Record primary key
46
+ # @param attributes [Hash, nil] Attributes to set before validating
47
+ # @return [Hash] Bridge request
48
+ def console_validate_record(model:, id:, attributes: nil)
49
+ { tool: 'validate_record', params: { model: model, id: id, attributes: attributes }.compact }
50
+ end
51
+
52
+ # Check a configuration setting value.
53
+ #
54
+ # @param key [String] Setting key
55
+ # @param namespace [String, nil] Setting namespace
56
+ # @return [Hash] Bridge request
57
+ def console_check_setting(key:, namespace: nil)
58
+ { tool: 'check_setting', params: { key: key, namespace: namespace }.compact }
59
+ end
60
+
61
+ # Update a configuration setting (requires human confirmation).
62
+ #
63
+ # @param key [String] Setting key
64
+ # @param value [Object] New value
65
+ # @param namespace [String, nil] Setting namespace
66
+ # @return [Hash] Bridge request with requires_confirmation flag
67
+ def console_update_setting(key:, value:, namespace: nil)
68
+ { tool: 'update_setting',
69
+ params: { key: key, value: value, namespace: namespace }.compact,
70
+ requires_confirmation: true }
71
+ end
72
+
73
+ # Check authorization policy for a record and user.
74
+ #
75
+ # @param model [String] Model name
76
+ # @param id [Integer] Record primary key
77
+ # @param user_id [Integer] User to check authorization for
78
+ # @param action [String] Policy action (e.g., "update", "destroy")
79
+ # @return [Hash] Bridge request
80
+ def console_check_policy(model:, id:, user_id:, action:)
81
+ { tool: 'check_policy',
82
+ params: { model: model, id: id, user_id: user_id, action: action } }
83
+ end
84
+
85
+ # Validate attributes against a model without persisting.
86
+ #
87
+ # @param model [String] Model name
88
+ # @param attributes [Hash] Attributes to validate
89
+ # @param context [String, nil] Validation context (e.g., "create", "update")
90
+ # @return [Hash] Bridge request
91
+ def console_validate_with(model:, attributes:, context: nil)
92
+ { tool: 'validate_with', params: { model: model, attributes: attributes, context: context }.compact }
93
+ end
94
+
95
+ # Check feature eligibility for a record.
96
+ #
97
+ # @param model [String] Model name
98
+ # @param id [Integer] Record primary key
99
+ # @param feature [String] Feature name to check
100
+ # @return [Hash] Bridge request
101
+ def console_check_eligibility(model:, id:, feature:)
102
+ { tool: 'check_eligibility', params: { model: model, id: id, feature: feature } }
103
+ end
104
+
105
+ # Invoke a decorator on a record and return computed attributes.
106
+ #
107
+ # @param model [String] Model name
108
+ # @param id [Integer] Record primary key
109
+ # @param methods [Array<String>, nil] Specific decorator methods to call
110
+ # @return [Hash] Bridge request
111
+ def console_decorate(model:, id:, methods: nil)
112
+ { tool: 'decorate', params: { model: model, id: id, methods: methods }.compact }
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CodebaseIndex
4
+ module Console
5
+ module Tools
6
+ # Tier 3: Analytics tools for monitoring live Rails application state.
7
+ #
8
+ # Provides request performance metrics, job queue monitoring,
9
+ # cache statistics, and ActionCable channel status. Job-related
10
+ # tools delegate to backend-specific adapters (Sidekiq, Solid Queue, GoodJob).
11
+ #
12
+ # Each method builds a bridge request hash from validated parameters.
13
+ # The bridge executes the query against the Rails environment.
14
+ #
15
+ module Tier3
16
+ module_function
17
+
18
+ # List slowest endpoints by response time.
19
+ #
20
+ # @param limit [Integer] Max endpoints to return (default: 10, max: 100)
21
+ # @param period [String] Time period (default: "1h")
22
+ # @return [Hash] Bridge request
23
+ def console_slow_endpoints(limit: 10, period: '1h')
24
+ limit = [limit, 100].min
25
+ { tool: 'slow_endpoints', params: { limit: limit, period: period } }
26
+ end
27
+
28
+ # Get error rates by controller or overall.
29
+ #
30
+ # @param period [String] Time period (default: "1h")
31
+ # @param controller [String, nil] Filter by controller name
32
+ # @return [Hash] Bridge request
33
+ def console_error_rates(period: '1h', controller: nil)
34
+ { tool: 'error_rates', params: { period: period, controller: controller }.compact }
35
+ end
36
+
37
+ # Get request throughput over time.
38
+ #
39
+ # @param period [String] Time period (default: "1h")
40
+ # @param interval [String] Aggregation interval (default: "5m")
41
+ # @return [Hash] Bridge request
42
+ def console_throughput(period: '1h', interval: '5m')
43
+ { tool: 'throughput', params: { period: period, interval: interval } }
44
+ end
45
+
46
+ # Get job queue statistics.
47
+ #
48
+ # @param queue [String, nil] Filter by queue name
49
+ # @return [Hash] Bridge request
50
+ def console_job_queues(queue: nil)
51
+ { tool: 'job_queues', params: { queue: queue }.compact }
52
+ end
53
+
54
+ # List recent job failures.
55
+ #
56
+ # @param limit [Integer] Max failures to return (default: 10, max: 100)
57
+ # @param queue [String, nil] Filter by queue name
58
+ # @return [Hash] Bridge request
59
+ def console_job_failures(limit: 10, queue: nil)
60
+ limit = [limit, 100].min
61
+ { tool: 'job_failures', params: { limit: limit, queue: queue }.compact }
62
+ end
63
+
64
+ # Find a job by ID, optionally retry it (requires confirmation).
65
+ #
66
+ # @param job_id [String] Job identifier
67
+ # @param retry_job [Boolean, nil] Whether to retry the job
68
+ # @return [Hash] Bridge request, with requires_confirmation if retry requested
69
+ def console_job_find(job_id:, retry_job: nil)
70
+ result = { tool: 'job_find', params: { job_id: job_id, retry: retry_job }.compact }
71
+ result[:requires_confirmation] = true if retry_job
72
+ result
73
+ end
74
+
75
+ # List scheduled/upcoming jobs.
76
+ #
77
+ # @param limit [Integer] Max jobs to return (default: 20, max: 100)
78
+ # @return [Hash] Bridge request
79
+ def console_job_schedule(limit: 20)
80
+ limit = [limit, 100].min
81
+ { tool: 'job_schedule', params: { limit: limit } }
82
+ end
83
+
84
+ # Get Redis server information.
85
+ #
86
+ # @param section [String, nil] Redis INFO section filter (e.g., "memory", "stats")
87
+ # @return [Hash] Bridge request
88
+ def console_redis_info(section: nil)
89
+ { tool: 'redis_info', params: { section: section }.compact }
90
+ end
91
+
92
+ # Get cache store statistics.
93
+ #
94
+ # @param namespace [String, nil] Cache namespace filter
95
+ # @return [Hash] Bridge request
96
+ def console_cache_stats(namespace: nil)
97
+ { tool: 'cache_stats', params: { namespace: namespace }.compact }
98
+ end
99
+
100
+ # Get ActionCable channel status.
101
+ #
102
+ # @param channel [String, nil] Filter by channel name
103
+ # @return [Hash] Bridge request
104
+ def console_channel_status(channel: nil)
105
+ { tool: 'channel_status', params: { channel: channel }.compact }
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CodebaseIndex
4
+ module Console
5
+ module Tools
6
+ # Tier 4: Guarded tools requiring confirmation or SQL validation.
7
+ #
8
+ # - `console_eval` — Arbitrary Ruby execution with confirmation + timeout
9
+ # - `console_sql` — Read-only SQL (validated by SqlValidator)
10
+ # - `console_query` — Enhanced query builder with joins/grouping
11
+ #
12
+ # Each method builds a bridge request hash. The bridge executes against
13
+ # the live Rails environment.
14
+ #
15
+ module Tier4
16
+ MAX_EVAL_TIMEOUT = 30
17
+ MIN_EVAL_TIMEOUT = 1
18
+ DEFAULT_EVAL_TIMEOUT = 10
19
+ MAX_SQL_LIMIT = 10_000
20
+ MAX_QUERY_LIMIT = 10_000
21
+
22
+ module_function
23
+
24
+ # Arbitrary Ruby evaluation with timeout.
25
+ #
26
+ # @param code [String] Ruby code to execute
27
+ # @param timeout [Integer] Execution timeout in seconds (default 10, max 30)
28
+ # @return [Hash] Bridge request
29
+ def console_eval(code:, timeout: DEFAULT_EVAL_TIMEOUT)
30
+ timeout = timeout.clamp(MIN_EVAL_TIMEOUT, MAX_EVAL_TIMEOUT)
31
+ { tool: 'eval', params: { code: code, timeout: timeout } }
32
+ end
33
+
34
+ # Read-only SQL execution with validation.
35
+ #
36
+ # @param sql [String] SQL query (must be SELECT or WITH...SELECT)
37
+ # @param validator [SqlValidator] SQL validator instance
38
+ # @param limit [Integer, nil] Optional row limit (max 10000)
39
+ # @return [Hash] Bridge request
40
+ # @raise [SqlValidationError] if SQL is not read-only
41
+ def console_sql(sql:, validator:, limit: nil)
42
+ validator.validate!(sql)
43
+ limit = [limit, MAX_SQL_LIMIT].min if limit
44
+ { tool: 'sql', params: { sql: sql, limit: limit }.compact }
45
+ end
46
+
47
+ # Enhanced query builder with joins and grouping.
48
+ #
49
+ # @param model [String] Model name
50
+ # @param select [Array<String>] Columns to select
51
+ # @param joins [Array<String>, nil] Associations to join
52
+ # @param group_by [Array<String>, nil] Columns to group by
53
+ # @param having [String, nil] HAVING clause
54
+ # @param order [Hash, nil] Order specification (e.g., { id: :desc })
55
+ # @param scope [Hash, nil] Filter conditions
56
+ # @param limit [Integer, nil] Row limit (max 10000)
57
+ # @return [Hash] Bridge request
58
+ # rubocop:disable Metrics/ParameterLists
59
+ def console_query(model:, select:, joins: nil, group_by: nil, having: nil, order: nil, scope: nil, limit: nil)
60
+ limit = [limit, MAX_QUERY_LIMIT].min if limit
61
+ {
62
+ tool: 'query',
63
+ params: {
64
+ model: model,
65
+ select: select,
66
+ joins: joins,
67
+ group_by: group_by,
68
+ having: having,
69
+ order: order,
70
+ scope: scope,
71
+ limit: limit
72
+ }.compact
73
+ }
74
+ end
75
+ # rubocop:enable Metrics/ParameterLists
76
+ end
77
+ end
78
+ end
79
+ end