rails-ai-context 4.0.0 → 4.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/CLAUDE.md +9 -7
  4. data/CONTRIBUTING.md +2 -2
  5. data/README.md +12 -10
  6. data/SECURITY.md +2 -1
  7. data/docs/GUIDE.md +7 -6
  8. data/lib/generators/rails_ai_context/install/install_generator.rb +212 -34
  9. data/lib/rails_ai_context/cli/tool_runner.rb +1 -0
  10. data/lib/rails_ai_context/configuration.rb +7 -1
  11. data/lib/rails_ai_context/introspector.rb +1 -0
  12. data/lib/rails_ai_context/introspectors/api_introspector.rb +47 -1
  13. data/lib/rails_ai_context/introspectors/auth_introspector.rb +63 -1
  14. data/lib/rails_ai_context/introspectors/frontend_framework_introspector.rb +426 -0
  15. data/lib/rails_ai_context/introspectors/gem_introspector.rb +6 -0
  16. data/lib/rails_ai_context/introspectors/turbo_introspector.rb +77 -1
  17. data/lib/rails_ai_context/serializers/stack_overview_helper.rb +8 -0
  18. data/lib/rails_ai_context/serializers/tool_guide_helper.rb +9 -6
  19. data/lib/rails_ai_context/server.rb +2 -1
  20. data/lib/rails_ai_context/tasks/rails_ai_context.rake +33 -0
  21. data/lib/rails_ai_context/tools/analyze_feature.rb +9 -7
  22. data/lib/rails_ai_context/tools/get_component_catalog.rb +6 -1
  23. data/lib/rails_ai_context/tools/get_context.rb +18 -6
  24. data/lib/rails_ai_context/tools/get_edit_context.rb +10 -1
  25. data/lib/rails_ai_context/tools/get_frontend_stack.rb +160 -0
  26. data/lib/rails_ai_context/tools/get_stimulus.rb +2 -2
  27. data/lib/rails_ai_context/tools/get_view.rb +3 -1
  28. data/lib/rails_ai_context/tools/migration_advisor.rb +16 -2
  29. data/lib/rails_ai_context/tools/performance_check.rb +11 -0
  30. data/lib/rails_ai_context/tools/search_code.rb +6 -0
  31. data/lib/rails_ai_context/tools/validate.rb +3 -0
  32. data/lib/rails_ai_context/version.rb +1 -1
  33. metadata +6 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4dfcbb5d44af0ed1defb25731f71a4777f9e0905dd662f0e7fa3ff83a870f02
4
- data.tar.gz: 8321f5166e78daa800a68c34f05b4b30eb83c6096a43d87916463d4ff7f07ee2
3
+ metadata.gz: af24e6340b8328b0526c9f030d60b495f4cb1e7130b7f16cadd039e2b9912aab
4
+ data.tar.gz: e74d337f614f853fec1bacaff748bea4b212ed96034c22b5fd3d75326da6fc13
5
5
  SHA512:
