Class: RailsAiBridge::Generators::InstallGenerator

Inherits:
Rails::Generators::Base
  • Object
show all
Includes:
CommandHelp
Defined in:
lib/generators/rails_ai_bridge/install/install_generator.rb

Instance Method Summary collapse

Methods included from CommandHelp

#print_command_reference

Instance Method Details

#add_to_gitignorevoid

This method returns an undefined value.

Appends rails-ai-bridge-specific entries to .gitignore when the file exists and the entries are not already present. Does nothing if .gitignore is absent. Uses Thor's #append_to_file so the operation respects +--pretend+ (dry-run).



233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/generators/rails_ai_bridge/install/install_generator.rb', line 233

def add_to_gitignore
  gitignore_path = '.gitignore'
  gitignore_full = File.join(destination_root, gitignore_path)
  return unless File.exist?(gitignore_full)

  content = File.read(gitignore_full)
  append = []
  append << '.ai-context.json' unless content.include?('.ai-context.json')

  return unless append.any?

  append_to_file gitignore_path, "\n# rails-ai-bridge (JSON cache — markdown files should be committed)\n#{append.join("\n")}\n"
  say 'Updated .gitignore', :green
end

#create_assistant_overrides_templatevoid

This method returns an undefined value.

Creates the config/rails_ai_bridge/ directory with an overrides.md stub and an overrides.md.example reference file. Skips each file if it already exists so the method is safe to re-run (idempotent).



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/generators/rails_ai_bridge/install/install_generator.rb', line 207

def create_assistant_overrides_template
  dir = 'config/rails_ai_bridge'
  empty_directory dir

  stub = File.join(destination_root, dir, 'overrides.md')
  unless File.exist?(stub)
    create_file "#{dir}/overrides.md", <<~MD
      <!-- rails-ai-bridge:omit-merge -->

    MD
    say "Created #{dir}/overrides.md (stub — remove omit-merge line when adding real rules)", :green
  end

  example = File.join(destination_root, dir, 'overrides.md.example')
  return if File.exist?(example)

  copy_file 'overrides.md.example', "#{dir}/overrides.md.example"
  say "Created #{dir}/overrides.md.example (reference outline, not merged)", :green
end

#create_initializervoid

This method returns an undefined value.

Creates config/initializers/rails_ai_bridge.rb containing a commented configuration guide for rails-ai-bridge. The generated initializer documents introspector presets (with interpolated counts), options for enabling/disabling introspectors, security exclusions (tables, models, paths), primary domain model hints, context/output controls, assistant override guidance, and a SECURITY CRITICAL HTTP MCP / auto_mount section with recommended authentication approaches.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/generators/rails_ai_bridge/install/install_generator.rb', line 48

