rails-mcp-insight 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 (37) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +25 -0
  3. data/LICENSE +21 -0
  4. data/README.md +308 -0
  5. data/bin/rails-mcp-insight +100 -0
  6. data/lib/rails_mcp_insight/analyzers/ast_parser.rb +200 -0
  7. data/lib/rails_mcp_insight/analyzers/code_searcher.rb +155 -0
  8. data/lib/rails_mcp_insight/analyzers/controller_analyzer.rb +124 -0
  9. data/lib/rails_mcp_insight/analyzers/gem_analyzer.rb +113 -0
  10. data/lib/rails_mcp_insight/analyzers/job_analyzer.rb +126 -0
  11. data/lib/rails_mcp_insight/analyzers/migration_analyzer.rb +98 -0
  12. data/lib/rails_mcp_insight/analyzers/model_analyzer.rb +239 -0
  13. data/lib/rails_mcp_insight/analyzers/route_analyzer.rb +171 -0
  14. data/lib/rails_mcp_insight/analyzers/security_analyzer.rb +127 -0
  15. data/lib/rails_mcp_insight/analyzers/test_analyzer.rb +91 -0
  16. data/lib/rails_mcp_insight/configuration.rb +74 -0
  17. data/lib/rails_mcp_insight/formatters/mermaid_formatter.rb +68 -0
  18. data/lib/rails_mcp_insight/project_detector.rb +84 -0
  19. data/lib/rails_mcp_insight/server.rb +51 -0
  20. data/lib/rails_mcp_insight/tools/analyze_controller.rb +34 -0
  21. data/lib/rails_mcp_insight/tools/analyze_job.rb +33 -0
  22. data/lib/rails_mcp_insight/tools/analyze_migration_history.rb +30 -0
  23. data/lib/rails_mcp_insight/tools/analyze_model.rb +34 -0
  24. data/lib/rails_mcp_insight/tools/audit_gems.rb +24 -0
  25. data/lib/rails_mcp_insight/tools/check_security.rb +34 -0
  26. data/lib/rails_mcp_insight/tools/explain_association_chain.rb +101 -0
  27. data/lib/rails_mcp_insight/tools/find_definition.rb +34 -0
  28. data/lib/rails_mcp_insight/tools/find_tests.rb +32 -0
  29. data/lib/rails_mcp_insight/tools/generate_erd.rb +38 -0
  30. data/lib/rails_mcp_insight/tools/list_models.rb +25 -0
  31. data/lib/rails_mcp_insight/tools/search_code.rb +31 -0
  32. data/lib/rails_mcp_insight/tools/search_routes.rb +34 -0
  33. data/lib/rails_mcp_insight/tools/stats_overview.rb +76 -0
  34. data/lib/rails_mcp_insight/tools/trace_request.rb +162 -0
  35. data/lib/rails_mcp_insight/version.rb +5 -0
  36. data/lib/rails_mcp_insight.rb +55 -0
  37. metadata +155 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5bef39c81f49c39312a2a53416779eaf9051ccd35aae59154fd48eda61fa9ac6