6
- metadata.gz: e66d58d898c8136321ba0d7bd3e188a446f219746bbb2073f73bf9b270b7e71ff0e776afa568ab91f9abe4343e9957b066384c63d0571d68592643bf740811ea
7
- data.tar.gz: fcfc393aa754c35ee729157322e58fd03d47649ddd4c1e4d68ff8e6f40c4498ab2c9375381c28e10b0016880cf0672ed030de927d432762651b32493e7a64889
6
+ metadata.gz: 9e8d9261310e038cce1cc20ba21d81d203eec2d813dcf318aa3f2071da646a67dc11fd812671a0d982d6d0d234ed84c2079144be3b62863b311e09d0b12181f9
7
+ data.tar.gz: 3f6001ba2d788b76e4a6e7b576a9447e7eed91df1a0b689dba679a3c6fbd876db1278e7ebd70637b35bedb01e2e77b1546cf3b96d8c5d37536f1f053fd35f9d3
data/CHANGELOG.md CHANGED
@@ -5,6 +5,50 @@ 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
+ ## [4.1.0] — 2026-03-29
9
+
10
+ ### Added
11
+ - New `rails_get_frontend_stack` tool: detects React/Vue/Svelte/Angular, Inertia/react-rails mounting, state management, TypeScript config, monorepo layout, package manager
12
+ - New `FrontendFrameworkIntrospector`: parses package.json (JSON.parse with BOM-safe reading), config/vite.json, config/shakapacker.yml, tsconfig.json
13
+ - Frontend framework detection covers patterns 3 (hybrid SPA), 4 (API+SPA), and 7 (Turbo Native)
14
+ - API introspector: OpenAPI/Swagger spec detection, CORS config parsing, API codegen tool detection (openapi-typescript, graphql-codegen, orval)
15
+ - Auth introspector: JWT strategy (devise-jwt, Doorkeeper config), HTTP token auth detection
16
+ - Turbo introspector: Turbo Native detection (turbo_native_app?, native navigation patterns, native conditionals in views)
17
+ - Gem introspector: 6 new notable gems (devise-jwt, rswag-api, rswag-ui, grape-swagger, apipie-rails, hotwire-native-rails)
18
+ - Optional config: `frontend_paths`, `mobile_paths` (auto-detected if nil, user override for edge cases)
19
+ - Install generator: re-install now updates `ai_tools` and `tool_mode` selections, adds missing config sections without removing existing settings
20
+ - Install generator: prompts to remove generated files when AI tools are deselected (per-tool chooser)
21
+ - `rails ai:context:cursor` (and other format tasks) now auto-adds the format to `config.ai_tools`
22
+ - CLI tool_runner: warns on invalid enum values instead of silent fallback
23
+
24
+ ### Fixed
25
+ - `analyze_feature` crash on nil/empty input — now returns helpful prompt
26
+ - `analyze_feature` with nonexistent feature — returns clean "no match" instead of scaffolded empty sections
27
+ - `migration_advisor` crash on empty/invalid action — now validates with "Did you mean?" suggestions
28
+ - `migration_advisor` generates broken SQL with empty table/column — now validates required params
29
+ - `migration_advisor` doesn't normalize table names — "Cook" now auto-resolves to "cooks"
30
+ - `migration_advisor` no duplicate column/index detection — now warns on existing columns, indexes, and FKs
31
+ - `migration_advisor` no nonexistent column detection — now warns on remove/rename/change_type/add_index for missing columns
32
+ - `edit_context` "File not found" with no hint — now suggests full path with "Did you mean?"
33
+ - `performance_check` model filter fails for multi-word models — "BrandProfile" now resolves to "brand_profiles"
34
+ - `performance_check` unknown model silently ignored — now returns "not found" with suggestions
35
+ - `turbo_map` stream filter misses dynamic broadcasts — multi-line call handling + snippet fallback + fuzzy prefix matching
36
+ - `turbo_map` controller filter misses job broadcasts — now includes broadcasts matching filtered subscriptions' streams
37
+ - `security_scan` wrong check name examples — added CHECK_ALIASES mapping (CheckXSS → CheckCrossSiteScripting, sql → CheckSQL, etc.)
38
+ - `search_code` unknown match_type silently ignored — now returns error with valid values
39
+ - `validate` unknown level silently ignored — now returns error with valid values
40
+ - `get_view` no "Did you mean?" on wrong controller — now uses `find_closest_match`
41
+ - `get_context` plural model name ("Cooks") produces mixed output — now normalizes via singularize/classify, fails fast when not found
42
+ - `component_catalog` specific component returns generic "no components" — now acknowledges the input
43
+ - `stimulus` doesn't strip `_controller` suffix — now auto-strips for lookup
44
+ - `controller_introspector_spec` rate_limit test crashes on Rails 7.1 — split into source-parsing test (no class loading)
45
+
46
+ ### Changed
47
+ - Full preset: 31 → 32 introspectors (added :frontend_frameworks)
48
+ - Tool count: 29 → 30
49
+ - Test count: 817 → 893
50
+ - Install generator always writes `config.ai_tools` and `config.tool_mode` uncommented for re-install detection
51
+
8
52
  ## [4.0.0] — 2026-03-26
9
53
 
10
54
  ### 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/` — 32 introspectors (schema, models, routes, jobs, gems, conventions, stimulus, database_stats, controllers, views, view_templates, design_tokens, turbo, i18n, config, active_storage, action_text, auth, api, tests, rake_tasks, assets, devops, action_mailbox, migrations, seeds, middleware, engines, multi_database, components, accessibility, performance)
12
- - `lib/rails_ai_context/tools/` — 29 MCP tools using the official mcp SDK
11
+ - `lib/rails_ai_context/introspectors/` — 33 introspectors (schema, models, routes, jobs, gems, conventions, stimulus, database_stats, controllers, views, view_templates, design_tokens, turbo, i18n, config, active_storage, action_text, auth, api, tests, rake_tasks, assets, devops, action_mailbox, migrations, seeds, middleware, engines, multi_database, components, accessibility, performance, frontend_frameworks)
12
+ - `lib/rails_ai_context/tools/` — 30 MCP tools using the official mcp SDK
13
13
  - `lib/rails_ai_context/cli/` — CLI tool runner (`tool_runner.rb`) — executes MCP tools from rake/Thor
14
14
  - `lib/rails_ai_context/serializers/` — Output formatters (claude, claude_rules, opencode, opencode_rules, cursor_rules, copilot, copilot_instructions, rules, markdown, JSON, context_file_serializer, test_command_detection, tool_guide_helper, design_system_helper, stack_overview_helper)
15
15
  - `lib/rails_ai_context/resources.rb` — MCP resources (static data AI clients read directly)
@@ -34,28 +34,30 @@ structure to AI assistants via the Model Context Protocol (MCP).
34
34
  7. **Diff-aware** — context regeneration skips unchanged files
35
35
  8. **Per-assistant serializers** — each AI tool gets tailored output format
36
36
  9. **Zeitwerk autoloading** — files loaded on-demand, not all upfront
37
- 10. **Introspector presets** — `:full` (31) default, `:standard` (14 core) for lightweight usage
37
+ 10. **Introspector presets** — `:full` (32) default, `:standard` (14 core) for lightweight usage
38
38
  11. **MCP auto-discovery** — `.mcp.json` generated by install generator
39
39
  12. **Compact by default** — context files ≤150 lines, MCP tools use `detail` parameter (summary/standard/full)
40
40
  13. **Per-tool split rules** — `.claude/rules/`, `.cursor/rules/`, `.github/instructions/`