def create_initializer
  standard_count = RailsAiBridge::Configuration::PRESETS[:standard].size
  full_count     = RailsAiBridge::Configuration::PRESETS[:full].size
  regulated_count = RailsAiBridge::Configuration::PRESETS[:regulated].size

  create_file 'config/initializers/rails_ai_bridge.rb', <<~RUBY
    # frozen_string_literal: true

    # rails-ai-bridge configuration
    # All settings are commented out — uncomment only what you need to change.
    # Defaults are production-safe: read-only introspection, no HTTP exposure.
    # Run `rails ai:doctor` after changes to verify your setup.

    RailsAiBridge.configure do |config|
      # ---------------------------------------------------------------------------
      # Introspector preset
      # ---------------------------------------------------------------------------
      # Controls how much of your app is introspected when generating context files
      # and answering MCP tool requests.
      #
      # :standard (default) — #{standard_count} core introspectors covering the essentials:
      #   schema, models, routes, controllers, jobs, gems, conventions, tests, migrations
      #   Best for most apps. Fast and focused.
      #
      # :full — all #{full_count} introspectors (everything in :standard plus):
      #   views, Turbo/Stimulus, auth, API serializers, config, assets, DevOps
      #   Use for full-stack Hotwire apps or when AI needs frontend/auth/API context.
      #
      # :regulated — #{regulated_count} introspectors — omits schema, models, and migrations.
      #   Use for apps with strict data governance where schema must not be exposed.
      #
      # config.preset = :standard   # already the default — uncomment only to switch

      # Add individual introspectors on top of the preset (does not change the preset):
      # Effect: each listed symbol enables one additional introspector.
      # config.introspectors += %i[non_ar_models views turbo auth api database_stats]
      #
      # database_stats: adds small/medium/large/hot hints to table context using
      # PostgreSQL table statistics. Opt-in because it queries the DB at introspection time.

      # Disable a whole category at runtime (overrides preset and individual additions):
      # :domain_metadata disables schema + models + migrations + non_ar_models
      # config.disabled_introspection_categories << :domain_metadata

      # ---------------------------------------------------------------------------
      # Security exclusions
      # ---------------------------------------------------------------------------
      # These settings control what gets included in generated context files and
      # MCP tool responses. Excluded items are silently omitted — not replaced.

      # Tables to hide from schema introspection and model output.
      # Accepts exact table names or globs ("pii_*" matches pii_users, pii_logs, etc.)
      # Effect: excluded tables disappear from rails_get_schema and model details.
      # config.excluded_tables += %w[secrets audit_logs pii_*]

      # ActiveRecord models to exclude from introspection.
      # Effect: excluded models are not listed in any generated context file or MCP response.
      # config.excluded_models += %w[AdminUser InternalAuditLog]

      # Paths excluded from rails_search_code results.
      # Effect: files under these paths are skipped in code search results.
      # config.excluded_paths += %w[vendor/bundle node_modules]

      # ---------------------------------------------------------------------------
      # Domain model hints
      # ---------------------------------------------------------------------------
      # Mark your primary business models as core_entity. This affects:
      #   - Ordering in generated context files (core models listed first)
      #   - Semantic tier in rails_get_model_details responses ("core_entity")
      #   - .claude/rules/rails-models.md (tagged for Claude Code)
      # Effect: these models get promoted in AI context. Use your 3-7 most central models.
      # config.core_models += %w[User Order Project]

      # ---------------------------------------------------------------------------
      # Context output
      # ---------------------------------------------------------------------------
      # Controls how much detail goes into generated static files (CLAUDE.md, AGENTS.md, etc.)
      #
      # :compact (default) — ≤150 lines per file. Key models and routes are listed;
      #   everything else is referenced via MCP tools. Suitable for large apps.
      #   The AI asks MCP for details on demand — no context bloat.
      #
      # :full — dumps everything into the static files. No MCP needed for orientation,
      #   but files can be large. Best for small apps with fewer than ~30 models.
      #
      # config.context_mode = :compact   # already the default

      # Max lines for CLAUDE.md in compact mode (default: 150):
      # config.claude_max_lines = 150

      # Safety cap for MCP tool responses in characters (default: 120_000):
      # Oversized responses are truncated with a hint to use filters or pagination.
      # config.max_tool_response_chars = 120_000

      # Team-specific rules merged into Copilot and Codex output.
      # Effect: content of overrides.md is appended to .github/copilot-instructions.md
      # and AGENTS.md on each `rails ai:bridge` run.
      # IMPORTANT: Remove the first-line "<!-- rails-ai-bridge:omit-merge -->" guard
      # from config/rails_ai_bridge/overrides.md before this has any effect.
      # config.assistant_overrides_path = "config/rails_ai_bridge/overrides.md"

      # Model list size caps for compact output (0 = show no names, only MCP pointer):
      # Reduce these for apps with large model counts to keep files within size limits.
      # config.copilot_compact_model_list_limit = 15   # default
      # config.codex_compact_model_list_limit   = 15   # default

      # ==========================================================================
      # HTTP MCP / auto_mount — SECURITY CRITICAL
      # ==========================================================================
      # By default, MCP runs only via stdio (`rails ai:serve`), which is local-only
      # and safe. The HTTP transport is an opt-in alternative for clients that cannot
      # spawn sub-processes (e.g. browser-based AI tools, remote agents).
      #
      # Even though tools are read-only, the HTTP endpoint exposes routes, schema,
      # and code structure. Treat it as an internal service — keep it on localhost
      # unless you add authentication AND network controls.
      #
      # To enable HTTP MCP locally (development only):
      #   config.auto_mount = true
      #   config.http_path  = "/mcp"        # endpoint path
      #   config.http_bind  = "127.0.0.1"   # localhost only
      # Then start your Rails server and point your AI client to http://localhost:3000/mcp
      #
      # For production, you MUST also set allow_auto_mount_in_production = true AND
      # configure one of these auth mechanisms (highest priority first):
      #
      #   1. JWT decoder (bring your own JWT gem):
      #      config.mcp_jwt_decoder = ->(token) {
      #        JWT.decode(token, credentials.jwt_secret, true, algorithm: "HS256").first
      #      rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature
      #        nil
      #      }
      #
      #   2. Token resolver (Devise, database lookup, etc.):
      #      config.mcp_token_resolver = ->(token) { User.find_by(mcp_api_token: token) }
      #
      #   3. Static shared secret (simplest — fine for internal tools):
      #      config.http_mcp_token = "generate-a-long-random-secret"
      #      # ENV["RAILS_AI_BRIDGE_MCP_TOKEN"] takes precedence when set
      #
      # Timing-safe token comparison is built in, but add rate limiting too
      # (e.g. Rack::Attack throttle on config.http_path) to prevent brute-force.
      #
      # config.auto_mount = false
      # config.allow_auto_mount_in_production = false
      # config.http_path = "/mcp"
      # config.http_port = 6029
    end
  RUBY

  say 'Created config/initializers/rails_ai_bridge.rb', :green
