rails-ai-context 5.13.0 → 5.15.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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +113 -0
  3. data/README.md +49 -10
  4. data/docs/CLI.md +3 -1
  5. data/docs/COMPATIBILITY.md +219 -0
  6. data/docs/TOOLS.md +10 -2
  7. data/docs/index.md +1 -1
  8. data/exe/rails-ai-context +231 -134
  9. data/lib/generators/rails_ai_context/install/install_generator.rb +120 -30
  10. data/lib/rails_ai_context/app_kind.rb +22 -0
  11. data/lib/rails_ai_context/boot_manager.rb +68 -0
  12. data/lib/rails_ai_context/cli/tool_runner.rb +46 -11
  13. data/lib/rails_ai_context/confidence.rb +16 -4
  14. data/lib/rails_ai_context/configuration.rb +17 -2
  15. data/lib/rails_ai_context/doctor.rb +39 -24
  16. data/lib/rails_ai_context/engine.rb +5 -1
  17. data/lib/rails_ai_context/facts_formatter.rb +102 -0
  18. data/lib/rails_ai_context/install_mode.rb +39 -0
  19. data/lib/rails_ai_context/introspector.rb +39 -5
  20. data/lib/rails_ai_context/introspectors/controller_introspector.rb +100 -32
  21. data/lib/rails_ai_context/introspectors/convention_introspector.rb +9 -2
  22. data/lib/rails_ai_context/introspectors/database_stats_introspector.rb +6 -1
  23. data/lib/rails_ai_context/introspectors/gem_introspector.rb +2 -0
  24. data/lib/rails_ai_context/introspectors/listeners/mongoid_fields_listener.rb +27 -0
  25. data/lib/rails_ai_context/introspectors/listeners/routes_dsl_listener.rb +372 -0
  26. data/lib/rails_ai_context/introspectors/migration_introspector.rb +11 -20
  27. data/lib/rails_ai_context/introspectors/model_introspector.rb +133 -4
  28. data/lib/rails_ai_context/introspectors/route_introspector.rb +46 -1
  29. data/lib/rails_ai_context/introspectors/schema_introspector.rb +161 -27
  30. data/lib/rails_ai_context/introspectors/view_template_introspector.rb +14 -0
  31. data/lib/rails_ai_context/mcp_config_generator.rb +18 -6
  32. data/lib/rails_ai_context/middleware.rb +1 -1
  33. data/lib/rails_ai_context/migration_status.rb +53 -0
  34. data/lib/rails_ai_context/output_guard.rb +48 -0
  35. data/lib/rails_ai_context/path_resolver.rb +29 -0
  36. data/lib/rails_ai_context/presets.rb +46 -0
  37. data/lib/rails_ai_context/resources.rb +5 -2
  38. data/lib/rails_ai_context/schema_version.rb +53 -0
  39. data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +16 -1
  40. data/lib/rails_ai_context/serializers/stack_overview_helper.rb +1 -1
  41. data/lib/rails_ai_context/serializers/tool_guide_helper.rb +80 -14
  42. data/lib/rails_ai_context/server.rb +14 -0
  43. data/lib/rails_ai_context/static_app.rb +17 -0
  44. data/lib/rails_ai_context/tasks/rails_ai_context.rake +55 -103
  45. data/lib/rails_ai_context/tools/analyze_feature.rb +43 -23
  46. data/lib/rails_ai_context/tools/base_tool.rb +133 -5
  47. data/lib/rails_ai_context/tools/dependency_graph.rb +3 -0
  48. data/lib/rails_ai_context/tools/diagnose.rb +80 -1
  49. data/lib/rails_ai_context/tools/generate_test.rb +532 -128
  50. data/lib/rails_ai_context/tools/get_api.rb +203 -0
  51. data/lib/rails_ai_context/tools/get_callbacks.rb +4 -2
  52. data/lib/rails_ai_context/tools/get_component_catalog.rb +9 -0
  53. data/lib/rails_ai_context/tools/get_config.rb +44 -15
  54. data/lib/rails_ai_context/tools/get_context.rb +44 -20
  55. data/lib/rails_ai_context/tools/get_controllers.rb +5 -3
  56. data/lib/rails_ai_context/tools/get_conventions.rb +89 -32
  57. data/lib/rails_ai_context/tools/get_edit_context.rb +4 -4
  58. data/lib/rails_ai_context/tools/get_env.rb +2 -2
  59. data/lib/rails_ai_context/tools/get_frontend_stack.rb +59 -16
  60. data/lib/rails_ai_context/tools/get_gems.rb +22 -2
  61. data/lib/rails_ai_context/tools/get_job_pattern.rb +41 -6
  62. data/lib/rails_ai_context/tools/get_model_details.rb +27 -6
  63. data/lib/rails_ai_context/tools/get_partial_interface.rb +17 -3
  64. data/lib/rails_ai_context/tools/get_routes.rb +15 -17
  65. data/lib/rails_ai_context/tools/get_schema.rb +25 -4
  66. data/lib/rails_ai_context/tools/get_service_pattern.rb +1 -1
  67. data/lib/rails_ai_context/tools/get_stimulus.rb +11 -4
  68. data/lib/rails_ai_context/tools/get_test_info.rb +16 -14
  69. data/lib/rails_ai_context/tools/get_turbo_map.rb +34 -1
  70. data/lib/rails_ai_context/tools/get_view.rb +41 -8
  71. data/lib/rails_ai_context/tools/migration_advisor.rb +46 -11
  72. data/lib/rails_ai_context/tools/onboard.rb +70 -32
  73. data/lib/rails_ai_context/tools/performance_check.rb +13 -1
  74. data/lib/rails_ai_context/tools/query.rb +41 -11
  75. data/lib/rails_ai_context/tools/read_logs.rb +4 -4
  76. data/lib/rails_ai_context/tools/review_changes.rb +15 -13
  77. data/lib/rails_ai_context/tools/runtime_info.rb +21 -14
  78. data/lib/rails_ai_context/tools/safe_call.rb +33 -0
  79. data/lib/rails_ai_context/tools/search_code.rb +36 -17
  80. data/lib/rails_ai_context/tools/search_docs.rb +2 -2
  81. data/lib/rails_ai_context/tools/security_scan.rb +2 -2
  82. data/lib/rails_ai_context/tools/validate.rb +17 -12
  83. data/lib/rails_ai_context/version.rb +1 -1
  84. data/lib/rails_ai_context/vfs.rb +28 -15
  85. data/lib/rails_ai_context.rb +55 -3
  86. metadata +30 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3747460b78ef9b52d5e2d4bbe59e348599499285fb0215a97ce029ad49b27aa0
4
- data.tar.gz: 2f0fff5d8986ca5ead4d74933dac2c7933d2ca7bd7dd1cab11fc3c4c5c756106
3
+ metadata.gz: 0250405dffbfbb2100bf513cc75840ae980f68970e1a76911c4b2cc5ba3fac77
4
+ data.tar.gz: 25606a4264641a0a1be621bb30f3476c9ce1ecc1fd8dc37f7719ab15acee5e14
5
5
  SHA512:
6
- metadata.gz: b343b7456a8f26c0738ebf3ca19b50758f54e859d82b3c40dba97765bde151f429f7da7b6d7a9abba2ac35438faaa858686c306e4f99482e2fc5a0b8692a7055
7
- data.tar.gz: b18a15e6fcb931bc9ae34c340c8c3c2481092273e7c0d1ca3d63da3b40282f2a18988656a96588c2e80ea271ab2f4ebc224124b7872328663eb5bc63da93ea0f
6
+ metadata.gz: 88b6c43ddb2d47e8c7d428e0188fc9060584252d7457bcd5d5b580cba6029a73f383684582d04228c79aee1a5c014f5d65494439697e718d38844900a3b5890a
7
+ data.tar.gz: 00e2c762d693c804f0260b5e0d054ffb0443afd8271299a8711638f23b7a6aeaeaee68ea833ed788225086fc38ddb289b5de562fb3e5379d78e8a84cd6c75f3b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,119 @@ 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
+ ## [5.15.0] - 2026-07-11
9
+
10
+ ### Added
11
+
12
+ - Static tier: `serve` and `tool` fall back to source-file analysis when the
13
+ app cannot boot, instead of exiting. Routes (new `config/routes.rb` Prism
14
+ walker), models, controllers, schema, and migrations answer statically;
15
+ other sections report `[UNAVAILABLE]` with the reason; every response
16
+ carries a tier banner.
17
+ - CLI flags: `--no-boot` (serve/tool), `--app-path` (all app-reading
18
+ commands), and `--environment` (all commands).
19
+ - `[STATIC]` and `[UNAVAILABLE: <reason>]` confidence tags.
20
+ - structure.sql parsing is dialect-aware: MySQL (backticks, inline
21
+ KEY/CONSTRAINT definitions, ENGINE trailers) and SQLite (quoted
22
+ identifiers, IF NOT EXISTS) now parse; output gains a `dialect` key.
23
+ - Shape-aware discovery: models and controllers are found in packwerk
24
+ `packs/`, in-repo `engines/`, and configured `extra_app_paths`.
25
+ - Multi-database schema dumps (`db/*_schema.rb`, `db/*_structure.sql`)
26
+ reported under `secondary_databases`.
27
+ - Mongoid detection: honest `[UNAVAILABLE]` schema signal and basic static
28
+ model extraction (fields, embeds, store_in) for Mongoid apps.
29
+ - API-only apps get explicit "not applicable" answers from view/frontend
30
+ tools instead of empty listings.
31
+ - **Loud Rails 9 warning in the standalone CLI.** In-Gemfile installs already
32
+ fail Bundler resolution against Rails 9 (`railties < 9.0`); the standalone
33
+ CLI has no such check, so it used to boot silently against an untested
34
+ Rails version. It now prints a stderr warning naming the installed Rails
35
+ version and pointing at checking for a newer gem release.
36
+ - **`docs/COMPATIBILITY.md`** publishes the supported-version matrix, the CI
37
+ version grid, the RUNTIME/STATIC tier contract with the confidence-tag
38
+ vocabulary, and a shape-by-introspector matrix that cites the spec or QA
39
+ run proving every cell.
40
+
41
+ ### Changed
42
+
43
+ - Boot failure in `tool`/`serve` no longer exits 1; it degrades to the
44
+ static tier with a stderr notice. `doctor` keeps requiring a bootable app.
45
+ - CI runs Rails 8.x on Ruby 3.2 again. The exclusion assumed Rails 8
46
+ required Ruby 3.3+; both railties 8.0.5 and 8.1.3 declare
47
+ `required_ruby_version >= 3.2.0`.
48
+
49
+ ### Fixed
50
+
51
+ - `config.auto_mount = true` set in a user initializer now takes effect
52
+ (the middleware initializer ran before user initializers loaded).
53
+ - `prism` and `concurrent-ruby` are now capped below their next majors
54
+ (`< 2.0`, `< 3.0`) instead of an open-ended floor.
55
+ - A syntax error in one model file now costs exactly that model - the rest
56
+ of the models section, and every other section, still answers normally
57
+ (verified end-to-end in the static tier).
58
+
59
+ ## [5.14.0] - 2026-07-11
60
+
61
+ ### Added
62
+
63
+ - **New `rails_get_api` tool (CLI: `api`)** exposing the API-layer introspection that was previously invisible on demand: API-only mode, serialization strategy (Jbuilder templates, serializer classes), GraphQL, versioning, rate limiting, OpenAPI specs, CORS, and pagination. On API-only apps the API layer was the least-served layer while six view tools returned nothing; this closes that gap. Tool count is now 39.
64
+ - **`doctor --strict`** exits 1 when any check fails, giving CI a real gate. Default behavior is unchanged: doctor is a readiness report and exits 0.
65
+ - **`rails_generate_test` scaffolds tests that pass out of the box.** Generated controller tests use the app's real routes, fixtures, and strong params; destroy tests create a fresh record instead of deleting a fixture row other fixtures hold foreign keys on; create/update params mutate values for columns backed by a unique index (a fixture's own value would collide). Verified by running the generated tests for real on scaffold HTML and API-only JSON controllers.
66
+
67
+ ### Added - Boot resilience
68
+
69
+ - **Friendly boot-failure diagnostics.** Every CLI command (`tool`, `doctor`, `serve`, `context`, etc.) now boots the host app through `BootManager`, which catches `StandardError` and `ScriptError` alike. When the app can't boot -- a missing ENV var, an unreachable database or Redis, a syntax error in an initializer -- the CLI prints a one-line cause plus common-cause hints and exits 1, instead of a raw Thor backtrace. Boot is bounded by a 60-second timeout so a hanging initializer fails loudly rather than wedging the process; the timeout is configurable via `RAILS_AI_CONTEXT_BOOT_TIMEOUT`. A timed-out boot gets a dedicated message naming the configured limit and how to raise it, instead of the stdlib's `Timeout::Error: execution expired`. Set `DEBUG=1` for the full backtrace. `doctor` on an app that fails to boot appends one line noting it needs a bootable app to run its checks.
70
+ - **stdout quarantine on every stdio launch path.** The stdio MCP transport carries JSON-RPC on stdout, so anything the host app prints while booting (initializer `puts`, deprecation warnings) used to corrupt the handshake and leave the client reporting a dead server. `OutputGuard` now redirects `$stdout` to `$stderr` for the duration of boot on every stdio launch path -- the `rails-ai-context serve` binary, the `rails ai:serve` rake task, and standalone mode. It also reopens file descriptor 1 onto `$stderr`'s target for that duration, so writes through the `STDOUT` constant (which bypass the `$stdout` global) and subprocess output inherited on fd 1 are caught too; only a descriptor duplicated before boot starts stays out of reach.
71
+ - **Unexpected tool errors return in-band MCP error results instead of protocol errors.** Every registered tool's `call` is now wrapped so an unhandled exception produces an `isError: true` tool response (error class, origin line, and a recovery hint) rather than escaping to the MCP SDK, which turned it into a JSON-RPC -32603 protocol error that most AI clients silently swallow. Exceptions that still escape at the SDK level get a stderr backtrace via a new `exception_reporter`.
72
+ - **Aggregate tools flag partial context.** `rails_get_context` and `rails_onboard` append a "Partial context" note listing which introspectors failed when any did, so an AI client can tell missing data from empty data. The note is appended after the truncation decision, so it survives response truncation instead of being cut off with the rest of the body.
73
+ - **Generated MCP configs launch the quarantined CLI command.** All 5 generated config formats (Claude Code, Cursor, VS Code, OpenCode, and Codex CLI) now point at `bundle exec rails-ai-context serve` instead of `bundle exec rails ai:serve`, so new installs get stdout quarantine from before `Bundler.require` runs. `rails ai:serve` still works and now quarantines its own boot output too.
74
+
75
+ ### Fixed
76
+
77
+ - Introspector fault isolation no longer crashes when `Rails.logger` is nil (early-boot rake tasks, engine dummy apps). The failure-logging call that the rescue exists to protect now falls back to `$stderr` via `RailsAiContext.log_warn` instead of raising `NoMethodError` on a nil logger.
78
+ - `BaseTool.abstract!` now works when called on a subclass. It read `registry_mutex` and `descendants` off `self`, but those are ivars set on `BaseTool` itself -- a subclass has no ivar storage of its own to read them from, so the call raised `NoMethodError` on the nil mutex. It now reaches back to `BaseTool` explicitly.
79
+ - Custom tools that subclass `RailsAiContext::Tools::BaseTool` must accept a `server_context:` keyword (or `**kwargs`) in `self.call` -- the MCP dispatch now always passes it.
80
+ - **The install generator no longer crashes on non-interactive stdin.** Thor's `ask` returns `nil` on EOF (piping fewer answers than prompts, or `< /dev/null`), and the very next `.strip` call raised `NoMethodError`. Every prompt now goes through `ask_safe`, which treats EOF the same as an empty answer -- each prompt's existing default-handling logic takes it from there. A new `--defaults` flag skips every prompt outright for CI/scripted installs.
81
+ - **`rails-ai-context --version` and `-v` work**, not just the `version` subcommand -- Thor only recognized the bare subcommand by default.
82
+ - **Presets share one definition.** The rake task (`rails 'ai:preset[name]'`) and the CLI (`rails-ai-context preset name`) used to define their own, different tool lists under the same preset name. Both now read from `RailsAiContext::Presets::DEFINITIONS`, the single source of truth. The `migration` preset's old tool list called `migration_advisor` with an invalid `action` and `validate` with no files -- both require a caller-supplied target and only emitted errors on a healthy app; it now runs `get_schema`, `runtime_info`, and `performance_check` with zero arguments, matching what `architecture` and `debugging` already did correctly.
83
+ - **`rails_performance_check` no longer claims "Your app looks good!" when a category or model filter is hiding issues elsewhere.** A zero count under a filter now says so explicitly and points back at the unfiltered call, instead of implying the whole app is clean.
84
+ - **`rails_get_view`'s render map now detects the Rails 7+ bare local-variable render form** (`render article`, as the default scaffold generates), not just `render "partial"` and `render @article`. The scan excludes keyword-argument hashes (`render json:`, `render partial:`, ...) so it doesn't misread those as local-variable renders.
85
+ - **`rails_get_view` accepts a full repo-relative path** (e.g. `app/views/layouts/mailer.html.erb`) in addition to the documented app/views-relative form -- previously a genuinely-existing file read as "not found" solely because of which convention the caller used.
86
+ - **`rails_analyze_feature` no longer substring-matches unrelated identifiers.** `feature:"post"` used to pull in ActionMailbox's `postmark` ingress route because `"postmark".include?("post")` is true. Matching is now word-boundary aware (with basic singular/plural handling), so `post` still matches `posts`, `post_comments`, and `PostsController`, but not `postmark`.
87
+ - Empty-result "not found" responses no longer end with a dangling `Available:` line when there's nothing to list (`rails_get_concern`, and any other tool built on the shared `not_found_response` helper).
88
+ - `rails_get_partial_interface`'s "not found" suggestion list no longer lists the same partial twice when it exists under two extensions (e.g. `_article.html.erb` and `_article.json.jbuilder` both display as `articles/article`).
89
+ - A non-numeric `RAILS_AI_CONTEXT_BOOT_TIMEOUT` value now falls back to the default with a stderr warning instead of raising a raw `ArgumentError` that surfaced as a Thor backtrace on `doctor`, `inspect_app`, and `watch`.
90
+ - **`rails_get_conventions`'s create-action and read-only-page skeletons now reflect what's actually detected**, instead of a hardcoded `current_user`-based template labeled with real controller names regardless of whether any auth pattern existed. They're built from the union of flow parts actually matched, and only reference `current_user` when a permission or auth check was really found.
91
+ - **`rails_analyze_feature`'s test counts are accurate again.** `discover_tests` counted the bare words `it`/`test`/`should` anywhere in a file's text, including comments and prose. It now counts line-anchored `test`/`it`/`specify` declarations and `def test_*` methods, matching how `rails_get_test_info` already counted correctly.
92
+ - **CLI and rake tool failures exit non-zero.** `rails-ai-context tool NAME` and `rails 'ai:tool[NAME]'` used to exit 0 even when the tool itself reported failure (`isError: true`); both now exit 1 in that case, and `--json` output gains an `error` key so scripts can check it without parsing text. An unrecognized `rails-ai-context` subcommand also exits 1 instead of Thor's default 0. The "unknown tool" error message points at whichever `--list` invocation actually works for the caller (CLI vs rake) instead of always suggesting the CLI-only flag, and a blank tool-name query no longer produces an arbitrary "Did you mean" suggestion.
93
+ - Routine MCP protocol errors (unknown tool, invalid params) now log as a single quiet line instead of a 10-line backtrace; only genuinely unhandled exceptions still get the full backtrace.
94
+ - **Install generator output is honest about install mode and idempotency.** Generating context files for multiple selected AI-tool formats no longer reports the shared `AGENTS.md` trio as both written and unchanged in the same run. `.rails-ai-context.yml` writes report Created/Updated/unchanged consistently across the generator, standalone `init`, and the `ai:context` rake task. `.codex/config.toml` (which embeds a machine's Ruby PATH/GEM_HOME) is now flagged as local and added to `.gitignore` instead of told to commit it. Generated context files reference the `rails-ai-context` CLI binary instead of rake tasks on standalone installs, detected via a Gemfile.lock scan for the gem's own spec line.
95
+ - Eight tool-output and doctor accuracy fixes from live QA: `rails_get_gems` points the Solid Queue config hint at `config/queue.yml`/`config/recurring.yml` (checked at runtime) instead of `config/solid_queue.yml`, which Rails 8's generator never writes; `doctor`'s gitignore check distinguishes a missing `.gitignore` file from a missing entry; `rails_get_model_details` no longer renders a bare "Key instance methods" heading when every candidate method was filtered out as an association accessor; `rails_onboard`'s route total shares dedup logic and prefix config with `rails_get_routes` instead of disagreeing with it; CLI `tool --list` truncates descriptions with an ellipsis instead of mid-word; `tool --list` falls back to loading the gem standalone when there's no app to boot; and `doctor`'s introspector-health message computes the tool count it compares against dynamically instead of hardcoding it.
96
+ - **Tools stay truthful on API-only apps.** `rails_get_frontend_stack` reports "No frontend stack detected" instead of a lone "TypeScript: disabled" line implying a JS frontend exists. `rails_get_turbo_map` reports that Turbo isn't installed when `turbo-rails` isn't in `Gemfile.lock`, instead of an empty broadcast/subscription report. `rails_get_context`'s instance-variable cross-check recognizes ivars rendered via `render json: @foo`/`render xml: @foo` (which have no view template to cross-reference), and swaps "used in view" language for "used in response" on API-only apps; the generated CLAUDE.md/AGENTS.md workflow guide swaps its view-editing walkthrough for a JSON-endpoint walkthrough on the same apps. `app/models/concerns` and `app/controllers/concerns` are only reported as in-use patterns when they contain a real Ruby file, not just the `.keep` placeholder a fresh `rails new` leaves behind. `rails_get_conventions`'s create-action skeleton now emits `render json:` (and `render json: @record.errors`) instead of an HTML redirect when the app's create actions render JSON directly, not just via a scaffold's `respond_to` JSON branch.
97
+ - **MySQL/Trilogy adapter parity.** Rails 8's default MySQL adapter reports `adapter_name` as "Trilogy", not "Mysql2", so every `/mysql/`-only regex dispatch silently fell through to unsafe or degraded fallbacks -- `rails_query`'s read-only transaction and statement timeout, `rails_runtime_info`'s table sizes, and the database-stats introspector now all match `/mysql|trilogy/i`. `rails_query`'s `SHOW`/`DESCRIBE`/`EXPLAIN` are exempted from row-limit appending (invalid syntax after those statements) and from column-name-based redaction (MySQL's `DESCRIBE` returns a literal "Key" column that isn't secret data); `EXPLAIN` on MySQL forces `FORMAT=TRADITIONAL` so parsing doesn't depend on the server's default explain format.
98
+ - **Pending-migrations detection works again.** All three call sites (`rails_runtime_info`, `doctor`, the migration introspector) guarded on `respond_to?(:pending_migrations)`, which is never true on any currently supported Rails version, and fell back to an `ActiveRecord::Migrator.new` signature that raises on Rails 7.0+ -- it always reported zero pending migrations. A shared `MigrationStatus` helper now walks `migration_context.open.pending_migrations`, the same path Rails uses for its own "migrations are pending" error page; the file-based fallback also reads the schema version from `db/structure.sql`'s trailing `schema_migrations` insert, so `:sql`-format apps get correct results instead of always reporting zero.
99
+ - `migration_advisor`'s locking advice is now adapter-aware instead of always suggesting Postgres-only `algorithm: :concurrently` and two-step foreign key validation on every adapter.
100
+ - The generated tool-usage guide names `db/structure.sql` instead of `db/schema.rb` in its "don't read this file" guidance on apps configured with `schema_format = :sql`.
101
+ - Fixed the remaining "N indexes"/"N files" pluralization misses: `rails_get_schema`'s summary view now says "1 column, 1 index" and `rails_get_conventions`'s directory structure says "1 file", matching the singular/plural handling already applied to table, issue, and test counts.
102
+ - The CLI `preset` command's banner now uses a plain hyphen instead of an em dash, matching the rake task's output, and its per-tool "Running:" headers print to stdout (the same stream as the tool's own output) instead of stderr, so `2>&1` captures stay in order.
103
+ - The install generator no longer emits raw terminal escape sequences (cursor hide/show, line clears) when stdin or stdout isn't a real terminal (`rails generate rails_ai_context:install < /dev/null`, captured CI logs). Thor's line editor now falls back to its plain, escape-free reader outside a genuine interactive TTY session.
104
+ - **The generated initializer no longer crashes `bin/rails test` on path:/git: Gemfile installs scoped to `group :development`.** Bundler evaluates a path:/git: gemspec in-process during dependency resolution in every environment, so `require_relative "lib/rails_ai_context/version"` defines a VERSION-only stub `RailsAiContext` module even in environments where `Bundler.require` correctly skips the gem. The initializer's `if defined?(RailsAiContext)` guard was satisfied by that stub and called `.configure` on it anyway, raising `NoMethodError`. The guard now also checks `RailsAiContext.respond_to?(:configure)`; re-running the install generator upgrades an existing initializer's bare guard in place, and `doctor` warns when one is still on the old guard.
105
+ - **`DEBUGGER__::TrapInterceptor` no longer shows up as a model concern.** The `debug` gem, default in Rails 7.0+ Gemfiles, prepends this module onto `Kernel`, so it appeared in every model's ancestors. It's now excluded the same way other framework-internal modules are.
106
+ - **The `rails-ai-context://routes/{controller}` MCP resource returns real routes.** It filtered a flat `:routes` list that the route introspector never emits (routes are grouped under `:by_controller`), so every read returned an empty array. It now reads the real shape, merges the controller name back into each entry, and dedupes PUT/PATCH update pairs into one `PATCH|PUT` entry so the resource reports the same counts as the routes tool.
107
+ - MCP resource contents use the spec field name `mimeType` instead of snake_case `mime_type`.
108
+ - **Rails-internal routes are excluded correctly.** The route introspector guarded on `route.respond_to?(:internal?)`, but `ActionDispatch::Journey::Route` exposes the flag as a plain `internal` attribute, so the guard never matched and Rails' info/mailers/welcome routes leaked into route listings as "framework routes". The tool and the MCP resource now agree on route counts, and the routes header notes how many framework routes were excluded.
109
+ - **`.mcp.json` content is stable across entry points.** The standalone `init` wrote `rails-ai-context serve` while the in-Gemfile generator and `ai:context` rake task wrote `bundle exec rails-ai-context serve`, so alternating commands rewrote the file on every run. All writers now derive the command from the detected install mode.
110
+ - **`rails_validate` failures exit non-zero.** A failed validation ("0/1 files passed", real Prism diagnostics) is now an MCP error result, so the CLI exits 1 and CI can gate on it; passing validations still exit 0. `rails_query`'s genuine SQL execution errors (unknown column, syntax error) get the same treatment, while policy guardrail messages ("Blocked: contains UPDATE") remain informational at exit 0.
111
+ - `rails-ai-context preset bogus` exits 1 with the available-presets list instead of exiting 0.
112
+ - **`rails_get_conventions` renders the status symbol actually detected in the app's controllers** (`:unprocessable_content` on Rails 7.1+ scaffolds) instead of hardcoding `:unprocessable_entity` in a skeleton labeled "Detected in".
113
+ - `rails_get_frontend_stack` reads the introspector's real output keys, so the Framework and Testing lines (React/Vue/Jest and friends) render again; they had been silently dropped for every app.
114
+ - `rails_search_code` marks match lines with a `>` prefix and indents context lines, so matches are distinguishable in captured output; a legend line explains the markers.
115
+ - `rails_diagnose` classifies an undefined method on a model by checking the model's real associations and columns, reporting "no association or column named X" instead of the generic nil-reference guidance.
116
+ - `rails_review_changes` no longer leaks git's raw "fatal: not a git repository" stderr line above its own friendly message when run outside a repository.
117
+ - The config tool lists every file in `config/initializers/`, not just the gem's own initializer.
118
+ - CLI copy is install-mode aware everywhere: `tool --list`, per-tool help, unknown-tool errors, and the generated git pre-commit hook show only invocation forms that exist in the current install (standalone installs have no rake tasks). The `tree` command shows the binary name as its root and `inspect` under its public name.
119
+ - Assorted output honesty from the final QA sweep: onboard only recommends `bin/dev` when the app has one, names the real queue adapter instead of mentioning Sidekiq on non-Sidekiq apps, labels implicit `belongs_to` validations as such, and pluralizes counts correctly; the view tool omits its "Directories with views:" enumeration when there are none; the standalone `facts` command matches the rake task's singular/plural forms and prints `_none_` instead of a bare Associations header; `rails_get_partial_interface` no longer lists ERB block parameters (`|form|`, `|error|`) as partial locals.
120
+
8
121
  ## [5.13.0] - 2026-07-10
9
122
 
10
123
  ### Added
data/README.md CHANGED
@@ -20,8 +20,8 @@
20
20
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-green)](https://registry.modelcontextprotocol.io)
21
21
  <br>
22
22
  [![Ruby](https://img.shields.io/badge/Ruby-3.1%20%7C%203.2%20%7C%203.3%20%7C%203.4-CC342D)](https://github.com/crisnahine/rails-ai-context)
23
- [![Rails](https://img.shields.io/badge/Rails-7.0%20%7C%207.1%20%7C%207.2%20%7C%208.0-CC0000)](https://github.com/crisnahine/rails-ai-context)
24
- [![Tests](https://img.shields.io/badge/Tests-2257%20passing-brightgreen)](https://github.com/crisnahine/rails-ai-context/actions)
23
+ [![Rails](https://img.shields.io/badge/Rails-7.0%20%7C%207.1%20%7C%207.2%20%7C%208.0%20%7C%208.1-CC0000)](https://github.com/crisnahine/rails-ai-context)
24
+ [![Tests](https://img.shields.io/badge/Tests-2501%20passing-brightgreen)](https://github.com/crisnahine/rails-ai-context/actions)
25
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
26
26
 
27
27
  </div>
@@ -68,7 +68,7 @@ rails-ai-context serve # start MCP server
68
68
 
69
69
  </div>
70
70
 
71
- Now your AI doesn't guess - it **asks your app directly.** 38 tools and 5 resource templates that query your schema, models, routes, controllers, views, and conventions on demand. Model introspection uses Prism AST parsing - every result carries a `[VERIFIED]` or `[INFERRED]` confidence tag so AI knows what's ground truth and what needs runtime checking.
71
+ Now your AI doesn't guess - it **asks your app directly.** 39 tools and 5 resource templates that query your schema, models, routes, controllers, views, and conventions on demand. Model introspection uses Prism AST parsing - every result carries a `[VERIFIED]` or `[INFERRED]` confidence tag so AI knows what's ground truth and what needs runtime checking.
72
72
 
73
73
  <br>
74
74
 
@@ -163,7 +163,7 @@ Native Rails controller transport. No separate process needed.
163
163
 
164
164
  ### CLI
165
165
 
166
- Same 38 tools, no server needed. Works in any terminal, any AI tool.
166
+ Same 39 tools, no server needed. Works in any terminal, any AI tool.
167
167
 
168
168
  ```bash
169
169
  rails 'ai:tool[search_code]' pattern="publishable?" match_type=trace
@@ -351,7 +351,7 @@ Every tool is **read-only** and returns data verified against your actual app -
351
351
 
352
352
  </details>
353
353
 
354
- > **[All 38 tools with parameters →](docs/TOOLS.md)** &nbsp;|&nbsp; **[Real-world recipes →](docs/RECIPES.md)**
354
+ > **[All 39 tools with parameters →](docs/TOOLS.md)** &nbsp;|&nbsp; **[Real-world recipes →](docs/RECIPES.md)**
355
355
 
356
356
  <br>
357
357
 
@@ -401,8 +401,8 @@ graph TD
401
401
 
402
402
  B["rails-ai-context\nPrism AST parsing · Cached · Confidence-tagged\nVFS: rails-ai-context:// URIs introspected fresh"]
403
403
 
404
- B --> C["MCP Server\nstdio / HTTP\n38 tools · 5 templates"]
405
- B --> D["CLI Tools\nRake / Thor\nSame 38 tools"]
404
+ B --> C["MCP Server\nstdio / HTTP\n39 tools · 5 templates"]
405
+ B --> D["CLI Tools\nRake / Thor\nSame 39 tools"]
406
406
  B --> E["Static Files\nCLAUDE.md · .cursor/rules/ · .cursorrules\n.github/instructions/"]
407
407
 
408
408
  style A fill:#4a9eff,stroke:#2d7ad4,color:#fff
@@ -440,7 +440,7 @@ Both paths ask which AI tools you use (Claude Code, Cursor, GitHub Copilot, Open
440
440
  | In-Gemfile | Standalone | What it does |
441
441
  |:-----------|:-----------|:------------|
442
442
  | `rails ai:context` | `rails-ai-context context` | Generate context files |
443
- | `rails 'ai:tool[NAME]'` | `rails-ai-context tool NAME` | Run any of the 38 tools |
443
+ | `rails 'ai:tool[NAME]'` | `rails-ai-context tool NAME` | Run any of the 39 tools |
444
444
  | `rails ai:tool` | `rails-ai-context tool --list` | List all available tools |
445
445
  | `rails ai:serve` | `rails-ai-context serve` | Start MCP server (stdio) |
446
446
  | `rails ai:doctor` | `rails-ai-context doctor` | Diagnostics + AI readiness score |
@@ -448,12 +448,50 @@ Both paths ask which AI tools you use (Claude Code, Cursor, GitHub Copilot, Open
448
448
 
449
449
  <br>
450
450
 
451
+ ## Static tier: works even when the app can't boot
452
+
453
+ `rails-ai-context` attempts a full boot for live reflection. When boot fails
454
+ (missing ENV vars, unreachable services, a broken initializer) the `serve`
455
+ and `tool` commands fall back to the **static tier** instead of dying:
456
+ routes come from parsing `config/routes.rb`, schema from `db/schema.rb` /
457
+ `db/structure.sql` / migrations, models and controllers from their source
458
+ files. Every response carries a banner explaining the degradation, static
459
+ data is tagged `[STATIC]`, and sections that genuinely need a booted app
460
+ report `[UNAVAILABLE]` with the reason.
461
+
462
+ Flags:
463
+
464
+ - `--no-boot` (serve, tool) - skip the boot attempt entirely and serve
465
+ static analysis. Fast, and immune to boot-time side effects.
466
+ - `--app-path PATH` (all app-reading commands) - run against a Rails app in
467
+ another directory.
468
+ - `--environment ENV` (all commands) - set RAILS_ENV for the boot attempt.
469
+
470
+ `rails-ai-context doctor` still requires a bootable app: its job is
471
+ diagnosing why boot fails.
472
+
473
+ <br>
474
+
475
+ ## App shapes
476
+
477
+ Beyond the conventional layout, code is discovered in packwerk packs
478
+ (`packs/*/app/*`), in-repo engines (`engines/*/app/*`), and any directories
479
+ listed in `extra_app_paths` in `.rails-ai-context.yml`. Rails multi-database
480
+ schema dumps (`db/queue_schema.rb` and friends) are reported under a
481
+ `Secondary databases` section. Mongoid apps get an honest
482
+ `[UNAVAILABLE]` schema signal plus basic static model data (fields, embedded
483
+ relations) instead of misleading empty output, and API-only apps get
484
+ "not applicable" answers from view and frontend tools instead of silent
485
+ empty listings.
486
+
487
+ <br>
488
+
451
489
  ## Documentation
452
490
 
453
491
  | | |
454
492
  |:------|:------------|
455
493
  | **[Quickstart](docs/QUICKSTART.md)** | 5-minute getting started |
456
- | **[Tools Reference](docs/TOOLS.md)** | All 38 tools with every parameter |
494
+ | **[Tools Reference](docs/TOOLS.md)** | All 39 tools with every parameter |
457
495
  | **[Recipes](docs/RECIPES.md)** | Real-world workflows and examples |
458
496
  | **[Custom Tools](docs/CUSTOM_TOOLS.md)** | Build and test your own MCP tools |
459
497
  | **[Configuration](docs/CONFIGURATION.md)** | 40+ config options with defaults |
@@ -465,6 +503,7 @@ Both paths ask which AI tools you use (Claude Code, Cursor, GitHub Copilot, Open
465
503
  | **[Standalone](docs/STANDALONE.md)** | Use without Gemfile entry |
466
504
  | **[Troubleshooting](docs/TROUBLESHOOTING.md)** | Common issues and fixes |
467
505
  | **[FAQ](docs/FAQ.md)** | Frequently asked questions |
506
+ | **[Compatibility](docs/COMPATIBILITY.md)** | Supported versions, operating tiers, and the shape matrix with proof sources |
468
507
 
469
508
  <br>
470
509
 
@@ -543,7 +582,7 @@ end
543
582
  ## About
544
583
 
545
584
  Built by a Rails developer with 10+ years of production experience.<br>
546
- 2154 tests + 100-example e2e harness. 38 tools. 5 resource templates. 39 introspectors. Standalone or in-Gemfile.<br>
585
+ 2154 tests + 100-example e2e harness. 39 tools. 5 resource templates. 39 introspectors. Standalone or in-Gemfile.<br>
547
586
  MIT licensed. [Contributions welcome.](CONTRIBUTING.md)
548
587
 
549
588
  <br>
data/docs/CLI.md CHANGED
@@ -17,7 +17,7 @@
17
17
  | In-Gemfile (Rake) | `rails ai:` | `rails 'ai:tool[schema]' table=users` |
18
18
  | Standalone (Thor) | `rails-ai-context` | `rails-ai-context tool schema --table users` |
19
19
 
20
- Both provide the same 38 tools and functionality.
20
+ Both provide the same 39 tools and functionality.
21
21
 
22
22
  ---
23
23
 
@@ -98,6 +98,8 @@ Run 23 diagnostic checks and report AI readiness score.
98
98
  ```bash
99
99
  rails ai:doctor
100
100
  rails-ai-context doctor
101
+ rails-ai-context doctor --strict # exit 1 when any check fails (CI gate)
102
+ rails ai:doctor STRICT=1 # rake equivalent
101
103
  ```
102
104
 
103
105
  Checks include: schema existence, pending migrations, model files, routes, MCP config validity, introspector health, ripgrep availability, Prism gem, Brakeman gem, listen gem, gitignore security, auto_mount security, schema size, view count, and more.
@@ -0,0 +1,219 @@
1
+ <div align="center" markdown="1">
2
+
3
+ # Compatibility
4
+
5
+ **What's actually supported, what's actually proven, and where each claim comes from.**
6
+
7
+ [Architecture](ARCHITECTURE.md) · [Introspectors](INTROSPECTORS.md) · [Troubleshooting](TROUBLESHOOTING.md) · [FAQ](FAQ.md)
8
+
9
+ </div>
10
+
11
+ ---
12
+
13
+ Every claim below cites the spec file or release that proves it. No entry in this
14
+ document is aspirational.
15
+
16
+ ## Supported versions
17
+
18
+ - **Ruby:** 3.1 - 4.0 (gemspec: `required_ruby_version >= 3.1.0`, no upper cap)
19
+ - **Rails (railties):** 7.0 - 8.1 (gemspec: `railties >= 7.0, < 9.0`)
20
+ - **mcp gem:** `>= 0.8, < 2.0`
21
+ - **thor:** `>= 1.0, < 3.0`
22
+ - **prism:** `>= 0.28, < 2.0`
23
+ - **concurrent-ruby:** `>= 1.2, < 3.0`
24
+
25
+ The gemspec's `railties` bound is wider than the CI matrix: point releases inside
26
+ 7.0-8.1 and any future 8.x minor satisfy Bundler's constraint without a gem
27
+ release, but only the combinations in the table below are actually exercised by
28
+ CI.
29
+
30
+ ### CI matrix
31
+
32
+ Source: `.github/workflows/ci.yml`. 17 of 25 possible Ruby x Rails combinations
33
+ run (`fail-fast: false`, so one failing cell doesn't hide the rest):
34
+
35
+ | Ruby \ Rails | 7.0 | 7.1 | 7.2 | 8.0 | 8.1 |
36
+ |:---|:---:|:---:|:---:|:---:|:---:|
37
+ | 3.1 | yes | yes | yes | - | - |
38
+ | 3.2 | - | yes | yes | yes | yes |
39
+ | 3.3 | - | yes | yes | yes | yes |
40
+ | 3.4 | - | yes | yes | yes | yes |
41
+ | 4.0 | - | - | - | yes | yes |
42
+
43
+ - Rails 7.0 runs only on Ruby 3.1 (7.0 predates upstream Ruby 3.2+ support).
44
+ - Rails 8.0/8.1 run on Ruby 3.2+ (both declare `required_ruby_version >= 3.2.0`
45
+ upstream; they never needed 3.3).
46
+ - Ruby 4.0 runs only against Rails 8.0/8.1 (7.x has no upstream Ruby 4
47
+ support).
48
+ - RuboCop runs once inside the matrix (Ruby 3.3 / Rails 8.0) and again in a
49
+ dedicated `lint` job pinned to Ruby 3.3.
50
+
51
+ ### Rails 9
52
+
53
+ Not supported, and the two install paths fail differently:
54
+
55
+ - **In-Gemfile:** the gemspec's `railties < 9.0` constraint makes `bundle
56
+ install`/`bundle exec` fail dependency resolution outright when the host
57
+ app's Gemfile locks Rails 9 - the gem never loads.
58
+ - **Standalone:** the CLI pre-loads the gem into its own `GEM_HOME` before the
59
+ host app boots, so no Bundler constraint applies. `exe/rails-ai-context`
60
+ prints a stderr warning after a successful boot when
61
+ `Rails::VERSION::MAJOR >= 9`, naming the installed Rails version and
62
+ pointing at checking for a newer gem release. Introspection itself is
63
+ untested on Rails 9 either way.
64
+
65
+ ## Operating tiers
66
+
67
+ Two tiers, both reachable over the CLI and MCP (stdio and HTTP):
68
+
69
+ **RUNTIME** - the app booted. Introspectors read live Rails reflection
70
+ (ActiveRecord connections, `Rails.application.routes`, loaded classes) plus the
71
+ Prism AST layer for source-level facts (scopes, callbacks, strong params).
72
+
73
+ **STATIC** - the app didn't boot, or `--no-boot` was passed. Only introspectors
74
+ that define a `static_call` path can answer without a booted app:
75
+
76
+ | Introspector | Static source |
77
+ |:---|:---|
78
+ | `schema` | `db/schema.rb` / `db/structure.sql` / migration files |
79
+ | `migrations` | migration file list + `db/structure.sql`'s trailing `schema_migrations` insert |
80
+ | `routes` | `config/routes.rb` parsed with a dedicated Prism listener |
81
+ | `models` | `app/models/**/*.rb` (plus packs/engines/extra paths) parsed, not constantized |
82
+ | `controllers` | `app/controllers/**/*.rb` (plus packs/engines/extra paths) parsed, not constantized |
83
+
84
+ The other 34 introspectors (views, jobs, gems, turbo, i18n, active_storage,
85
+ auth, api, and the rest) have no static path and report `{ unavailable: reason
86
+ }` in this tier - by construction, not by shape: `Introspector#run_introspector`
87
+ (`lib/rails_ai_context/introspector.rb`) falls back to the same message
88
+ regardless of what triggered the static tier.
89
+
90
+ `doctor` never enters the static tier - its job is diagnosing why boot failed,
91
+ so it always requires a bootable app.
92
+
93
+ Boot failure degrades `serve`/`tool` to the static tier automatically (proven
94
+ across four boot-failure modes - raises, prints to stdout, writes via the
95
+ `STDOUT` constant, and hangs past the timeout - in
96
+ `spec/e2e/boot_resilience_spec.rb`); `--no-boot` forces it without attempting a
97
+ boot at all (`spec/e2e/static_tier_spec.rb`).
98
+
99
+ ### Confidence vocabulary
100
+
101
+ Source: `lib/rails_ai_context/confidence.rb`.
102
+
103
+ | Tag | Meaning |
104
+ |:---|:---|
105
+ | `[VERIFIED]` | Runtime-confirmed value, or a static literal the AST resolves with certainty |
106
+ | `[STATIC]` | Derived from source files without a booted app - trustworthy structure, not runtime-confirmed |
107
+ | `[INFERRED]` | Heuristic: a dynamic expression, metaprogramming, or a runtime-only construct the AST can't fully resolve |
108
+ | `[UNAVAILABLE: reason]` | The data source is absent entirely |
109
+
110
+ `[VERIFIED]`/`[INFERRED]` are per-value tags applied inside the schema, model,
111
+ route, controller, and mailbox-routing introspectors as they walk the AST
112
+ (`Confidence.for_node`). `[STATIC]`/`[UNAVAILABLE]` are whole-response tags:
113
+ `[STATIC]` marks any answer that came from the static tier; `[UNAVAILABLE]`
114
+ marks a section with no static path, or (in either tier) a data source that
115
+ genuinely doesn't exist for this app.
116
+
117
+ ## Shape matrix
118
+
119
+ Rows are app shapes; columns are schema, models, routes, and controllers
120
+ (the four whose static behavior varies by shape), plus views for contrast.
121
+ `n/a` means the shape doesn't change that introspector's behavior - see
122
+ the stock full-stack row for its baseline proof. Reference numbers point at
123
+ the proof list below the table.
124
+
125
+ | Shape | Schema | Models | Routes | Controllers | Views |
126
+ |:---|:---|:---|:---|:---|:---|
127
+ | Stock full-stack (`schema.rb`) | runtime + static [1] | runtime + static [1] | runtime + static [1] | runtime + static [1] | runtime only [2] |
128
+ | API-only | runtime + static [1] | runtime + static [1] | runtime + static [1] | runtime + static [1] | runtime, "not applicable" [3] |
129
+ | `structure.sql` - PostgreSQL dialect | static [4] | n/a | n/a | n/a | n/a |
130
+ | `structure.sql` - MySQL/Trilogy dialect | static [4] | n/a | n/a | n/a | n/a |
131
+ | `structure.sql` - SQLite dialect | static [4] | n/a | n/a | n/a | n/a |
132
+ | Multi-DB `solid_*` schema dumps | static, `secondary_databases` [5] | n/a | n/a | n/a | n/a |
133
+ | Packwerk `packs/` | n/a | static [5] | n/a | n/a | n/a |
134
+ | In-repo `engines/` | n/a | n/a | n/a | static [5] | n/a |
135
+ | Mongoid | `[UNAVAILABLE]`, honest signal [6] | static (fields + embeds) [6] | n/a | n/a | n/a |
136
+ | Broken-boot (any full-stack app) | static [7] | static, per-file isolation [7] | static [7] | static [7] | `[UNAVAILABLE]` [8] |
137
+ | Packs + engines under `--no-boot` | n/a | static [5] | n/a | static [5] | n/a |
138
+ | Empty/greenfield app (no scaffold) | graceful, no crash [9] | graceful, no crash [9] | graceful, no crash [9] | graceful, no crash [9] | graceful, no crash [9] |
139
+ | Massive app (500 models/tables) | no crash at scale [10] | no crash at scale [10] | no crash at scale [10] | n/a | n/a |
140
+
141
+ Proof sources:
142
+
143
+ 1. `spec/e2e/in_gemfile_install_spec.rb`, `standalone_install_spec.rb`,
144
+ `zero_config_install_spec.rb` (schema, routes, model_details, and controllers
145
+ exercised across the install-path suite - in-Gemfile, standalone, zero-config);
146
+ real Rails 8.0 apps in the v5.14.0 release QA (`blog`, `sandbox`).
147
+ 2. Non-crash coverage for every built-in tool including `get_view` in
148
+ `spec/e2e/in_gemfile_install_spec.rb`'s full-tool sweep; output correctness
149
+ (ivar cross-check, render-form detection, partial interfaces) verified
150
+ against the real `blog` app in the v5.14.0 release QA.
151
+ 3. Unit specs for the "not applicable" API-only messaging: `get_view_spec.rb`,
152
+ `get_component_catalog_spec.rb`, `get_turbo_map_spec.rb`,
153
+ `get_partial_interface_spec.rb`, `get_stimulus_spec.rb`, `get_api_spec.rb`;
154
+ real Rails 8 `--api` app (`api_app`) in the v5.14.0 release QA.
155
+ 4. `spec/lib/rails_ai_context/introspectors/schema_introspector_spec.rb`
156
+ ("with a valid structure.sql fixture", "with a MySQL (mysqldump)
157
+ structure.sql", "with a SQLite structure.sql"). Unit-proven only - no
158
+ committed e2e fixture writes a `structure.sql` file. The MySQL/Trilogy
159
+ adapter itself (live queries, not the dump format) was also run for real
160
+ against MySQL 9.6 in the v5.14.0 release QA (`store` app,
161
+ `schema_format = :sql`).
162
+ 5. `spec/e2e/shapes_spec.rb`: "packs and engines code discovery (static
163
+ tier)" and "multi-database schema dumps".
164
+ 6. `spec/e2e/shapes_spec.rb`: "Mongoid app (bare directory, --app-path, no
165
+ boot possible)".
166
+ 7. `spec/e2e/boot_resilience_spec.rb` (four boot-failure modes: raises,
167
+ prints to stdout, writes via the `STDOUT` constant, hangs past the
168
+ timeout) plus `spec/e2e/static_tier_spec.rb` ("broken-boot app over the
169
+ CLI", "broken-boot app over MCP stdio", "syntax error in one model file").
170
+ 8. No introspector outside the five in the operating-tiers table defines
171
+ `static_call` (`lib/rails_ai_context/introspectors/view_introspector.rb`
172
+ has none); `Introspector#run_introspector` reports `{ unavailable: reason
173
+ }` for every such section regardless of shape.
174
+ 9. `spec/e2e/empty_app_spec.rb` - all 39 built-in tools swept against an app
175
+ with no scaffold, no models, no controllers beyond
176
+ `ApplicationController`, no routes beyond root.
177
+ 10. `spec/e2e/massive_app_spec.rb` - `schema`, `model_details`, and `routes`
178
+ (plus `context`, `onboard`, `analyze_feature`, `get_turbo_map`, `get_env`)
179
+ sampled against 500 generated models/tables under a 180-second cap.
180
+
181
+ Postgres itself - live query safety (read-only transactions, the
182
+ `BLOCKED_FUNCTIONS` guard against `pg_read_file`/`dblink`/`COPY ... PROGRAM`,
183
+ DDL rejection) alongside schema/routes - is proven end-to-end against a real
184
+ Postgres instance in `spec/e2e/postgres_install_spec.rb`, opt-in via
185
+ `TEST_POSTGRES=1` and skipped otherwise.
186
+
187
+ ## Known limits
188
+
189
+ - **Concern-style Mongoid documents in runtime results.** Mongoid documents
190
+ are invisible to ActiveRecord reflection, so `ModelIntrospector#call` falls
191
+ back to the same source-parsing pass used in the static tier even when the
192
+ app is booted (`lib/rails_ai_context/introspectors/model_introspector.rb`).
193
+ Fields or associations defined in an included concern module rather than
194
+ directly in the document class body are not resolved, in either tier.
195
+ - **Static-tier API-only detection.** `Tools::BaseTool#api_only_app?` reads
196
+ `app.config.api_only`. `RailsAiContext::StaticApp` exposes no `config`
197
+ method, so this check is always false in the static tier - the "not
198
+ applicable" messaging for view/frontend tools on API-only apps only fires
199
+ once the app has actually booted.
200
+ - **Live multi-DB connections are not iterated, only dumps.**
201
+ `MultiDatabaseIntrospector` (replicas, sharding, per-model connection
202
+ assignment) has no static path and reports `[UNAVAILABLE]` in the static
203
+ tier. Only the schema introspector's own secondary-database dump parsing
204
+ (`db/*_schema.rb`, `db/*_structure.sql`) works without a boot.
205
+ - **Constraints and lambda routes surface as a dynamic tally, not resolved
206
+ entries.** `RouteIntrospector#static_call` counts routes behind
207
+ `constraints do...end` blocks, lambdas, `devise_for`, and `concern`-based
208
+ route declarations into a `dynamic_routes` count rather than fabricating
209
+ per-route controller/action pairs it can't actually determine from source.
210
+
211
+ <br>
212
+
213
+ ---
214
+
215
+ <div align="center">
216
+
217
+ [Back to README](../README.md)
218
+
219
+ </div>
data/docs/TOOLS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # MCP Tools Reference
4
4
 
5
- **All 38 read-only tools, with every parameter.**
5
+ **All 39 read-only tools, with every parameter.**
6
6
 
7
7
  [Quickstart](QUICKSTART.md) · [Recipes](RECIPES.md) · [Custom Tools](CUSTOM_TOOLS.md) · [CLI Reference](CLI.md)
8
8
 
@@ -59,7 +59,7 @@ Individual lookup tools accept a **`detail`** parameter: `summary` (compact), `s
59
59
  | [Controllers & Routes](#controllers--routes) | `get_controllers`, `get_routes` |
60
60
  | [Views & Frontend](#views--frontend) | `get_view`, `get_stimulus`, `get_partial_interface`, `get_turbo_map`, `get_frontend_stack` |
61
61
  | [Testing & Quality](#testing--quality) | `get_test_info`, `generate_test`, `validate`, `security_scan`, `performance_check` |
62
- | [App Config & Services](#app-config--services) | `get_conventions`, `get_config`, `get_gems`, `get_env`, `get_helper_methods`, `get_service_pattern`, `get_job_pattern`, `get_component_catalog` |
62
+ | [App Config & Services](#app-config--services) | `get_api`, `get_conventions`, `get_config`, `get_gems`, `get_env`, `get_helper_methods`, `get_service_pattern`, `get_job_pattern`, `get_component_catalog` |
63
63
  | [Data & Debugging](#data--debugging) | `dependency_graph`, `migration_advisor`, `search_docs`, `query`, `read_logs`, `diagnose`, `review_changes`, `runtime_info`, `session_context` |
64
64
 
65
65
  <p align="right"><a href="#table-of-contents">↑ back to top</a></p>
@@ -303,6 +303,14 @@ N+1 query risks, missing indexes, missing counter_cache, eager load candidates.
303
303
 
304
304
  ## App Config & Services
305
305
 
306
+ ### `rails_get_api`
307
+
308
+ API layer: api_only mode, serialization strategy (Jbuilder, serializers), GraphQL, versioning, rate limiting, OpenAPI specs, CORS, pagination.
309
+
310
+ | Parameter | Type | Default | Description |
311
+ |:----------|:-----|:--------|:------------|
312
+ | `detail` | enum | `standard` | `summary` (one-liner), `standard` (per-area breakdown), `full` (adds OpenAPI spec paths) |
313
+
306
314
  ### `rails_get_conventions`
307
315
 
308
316
  Auth checks, flash messages, create action template, test patterns.
data/docs/index.md CHANGED
@@ -22,7 +22,7 @@ Works with Claude Code, Cursor, GitHub Copilot, OpenCode, and Codex CLI.
22
22
 
23
23
  | Guide | Description |
24
24
  |:------|:------------|
25
- | **[Tools Reference](TOOLS.md)** | All 38 tools with every parameter |
25
+ | **[Tools Reference](TOOLS.md)** | All 39 tools with every parameter |
26
26
  | **[Configuration](CONFIGURATION.md)** | 40+ config options with defaults |
27
27
  | **[CLI Reference](CLI.md)** | Commands and argument syntax |
28
28
  | **[Introspectors](INTROSPECTORS.md)** | All 39 introspectors and AST engine |