41
41
  14. **Section markers** — root file content wrapped in `<!-- BEGIN/END rails-ai-context -->` to preserve user content
42
42
  15. **generate_root_files toggle** — when false, skip root files (CLAUDE.md, etc.), only generate split rules
43
- 16. **custom_tools API** — `config.custom_tools` array lets users register additional MCP::Tool subclasses alongside the 29 built-in tools
43
+ 16. **custom_tools API** — `config.custom_tools` array lets users register additional MCP::Tool subclasses alongside the 30 built-in tools
44
44
  17. **Design system extraction** — view templates analyzed for canonical examples, color palette, typography, responsive patterns, interactive states, dark mode
45
45
  18. **skip_tools API** — `config.skip_tools` array lets users exclude specific built-in tools (e.g. `%w[rails_security_scan]`)
46
46
  19. **Security scanning** — optional Brakeman integration via `rails_security_scan` tool (graceful degradation if not installed)
47
47
  20. **tool_mode config** — `:mcp` (default, MCP primary + CLI fallback) or `:cli` (CLI only, no MCP server needed). Selected during install.
48
- 21. **CLI tool access** — all 29 MCP tools callable from terminal: `rails ai:tool[schema]`, `rails-ai-context tool schema`. Tool name resolution: `schema` → `get_schema` → `rails_get_schema`.
48
+ 21. **CLI tool access** — all 30 MCP tools callable from terminal: `rails ai:tool[schema]`, `rails-ai-context tool schema`. Tool name resolution: `schema` → `get_schema` → `rails_get_schema`.
49
49
  22. **Shared ToolGuideHelper** — serializers use a shared module for tool reference sections, rendering MCP or CLI syntax based on `tool_mode`
50
50
  23. **Component catalog** — ViewComponent/Phlex introspection: props, slots, previews, sidecar assets, usage examples via `rails_get_component_catalog`
51
51
  24. **Accessibility scanning** — ARIA attributes, semantic HTML, screen reader text, alt text, landmark roles, accessibility score via AccessibilityIntrospector
52
52
  25. **Performance analysis** — N+1 query risks, missing counter_cache, missing FK indexes, eager load candidates via `rails_performance_check`
53
- 26. **Migration advisor** — migration code generation with reversibility warnings and affected model detection via `rails_migration_advisor`
53
+ 26. **Migration advisor** — migration code generation with duplicate/nonexistent column warnings, reversibility flags, table name normalization via `rails_migration_advisor`
54
+ 27. **Frontend framework detection** — auto-detects React/Vue/Svelte/Angular from package.json, Vite/Shakapacker/Webpacker config, TypeScript setup, monorepo layout, package manager via `rails_get_frontend_stack`
55
+ 28. **Install generator idempotency** — re-install preserves existing config, only adds new sections, updates ai_tools/tool_mode selections, prompts per-tool file cleanup for removed tools
54
56
 
55
57
  ## Testing
56
58
 
57
59
  ```bash
58
- bundle exec rspec # Run specs (806 examples)
60
+ bundle exec rspec # Run specs (893 examples)
59
61
  bundle exec rubocop # Lint
60
62
  ```
61
63
 