4
+ data.tar.gz: 3c47c8d03e72b32e035b3ec5c834ef3003baac65241988ad8dca4fde1a688798
5
+ SHA512:
6
+ metadata.gz: fc2fab417e9a718ab06a0b755b8e39ddf310944e11571c4ad3461877689a54f2fc99fb87d138499005180ee0ff2810aefcbbd2e867069d0e6fefe99620145b8e
7
+ data.tar.gz: 735a7950a20ab12dff1daf86e3c00f5c76b3eda133fd129c883bf66ce61697f888addf64952c1dcb3d5604396f51887da6b1a3127a6db135afb1aca7d9bcacdf
data/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2026-07-31
6
+
7
+ ### Added
8
+ - Initial release with 15 MCP tools for Rails introspection
9
+ - `stats_overview` — Project health dashboard
10
+ - `search_routes` — Smart route search with filtering
11
+ - `analyze_model` — Deep model analysis (columns, associations, validations, callbacks)
12
+ - `analyze_controller` — Controller analysis (actions, filters, strong params)
13
+ - `search_code` — Regex code search with context
14
+ - `find_definition` — Symbol definition lookup
15
+ - `list_models` — List all models with metadata
16
+ - `trace_request` — Full HTTP request lifecycle tracing
17
+ - `analyze_migration_history` — Schema evolution timeline
18
+ - `audit_gems` — Dependency analysis
19
+ - `check_security` — Static security scanning
20
+ - `find_tests` — Test file mapping
21
+ - `analyze_job` — ActiveJob analysis
22
+ - `explain_association_chain` — Association pathfinding between models
23
+ - `generate_erd` — Mermaid ERD generation
24
+ - CLI with setup instructions for Claude Desktop, Cursor, and VS Code
25
+ - Built on official MCP Ruby SDK
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aditya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,308 @@
1
+ <p align="center">
2
+ <h1 align="center">🔍 rails-mcp-insight</h1>
3
+ <p align="center">
4
+ <strong>Deep Rails introspection MCP server for AI assistants</strong>
5
+ </p>
6
+ <p align="center">
7
+ <a href="https://rubygems.org/gems/rails-mcp-insight"><img src="https://img.shields.io/gem/v/rails-mcp-insight?color=%23e9573f" alt="Gem Version"></a>
8
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
9
+ <a href="https://rubygems.org/gems/rails-mcp-insight"><img src="https://img.shields.io/gem/dt/rails-mcp-insight?color=green" alt="Downloads"></a>
10
+ </p>
11
+ </p>
12
+
13
+ ---
14
+
15
+ Give your AI assistant (**Claude Desktop**, **Cursor**, **VS Code / Roo Code / Cline**) **deep, intelligent understanding** of your Ruby on Rails application through the open [Model Context Protocol (MCP)](https://modelcontextprotocol.io/).
16
+
17
+ Unlike basic tools that dump raw files or schema definitions, `rails-mcp-insight` provides **cross-referenced, contextual intelligence** — tracing the full request lifecycle from route → controller → model → callbacks → background jobs → mailers.
18
+
19
+ ---
20
+
21
+ ## ✨ Available Tools (15)
22
+
23
+ | Tool | Description | Example Prompt |
24
+ |------|-------------|----------------|
25
+ | `stats_overview` | Project dashboard: versions, counts, LOC, test coverage | *"Give me an overview of this Rails project."* |
26
+ | `search_routes` | Smart route search with HTTP method & path filtering | *"Find all POST routes in config/routes.rb"* |
27
+ | `analyze_model` | Deep dive: columns, associations, validations, callbacks, scopes | *"Analyze the Order model."* |
28
+ | `analyze_controller` | Actions, filters, strong params, rescue handlers | *"Show details for OrdersController."* |
29
+ | `search_code` | Regex code search with surrounding context lines | *"Search for stripe_customer_id across the codebase."* |
30
+ | `find_definition` | Locate where any class, module, method, or constant is defined | *"Where is OrderService defined?"* |
31
+ | `list_models` | List all models with table names & metadata at a glance | *"List all models in this application."* |
32
+ | **`trace_request`** | **🔥 Full request lifecycle tracing (killer feature!)** | *"Trace what happens when POST /orders is called."* |
33
+ | `analyze_migration_history` | Chronological schema evolution & migration timeline | *"Show migration history for the users table."* |
34
+ | `audit_gems` | Dependency health check from Gemfile / Gemfile.lock | *"Audit our installed gems."* |
35
+ | `check_security` | Static analysis for SQL injection, mass assignment, XSS, etc. | *"Scan this project for security vulnerabilities."* |
36
+ | `find_tests` | Map source files → test/spec files + factories | *"Find test files for app/models/user.rb"* |
37
+ | `analyze_job` | ActiveJob queue, retry/discard config, callbacks, triggers | *"Analyze OrderConfirmationJob."* |
38
+ | `explain_association_chain` | Find all association paths between two models | *"How does User relate to Product?"* |
39
+ | `generate_erd` | Mermaid Entity Relationship Diagrams | *"Generate an ERD for User, Order, Product."* |
40
+
41
+ ---
42
+
43
+ ## 🎯 The Killer Feature: `trace_request`
44
+
45
+ Ask your AI: *"What happens when a user submits `POST /orders`?"*
46
+
47
+ `rails-mcp-insight` traces the full request path statically:
48
+
49
+ ```json
50
+ {
51
+ "route": { "method": "POST", "path": "/orders", "controller_action": "orders#create" },
52
+ "controller": {
53
+ "name": "OrdersController",
54
+ "action": "create",
55
+ "before_actions": ["authenticate_user!", "set_cart"],
56
+ "strong_params": [{ "resource": "order", "permitted_attributes": ["product_id", "quantity", "notes"] }]
57
+ },
58
+ "model_operations": {
59
+ "Order": {
60
+ "callbacks": [
61
+ { "type": "before_create", "target": "calculate_total" },
62
+ { "type": "after_create", "target": "notify_warehouse" },
63
+ { "type": "after_commit", "target": "update_inventory" }
64
+ ],
65
+ "validations": ["validates :quantity, presence: true"]
66
+ }
67
+ },
68
+ "side_effects": {
69
+ "jobs": ["OrderConfirmationJob"],
70
+ "mailers": ["OrderMailer"],
71
+ "broadcasts": false
72
+ }
73
+ }
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 📦 Installation
79
+
80
+ ### Option 1: Global Gem Installation (Recommended)
81
+
82
+ ```bash
83
+ gem install rails-mcp-insight
84
+ ```
85
+
86
+ Verify installation:
87
+ ```bash
88
+ rails-mcp-insight --version
89
+ ```
90
+
91
+ ### Option 2: Add to Gemfile
92
+
93
+ Add to your Rails application's `Gemfile`:
94
+
95
+ ```ruby
96
+ group :development do
97
+ gem "rails-mcp-insight", require: false
98
+ end
99
+ ```
100
+
101
+ Then run:
102
+ ```bash
103
+ bundle install
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 📖 Step-by-Step Setup Guide
109
+
110
+ ### 1. Claude Desktop Setup
111
+
112
+ #### Step 1: Open the configuration file
113
+ - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
114
+ - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
115
+ - **Linux**: `~/.config/Claude/claude_desktop_config.json`
116
+
117
+ #### Step 2: Add the server entry
118
+ ```json
119
+ {
120
+ "mcpServers": {
121
+ "rails-insight": {
122
+ "command": "rails-mcp-insight",
123
+ "args": ["--project", "/absolute/path/to/your/rails/app"]
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ #### Step 3: Restart Claude Desktop
130
+ Restart Claude Desktop. You will see a hammer icon 🔨 indicating that `rails-mcp-insight` tools are active.
131
+
132
+ ---
133
+
134
+ ### 2. Cursor IDE Setup
135
+
136
+ #### Option A: Project-specific config (Recommended)
137
+ Create a file at `.cursor/mcp.json` in your Rails root directory:
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "rails-insight": {
143
+ "command": "rails-mcp-insight",
144
+ "args": ["--project", "/absolute/path/to/your/rails/app"]
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ #### Option B: Cursor Global Settings
151
+ 1. Open Cursor Settings (`Ctrl+,` or `Cmd+,`).
152
+ 2. Navigate to **Features** → **MCP Servers**.
153
+ 3. Click **Add New MCP Server**.
154
+ 4. Name: `rails-insight`
155
+ 5. Type: `stdio`
156
+ 6. Command: `rails-mcp-insight --project /absolute/path/to/your/rails/app`
157
+
158
+ ---
159
+
160
+ ### 3. VS Code / Roo Code / Cline Setup
161
+
162
+ Add to your workspace or global `.vscode/mcp.json`:
163
+
164
+ ```json
165
+ {
166
+ "servers": {
167
+ "rails-insight": {
168
+ "type": "stdio",
169
+ "command": "rails-mcp-insight",
170
+ "args": ["--project", "/absolute/path/to/your/rails/app"]
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 💬 Step-by-Step Usage & Example Prompts
179
+
180
+ Once configured, simply chat naturally with your AI assistant. Here are concrete examples of how to use each tool:
181
+
182
+ ### 1. Full Lifecycle Tracing
183
+ > **Prompt:** *"Trace what happens when a user calls POST /orders"*
184
+ > **Tool called:** `trace_request`
185
+ > **Result:** Traces routes → filters → strong params → model validations/callbacks → background jobs & mailers.
186
+
187
+ ---
188
+
189
+ ### 2. Project Health Overview
190
+ > **Prompt:** *"Give me a health dashboard for this Rails app."*
191
+ > **Tool called:** `stats_overview`
192
+ > **Result:** Displays Ruby/Rails versions, line counts, model/controller totals, and test coverage ratios.
193
+
194
+ ---
195
+
196
+ ### 3. Deep Model Inspection
197
+ > **Prompt:** *"Analyze the Order model including columns, validations, and callbacks."*
198
+ > **Tool called:** `analyze_model`
199
+ > **Result:** Returns schema columns with types, associations (`belongs_to :user`), scopes, and callback chains.
200
+
201
+ ---
202
+
203
+ ### 4. Controller Details
204
+ > **Prompt:** *"Show me all before_actions and strong params for OrdersController."*
205
+ > **Tool called:** `analyze_controller`
206
+ > **Result:** Extracts filters, allowed params, rescue handlers, and action methods.
207
+
208
+ ---
209
+
210
+ ### 5. Relationship Pathfinding
211
+ > **Prompt:** *"How does User relate to Product?"*
212
+ > **Tool called:** `explain_association_chain`
213
+ > **Result:** Finds all association paths (e.g., `User → has_many :orders → Order → belongs_to :product → Product`).
214
+
215
+ ---
216
+
217
+ ### 6. Visual ERD Generation
218
+ > **Prompt:** *"Generate a Mermaid ERD diagram for User, Order, and Product."*
219
+ > **Tool called:** `generate_erd`
220
+ > **Result:** Produces a rendered Mermaid `erDiagram` showing tables, attributes, and relationships.
221
+
222
+ ---
223
+
224
+ ### 7. Security Auditing
225
+ > **Prompt:** *"Scan this project for potential SQL injection or mass assignment issues."*
226
+ > **Tool called:** `check_security`
227
+ > **Result:** Static scan checking 10 security patterns (raw SQL, `html_safe`, `params.permit`, `eval`, system calls).
228
+
229
+ ---
230
+
231
+ ### 8. Finding Definitions
232
+ > **Prompt:** *"Where is OrderConfirmationJob defined?"*
233
+ > **Tool called:** `find_definition`
234
+ > **Result:** Locates exact file and line number across the project.
235
+
236
+ ---
237
+
238
+ ## 🛠️ Local Development & Testing Guide
239
+
240
+ If you want to contribute or test `rails-mcp-insight` locally:
241
+
242
+ ### 1. Clone the repository
243
+ ```bash
244
+ git clone https://github.com/aditya/rails-mcp-insight.git
245
+ cd rails-mcp-insight
246
+ ```
247
+
248
+ ### 2. Install dependencies
249
+ ```bash
250
+ bundle install
251
+ ```
252
+
253
+ ### 3. Run the test suite (26 RSpec tests)
254
+ ```bash
255
+ bundle exec rspec
256
+ ```
257
+
258
+ ### 4. Run RuboCop (Code style check)
259
+ ```bash
260
+ bundle exec rubocop
261
+ ```
262
+
263
+ ### 5. Test against the included sample Rails fixture
264
+ ```bash
265
+ bundle exec bin/rails-mcp-insight --project spec/fixtures/sample_rails_app
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 🏗️ How It Works Under The Hood
271
+
272
+ `rails-mcp-insight` performs **pure static analysis**:
273
+ - ⚡ **Fast** — No Rails boot time
274
+ - 🔒 **Safe** — Read-only, never modifies your code or database
275
+ - 🔌 **Zero config** — Auto-detects Rails project structure
276
+ - 📦 **Lightweight** — Depends only on the official `mcp` gem
277
+
278
+ ```
279
+ AI Assistant ←→ MCP (JSON-RPC / stdio) ←→ rails-mcp-insight ←→ Rails App (Filesystem)
280
+ ```
281
+
282
+ ---
283
+
284
+ ## 🛡️ Security & Privacy
285
+
286
+ - **Read-Only**: Every tool is declared with `read_only_hint: true`.
287
+ - **Zero Execution**: Does not execute Ruby code inside your Rails application.
288
+ - **Local & Offline**: All processing happens locally on your computer. No data is sent to external servers by this gem.
289
+
290
+ ---
291
+
292
+ ## 📋 Requirements
293
+
294
+ - **Ruby**: `>= 3.0`
295
+ - **Rails App**: Rails 5.0+, 6.x, 7.x, 8.x
296
+ - **AI Host**: Any MCP-compatible host (Claude Desktop, Cursor, VS Code Roo Code / Cline)
297
+
298
+ ---
299
+
300
+ ## 📄 License
301
+
302
+ Distributed under the [MIT License](LICENSE).
303
+
304
+ ---
305
+
306
+ <p align="center">
307
+ <strong>⭐ Star this repo if it helps your Rails development!</strong>
308
+ </p>
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "bundler/setup"
6
+ rescue LoadError
7
+ # Running from installed gem
8
+ end
9
+
10
+ require_relative "../lib/rails_mcp_insight"
11
+
12
+ # Parse command line arguments
13
+ project_path = Dir.pwd
14
+ show_version = false
15
+
16
+ ARGV.each_with_index do |arg, idx|
17
+ case arg
18
+ when "--version", "-v"
19
+ show_version = true
20
+ when "--project", "-p"
21
+ project_path = ARGV[idx + 1] if ARGV[idx + 1]
22
+ when "--help", "-h"
23
+ $stderr.puts <<~HELP
24
+ rails-mcp-insight v#{RailsMcpInsight::VERSION}
25
+
26
+ Deep Rails introspection MCP server for AI assistants.
27
+ Provides 15 tools for analyzing routes, models, controllers,
28
+ security, testing, and full request lifecycle tracing.
29
+
30
+ Usage:
31
+ rails-mcp-insight Start MCP server (stdio transport)
32
+ rails-mcp-insight --project /path Specify Rails project directory
33
+ rails-mcp-insight --version Show version
34
+ rails-mcp-insight --help Show this help
35
+
36
+ Configuration for AI clients:
37
+
38
+ Claude Desktop (~/.claude/claude_desktop_config.json):
39
+ {
40
+ "mcpServers": {
41
+ "rails-insight": {
42
+ "command": "rails-mcp-insight",
43
+ "args": ["--project", "/path/to/your/rails/app"]
44
+ }
45
+ }
46
+ }
47
+
48
+ Cursor (.cursor/mcp.json):
49
+ {
50
+ "mcpServers": {
51
+ "rails-insight": {
52
+ "command": "rails-mcp-insight",
53
+ "args": ["--project", "/path/to/your/rails/app"]
54
+ }
55
+ }
56
+ }
57
+
58
+ VS Code (.vscode/mcp.json):
59
+ {
60
+ "servers": {
61
+ "rails-insight": {
62
+ "type": "stdio",
63
+ "command": "rails-mcp-insight",
64
+ "args": ["--project", "/path/to/your/rails/app"]
65
+ }
66
+ }
67
+ }
68
+
69
+ HELP
70
+ exit 0
71
+ end
72
+ end
73
+
74
+ if show_version
75
+ puts "rails-mcp-insight v#{RailsMcpInsight::VERSION}"
76
+ exit 0
77
+ end
78
+
79
+ # Log to stderr (stdout is reserved for MCP JSON-RPC)
80
+ $stderr.puts "rails-mcp-insight v#{RailsMcpInsight::VERSION}"
81
+ $stderr.puts "Project: #{project_path}"
82
+
83
+ # Validate the project
84
+ config = RailsMcpInsight::Configuration.new(project_path: project_path)
85
+ detector = RailsMcpInsight::ProjectDetector.new(config)
86
+
87
+ begin
88
+ detector.validate!
89
+ rescue RailsMcpInsight::Error => e
90
+ $stderr.puts "Error: #{e.message}"
91
+ exit 1
92
+ end
93
+
94
+ info = detector.detect
95
+ $stderr.puts "Detected: Rails #{info[:rails_version] || 'unknown'}, " \
96
+ "#{info[:model_count]} models, #{info[:controller_count]} controllers"
97
+ $stderr.puts "Starting MCP server on stdio..."
98
+
99
+ # Start the MCP server
100
+ RailsMcpInsight.start(project_path: project_path)
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module RailsMcpInsight
6
+ module Analyzers
7
+ # Lightweight Ruby AST parser using regex-based analysis.
8
+ # Extracts class definitions, method definitions, module inclusions,
9
+ # DSL calls (has_many, belongs_to, validates, etc.) from Ruby source files.
10
+ #
11
+ # This avoids requiring Prism as a hard dependency while still providing
12
+ # useful structural information. Can be upgraded to Prism later for
13
+ # more accurate parsing.
14
+ class AstParser
15
+ # Represents a parsed Ruby file's structural information
16
+ ParseResult = Struct.new(
17
+ :file_path,
18
+ :classes,
19
+ :modules,
20
+ :methods,
21
+ :includes,
22
+ :extends,
23
+ :dsl_calls,
24
+ :constants,
25
+ :comments,
26
+ keyword_init: true
27
+ )
28
+
29
+ ClassInfo = Struct.new(:name, :parent, :line, :body_lines, keyword_init: true)
30
+ MethodInfo = Struct.new(:name, :line, :visibility, :args, keyword_init: true)
31
+ DslCall = Struct.new(:method_name, :args, :line, :options, keyword_init: true)
32
+
33
+ def initialize
34
+ @cache = {}
35
+ end
36
+
37
+ # Parse a Ruby file and return structured information
38
+ def parse_file(file_path)
39
+ return nil unless File.exist?(file_path)
40
+
41
+ @cache[file_path] ||= begin
42
+ content = File.read(file_path)
43
+ parse_content(content, file_path)
44
+ end
45
+ end
46
+
47
+ # Clear the parse cache
48
+ def clear_cache!
49
+ @cache.clear
50
+ end
51
+
52
+ # Parse Ruby source content
53
+ def parse_content(content, file_path = "(string)")
54
+ lines = content.lines
55
+
56
+ ParseResult.new(
57
+ file_path: file_path,
58
+ classes: extract_classes(lines),
59
+ modules: extract_modules(lines),
60
+ methods: extract_methods(lines),
61
+ includes: extract_includes(lines),
62
+ extends: extract_extends(lines),
63
+ dsl_calls: extract_dsl_calls(lines),
64
+ constants: extract_constants(lines),
65
+ comments: extract_comments(lines)
66
+ )
67
+ end
68
+
69
+ private
70
+
71
+ def extract_classes(lines)
72
+ classes = []
73
+ lines.each_with_index do |line, idx|
74
+ next unless (match = line.match(/^\s*class\s+([A-Z][\w:]*)\s*(?:<\s*([A-Z][\w:]*))?/))
75
+
76
+ classes << ClassInfo.new(
77
+ name: match[1],
78
+ parent: match[2],
79
+ line: idx + 1,
80
+ body_lines: []
81
+ )
82
+ end
83
+ classes
84
+ end
85
+
86
+ def extract_modules(lines)
87
+ modules = []
88
+ lines.each_with_index do |line, idx|
89
+ if (match = line.match(/^\s*module\s+([A-Z][\w:]*)/))
90
+ modules << { name: match[1], line: idx + 1 }
91
+ end
92
+ end
93
+ modules
94
+ end
95
+
96
+ def extract_methods(lines)
97
+ methods = []
98
+ current_visibility = :public
99
+
100
+ lines.each_with_index do |line, idx|
101
+ stripped = line.strip
102
+
103
+ # Track visibility changes
104
+ case stripped
105
+ when "private" then current_visibility = :private
106
+ when "protected" then current_visibility = :protected
107
+ when "public" then current_visibility = :public
108
+ end
109
+
110
+ # Match method definitions
111
+ next unless (match = stripped.match(/^def\s+(self\.)?(\w+[?!=]?)(?:\((.*?)\))?/))
112
+
113
+ is_class_method = !match[1].nil?
114
+ method_name = match[2]
115
+ args = match[3]&.strip
116
+
117
+ methods << MethodInfo.new(
118
+ name: is_class_method ? "self.#{method_name}" : method_name,
119
+ line: idx + 1,
120
+ visibility: is_class_method ? :public : current_visibility,
121
+ args: args
122
+ )
123
+ end
124
+ methods
125
+ end
126
+
127
+ def extract_includes(lines)
128
+ results = []
129
+ lines.each_with_index do |line, idx|
130
+ if (match = line.match(/^\s*include\s+(.+)/))
131
+ results << { module: match[1].strip, line: idx + 1 }
132
+ end
133
+ end
134
+ results
135
+ end
136
+
137
+ def extract_extends(lines)
138
+ results = []
139
+ lines.each_with_index do |line, idx|
140
+ if (match = line.match(/^\s*extend\s+(.+)/))
141
+ results << { module: match[1].strip, line: idx + 1 }
142
+ end
143
+ end
144
+ results
145
+ end
146
+
147
+ # Extract Rails DSL calls: associations, validations, callbacks, scopes, etc.
148
+ def extract_dsl_calls(lines)
149
+ dsl_methods = [
150
+ "has_many", "has_one", "belongs_to", "has_and_belongs_to_many", "validates", "validates_presence_of", "validates_uniqueness_of", "validates_format_of", "validate", "validates_with", "validates_each", "validates_length_of", "validates_numericality_of", "before_action", "after_action", "around_action", "skip_before_action", "before_create", "after_create", "before_save", "after_save", "before_update", "after_update", "before_destroy", "after_destroy", "before_validation", "after_validation", "after_commit", "after_create_commit", "after_update_commit", "after_destroy_commit", "scope", "enum", "delegate", "has_secure_password", "has_one_attached", "has_many_attached", "rescue_from", "before_enqueue", "before_perform", "after_perform", "around_perform", "queue_as", "retry_on", "discard_on"
151
+ ]
152
+
153
+ pattern = /^\s*(#{dsl_methods.join('|')})\s+(.+)/
154
+
155
+ results = []
156
+ lines.each_with_index do |line, idx|
157
+ next unless (match = line.match(pattern))
158
+
159
+ method_name = match[1]
160
+ raw_args = match[2].strip.chomp(",")
161
+
162
+ results << DslCall.new(
163
+ method_name: method_name,
164
+ args: raw_args,
165
+ line: idx + 1,
166
+ options: extract_options_from_args(raw_args)
167
+ )
168
+ end
169
+ results
170
+ end
171
+
172
+ def extract_constants(lines)
173
+ results = []
174
+ lines.each_with_index do |line, idx|
175
+ if (match = line.match(/^\s*([A-Z][A-Z_0-9]*)\s*=\s*(.+)/))
176
+ results << { name: match[1], value: match[2].strip, line: idx + 1 }
177
+ end
178
+ end
179
+ results
180
+ end
181
+
182
+ def extract_comments(lines)
183
+ results = []
184
+ lines.each_with_index do |line, idx|
185
+ results << { text: line.strip.sub(/^#\s*/, ""), line: idx + 1 } if line.strip.start_with?("#")
186
+ end
187
+ results
188
+ end
189
+
190
+ def extract_options_from_args(raw_args)
191
+ # Simple extraction of hash-style options from DSL args
192
+ options = {}
193
+ raw_args.scan(/(\w+):\s*(?::(\w+)|"([^"]*)"|(true|false|nil|\d+))/) do |key, sym, str, lit|
194
+ options[key] = sym || str || lit
195
+ end
196
+ options
197
+ end
198
+ end
199
+ end
200
+ end