Class: RailsAiBridge::Serializers::Providers::BaseProviderSerializer

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb

Overview

Base class for AI assistant provider serializers (Claude, Copilot, Gemini, Codex, Cursor). Shared compact-mode sections: header, stack, models, gems, architecture, MCP guide, commands, footer.

Defined Under Namespace

Classes: ModelEntries, NotableGemPayload

Constant Summary collapse

MAX_KEY_MODELS =

Maximum number of key models to display in compact mode Models beyond this count are truncated with an overflow hint

15
MAX_PATTERNS =

Maximum number of architectural patterns to display Limits the patterns section to prevent excessive output

8
MAX_CONFIG_FILES =

Maximum number of configuration files to list Shows only the most important config files

5

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context, config: RailsAiBridge.configuration) ⇒ BaseProviderSerializer

Returns a new instance of BaseProviderSerializer.

Parameters:



25
26
27
28
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 25

def initialize(context, config: RailsAiBridge.configuration)
  @context = context
  @config = config
end

Instance Attribute Details

#config ⇒ Object (readonly)

Returns the value of attribute config.



21
22
23
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 21

def config
  @config
end

#context ⇒ Object (readonly)

Returns the value of attribute context.



21
22
23
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 21

def context
  @context
end

Instance Method Details

#intro_sections ⇒ Array<String>

Document intro: header followed by the anti-hallucination rules, which must sit near the top so bottom-trimming never drops them.

Returns:

  • (Array<String>) —

    header lines, blank separator, rules lines



56
57
58
59
60
61
62
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 56

def intro_sections
  [
    *render_header,
    '',
    *SharedAssistantGuidance.anti_hallucination_rules_lines
  ]
end

#render_architecture ⇒ Array<String>

Renders the architecture section with detected styles and common patterns. Caps patterns at MAX_PATTERNS entries. Returns [] when conventions are absent, have an :error key, or both :architecture and :patterns are empty.

Returns:

  • (Array<String>) —

    Lines for the architecture section, or [] if unavailable.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 139

def render_architecture
  conv = context[:conventions]
  return [] unless conv.is_a?(Hash) && !conv[:error]

  arch = conv[:architecture] || []
  patterns = conv[:patterns] || []
  return [] if arch.empty? && patterns.empty?

  lines = ['## Architecture', 'Detected architectural styles and common patterns:']
  arch.each { |p| lines << "- #{p}" }
  patterns.first(MAX_PATTERNS).each { |p| lines << "- #{p}" }
  lines << ''
  lines
end

#render_commands ⇒ Array<String>

Renders the command reference section with common dev, test, lint and migration commands. The test command is resolved via ContextSummary.test_command.

Returns:

  • (Array<String>) —

    Lines for the commands section.



226
227
228
229
230
231
232
233
234
235
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 226

def render_commands
  [
    '## Commands',
    '- `bin/dev` — start dev server',
    "- `#{ContextSummary.test_command(context)}` — run tests",
    '- `bundle exec rubocop` — run linter',
    '- `rails db:migrate` — run pending migrations',
    ''
  ]
end

#render_compact ⇒ String

Renders the default compact AI context document (newline-joined sections). Enforces Configuration#claude_max_lines by trimming with an MCP pointer when exceeded. Subclasses may override entirely or compose with individual #render_* helpers.

Returns:

  • (String) —

    Compact markdown body.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 35

def render_compact
  lines = []
  lines.concat(intro_sections)
  lines.concat(render_stack_overview)
  lines.concat(render_key_models)
  lines.concat(render_notable_gems)
  lines.concat(render_architecture)
  lines.concat(render_semantic_insights)
  lines.concat(render_key_considerations)
  lines.concat(Formatters::Providers::McpGuideFormatter.new(context).call.split("\n"))
  lines.concat(render_key_config_files)
  lines.concat(render_commands)
  lines.concat(render_footer)

  line_enforcer.enforce(lines).join("\n")
end

Renders the closing engineering rules and regeneration attribution footer. Delegates to SharedAssistantGuidance.compact_engineering_rules_footer_lines.

Returns:

  • (Array<String>) —

    Lines for the footer section.



241
242
243
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 241

def render_footer
  SharedAssistantGuidance.compact_engineering_rules_footer_lines(context, include_anti_hallucination: false)
end

#render_header ⇒ Array<String>

Renders the header section of the context file.

Returns:

  • (Array<String>) —

    Lines for the header.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 66

def render_header
  [
    "# #{context[:app_name]} — AI Context",
    '',
    "> Auto-generated by rails-ai-bridge v#{RailsAiBridge::VERSION}",
    "> Generated: #{context[:generated_at]}",
    "> Rails #{context[:rails_version]} | Ruby #{context[:ruby_version]}",
    '',
    "This file provides a high-level overview of this Rails application's",
    'structure, patterns, and conventions. As an AI assistant, use this context',
    'to quickly understand the project and generate idiomatic code that',
    'adheres to its design decisions. For deeper dives, use the live',
    'MCP tools referenced throughout this document.'
  ]
end

#render_key_config_files ⇒ Array<String>

Renders the key configuration files section. Caps display at MAX_CONFIG_FILES files. Returns [] when conventions are absent, have an :error key, or :config_files is empty.