data/CONTRIBUTING.md CHANGED
@@ -19,8 +19,8 @@ The test suite uses [Combustion](https://github.com/pat/combustion) to boot a mi
19
19
  ```
20
20
  lib/rails_ai_context/
21
21
  ├── cli/ # CLI tool runner (tool_runner.rb) — executes MCP tools from rake/Thor
22
- ├── introspectors/ # 32 introspectors (schema, models, routes, etc.)
23
- ├── tools/ # 29 MCP tools with detail levels and pagination
22
+ ├── introspectors/ # 33 introspectors (schema, models, routes, etc.)
23
+ ├── tools/ # 30 MCP tools with detail levels and pagination
24
24
  ├── serializers/ # Per-assistant formatters + shared ToolGuideHelper
25
25
  ├── server.rb # MCP server setup (stdio + HTTP)
26
26
  ├── live_reload.rb # MCP live reload (file watcher + cache invalidation)
data/README.md CHANGED
@@ -9,14 +9,14 @@
9
9
 
10
10
  **Works with:** Claude Code &bull; Cursor &bull; GitHub Copilot &bull; OpenCode &bull; Any terminal
11
11
 
12
- > Built by a Rails developer with 10+ years of production experience. AI assisted — the same way it assists me shipping features at work. I designed the architecture, made every decision, reviewed every line, and wrote 806 tests. This gem exists because I understand Rails deeply enough to know exactly what AI agents get wrong and what context they need to get it right.
12
+ > Built by a Rails developer with 10+ years of production experience. AI assisted — the same way it assists me shipping features at work. I designed the architecture, made every decision, reviewed every line, and wrote 893 tests. This gem exists because I understand Rails deeply enough to know exactly what AI agents get wrong and what context they need to get it right.
13
13
 
14
14
  ```bash
15
15
  gem "rails-ai-context", group: :development
16
16
  rails generate rails_ai_context:install
17
17
  ```
18
18
 
19
- That's it. Your AI now has 29 tools that understand your entire Rails app — via MCP server or CLI. Zero config.
19
+ That's it. Your AI now has 30 tools that understand your entire Rails app — via MCP server or CLI. Zero config.
20
20
 
21
21
  > **[Full Guide →](docs/GUIDE.md)** — every command, every parameter, every configuration option.
22
22
 
@@ -46,7 +46,7 @@ rails 'ai:tool[schema]' table=users
46
46
  rails 'ai:tool[analyze_feature]' feature=billing
47
47
  ```
48
48
 
49
- Same 29 tools. Same output. AI agents run these as shell commands. **Works in any terminal, any AI tool, any workflow.** No MCP client required.
49
+ Same 30 tools. Same output. AI agents run these as shell commands. **Works in any terminal, any AI tool, any workflow.** No MCP client required.
50
50
 
51
51
  ---
52
52
 
@@ -170,7 +170,7 @@ Tested on a real Rails 8 app (5 models, 19 controllers, 95 routes):
170
170
 
171
171
  ---
172
172
 
173
- ## 29 Tools
173
+ ## 30 Tools
174
174
 
175
175
  Every tool is **read-only** and returns structured, token-efficient data.
176
176
 
@@ -214,6 +214,8 @@ Every tool is **read-only** and returns structured, token-efficient data.
214
214
  | `performance_check` | `rails_performance_check` | `rails 'ai:tool[performance_check]'` | N+1 risks, missing indexes, counter_cache, eager load candidates |
215
215
  | `dependency_graph` | `rails_dependency_graph` | `rails 'ai:tool[dependency_graph]'` | Model/service dependency graph in Mermaid or text format |
216
216
  | `migration_advisor` | `rails_migration_advisor` | `rails 'ai:tool[migration_advisor]'` | Migration code generation with reversibility + affected models |
217
+ | **Frontend** | | | |
218
+ | `get_frontend_stack` | `rails_get_frontend_stack` | `rails 'ai:tool[frontend_stack]'` | React/Vue/Svelte/Angular, Inertia, TypeScript, package manager |
217
219
 
218
220
  > **[Full parameter docs →](docs/GUIDE.md)**
219
221
 
@@ -226,7 +228,7 @@ Every tool is **read-only** and returns structured, token-efficient data.
226
228
  │ Your Rails App │
227
229
  │ models + schema + routes + controllers + views + jobs │
228
230
  └────────────────────────┬────────────────────────────────┘
229
- │ introspects (32 introspectors)
231
+ │ introspects (33 introspectors)
230
232
 
231
233
  ┌─────────────────────────────────────────────────────────┐
232
234
  │ rails-ai-context │
@@ -236,7 +238,7 @@ Every tool is **read-only** and returns structured, token-efficient data.
236
238
  ▼ ▼ ▼
237
239
  ┌──────────────────┐ ┌────────────┐ ┌────────────────────┐
238
240
  │ Static Files │ │ MCP Server │ │ CLI Tools │
239
- │ CLAUDE.md │ │ 29 tools │ │ Same 29 tools │
241
+ │ CLAUDE.md │ │ 30 tools │ │ Same 30 tools │
240
242
  │ .cursor/rules/ │ │ stdio/HTTP │ │ No server needed │
241
243
  │ .github/instr... │ │ .mcp.json │ │ rails 'ai:tool[X]' │
242
244
  └──────────────────┘ └────────────┘ └────────────────────┘
@@ -272,7 +274,7 @@ MCP auto-discovery: `.mcp.json` is detected automatically by Claude Code and Cur
272
274
  | Command | What it does |
273
275
  |---------|-------------|
274
276
  | `rails ai:context` | Generate context files for your AI tools |
275
- | `rails 'ai:tool[NAME]'` | Run any of the 29 tools from the CLI |
277
+ | `rails 'ai:tool[NAME]'` | Run any of the 30 tools from the CLI |
276
278
  | `rails ai:tool` | List all available tools with short names |
277
279
  | `rails ai:serve` | Start MCP server (stdio) |
278
280
  | `rails ai:doctor` | Diagnostics + AI readiness score |
@@ -292,7 +294,7 @@ RailsAiContext.configure do |config|
292
294
  # Tool mode: :mcp (default, MCP + CLI fallback) or :cli (CLI only)
293
295
  # config.tool_mode = :mcp
294
296
 
295
- # Presets: :full (31 introspectors, default) or :standard (14 core)
297
+ # Presets: :full (32 introspectors, default) or :standard (14 core)
296
298
  # config.preset = :full
297
299
 
298
300
  # Exclude models from introspection
@@ -310,7 +312,7 @@ end
310
312
  |--------|---------|-------------|
311
313
  | **Presets & Introspectors** | | |
312
314
  | `preset` | `:full` | Introspector preset (`:full` or `:standard`) |
313
- | `introspectors` | 31 (full) | Array of introspector symbols |
315
+ | `introspectors` | 32 (full) | Array of introspector symbols |
314
316
  | **Context Generation** | | |
315
317
  | `context_mode` | `:compact` | `:compact` (≤150 lines) or `:full` (dump everything) |
316
318
  | `claude_max_lines` | `150` | Max lines for CLAUDE.md in compact mode |
@@ -345,7 +347,7 @@ end
345
347
  ```bash
346
348
  git clone https://github.com/crisnahine/rails-ai-context.git
347
349
  cd rails-ai-context && bundle install
348
- bundle exec rspec # 806 examples
350
+ bundle exec rspec # 893 examples
349
351
  bundle exec rubocop # Lint
350
352
  ```
351
353
 
data/SECURITY.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|--------------------|
7
+ | 4.1.x | :white_check_mark: |
7
8
  | 4.0.x | :white_check_mark: |
8
9
  | 3.1.x | :white_check_mark: |
9
10
  | 3.0.x | :x: |
@@ -24,7 +25,7 @@ If you discover a security vulnerability in rails-ai-context, please report it r
24
25
 
25
26
  ## Security Design
26
27
 
27
- - All 29 MCP tools are **read-only** and never modify your application or database.
28
+ - All 30 MCP tools are **read-only** and never modify your application or database.
28
29
  - **Sensitive file blocking** — configurable `sensitive_patterns` blocks access to `.env`, `*.key`, `*.pem`, `credentials.yml.enc` across all search and read tools. Patterns are checked in `rails_search_code`, `rails_get_edit_context`, and all new tools.
29
30
  - **Path traversal protection** — all file-reading tools validate paths with `File.realpath()` against `Rails.root` to prevent directory escape.
30
31
  - **Command injection prevention** — code search uses `Open3.capture2` with array arguments (never shell strings). The `--` flag separator prevents pattern injection.
data/docs/GUIDE.md CHANGED
@@ -252,7 +252,7 @@ rails ai:context:claude # Use this instead (no quoting needed)
252
252
 
253
253
  ## CLI Tools
254
254
 
255
- All 29 MCP tools can be run directly from the terminal — no MCP server or AI client needed.
255
+ All 30 MCP tools can be run directly from the terminal — no MCP server or AI client needed.
256
256
 
257
257
  ### Rake
258
258
 
@@ -316,7 +316,7 @@ The `tool_mode` is selected during `rails generate rails_ai_context:install`.
316
316
 
317
317
  ## MCP Tools — Full Reference
318
318
 
319
- All 29 tools are **read-only** and **idempotent** — they never modify your application or database.
319
+ All 30 tools are **read-only** and **idempotent** — they never modify your application or database.
320
320
 
321
321
  ### rails_get_schema
322
322
 
@@ -1117,7 +1117,7 @@ RailsAiContext.configure do |config|
1117
1117
  end
1118
1118
  ```
1119
1119
 
1120
- Both transports are **read-only** — they expose the same 29 tools and never modify your app.
1120
+ Both transports are **read-only** — they expose the same 30 tools and never modify your app.
1121
1121
 
1122
1122
  ---
1123
1123
 
@@ -1128,7 +1128,7 @@ Both transports are **read-only** — they expose the same 29 tools and never mo
1128
1128
  RailsAiContext.configure do |config|
1129
1129
  # --- Introspectors ---
1130
1130
 
1131
- # Presets: :full (31 introspectors, default) or :standard (14 core)
1131
+ # Presets: :full (32 introspectors, default) or :standard (14 core)
1132
1132
  config.preset = :full
1133
1133
 
1134
1134
  # Cherry-pick on top of a preset
@@ -1243,7 +1243,7 @@ end
1243
1243
  | Option | Type | Default | Description |
1244
1244
  |--------|------|---------|-------------|
1245
1245
  | `preset` | Symbol | `:full` | Introspector preset (`:full` or `:standard`) |
1246
- | `introspectors` | Array | 28 (full preset) | Which introspectors to run |
1246
+ | `introspectors` | Array | 32 (full preset) | Which introspectors to run |
1247
1247
  | `context_mode` | Symbol | `:compact` | `:compact` or `:full` |
1248
1248
  | `claude_max_lines` | Integer | `150` | Max lines for CLAUDE.md in compact mode |
1249
1249
  | `max_tool_response_chars` | Integer | `200_000` | Safety cap for MCP tool responses |
@@ -1321,7 +1321,7 @@ Core Rails structure only. Use `config.preset = :standard` for a lighter footpri
1321
1321
  | `design_tokens` | Auto-detects CSS framework (Tailwind v3/v4, Bootstrap, Sass, plain CSS) and extracts design tokens from config files and built CSS. |
1322
1322
  | `components` | ViewComponent/Phlex components: props, slots, previews, sidecar assets, usage examples. |
1323
1323
 
1324
- ### Full preset (31 introspectors) — default
1324
+ ### Full preset (32 introspectors) — default
1325
1325
 
1326
1326
  Includes all standard introspectors plus:
1327
1327
 
@@ -1344,6 +1344,7 @@ Includes all standard introspectors plus:
1344
1344
  | `multi_database` | Multiple databases, replicas, sharding config, model-specific `connects_to` declarations. database.yml parsing fallback. |
1345
1345
  | `accessibility` | ARIA attributes, semantic HTML elements, screen reader text, alt text coverage, landmark roles, accessibility score. |
1346
1346
  | `performance` | N+1 query risks, missing counter_cache, missing FK indexes, Model.all anti-patterns, eager load candidates. |
1347
+ | `frontend_frameworks` | Frontend JS framework detection (React/Vue/Svelte/Angular), mounting strategy (Inertia/react-rails), TypeScript config, state management, package manager. |
1347
1348
  | `database_stats` | PostgreSQL approximate row counts via `pg_stat_user_tables`. **Opt-in only** — not in any preset, add manually: `config.introspectors += [:database_stats]`. |
1348
1349
 
1349
1350
  ### Using the standard preset
@@ -16,6 +16,14 @@ module RailsAiContext
16
16
  "4" => { key: :opencode, name: "OpenCode", files: "AGENTS.md", format: :opencode }
17
17
  }.freeze
18
18
 
19
+ # Files/dirs generated per AI tool format — used for cleanup on tool removal
20
+ FORMAT_PATHS = {
21
+ claude: %w[CLAUDE.md .claude/rules],
22
+ cursor: %w[.cursor/rules],
23
+ copilot: %w[.github/copilot-instructions.md .github/instructions],
24
+ opencode: %w[AGENTS.md app/models/AGENTS.md app/controllers/AGENTS.md]
25
+ }.freeze
26
+
19
27
  def select_ai_tools
20
28
  say ""
21
29
  say "Which AI tools do you use? (select all that apply)", :yellow
@@ -45,6 +53,56 @@ module RailsAiContext
45
53
  say "Selected: #{selected_names.join(', ')}", :green
46
54
  end
47
55
 
56
+ def cleanup_removed_tools
57
+ @previous_formats = read_previous_ai_tools
58
+ return unless @previous_formats&.any?
59
+
60
+ removed = @previous_formats - @selected_formats
61
+ return if removed.empty?
62
+
63
+ say ""
64
+ say "These AI tools were removed from your selection:", :yellow
65
+ removed.each_with_index do |fmt, idx|
66
+ tool = AI_TOOLS.values.find { |t| t[:format] == fmt }
67
+ say " #{idx + 1}. #{tool[:name]} (#{tool[:files]})" if tool
68
+ end
69
+ say ""
70
+
71
+ say "Remove their generated files?", :yellow
72
+ say " y — remove all listed above"
73
+ say " n — keep all (default)"
74
+ say " 1,2 — remove only specific ones by number"
75
+ say ""
76
+
77
+ input = ask("Enter choice:").strip.downcase
78
+ return if input.empty? || input == "n" || input == "no"
79
+
80
+ to_remove = if input == "y" || input == "yes" || input == "a"
81
+ removed
82
+ else
83
+ nums = input.split(/[\s,]+/).filter_map { |n| n.to_i - 1 }
84
+ nums.filter_map { |i| removed[i] if i >= 0 && i < removed.size }
85
+ end
86
+
87
+ return if to_remove.empty?
88
+
89
+ to_remove.each do |fmt|
90
+ tool = AI_TOOLS.values.find { |t| t[:format] == fmt }
91
+ paths = FORMAT_PATHS[fmt] || []
92
+ paths.each do |rel_path|
93
+ full = Rails.root.join(rel_path)
94
+ if File.directory?(full)
95
+ FileUtils.rm_rf(full)
96
+ say " Removed #{rel_path}/", :red
97
+ elsif File.exist?(full)
98
+ FileUtils.rm_f(full)
99
+ say " Removed #{rel_path}", :red
100
+ end
101
+ end
102
+ say " ✓ #{tool[:name]} files removed", :green if tool
103
+ end
104
+ end
105
+
48
106
  def select_tool_mode
49
107
  say ""
50
108
  say "Do you also want MCP server support?", :yellow
@@ -95,39 +153,28 @@ module RailsAiContext
95
153
  end
96
154
  end
97
155
 
98
- def create_initializer
99
- tools_line = if @selected_formats.size == AI_TOOLS.size
100
- " # config.ai_tools = %i[claude cursor copilot opencode] # default: all"
101
- else
102
- " config.ai_tools = %i[#{@selected_formats.join(' ')}]"
103
- end
104
-
105
- tool_mode_line = if @tool_mode == :cli
106
- " config.tool_mode = :cli # CLI only (no MCP server needed)"
107
- else
108
- " # config.tool_mode = :mcp # MCP primary + CLI fallback (default)"
109
- end
110
-
111
- create_file "config/initializers/rails_ai_context.rb", <<~RUBY
112
- # frozen_string_literal: true
113
-
114
- RailsAiContext.configure do |config|
156
+ # All config sections with their marker comment and content.
157
+ # Each section is identified by its marker (e.g., "── AI Tools ──").
158
+ # On re-install, only sections NOT already present are appended.
159
+ CONFIG_SECTIONS = {
160
+ "AI Tools" => <<~SECTION,
115
161
  # ── AI Tools ──────────────────────────────────────────────────────
116
162
  # Which AI tools to generate context files for (selected during install)
117
163
  # Run `rails generate rails_ai_context:install` to change selection
118
- #{tools_line}
164
+ # config.ai_tools = %i[claude cursor copilot opencode] # default: all
119
165
 
120
166
  # Tool invocation mode:
121
167
  # :mcp — MCP primary + CLI fallback (default, requires `rails ai:serve`)
122
168
  # :cli — CLI only (no MCP server needed, uses `rails 'ai:tool[NAME]'`)
123
- #{tool_mode_line}
124
-
169
+ # config.tool_mode = :mcp
170
+ SECTION
171
+ "Introspection" => <<~SECTION,
125
172
  # ── Introspection ─────────────────────────────────────────────────
126
173
  # Introspector preset:
127
- # :full — all 28 introspectors (default)
128
- # :standard — 13 core introspectors (schema, models, routes, jobs, gems,
174
+ # :full — all #{RailsAiContext::Configuration::PRESETS[:full].size} introspectors (default)
175
+ # :standard — #{RailsAiContext::Configuration::PRESETS[:standard].size} core introspectors (schema, models, routes, jobs, gems,
129
176
  # conventions, controllers, tests, migrations, stimulus,
130
- # view_templates, design_tokens, config)
177
+ # view_templates, design_tokens, config, components)
131
178
  # config.preset = :full
132
179
 
133
180
  # Context mode: :compact (default, ≤150 lines) or :full (dumps everything)
@@ -139,7 +186,8 @@ module RailsAiContext
139
186
  # Whether to generate root files (CLAUDE.md, AGENTS.md, etc.)
140
187
  # Set false to only generate split rules (.claude/rules/, .cursor/rules/, etc.)
141
188
  # config.generate_root_files = true
142
-
189
+ SECTION
190
+ "Models & Filtering" => <<~SECTION,
143
191
  # ── Models & Filtering ────────────────────────────────────────────
144
192
  # Models to exclude from introspection
145
193
  # config.excluded_models += %w[AdminUser InternalThing]
@@ -149,7 +197,8 @@ module RailsAiContext
149
197
 
150
198
  # Route prefixes to hide with app_only filter
151
199
  # config.excluded_route_prefixes += %w[sidekiq/]
152
-
200
+ SECTION
201
+ "MCP Server" => <<~SECTION,
153
202
  # ── MCP Server ────────────────────────────────────────────────────
154
203
  # Cache TTL in seconds for introspection data
155
204
  # config.cache_ttl = 60
@@ -167,7 +216,8 @@ module RailsAiContext
167
216
  # config.auto_mount = false
168
217
  # config.http_path = "/mcp"
169
218
  # config.http_port = 6029
170
-
219
+ SECTION
220
+ "File Size Limits" => <<~SECTION,
171
221
  # ── File Size Limits ──────────────────────────────────────────────
172
222
  # Increase for larger projects
173
223
  # config.max_file_size = 5_000_000 # Per-file read (5MB)
@@ -177,32 +227,160 @@ module RailsAiContext
177
227
  # config.max_view_file_size = 1_000_000 # Per-view file (1MB)
178
228
  # config.max_search_results = 200 # Max search results per call
179
229
  # config.max_validate_files = 50 # Max files per validate call
180
-
230
+ SECTION
231
+ "Extensibility" => <<~SECTION,
181
232
  # ── Extensibility ─────────────────────────────────────────────────
182
- # Register additional MCP tool classes alongside the 25 built-in tools
233
+ # Register additional MCP tool classes alongside the #{RailsAiContext::Server::TOOLS.size} built-in tools
183
234
  # config.custom_tools = [MyApp::CustomTool]
184
235
 
185
236
  # Exclude specific built-in tools by name
186
237
  # config.skip_tools = %w[rails_security_scan]
187
-
238
+ SECTION
239
+ "Security" => <<~SECTION,
188
240
  # ── Security ──────────────────────────────────────────────────────
189
241
  # Paths excluded from code search
190
242
  # config.excluded_paths += %w[vendor/cache]
191
243
 
192
244
  # File patterns blocked from search and read tools
193
245
  # config.sensitive_patterns += %w[config/secrets.yml]
194
-
246
+ SECTION
247
+ "Search" => <<~SECTION,
195
248
  # ── Search ────────────────────────────────────────────────────────
196
249
  # File extensions for fallback search (when ripgrep unavailable)
197
250
  # config.search_extensions = %w[rb js erb yml yaml json ts tsx vue svelte haml slim]
198
251
 
199
252
  # Where to look for concern source files
200
253
  # config.concern_paths = %w[app/models/concerns app/controllers/concerns]
254
+ SECTION
255
+ "Frontend" => <<~SECTION
256
+ # ── Frontend Framework Detection ─────────────────────────────────
257
+ # Auto-detected from package.json, config/vite.json, etc. Override only if needed.
258
+ # config.frontend_paths = ["app/frontend", "../web-client"]
259
+ # config.mobile_paths = ["../mobile-app"]
260
+ SECTION
261
+ }.freeze
262
+
263
+ def create_initializer
264
+ initializer_path = "config/initializers/rails_ai_context.rb"
265
+ full_path = Rails.root.join(initializer_path)
266
+
267
+ if File.exist?(full_path)
268
+ update_existing_initializer(full_path)
269
+ else
270
+ create_new_initializer(initializer_path)
271
+ end
272
+ end
273
+
274
+ no_tasks do
275
+ def create_new_initializer(path)
276
+ # Always write uncommented so re-install can detect previous selection
277
+ tools_line = " config.ai_tools = %i[#{@selected_formats.join(' ')}]"
278
+
279
+ tool_mode_line = if @tool_mode == :cli
280
+ " config.tool_mode = :cli # CLI only (no MCP server needed)"
281
+ else
282
+ " config.tool_mode = :mcp # MCP primary + CLI fallback"
283
+ end
284
+
285
+ content = "# frozen_string_literal: true\n\nRailsAiContext.configure do |config|\n"
286
+
287
+ # AI Tools section gets dynamic values from user selection
288
+ content += <<~SECTION
289
+ # ── AI Tools ──────────────────────────────────────────────────────
290
+ # Which AI tools to generate context files for (selected during install)
291
+ # Run `rails generate rails_ai_context:install` to change selection
292
+ #{tools_line}
293
+
294
+ # Tool invocation mode:
295
+ # :mcp — MCP primary + CLI fallback (default, requires `rails ai:serve`)
296
+ # :cli — CLI only (no MCP server needed, uses `rails 'ai:tool[NAME]'`)
297
+ #{tool_mode_line}
298
+
299
+ SECTION
300
+
301
+ # All remaining sections use defaults (commented out)
302
+ CONFIG_SECTIONS.each do |name, section_content|
303
+ next if name == "AI Tools" # already added with dynamic values
304
+ content += section_content + "\n"
305
+ end
306
+
307
+ content += "end\n"
308
+
309
+ create_file path, content
310
+ say "Created #{path} with all #{CONFIG_SECTIONS.size} config sections", :green
311
+ end
312
+
313
+ def update_existing_initializer(full_path)
314
+ existing = File.read(full_path)
315
+ changes = []
316
+
317
+ # 1. Update ai_tools selection if user picked new tools
318
+ existing, changed = update_config_line(existing, "config.ai_tools", build_ai_tools_line)
319
+ changes << "ai_tools" if changed
320
+
321
+ # 2. Update tool_mode if user picked a new mode
322
+ existing, changed = update_config_line(existing, "config.tool_mode", build_tool_mode_line)
323
+ changes << "tool_mode" if changed
324
+
325
+ # 3. Add any missing config sections
326
+ CONFIG_SECTIONS.each do |name, section_content|
327
+ marker = "── #{name}"
328
+ next if existing.include?(marker)
329
+
330
+ insert_point = existing.rindex(/^end\b/)
331
+ if insert_point
332
+ existing = existing.insert(insert_point, "\n#{section_content}\n")
333
+ changes << "section: #{name}"
201
334
  end
202
- RUBY
335
+ end
336
+
337
+ if changes.any?
338
+ File.write(full_path, existing)
339
+ say "Updated #{full_path.relative_path_from(Rails.root)}: #{changes.join(', ')}", :green
340
+ else
341
+ say "#{full_path.relative_path_from(Rails.root)} is up to date — no changes needed", :green
342
+ end
343
+ end
344
+
345
+ # Replace or uncomment a config line. Returns [new_content, changed?]
346
+ def update_config_line(content, key, new_line)
347
+ # Match both commented and uncommented versions of this config key
348
+ pattern = /^[ \t]*#?\s*#{Regexp.escape(key)}\s*=.*$/
349
+ if content.match?(pattern)
350
+ updated = content.sub(pattern, new_line)
351
+ [ updated, updated != content ]
352
+ else
353
+ # Key not found at all — don't add (it's in a section that will be added)
354
+ [ content, false ]
355
+ end
356
+ end
357
+
358
+ def build_ai_tools_line
359
+ # Always write uncommented so re-install can detect previous selection
360
+ " config.ai_tools = %i[#{@selected_formats.join(' ')}]"
361
+ end
362
+
363
+ def build_tool_mode_line
364
+ if @tool_mode == :cli
365
+ " config.tool_mode = :cli # CLI only (no MCP server needed)"
366
+ else
367
+ " config.tool_mode = :mcp # MCP primary + CLI fallback"
368
+ end
369
+ end
370
+
371
+ def read_previous_ai_tools
372
+ init_path = Rails.root.join("config/initializers/rails_ai_context.rb")
373
+ return nil unless File.exist?(init_path)
374
+
375
+ content = File.read(init_path)
376
+ match = content.match(/^\s*config\.ai_tools\s*=\s*%i\[([^\]]*)\]/)
377
+ return nil unless match
203
378
 
204
- say "Created config/initializers/rails_ai_context.rb", :green
379
+ match[1].split.map(&:to_sym)
380
+ rescue
381
+ nil
205
382
  end
383
+ end # no_tasks
206
384
 
207
385
  def add_to_gitignore
208
386
  gitignore = Rails.root.join(".gitignore")
@@ -258,9 +436,9 @@ module RailsAiContext
258
436
  say ""
259
437
  say "Commands:", :yellow
260
438
  say " rails ai:context # Regenerate context files"
261
- say " rails 'ai:tool[schema]' # Run any of the 25 tools from CLI"
439
+ say " rails 'ai:tool[schema]' # Run any of the 30 tools from CLI"
262
440
  if @tool_mode == :mcp
263
- say " rails ai:serve # Start MCP server (25 live tools)"
441
+ say " rails ai:serve # Start MCP server (30 live tools)"
264
442
  end
265
443
  say " rails ai:doctor # Check AI readiness"
266
444
  say " rails ai:inspect # Print introspection summary"
@@ -249,6 +249,7 @@ module RailsAiContext
249
249
  if matched
250
250
  kwargs[key] = matched
251
251
  else
252
+ $stderr.puts "Warning: '#{value}' is not a valid value for #{key}. Valid: #{prop[:enum].join(', ')}. Using default."
252
253
  kwargs.delete(key)
253
254
  end
254
255
  end