end

#create_mcp_configvoid

This method returns an undefined value.

Creates a .mcp.json MCP server definition named "rails-ai-bridge". The created file configures an MCP server that runs bundle exec rails ai:serve and is intended for auto-discovery by tools such as Claude Code and Cursor.



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/generators/rails_ai_bridge/install/install_generator.rb', line 26

def create_mcp_config
  mcp_config = {
    mcpServers: {
      'rails-ai-bridge' => {
        command: 'bundle',
        args: ['exec', 'rails', 'ai:serve']
      }
    }
  }
  create_file '.mcp.json', "#{JSON.pretty_generate(mcp_config)}\n"

  say 'Created .mcp.json (auto-discovered by Claude Code, Cursor, etc.)', :green
end

#generate_context_filesvoid

This method returns an undefined value.

Calls RailsAiBridge.generate_context to write initial bridge files (CLAUDE.md, .cursorrules, etc.) according to the selected install profile. Skipped when +Rails.application+ is not available. Any StandardError is rescued and reported with the error class only — no raw message or sensitive path/credential details are printed or logged. +Rails.logger.debug+ receives only the exception class name and a short 12-character fingerprint derived from it, never the raw exception message.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/generators/rails_ai_bridge/install/install_generator.rb', line 257

def generate_context_files
  say ''
  say 'Generating AI bridge files...', :yellow

  return handle_skip_context if options[:skip_context]
  return handle_no_rails_app unless Rails.application

  begin
    profile = resolve_profile
  rescue ArgumentError
    say '  Run `rails generate rails_ai_bridge:install --profile=minimal` (or full/custom/mcp).', :yellow
    return
  end
  @selected_profile = profile

  case profile
  when 'mcp'
    say '  Skipped (MCP-only profile). Run `rails ai:bridge` to generate context files later.', :yellow
    return
  when 'minimal', 'full'
    formats = ProfileResolver.formats_for(profile)
    split_rules = ProfileResolver.split_rules_for(profile)
    return generate_context_for_formats(formats, split_rules: split_rules)
  end

  return say('  Skipped. Run `rails ai:bridge` to generate context files later.', :yellow) unless yes?('Generate AI assistant context files? (y/n)')

  formats = collect_selected_formats
  return say('  No formats selected. Run `rails ai:bridge` to generate context files later.', :yellow) if formats.empty?

  generate_context_for_formats(formats, split_rules: true)
end

#show_instructionsvoid

This method returns an undefined value.

Prints post-install usage instructions to stdout: available rake tasks, generated file locations per AI assistant, MCP auto-discovery notes, and bridge mode options.



295
296
297
298
299
# File 'lib/generators/rails_ai_bridge/install/install_generator.rb', line 295

def show_instructions
  return show_skip_context_instructions if options[:skip_context]

  show_full_instructions
end