Returns:

  • (Array<String>) —

    Lines for the key config files section, or [] if unavailable.



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 209

def render_key_config_files
  conv = context[:conventions]
  return [] unless conv.is_a?(Hash) && !conv[:error]

  config_files = ContextSummary.safe_config_files(conv[:config_files], limit: MAX_CONFIG_FILES)
  return [] if config_files.empty?

  lines = ['## Key Config Files', 'Core configuration files for this application:']
  config_files.each { |f| lines << "- `#{f}`" }
  lines << ''
  lines
end

#render_key_considerations ⇒ Array<String>

Renders the static key considerations section covering performance, security, data drift, and MCP exposure. Content is fixed and does not depend on context.

Returns:

  • (Array<String>) —

    Lines for the key considerations section.



191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 191

def render_key_considerations
  [
    '## Key Considerations',
    '- **Performance:** For large or frequently accessed tables, always consider database performance. ' \
    'Use the `rails_get_schema` tool to verify indexes and be mindful of N+1 queries by using `includes` and other ActiveRecord optimizations.',
    '- **Security:** Treat all user-provided input as untrusted. Always use strong parameters in controllers ' \
    'and be aware of potential security vulnerabilities when using gems like `ransack` or `pg_search`.',
    '- **Data Drift:** This document is a snapshot. For the most up-to-date information, especially regarding schema and routes, use the live MCP tools.',
    '- **MCP Exposure:** The MCP tools are read-only but expose sensitive application structure. Avoid exposing the HTTP transport on untrusted networks.',
    ''
  ]
end

#render_key_models ⇒ Array<String>

Renders the key models section, sorted by task relevance (semantic tier, complexity, route density, recent migrations, and optional DB size hints). Caps display at MAX_KEY_MODELS models and appends an overflow hint when more exist. Returns [] when context[:models] is nil, non-Hash, or has an :error key.

Returns:

  • (Array<String>) —

    Lines for the key models section, or [] if unavailable.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 98

def render_key_models
  models = context[:models]
  return [] unless models.is_a?(Hash) && !models[:error] && models.any?

  max_show = MAX_KEY_MODELS

  lines = ['## Key Models',
           'The following are the most architecturally significant models, ordered by relevance:']
  model_entries = ModelEntries.new(models, context: context).sorted_by_relevance
  model_entries.first(max_show).each do |name, data|
    lines << model_line_formatter.format_line(name, data)
  end
  lines << "- _...#{model_entries.size - max_show} more (use `rails_get_model_details` tool)_" if model_entries.size > max_show
  lines << ''
  lines
end

#render_notable_gems ⇒ Array<String>

Renders the notable gems section grouped by category. Looks for notable gems under :notable_gems, :notable, or :detected keys. Returns [] when gems are absent, have an :error key, or the list is empty.

Returns:

  • (Array<String>) —

    Lines for the notable gems section, or [] if unavailable.



120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 120

def render_notable_gems
  notable = extract_notable_gems(context[:gems])
  return [] if notable.empty?

  lines = ['## Gems', 'Key gems, categorized by their primary function:']
  grouped = notable.group_by { |g| g[:category]&.to_s || 'other' }
  grouped.each do |category, gem_list|
    names = gem_list.map { |g| g[:name] }.join(', ')
    lines << "- **#{category}**: #{names}"
  end
  lines << ''
  lines
end

#render_semantic_insights ⇒ Array<String>

Renders semantic insights from rubydex analysis when available. Includes pattern detection results and complexity hotspot warnings. Returns [] when semantic data is absent or has an :error or :info key.

Returns:

  • (Array<String>) —

    Lines for the semantic insights section, or [] if unavailable.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 159

def render_semantic_insights
  semantic = context[:semantic]
  return [] unless semantic.is_a?(Hash) && !semantic[:error] && !semantic[:info]

  lines = ['## Semantic Analysis (rubydex)']

  # Codebase stats
  stats = semantic[:codebase_stats]
  if stats.is_a?(Hash) && stats.any?
    lines << "- **Files:** #{stats[:total_files]}, **Classes:** #{stats[:total_classes]}, " \
             "**Modules:** #{stats[:total_modules]}, **Methods:** #{stats[:total_methods]}"
  end

  # Detected patterns
  patterns = semantic.dig(:patterns, :common_patterns)
  lines << "- **Patterns:** #{patterns.join(', ')}" if patterns.is_a?(Array) && patterns.any?

  # Complexity hotspots
  hotspots = semantic[:complexity_hotspots]
  if hotspots.is_a?(Array) && hotspots.any?
    hotspot_list = hotspots.first(5).map { |h| "#{h[:name]} (score: #{h[:complexity_score]})" }.join(', ')
    lines << "- **Complexity hotspots:** #{hotspot_list}"
  end

  lines << ''
  lines
end

#render_stack_overview ⇒ Array<String>

Renders the stack overview section. Includes database adapter/table count, model count, routes, auth gems, async jobs/mailers/channels, and pending migrations when available. Silently skips any sub-section whose context key is missing or has an :error key.

Returns:

  • (Array<String>) —

    Lines for the stack overview section, always non-empty.



88
89
90
# File 'lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb', line 88

def render_stack_overview
  stack_overview_builder.build
end