rails-ai-context 0.9.0 → 0.10.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/CHANGELOG.md +11 -0
- data/CLAUDE.md +4 -4
- data/CONTRIBUTING.md +2 -2
- data/README.md +99 -114
- data/demo_script.sh +1 -1
- data/docs/GUIDE.md +6 -6
- data/docs/mcp-benchmark.mp4 +0 -0
- data/lib/rails_ai_context/configuration.rb +2 -2
- data/lib/rails_ai_context/introspector.rb +1 -0
- data/lib/rails_ai_context/introspectors/test_introspector.rb +69 -0
- data/lib/rails_ai_context/introspectors/view_template_introspector.rb +81 -0
- data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +20 -4
- data/lib/rails_ai_context/serializers/claude_serializer.rb +11 -40
- data/lib/rails_ai_context/serializers/copilot_instructions_serializer.rb +7 -8
- data/lib/rails_ai_context/serializers/cursor_rules_serializer.rb +11 -13
- data/lib/rails_ai_context/serializers/opencode_rules_serializer.rb +7 -3
- data/lib/rails_ai_context/serializers/opencode_serializer.rb +11 -40
- data/lib/rails_ai_context/serializers/windsurf_rules_serializer.rb +7 -6
- data/lib/rails_ai_context/server.rb +3 -1
- data/lib/rails_ai_context/tools/get_controllers.rb +76 -1
- data/lib/rails_ai_context/tools/get_stimulus.rb +85 -0
- data/lib/rails_ai_context/tools/get_test_info.rb +129 -15
- data/lib/rails_ai_context/tools/get_view.rb +142 -0
- data/lib/rails_ai_context/version.rb +1 -1
- data/server.json +3 -3
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c9dabd282aadc577e5a66416e23e27c72dc728328490e4ce1af213358c215f1
|
|
4
|
+
data.tar.gz: 291cfd489f7b0b639655e3d0cdb473b0859b68bfdc2ed7ca1199826ac234af0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 95fd8e8c760b88e74d321102d9a55fda265c8cc7fb1d7de1b15ac8a7bc09666d08ada3540c194a7e9c1f2b134c6913feee1583c788113ec6d368485a41259cbf
|
|
7
|
+
data.tar.gz: d6a09886b74f4ece17e4529cff42fe99cdd1c68c2a6e04413d979754d362f461cdd074c40fea576c98b4187c83306787eeb88f25ea4952ffa99d1dee9bec3a80
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ 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
|
+
## [0.10.0] - 2026-03-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`rails_get_view` MCP tool** — get view template contents, partials, Stimulus references. Filter by controller or specific path. Supports summary/standard/full detail levels. Eliminates reading 490+ lines of view files per task. ([#7](https://github.com/crisnahine/rails-ai-context/issues/7))
|
|
13
|
+
- **`rails_get_stimulus` MCP tool** — get Stimulus controller details (targets, values, actions, outlets, classes). Filter by controller name. Wraps existing StimulusIntrospector. ([#8](https://github.com/crisnahine/rails-ai-context/issues/8))
|
|
14
|
+
- **`rails_get_controllers` `action` parameter** — returns actual action source code + applicable filters instead of the entire controller file. Saves ~1,400 tokens per call. ([#9](https://github.com/crisnahine/rails-ai-context/issues/9))
|
|
15
|
+
- **`rails_get_test_info` enhanced** — now supports `detail` levels (summary/standard/full), `model` and `controller` params to find existing tests, fixture/factory names, test helper setup. ([#10](https://github.com/crisnahine/rails-ai-context/issues/10))
|
|
16
|
+
- **ViewTemplateIntrospector** — new introspector that reads view file contents and extracts partial references and Stimulus data attributes.
|
|
17
|
+
- **Stimulus in standard preset** — `:stimulus` introspector now included in the `:standard` preset (was `:full` only).
|
|
18
|
+
|
|
8
19
|
## [0.9.0] - 2026-03-19
|
|
9
20
|
|
|
10
21
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -8,8 +8,8 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
8
8
|
- `lib/rails_ai_context.rb` — Main entry point, public API (Zeitwerk autoloaded)
|
|
9
9
|
- `lib/rails_ai_context/configuration.rb` — User-facing config with presets (:standard, :full)
|
|
10
10
|
- `lib/rails_ai_context/introspector.rb` — Orchestrates sub-introspectors
|
|
11
|
-
- `lib/rails_ai_context/introspectors/` —
|
|
12
|
-
- `lib/rails_ai_context/tools/` —
|
|
11
|
+
- `lib/rails_ai_context/introspectors/` — 28 introspectors (schema, models, routes, jobs, gems, conventions, stimulus, database_stats, controllers, views, view_templates, turbo, i18n, config, active_storage, action_text, auth, api, tests, rake_tasks, assets, devops, action_mailbox, migrations, seeds, middleware, engines, multi_database)
|
|
12
|
+
- `lib/rails_ai_context/tools/` — 11 MCP tools using the official mcp SDK
|
|
13
13
|
- `lib/rails_ai_context/serializers/` — Output formatters (claude, claude_rules, opencode, cursor_rules, windsurf, windsurf_rules, copilot, copilot_instructions, rules, markdown, JSON)
|
|
14
14
|
- `lib/rails_ai_context/resources.rb` — MCP resources (static data AI clients read directly)
|
|
15
15
|
- `lib/rails_ai_context/server.rb` — MCP server configuration (stdio + HTTP transports)
|
|
@@ -32,7 +32,7 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
32
32
|
6. **Diff-aware** — context regeneration skips unchanged files
|
|
33
33
|
7. **Per-assistant serializers** — each AI tool gets tailored output format
|
|
34
34
|
8. **Zeitwerk autoloading** — files loaded on-demand, not all upfront
|
|
35
|
-
9. **Introspector presets** — `:standard` (
|
|
35
|
+
9. **Introspector presets** — `:standard` (10 core) default, `:full` (28) for power users
|
|
36
36
|
10. **MCP auto-discovery** — `.mcp.json` generated by install generator
|
|
37
37
|
11. **Compact by default** — context files ≤150 lines, MCP tools use `detail` parameter (summary/standard/full)
|
|
38
38
|
12. **Per-tool split rules** — `.claude/rules/`, `.cursor/rules/`, `.windsurf/rules/`, `.github/instructions/`
|
|
@@ -42,7 +42,7 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
42
42
|
## Testing
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
bundle exec rspec # Run specs (
|
|
45
|
+
bundle exec rspec # Run specs (412 examples)
|
|
46
46
|
bundle exec rubocop # Lint
|
|
47
47
|
```
|
|
48
48
|
|
data/CONTRIBUTING.md
CHANGED
|
@@ -18,8 +18,8 @@ The test suite uses [Combustion](https://github.com/pat/combustion) to boot a mi
|
|
|
18
18
|
|
|
19
19
|
```
|
|
20
20
|
lib/rails_ai_context/
|
|
21
|
-
├── introspectors/ #
|
|
22
|
-
├── tools/ #
|
|
21
|
+
├── introspectors/ # 28 introspectors (schema, models, routes, etc.)
|
|
22
|
+
├── tools/ # 11 MCP tools with detail levels and pagination
|
|
23
23
|
├── serializers/ # Per-assistant formatters (claude, opencode, cursor, windsurf, copilot, JSON)
|
|
24
24
|
├── server.rb # MCP server setup (stdio + HTTP)
|
|
25
25
|
├── live_reload.rb # MCP live reload (file watcher + cache invalidation)
|
data/README.md
CHANGED
|
@@ -7,15 +7,11 @@
|
|
|
7
7
|
[](https://github.com/crisnahine/rails-ai-context/actions)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
10
|
-

|
|
11
|
-
|
|
12
10
|
*Built by a Rails dev who got tired of burning tokens explaining his app to AI assistants every single session.*
|
|
13
11
|
|
|
14
|
-

|
|
15
|
-
|
|
16
12
|
---
|
|
17
13
|
|
|
18
|
-
##
|
|
14
|
+
## The Problem
|
|
19
15
|
|
|
20
16
|
You open Claude Code, Cursor, or Copilot and ask: *"Add a draft status to posts with a scheduled publish date."*
|
|
21
17
|
|
|
@@ -23,9 +19,24 @@ The AI doesn't know your schema, your Devise setup, your Sidekiq jobs, or that `
|
|
|
23
19
|
|
|
24
20
|
**rails-ai-context fixes this.** It auto-introspects your entire Rails app and feeds everything to your AI assistant — schema, models, routes, controllers, jobs, gems, auth, API, tests, config, and conventions — through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io).
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
---
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
## Proof: 35% Token Savings (Real Benchmark)
|
|
25
|
+
|
|
26
|
+
We ran the same feature task — *"Add status and date range filters to the Cooks index page"* — across 4 scenarios in parallel on a real Rails app:
|
|
27
|
+
|
|
28
|
+
| Scenario | MCP Tools | CLAUDE.md | Tokens Used | Savings |
|
|
29
|
+
|----------|-----------|-----------|-------------|---------|
|
|
30
|
+
| **MCP + CLAUDE.md** | Yes | Yes | **25,884** | **35% saved** |
|
|
31
|
+
| MCP only | Yes | No | 27,822 | 31% saved |
|
|
32
|
+
| CLAUDE.md only | No | Yes | 32,699 | 19% saved |
|
|
33
|
+
| Zero (nothing) | No | No | 40,129 | baseline |
|
|
34
|
+
|
|
35
|
+
All 4 produced the same working feature. The only difference was how many tokens were burned getting there.
|
|
36
|
+
|
|
37
|
+
https://github.com/user-attachments/assets/171f52ae-bd30-43f6-a44f-bcfdda7fc139
|
|
38
|
+
|
|
39
|
+
MCP tools give the AI structured, filtered access to your codebase instead of reading entire files. On this small 5-model app, that saved 35%. **On larger projects, the savings compound significantly.**
|
|
29
40
|
|
|
30
41
|
---
|
|
31
42
|
|
|
@@ -41,11 +52,64 @@ That's it. Three commands. Your AI assistant now understands your entire Rails a
|
|
|
41
52
|
|
|
42
53
|
The install generator creates `.mcp.json` for auto-discovery — Claude Code and Cursor detect it automatically. No manual MCP config needed.
|
|
43
54
|
|
|
55
|
+
> **[Full Guide](docs/GUIDE.md)** — complete documentation with every command, parameter, and configuration option.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## How It Saves Tokens
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+
- Compact context files load ≤150 lines instead of thousands
|
|
64
|
+
- MCP tools return `detail:"summary"` first (~55 tokens for schema overview), then drill into specifics
|
|
65
|
+
- Specific lookups (`table:`, `model:`, `controller:`) return only what's needed
|
|
66
|
+
- Pagination prevents dumping hundreds of tables/routes at once
|
|
67
|
+
- Split rule files only activate in relevant directories
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 11 Live MCP Tools
|
|
72
|
+
|
|
73
|
+
The gem exposes **11 read-only tools** via MCP that AI clients call on-demand:
|
|
74
|
+
|
|
75
|
+
| Tool | What it returns |
|
|
76
|
+
|------|----------------|
|
|
77
|
+
| `rails_get_schema` | Tables, columns, indexes, foreign keys |
|
|
78
|
+
| `rails_get_model_details` | Associations, validations, scopes, enums, callbacks |
|
|
79
|
+
| `rails_get_routes` | HTTP verbs, paths, controller actions |
|
|
80
|
+
| `rails_get_controllers` | Actions, filters, strong params, concerns |
|
|
81
|
+
| `rails_get_config` | Cache, session, timezone, middleware, initializers |
|
|
82
|
+
| `rails_get_test_info` | Test framework, factories, CI config, coverage |
|
|
83
|
+
| `rails_get_gems` | Notable gems categorized by function |
|
|
84
|
+
| `rails_get_conventions` | Architecture patterns, directory structure |
|
|
85
|
+
| `rails_search_code` | Ripgrep-powered regex search across the codebase |
|
|
86
|
+
| `rails_get_view` | View templates, partials, Stimulus references |
|
|
87
|
+
| `rails_get_stimulus` | Stimulus controllers — targets, values, actions, outlets |
|
|
88
|
+
|
|
89
|
+
### Smart Detail Levels
|
|
90
|
+
|
|
91
|
+
Schema, routes, models, and controllers tools support a `detail` parameter — critical for large apps:
|
|
92
|
+
|
|
93
|
+
| Level | Returns | Default limit |
|
|
94
|
+
|-------|---------|---------------|
|
|
95
|
+
| `summary` | Names + counts | 50 |
|
|
96
|
+
| `standard` | Names + key details *(default)* | 15 |
|
|
97
|
+
| `full` | Everything (indexes, FKs, constraints) | 5 |
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
rails_get_schema(detail: "summary") # → all tables with column counts
|
|
101
|
+
rails_get_schema(table: "users") # → full detail for one table
|
|
102
|
+
rails_get_routes(controller: "users") # → routes for one controller
|
|
103
|
+
rails_get_model_details(model: "User") # → associations, validations, scopes
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
A safety net (`max_tool_response_chars`, default 120K) truncates oversized responses with hints to use filters.
|
|
107
|
+
|
|
44
108
|
---
|
|
45
109
|
|
|
46
110
|
## What Gets Generated
|
|
47
111
|
|
|
48
|
-
`rails ai:context` generates
|
|
112
|
+
`rails ai:context` generates context files tailored to each AI assistant:
|
|
49
113
|
|
|
50
114
|
```
|
|
51
115
|
your-rails-app/
|
|
@@ -53,9 +117,9 @@ your-rails-app/
|
|
|
53
117
|
├── 🟣 Claude Code
|
|
54
118
|
│ ├── CLAUDE.md ≤150 lines (compact)
|
|
55
119
|
│ └── .claude/rules/
|
|
120
|
+
│ ├── rails-context.md app overview
|
|
56
121
|
│ ├── rails-schema.md table listing
|
|
57
122
|
│ ├── rails-models.md model listing
|
|
58
|
-
│ ├── rails-context.md app overview
|
|
59
123
|
│ └── rails-mcp-tools.md full tool reference
|
|
60
124
|
│
|
|
61
125
|
├── 🟢 Cursor
|
|
@@ -87,9 +151,7 @@ your-rails-app/
|
|
|
87
151
|
└── .mcp.json MCP auto-discovery
|
|
88
152
|
```
|
|
89
153
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
> Use `rails ai:context:full` to dump everything into the files (good for small apps <30 models).
|
|
154
|
+
Root files (CLAUDE.md, AGENTS.md, etc.) use **section markers** — your custom content outside the markers is preserved on re-generation. Set `config.generate_root_files = false` to only generate split rules.
|
|
93
155
|
|
|
94
156
|
---
|
|
95
157
|
|
|
@@ -98,13 +160,13 @@ Each file respects the AI tool's format and size limits. **Commit these files**
|
|
|
98
160
|
| Category | What's introspected |
|
|
99
161
|
|----------|-------------------|
|
|
100
162
|
| **Database** | Every table, column, index, foreign key, and migration |
|
|
101
|
-
| **Models** | Associations, validations, scopes, enums, callbacks, concerns, macros
|
|
163
|
+
| **Models** | Associations, validations, scopes, enums, callbacks, concerns, macros |
|
|
102
164
|
| **Routing** | Every route with HTTP verbs, paths, controller actions, API namespaces |
|
|
103
165
|
| **Controllers** | Actions, filters, strong params, concerns, API controllers |
|
|
104
166
|
| **Views** | Layouts, templates, partials, helpers, template engines, view components |
|
|
105
|
-
| **Frontend** | Stimulus controllers (targets, values, actions, outlets), Turbo Frames/Streams
|
|
167
|
+
| **Frontend** | Stimulus controllers (targets, values, actions, outlets), Turbo Frames/Streams |
|
|
106
168
|
| **Background** | ActiveJob classes, mailers, Action Cable channels |
|
|
107
|
-
| **Gems** | 70+ notable gems categorized (Devise = auth, Sidekiq = jobs, Pundit = authorization
|
|
169
|
+
| **Gems** | 70+ notable gems categorized (Devise = auth, Sidekiq = jobs, Pundit = authorization) |
|
|
108
170
|
| **Auth** | Devise modules, Pundit policies, CanCanCan, has_secure_password, CORS, CSP |
|
|
109
171
|
| **API** | Serializers, GraphQL, versioning, rate limiting, API-only mode |
|
|
110
172
|
| **Testing** | Framework, factories/fixtures, CI config, coverage, system tests |
|
|
@@ -112,76 +174,7 @@ Each file respects the AI tool's format and size limits. **Commit these files**
|
|
|
112
174
|
| **DevOps** | Puma, Procfile, Docker, deployment tools, asset pipeline |
|
|
113
175
|
| **Architecture** | Service objects, STI, polymorphism, state machines, multi-tenancy, engines |
|
|
114
176
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## MCP Tools
|
|
120
|
-
|
|
121
|
-
The gem exposes **9 live tools** via MCP that AI clients call on-demand:
|
|
122
|
-
|
|
123
|
-
| Tool | What it returns |
|
|
124
|
-
|------|----------------|
|
|
125
|
-
| `rails_get_schema` | Tables, columns, indexes, foreign keys |
|
|
126
|
-
| `rails_get_model_details` | Associations, validations, scopes, enums, callbacks |
|
|
127
|
-
| `rails_get_routes` | HTTP verbs, paths, controller actions |
|
|
128
|
-
| `rails_get_controllers` | Actions, filters, strong params, concerns |
|
|
129
|
-
| `rails_get_config` | Cache, session, timezone, middleware, initializers |
|
|
130
|
-
| `rails_get_test_info` | Test framework, factories, CI config, coverage |
|
|
131
|
-
| `rails_get_gems` | Notable gems categorized by function |
|
|
132
|
-
| `rails_get_conventions` | Architecture patterns, directory structure |
|
|
133
|
-
| `rails_search_code` | Ripgrep-powered regex search across the codebase |
|
|
134
|
-
|
|
135
|
-
All tools are **read-only** — they never modify your application or database.
|
|
136
|
-
|
|
137
|
-
### Smart Detail Levels
|
|
138
|
-
|
|
139
|
-
Schema, routes, models, and controllers tools support a `detail` parameter — critical for large apps:
|
|
140
|
-
|
|
141
|
-
| Level | Returns | Default limit |
|
|
142
|
-
|-------|---------|---------------|
|
|
143
|
-
| `summary` | Names + counts | 50 |
|
|
144
|
-
| `standard` | Names + key details *(default)* | 15 |
|
|
145
|
-
| `full` | Everything (indexes, FKs, constraints) | 5 |
|
|
146
|
-
|
|
147
|
-
```ruby
|
|
148
|
-
# Start broad
|
|
149
|
-
rails_get_schema(detail: "summary") # → all tables with column counts
|
|
150
|
-
|
|
151
|
-
# Drill into specifics
|
|
152
|
-
rails_get_schema(table: "users") # → full detail for one table
|
|
153
|
-
|
|
154
|
-
# Paginate large schemas
|
|
155
|
-
rails_get_schema(detail: "summary", limit: 20, offset: 40)
|
|
156
|
-
|
|
157
|
-
# Filter routes by controller
|
|
158
|
-
rails_get_routes(controller: "users")
|
|
159
|
-
|
|
160
|
-
# Get one model's full details
|
|
161
|
-
rails_get_model_details(model: "User")
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
A safety net (`max_tool_response_chars`, default 120K) truncates oversized responses with hints to use filters.
|
|
165
|
-
|
|
166
|
-
### Token Savings
|
|
167
|
-
|
|
168
|
-
The summary-first approach dramatically reduces AI token consumption — especially for large apps:
|
|
169
|
-
|
|
170
|
-
| Metric | Without gem | Full dump (v0.6) | Smart mode (v0.7+) |
|
|
171
|
-
|--------|-------------|------------------|---------------------|
|
|
172
|
-
| Context file | 0 tokens | ~15,000 tokens | ~1,500 tokens |
|
|
173
|
-
| Schema lookup | manual copy-paste | ~45,000 tokens (all tables) | ~800 tokens (summary) |
|
|
174
|
-
| Drill into 1 table | manual copy-paste | included above | ~400 tokens |
|
|
175
|
-
| **2-call workflow** | **error-prone** | **~60,000 tokens** | **~2,700 tokens** |
|
|
176
|
-
|
|
177
|
-
That's **~95% fewer tokens** for the same understanding. The AI gets a compact overview first, then only loads what it actually needs — you pay for precision, not bulk.
|
|
178
|
-
|
|
179
|
-
**How it saves:**
|
|
180
|
-
- Compact context files load ≤150 lines instead of thousands
|
|
181
|
-
- `detail:"summary"` gives the AI the full landscape in ~800 tokens
|
|
182
|
-
- Specific lookups (`table:`, `model:`, `controller:`) return only what's needed
|
|
183
|
-
- Pagination prevents dumping hundreds of tables/routes at once
|
|
184
|
-
- Split rule files only activate in relevant directories (e.g., model rules load only when editing `app/models/`)
|
|
177
|
+
28 introspectors total. The `:standard` preset runs 10 core ones by default; use `:full` for 27 (`database_stats` is opt-in, PostgreSQL only).
|
|
185
178
|
|
|
186
179
|
---
|
|
187
180
|
|
|
@@ -235,7 +228,7 @@ end
|
|
|
235
228
|
```ruby
|
|
236
229
|
# config/initializers/rails_ai_context.rb
|
|
237
230
|
RailsAiContext.configure do |config|
|
|
238
|
-
# Presets: :standard (
|
|
231
|
+
# Presets: :standard (10 introspectors, default) or :full (all 28)
|
|
239
232
|
config.preset = :standard
|
|
240
233
|
|
|
241
234
|
# Cherry-pick on top of a preset
|
|
@@ -269,7 +262,7 @@ end
|
|
|
269
262
|
| Option | Default | Description |
|
|
270
263
|
|--------|---------|-------------|
|
|
271
264
|
| `preset` | `:standard` | Introspector preset (`:standard` or `:full`) |
|
|
272
|
-
| `introspectors` |
|
|
265
|
+
| `introspectors` | 10 core | Array of introspector symbols |
|
|
273
266
|
| `context_mode` | `:compact` | `:compact` (≤150 lines) or `:full` (dump everything) |
|
|
274
267
|
| `claude_max_lines` | `150` | Max lines for CLAUDE.md in compact mode |
|
|
275
268
|
| `max_tool_response_chars` | `120_000` | Safety cap for MCP tool responses |
|
|
@@ -286,29 +279,13 @@ end
|
|
|
286
279
|
|
|
287
280
|
---
|
|
288
281
|
|
|
289
|
-
## Stack Compatibility
|
|
290
|
-
|
|
291
|
-
Works with every Rails architecture — auto-detects what's relevant:
|
|
292
|
-
|
|
293
|
-
| Setup | Coverage | Notes |
|
|
294
|
-
|-------|----------|-------|
|
|
295
|
-
| Rails full-stack (ERB + Hotwire) | 27/27 | All introspectors relevant |
|
|
296
|
-
| Rails + Inertia.js (React/Vue) | ~22/27 | Views/Turbo partially useful, backend fully covered |
|
|
297
|
-
| Rails API + React/Next.js SPA | ~20/27 | Schema, models, routes, API, auth, jobs — all covered |
|
|
298
|
-
| Rails API + mobile app | ~20/27 | Same as SPA — backend introspection is identical |
|
|
299
|
-
| Rails engine (mountable gem) | ~15/27 | Core introspectors (schema, models, routes, gems) work |
|
|
300
|
-
|
|
301
|
-
Frontend introspectors (views, Turbo, Stimulus, assets) degrade gracefully — they report nothing when those features aren't present.
|
|
302
|
-
|
|
303
|
-
---
|
|
304
|
-
|
|
305
282
|
## Commands
|
|
306
283
|
|
|
307
284
|
### Rake tasks (recommended)
|
|
308
285
|
|
|
309
286
|
| Command | Description |
|
|
310
287
|
|---------|-------------|
|
|
311
|
-
| `rails ai:context` | Generate all
|
|
288
|
+
| `rails ai:context` | Generate all context files (skips unchanged) |
|
|
312
289
|
| `rails ai:context:full` | Generate all files in full mode (dumps everything) |
|
|
313
290
|
| `rails ai:context:claude` | Generate Claude Code files only |
|
|
314
291
|
| `rails ai:context:opencode` | Generate OpenCode files only |
|
|
@@ -321,17 +298,9 @@ Frontend introspectors (views, Turbo, Stimulus, assets) degrade gracefully — t
|
|
|
321
298
|
| `rails ai:watch` | Auto-regenerate context files on code changes |
|
|
322
299
|
| `rails ai:inspect` | Print introspection summary to stdout |
|
|
323
300
|
|
|
324
|
-
> **Context modes:**
|
|
325
|
-
> ```bash
|
|
326
|
-
> rails ai:context # compact (default) — all formats
|
|
327
|
-
> rails ai:context:full # full dump — all formats
|
|
328
|
-
> CONTEXT_MODE=full rails ai:context:claude # full dump — Claude only
|
|
329
|
-
> CONTEXT_MODE=full rails ai:context:cursor # full dump — Cursor only
|
|
330
|
-
> ```
|
|
331
|
-
|
|
332
301
|
### Standalone CLI
|
|
333
302
|
|
|
334
|
-
The gem also ships a `rails-ai-context` executable — an alternative to rake tasks.
|
|
303
|
+
The gem also ships a `rails-ai-context` executable — an alternative to rake tasks.
|
|
335
304
|
|
|
336
305
|
| Command | Equivalent rake task |
|
|
337
306
|
|---------|---------------------|
|
|
@@ -348,6 +317,22 @@ Run from your Rails app root. Use `rails-ai-context help` for all options.
|
|
|
348
317
|
|
|
349
318
|
---
|
|
350
319
|
|
|
320
|
+
## Stack Compatibility
|
|
321
|
+
|
|
322
|
+
Works with every Rails architecture — auto-detects what's relevant:
|
|
323
|
+
|
|
324
|
+
| Setup | Coverage | Notes |
|
|
325
|
+
|-------|----------|-------|
|
|
326
|
+
| Rails full-stack (ERB + Hotwire) | 28/28 | All introspectors relevant |
|
|
327
|
+
| Rails + Inertia.js (React/Vue) | ~22/27 | Views/Turbo partially useful, backend fully covered |
|
|
328
|
+
| Rails API + React/Next.js SPA | ~20/27 | Schema, models, routes, API, auth, jobs — all covered |
|
|
329
|
+
| Rails API + mobile app | ~20/27 | Same as SPA — backend introspection is identical |
|
|
330
|
+
| Rails engine (mountable gem) | ~15/27 | Core introspectors (schema, models, routes, gems) work |
|
|
331
|
+
|
|
332
|
+
Frontend introspectors (views, Turbo, Stimulus, assets) degrade gracefully — they report nothing when those features aren't present.
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
351
336
|
## Works Without a Database
|
|
352
337
|
|
|
353
338
|
The gem parses `db/schema.rb` as text when no database is connected. Works in CI, Docker build stages, and Claude Code sessions without a running DB.
|
|
@@ -377,7 +362,7 @@ The gem parses `db/schema.rb` as text when no database is connected. Works in CI
|
|
|
377
362
|
```bash
|
|
378
363
|
git clone https://github.com/crisnahine/rails-ai-context.git
|
|
379
364
|
cd rails-ai-context && bundle install
|
|
380
|
-
bundle exec rspec #
|
|
365
|
+
bundle exec rspec # 412 examples
|
|
381
366
|
bundle exec rubocop # Lint
|
|
382
367
|
```
|
|
383
368
|
|
data/demo_script.sh
CHANGED
data/docs/GUIDE.md
CHANGED
|
@@ -246,7 +246,7 @@ rails ai:context:claude # Use this instead (no quoting needed)
|
|
|
246
246
|
|
|
247
247
|
## MCP Tools — Full Reference
|
|
248
248
|
|
|
249
|
-
All
|
|
249
|
+
All 11 tools are **read-only** and **idempotent** — they never modify your application or database.
|
|
250
250
|
|
|
251
251
|
### rails_get_schema
|
|
252
252
|
|
|
@@ -568,7 +568,7 @@ RailsAiContext.configure do |config|
|
|
|
568
568
|
end
|
|
569
569
|
```
|
|
570
570
|
|
|
571
|
-
Both transports are **read-only** — they expose the same
|
|
571
|
+
Both transports are **read-only** — they expose the same 11 tools and never modify your app.
|
|
572
572
|
|
|
573
573
|
---
|
|
574
574
|
|
|
@@ -579,7 +579,7 @@ Both transports are **read-only** — they expose the same 9 tools and never mod
|
|
|
579
579
|
RailsAiContext.configure do |config|
|
|
580
580
|
# --- Introspectors ---
|
|
581
581
|
|
|
582
|
-
# Presets: :standard (
|
|
582
|
+
# Presets: :standard (10 core, default) or :full (all 28)
|
|
583
583
|
config.preset = :standard
|
|
584
584
|
|
|
585
585
|
# Cherry-pick on top of a preset
|
|
@@ -636,7 +636,7 @@ end
|
|
|
636
636
|
| Option | Type | Default | Description |
|
|
637
637
|
|--------|------|---------|-------------|
|
|
638
638
|
| `preset` | Symbol | `:standard` | Introspector preset (`:standard` or `:full`) |
|
|
639
|
-
| `introspectors` | Array |
|
|
639
|
+
| `introspectors` | Array | 10 core symbols | Which introspectors to run |
|
|
640
640
|
| `context_mode` | Symbol | `:compact` | `:compact` or `:full` |
|
|
641
641
|
| `claude_max_lines` | Integer | `150` | Max lines for CLAUDE.md in compact mode |
|
|
642
642
|
| `max_tool_response_chars` | Integer | `120_000` | Safety cap for MCP tool responses |
|
|
@@ -689,7 +689,7 @@ These run by default. Fast and cover core Rails structure.
|
|
|
689
689
|
| `tests` | Test framework (rspec/minitest), factories/fixtures with locations and counts, system tests, CI config files, coverage tool, test helpers, VCR cassettes. |
|
|
690
690
|
| `migrations` | Total count, schema version, pending migrations, recent migration history with detected actions (create_table, add_column, etc.), migration statistics. |
|
|
691
691
|
|
|
692
|
-
### Full preset (
|
|
692
|
+
### Full preset (28 introspectors)
|
|
693
693
|
|
|
694
694
|
Includes all standard introspectors plus:
|
|
695
695
|
|
|
@@ -812,7 +812,7 @@ OpenCode uses **per-directory lazy-loading**: when the agent reads a file, it wa
|
|
|
812
812
|
|
|
813
813
|
| Setup | Coverage | Notes |
|
|
814
814
|
|-------|----------|-------|
|
|
815
|
-
| Rails full-stack (ERB + Hotwire) |
|
|
815
|
+
| Rails full-stack (ERB + Hotwire) | 28/28 | All introspectors relevant |
|
|
816
816
|
| Rails + Inertia.js (React/Vue) | ~22/27 | Views/Turbo partially useful, backend fully covered |
|
|
817
817
|
| Rails API + React/Next.js SPA | ~20/27 | Schema, models, routes, API, auth, jobs — all covered |
|
|
818
818
|
| Rails API + mobile app | ~20/27 | Same as SPA — backend introspection is identical |
|
|
Binary file
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module RailsAiContext
|
|
4
4
|
class Configuration
|
|
5
5
|
PRESETS = {
|
|
6
|
-
standard: %i[schema models routes jobs gems conventions controllers tests migrations],
|
|
7
|
-
full: %i[schema models routes jobs gems conventions stimulus controllers views turbo
|
|
6
|
+
standard: %i[schema models routes jobs gems conventions controllers tests migrations stimulus],
|
|
7
|
+
full: %i[schema models routes jobs gems conventions stimulus controllers views view_templates turbo
|
|
8
8
|
i18n config active_storage action_text auth api tests rake_tasks assets
|
|
9
9
|
devops action_mailbox migrations seeds middleware engines multi_database]
|
|
10
10
|
}.freeze
|
|
@@ -57,6 +57,7 @@ module RailsAiContext
|
|
|
57
57
|
when :database_stats then Introspectors::DatabaseStatsIntrospector.new(app)
|
|
58
58
|
when :controllers then Introspectors::ControllerIntrospector.new(app)
|
|
59
59
|
when :views then Introspectors::ViewIntrospector.new(app)
|
|
60
|
+
when :view_templates then Introspectors::ViewTemplateIntrospector.new(app)
|
|
60
61
|
when :turbo then Introspectors::TurboIntrospector.new(app)
|
|
61
62
|
when :i18n then Introspectors::I18nIntrospector.new(app)
|
|
62
63
|
when :config then Introspectors::ConfigIntrospector.new(app)
|
|
@@ -15,9 +15,13 @@ module RailsAiContext
|
|
|
15
15
|
{
|
|
16
16
|
framework: detect_framework,
|
|
17
17
|
factories: detect_factories,
|
|
18
|
+
factory_names: detect_factory_names,
|
|
18
19
|
fixtures: detect_fixtures,
|
|
20
|
+
fixture_names: detect_fixture_names,
|
|
19
21
|
system_tests: detect_system_tests,
|
|
20
22
|
test_helpers: detect_test_helpers,
|
|
23
|
+
test_helper_setup: detect_test_helper_setup,
|
|
24
|
+
test_files: detect_test_files,
|
|
21
25
|
vcr_cassettes: detect_vcr,
|
|
22
26
|
ci_config: detect_ci,
|
|
23
27
|
coverage: detect_coverage
|
|
@@ -97,6 +101,71 @@ module RailsAiContext
|
|
|
97
101
|
end.flatten.sort
|
|
98
102
|
end
|
|
99
103
|
|
|
104
|
+
def detect_factory_names
|
|
105
|
+
%w[spec/factories test/factories].each do |dir_rel|
|
|
106
|
+
dir = File.join(root, dir_rel)
|
|
107
|
+
next unless Dir.exist?(dir)
|
|
108
|
+
|
|
109
|
+
names = {}
|
|
110
|
+
Dir.glob(File.join(dir, "**/*.rb")).each do |path|
|
|
111
|
+
file = path.sub("#{root}/", "")
|
|
112
|
+
factories = File.read(path).scan(/factory\s+:(\w+)/).flatten
|
|
113
|
+
names[file] = factories if factories.any?
|
|
114
|
+
rescue
|
|
115
|
+
next
|
|
116
|
+
end
|
|
117
|
+
return names if names.any?
|
|
118
|
+
end
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def detect_fixture_names
|
|
123
|
+
%w[spec/fixtures test/fixtures].each do |dir_rel|
|
|
124
|
+
dir = File.join(root, dir_rel)
|
|
125
|
+
next unless Dir.exist?(dir)
|
|
126
|
+
|
|
127
|
+
names = {}
|
|
128
|
+
Dir.glob(File.join(dir, "**/*.yml")).each do |path|
|
|
129
|
+
file = File.basename(path, ".yml")
|
|
130
|
+
content = File.read(path) rescue next
|
|
131
|
+
# Top-level YAML keys are fixture names
|
|
132
|
+
keys = content.scan(/^(\w+):/).flatten
|
|
133
|
+
names[file] = keys if keys.any?
|
|
134
|
+
end
|
|
135
|
+
return names if names.any?
|
|
136
|
+
end
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def detect_test_helper_setup
|
|
141
|
+
helpers = %w[
|
|
142
|
+
spec/rails_helper.rb spec/spec_helper.rb
|
|
143
|
+
test/test_helper.rb
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
setup = []
|
|
147
|
+
helpers.each do |rel|
|
|
148
|
+
path = File.join(root, rel)
|
|
149
|
+
next unless File.exist?(path)
|
|
150
|
+
content = File.read(path) rescue next
|
|
151
|
+
content.scan(/(?:config\.)?include\s+([\w:]+)/).each { |m| setup << m[0] }
|
|
152
|
+
end
|
|
153
|
+
setup.uniq
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def detect_test_files
|
|
157
|
+
categories = {}
|
|
158
|
+
%w[models controllers requests system services integration features].each do |cat|
|
|
159
|
+
%w[spec test].each do |base|
|
|
160
|
+
dir = File.join(root, base, cat)
|
|
161
|
+
next unless Dir.exist?(dir)
|
|
162
|
+
count = Dir.glob(File.join(dir, "**/*.rb")).size
|
|
163
|
+
categories[cat] = { location: "#{base}/#{cat}", count: count } if count > 0
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
categories
|
|
167
|
+
end
|
|
168
|
+
|
|
100
169
|
def detect_vcr
|
|
101
170
|
dirs = [
|
|
102
171
|
File.join(root, "spec/cassettes"),
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAiContext
|
|
4
|
+
module Introspectors
|
|
5
|
+
# Reads actual view template contents and extracts metadata:
|
|
6
|
+
# partial references, Stimulus controller usage, line counts.
|
|
7
|
+
# Separate from ViewIntrospector which focuses on structural discovery.
|
|
8
|
+
class ViewTemplateIntrospector
|
|
9
|
+
attr_reader :app
|
|
10
|
+
|
|
11
|
+
def initialize(app)
|
|
12
|
+
@app = app
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
views_dir = File.join(app.root.to_s, "app", "views")
|
|
17
|
+
return { templates: {}, partials: {} } unless Dir.exist?(views_dir)
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
templates: scan_templates(views_dir),
|
|
21
|
+
partials: scan_partials(views_dir)
|
|
22
|
+
}
|
|
23
|
+
rescue => e
|
|
24
|
+
{ error: e.message }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def scan_templates(views_dir)
|
|
30
|
+
templates = {}
|
|
31
|
+
Dir.glob(File.join(views_dir, "**", "*")).each do |path|
|
|
32
|
+
next if File.directory?(path)
|
|
33
|
+
next if File.basename(path).start_with?("_") # skip partials
|
|
34
|
+
next if path.include?("/layouts/")
|
|
35
|
+
|
|
36
|
+
relative = path.sub("#{views_dir}/", "")
|
|
37
|
+
content = File.read(path) rescue next
|
|
38
|
+
templates[relative] = {
|
|
39
|
+
lines: content.lines.count,
|
|
40
|
+
partials: extract_partial_refs(content),
|
|
41
|
+
stimulus: extract_stimulus_refs(content)
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
templates
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def scan_partials(views_dir)
|
|
48
|
+
partials = {}
|
|
49
|
+
Dir.glob(File.join(views_dir, "**", "_*")).each do |path|
|
|
50
|
+
next if File.directory?(path)
|
|
51
|
+
relative = path.sub("#{views_dir}/", "")
|
|
52
|
+
lines = File.read(path).lines.count rescue 0
|
|
53
|
+
partials[relative] = { lines: lines }
|
|
54
|
+
end
|
|
55
|
+
partials
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def extract_partial_refs(content)
|
|
59
|
+
refs = []
|
|
60
|
+
# render "partial_name" or render partial: "name"
|
|
61
|
+
content.scan(/render\s+(?:partial:\s*)?["']([^"']+)["']/).each { |m| refs << m[0] }
|
|
62
|
+
# render @collection
|
|
63
|
+
content.scan(/render\s+@(\w+)/).each { |m| refs << m[0] }
|
|
64
|
+
refs.uniq
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def extract_stimulus_refs(content)
|
|
68
|
+
refs = []
|
|
69
|
+
# data-controller="name" or data-controller="name1 name2"
|
|
70
|
+
content.scan(/data-controller=["']([^"']+)["']/).each do |m|
|
|
71
|
+
m[0].split.each { |c| refs << c }
|
|
72
|
+
end
|
|
73
|
+
# data: { controller: "name" }
|
|
74
|
+
content.scan(/controller:\s*["']([^"']+)["']/).each do |m|
|
|
75
|
+
m[0].split.each { |c| refs << c }
|
|
76
|
+
end
|
|
77
|
+
refs.uniq
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|