rails-ai-context 5.14.0 → 5.16.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 +273 -0
- data/README.md +42 -2
- data/config/routes.rb +3 -1
- data/docs/COMPATIBILITY.md +219 -0
- data/docs/CUSTOM_TOOLS.md +17 -3
- data/docs/TROUBLESHOOTING.md +17 -0
- data/exe/rails-ai-context +121 -22
- data/lib/generators/rails_ai_context/install/install_generator.rb +1 -1
- data/lib/rails_ai_context/app_kind.rb +31 -0
- data/lib/rails_ai_context/cli/tool_runner.rb +1 -1
- data/lib/rails_ai_context/confidence.rb +16 -4
- data/lib/rails_ai_context/configuration.rb +13 -1
- data/lib/rails_ai_context/doctor.rb +121 -3
- data/lib/rails_ai_context/engine.rb +5 -1
- data/lib/rails_ai_context/introspector.rb +42 -5
- data/lib/rails_ai_context/introspectors/controller_introspector.rb +40 -10
- data/lib/rails_ai_context/introspectors/gem_introspector.rb +1 -0
- data/lib/rails_ai_context/introspectors/job_introspector.rb +19 -1
- data/lib/rails_ai_context/introspectors/listeners/mongoid_fields_listener.rb +27 -0
- data/lib/rails_ai_context/introspectors/listeners/routes_dsl_listener.rb +372 -0
- data/lib/rails_ai_context/introspectors/listeners/schema_dsl_listener.rb +3 -0
- data/lib/rails_ai_context/introspectors/listeners/scopes_listener.rb +21 -4
- data/lib/rails_ai_context/introspectors/migration_introspector.rb +4 -0
- data/lib/rails_ai_context/introspectors/model_introspector.rb +161 -8
- data/lib/rails_ai_context/introspectors/route_introspector.rb +64 -0
- data/lib/rails_ai_context/introspectors/schema_introspector.rb +313 -24
- data/lib/rails_ai_context/introspectors/turbo_introspector.rb +15 -2
- data/lib/rails_ai_context/path_resolver.rb +35 -0
- data/lib/rails_ai_context/safe_file.rb +5 -1
- data/lib/rails_ai_context/schema_version.rb +9 -4
- data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +2 -2
- data/lib/rails_ai_context/serializers/compact_serializer_helper.rb +20 -9
- data/lib/rails_ai_context/serializers/copilot_instructions_serializer.rb +1 -1
- data/lib/rails_ai_context/serializers/copilot_serializer.rb +13 -2
- data/lib/rails_ai_context/serializers/cursor_rules_serializer.rb +1 -1
- data/lib/rails_ai_context/serializers/markdown_serializer.rb +1 -1
- data/lib/rails_ai_context/serializers/section_guard.rb +16 -0
- data/lib/rails_ai_context/server.rb +49 -7
- data/lib/rails_ai_context/source_line.rb +208 -0
- data/lib/rails_ai_context/static_app.rb +17 -0
- data/lib/rails_ai_context/test_helper.rb +6 -1
- data/lib/rails_ai_context/tools/analyze_feature.rb +12 -5
- data/lib/rails_ai_context/tools/base_tool.rb +91 -2
- data/lib/rails_ai_context/tools/dependency_graph.rb +3 -0
- data/lib/rails_ai_context/tools/diagnose.rb +8 -4
- data/lib/rails_ai_context/tools/generate_test.rb +15 -3
- data/lib/rails_ai_context/tools/get_api.rb +3 -0
- data/lib/rails_ai_context/tools/get_callbacks.rb +4 -2
- data/lib/rails_ai_context/tools/get_component_catalog.rb +9 -0
- data/lib/rails_ai_context/tools/get_config.rb +13 -5
- data/lib/rails_ai_context/tools/get_context.rb +1 -15
- data/lib/rails_ai_context/tools/get_controllers.rb +5 -3
- data/lib/rails_ai_context/tools/get_conventions.rb +45 -23
- data/lib/rails_ai_context/tools/get_edit_context.rb +5 -5
- data/lib/rails_ai_context/tools/get_env.rb +5 -2
- data/lib/rails_ai_context/tools/get_frontend_stack.rb +7 -4
- data/lib/rails_ai_context/tools/get_gems.rb +3 -1
- data/lib/rails_ai_context/tools/get_job_pattern.rb +36 -12
- data/lib/rails_ai_context/tools/get_model_details.rb +41 -8
- data/lib/rails_ai_context/tools/get_partial_interface.rb +4 -1
- data/lib/rails_ai_context/tools/get_routes.rb +10 -0
- data/lib/rails_ai_context/tools/get_schema.rb +44 -0
- data/lib/rails_ai_context/tools/get_service_pattern.rb +10 -2
- data/lib/rails_ai_context/tools/get_stimulus.rb +11 -4
- data/lib/rails_ai_context/tools/get_test_info.rb +15 -13
- data/lib/rails_ai_context/tools/get_turbo_map.rb +42 -10
- data/lib/rails_ai_context/tools/get_view.rb +32 -7
- data/lib/rails_ai_context/tools/onboard.rb +4 -4
- data/lib/rails_ai_context/tools/performance_check.rb +3 -0
- data/lib/rails_ai_context/tools/query.rb +5 -1
- data/lib/rails_ai_context/tools/read_logs.rb +4 -4
- data/lib/rails_ai_context/tools/review_changes.rb +1 -1
- data/lib/rails_ai_context/tools/runtime_info.rb +5 -1
- data/lib/rails_ai_context/tools/search_code.rb +2 -2
- data/lib/rails_ai_context/tools/search_docs.rb +4 -4
- data/lib/rails_ai_context/tools/security_scan.rb +2 -2
- data/lib/rails_ai_context/tools/validate.rb +12 -12
- data/lib/rails_ai_context/version.rb +1 -1
- data/lib/rails_ai_context.rb +43 -3
- metadata +21 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59c335a48ab2976318ed2fc58d1cce4e2153c0db0144b61ca8d902f160bbeb76
|
|
4
|
+
data.tar.gz: 334cc340093515797006c8326b47fa97721c9e66f07f940f3ec0864db2ae241a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0e5532e52f873a3cb659dbf90fde582974f4840609f0c8365318f5b862659475484232e9ead3fd0074ec1a079db565cd8046a157ceb4f1bc1099b08877feded
|
|
7
|
+
data.tar.gz: e6bd4fe02b30344a494a8af944ab6ca94e07f944d143d99bd4f116ca3f15099a5fbbe840b38c1b186398c43c3b23825889fd4b852593fc09342edc50910cd4e9
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,279 @@ 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.16.0] - 2026-07-12
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **HTTP engine mount works again.** `mount RailsAiContext::Engine, at: "/mcp"`
|
|
13
|
+
500'd with `uninitialized constant McpController`: the engine is not
|
|
14
|
+
namespace-isolated, so its route needed the fully-qualified
|
|
15
|
+
`rails_ai_context/mcp#handle` controller path. Verified with a real curl
|
|
16
|
+
MCP session (initialize + session-id + tools/call) against a booted app.
|
|
17
|
+
- **`config.custom_tools` is usable as documented.** Referencing an
|
|
18
|
+
`app/mcp_tools/` class constant in the initializer aborted boot with
|
|
19
|
+
`NameError` (app/ constants are not autoloadable while initializers run),
|
|
20
|
+
and a class-name string crashed every CLI tool invocation with
|
|
21
|
+
`undefined method 'tool_name' for an instance of String`. Entries may now
|
|
22
|
+
be classes or class-name strings; strings resolve lazily after boot, and
|
|
23
|
+
invalid entries are warn-skipped everywhere (server, CLI runner,
|
|
24
|
+
TestHelper) instead of taking down unrelated tools. Docs and the
|
|
25
|
+
initializer template now show the string form.
|
|
26
|
+
- **Mailers are no longer invisible in development.** `extract_mailers` read
|
|
27
|
+
`ActionMailer::Base.descendants` without the eager-load fallback that
|
|
28
|
+
channels already had, so every dev-mode run reported zero mailers
|
|
29
|
+
(`ai:inspect`, context files, onboard).
|
|
30
|
+
- **TestHelper is self-sufficient.** `require "rails_ai_context/test_helper"`
|
|
31
|
+
alone raised `uninitialized constant RailsAiContext::Tools` - the common
|
|
32
|
+
case, since a `group: :development` install is never Bundler-required in
|
|
33
|
+
the test environment. The helper now requires the gem core itself; docs
|
|
34
|
+
explain custom-tool name lookup in the test environment.
|
|
35
|
+
- **`claude_max_lines` counts physical lines.** Dual MCP/CLI tool examples
|
|
36
|
+
embed newlines inside single entries, so a default-capped CLAUDE.md could
|
|
37
|
+
ship 161 real lines with no trim marker. The cap (and the trim) now apply
|
|
38
|
+
to physical lines of the managed block.
|
|
39
|
+
- **turbo_map no longer flags correct wiring.** `broadcast_append_to post`
|
|
40
|
+
against `turbo_stream_from @post` warned "no matching turbo_stream_from";
|
|
41
|
+
stream matching now strips the ivar sigil before comparing.
|
|
42
|
+
- **frontend_stack's Turbo wiring line can actually render.** It read
|
|
43
|
+
`turbo[:broadcasts]`/`[:frames]` - keys the introspector never emits
|
|
44
|
+
(`model_broadcasts`/`turbo_frames`) - so the line was dead code for every
|
|
45
|
+
app; callback-style `broadcast_*_to` calls are now also detected.
|
|
46
|
+
- **generate_test emits runnable tests for arg-taking scopes.** A scope like
|
|
47
|
+
`scope :by_status, ->(s) { ... }` produced `Post.by_status` (ArgumentError
|
|
48
|
+
at runtime); scope lambdas' required params are now captured through the
|
|
49
|
+
AST layer and such scopes get an `assert_respond_to` test with a
|
|
50
|
+
fill-in-the-args call site. Verified by running the generated file for real.
|
|
51
|
+
- **job_pattern keeps `wait:` in retry configuration.** One ordered regex
|
|
52
|
+
dropped whichever of `wait:`/`attempts:` came first in `retry_on`; the two
|
|
53
|
+
options are now scanned independently.
|
|
54
|
+
- **get_env skips comment lines.** `# ENV["WEB_CONCURRENCY"]` in the stock
|
|
55
|
+
puma.rb comment was reported as a real environment variable.
|
|
56
|
+
- **diagnose omits an empty "Next Steps" section** instead of rendering a
|
|
57
|
+
bare header when no file/method context was parsed.
|
|
58
|
+
- **analyze_feature lists jbuilder templates and record renders**
|
|
59
|
+
(`render @post` / `render post`), matching what get_view reports.
|
|
60
|
+
- **service_pattern surfaces mailer dependencies** (`PostMailer.published_email`
|
|
61
|
+
style calls) and recognizes `perform_now`.
|
|
62
|
+
- **README observability example reads `event.payload[:duration]`** - the
|
|
63
|
+
bridge emits a zero-width event, so `event.duration` is always ~0ms.
|
|
64
|
+
- Singular/plural agreement in tool output ("1 result", "1 site",
|
|
65
|
+
"1 broadcast", "1 line", "1 table", "1 controller", "1 migration file").
|
|
66
|
+
- edit_context's footer no longer suggests pasting the line-numbered block
|
|
67
|
+
verbatim as an Edit old_string.
|
|
68
|
+
- **A syntax-broken model or controller file no longer kills the process in
|
|
69
|
+
the runtime tier.** Eager loading aborts at the first bad file with a
|
|
70
|
+
`SyntaxError` - a `ScriptError`, which sailed past every bare `rescue` and
|
|
71
|
+
took down the MCP server mid-session and every model-touching rake task.
|
|
72
|
+
Introspection now rescues `ScriptError` at the dispatch level, eager
|
|
73
|
+
loading falls back to per-constant loading, and `discover_models`
|
|
74
|
+
tolerates unloadable classes - one broken file costs exactly that file,
|
|
75
|
+
matching the static tier's contract.
|
|
76
|
+
- **Standalone CLI no longer mixes framework versions.** The RubyGems
|
|
77
|
+
binstub activates the newest installed railties before the app's
|
|
78
|
+
Gemfile.lock pins its own, leaving newer framework paths in $LOAD_PATH:
|
|
79
|
+
every generated fact reported the wrong Rails version (e.g. 8.1.3 for an
|
|
80
|
+
8.0.5 app) with "already initialized constant" warnings polluting all
|
|
81
|
+
output. The exe now strips Rails-family gem paths at startup; the app's
|
|
82
|
+
bundle is the only framework source.
|
|
83
|
+
- **`--app-path` and `--environment` work after the tool name** (`tool
|
|
84
|
+
schema --table notes --app-path /x`), matching the `--json`/`--no-boot`
|
|
85
|
+
passthrough handling. Previously they were silently discarded and the
|
|
86
|
+
command ran against the wrong directory.
|
|
87
|
+
- **structure.sql parsing: single-line CREATE TABLE statements** (the shape
|
|
88
|
+
sqlite's `.schema` emits) no longer lose every column but the first;
|
|
89
|
+
bodies are split on top-level commas before per-line parsing.
|
|
90
|
+
- **structure.sql parsing: foreign keys land on the right table.** The
|
|
91
|
+
`ALTER TABLE ... FOREIGN KEY` regex could span statement boundaries and
|
|
92
|
+
attribute a FK to whichever table had the first `ADD CONSTRAINT`.
|
|
93
|
+
- **structure.sql parsing: NOT NULL columns are no longer reported as
|
|
94
|
+
nullable** (nullability was simply not extracted).
|
|
95
|
+
- **Runtime-only tools refuse in the static tier.** After a mid-boot
|
|
96
|
+
failure, `runtime_info` and `query` ran against the half-initialized app
|
|
97
|
+
and returned live, unmarked data contradicting the static-tier banner in
|
|
98
|
+
the same response; both now answer `[UNAVAILABLE: static tier]` with the
|
|
99
|
+
boot-failure reason. This also removes a raw NameError leak in
|
|
100
|
+
runtime_info's Cache section and query's wrong `--skip-active-record`
|
|
101
|
+
blame under `--no-boot`.
|
|
102
|
+
- **The static-tier banner carries the literal `[STATIC]` tag** that README
|
|
103
|
+
and COMPATIBILITY.md promise.
|
|
104
|
+
- **doctor's "Secrets in .gitignore" check understands globs.** Rails 8.1
|
|
105
|
+
generates `/config/*.key`; the substring check false-failed every default
|
|
106
|
+
8.1 app and docked its readiness score.
|
|
107
|
+
- **model_details carries confidence tags**: `[VERIFIED]` on the runtime
|
|
108
|
+
model header, per-scope `[VERIFIED]`/`[INFERRED]` from the AST layer
|
|
109
|
+
(README promised tags this tool never rendered).
|
|
110
|
+
- **Route stats agree across generated context files.** CLAUDE.md counted
|
|
111
|
+
all routes over app controllers while copilot-instructions counted all
|
|
112
|
+
routes over all controllers ("35 across 1" vs "35 across 18"); every
|
|
113
|
+
serializer now reports app routes across app controllers with the
|
|
114
|
+
framework-inclusive total alongside.
|
|
115
|
+
- **get_routes counts engine mounts.** Verbless rack mounts (propshaft's
|
|
116
|
+
`/assets`) vanished from both the app list and the excluded-framework
|
|
117
|
+
count; the header now reports them explicitly.
|
|
118
|
+
- **Mongoid apps are detected from the Gemfile too** (previously only
|
|
119
|
+
Gemfile.lock or config/mongoid.yml), so a fresh checkout without a
|
|
120
|
+
lockfile gets honest Mongoid treatment instead of ActiveRecord answers.
|
|
121
|
+
- **`extra_app_paths` accepts entries ending in `/app`** (`custom/app`
|
|
122
|
+
previously globbed `custom/app/app/models` and silently missed).
|
|
123
|
+
- **analyze_feature no longer invents model names** ("user, payment, order")
|
|
124
|
+
on apps with zero models.
|
|
125
|
+
- **A missing schema is "unavailable", not "failed".** A greenfield app's
|
|
126
|
+
`context` output framed the absent db/schema.rb as "introspection failed";
|
|
127
|
+
absent data sources now use the `[UNAVAILABLE]` vocabulary.
|
|
128
|
+
- **conventions: `create!` flows are no longer attributed the save-and-branch
|
|
129
|
+
skeleton**, zero-signal test files are no longer credited as pattern
|
|
130
|
+
sources, and empty custom-directory descriptions drop the dangling dash.
|
|
131
|
+
- **get_config names the environment its values reflect** (cache store,
|
|
132
|
+
queue adapter, and Action Cable differ per environment).
|
|
133
|
+
- **get_env skips ERB-only false positives from comments** and standalone
|
|
134
|
+
`doctor` writes its report to stdout (`doctor > report.txt` works).
|
|
135
|
+
- docs: TROUBLESHOOTING covers RubyGems' "Resolving dependencies..." stdout
|
|
136
|
+
pollution of the MCP stdio stream and its fixes; a new doctor check ("MCP
|
|
137
|
+
stdio hygiene") detects the condition by re-running gem activation the way
|
|
138
|
+
an MCP client's binstub launch does.
|
|
139
|
+
- **Static schema answers include the implicit id primary key**, typed per
|
|
140
|
+
the adapter in config/database.yml (bigint everywhere, integer on SQLite);
|
|
141
|
+
composite-primary-key tables (`primary_key: [...]`) are left to their
|
|
142
|
+
explicitly dumped columns instead of gaining a phantom id.
|
|
143
|
+
- **Static schema answers name their source dialect and migration state**:
|
|
144
|
+
structure.sql parses report "Dialect", "Schema version" (from the
|
|
145
|
+
schema_migrations insert tail), and the exact pending migration files;
|
|
146
|
+
schema.rb parses report version plus files newer than it. Secondary
|
|
147
|
+
database dumps compare against their own db/<name>_migrate directories.
|
|
148
|
+
- Review-round regression fixes (found by adversarial review of this batch):
|
|
149
|
+
the missing-schema `{unavailable:}` result no longer leaks blank
|
|
150
|
+
"- Database:" lines into generated context files; the gitignore matcher
|
|
151
|
+
honors git's basename rule (`*.key`, bare `master.key`); multi-line
|
|
152
|
+
`retry_on` declarations keep their continuation-line options; a
|
|
153
|
+
syntax-broken `custom_tools` string entry warn-skips instead of crashing
|
|
154
|
+
the server and CLI; the Turbo broadcast scan ignores comments and method
|
|
155
|
+
definitions; redirect and lambda routes are not counted as engine mounts.
|
|
156
|
+
- Second review-round fixes: the gitignore security check honors `!`
|
|
157
|
+
negation patterns (a re-included secret now correctly fails the check);
|
|
158
|
+
`SafeFile.read` scrubs invalid UTF-8 so one bad byte in one file can no
|
|
159
|
+
longer raise out of any scan (previously it could erase every model's
|
|
160
|
+
Turbo broadcasts, or crash `get_turbo_map` outright); legacy zero-padded
|
|
161
|
+
migration versions (`001_` storing version "1") no longer read as
|
|
162
|
+
forever-pending; the synthesized primary-key type is resolved per
|
|
163
|
+
database from config/database.yml (a mixed postgres/sqlite multi-db app
|
|
164
|
+
types each dump correctly); endless-method broadcasts
|
|
165
|
+
(`def refresh = broadcast_replace_to(...)`) are detected while trailing
|
|
166
|
+
`#comments` (with or without spaces) are not, in both the introspector
|
|
167
|
+
and get_turbo_map's own scanners; `retry_on` options inside trailing
|
|
168
|
+
comments are not reported as real retry config; the doctor stdio-hygiene
|
|
169
|
+
probe launches from Bundler's original environment (a configured bundle
|
|
170
|
+
path no longer produces a spurious warning); a custom tool whose class
|
|
171
|
+
body raises during autoload is reported as broken rather than
|
|
172
|
+
"class not found"; the schema_migrations INSERT regex and the
|
|
173
|
+
section-usability predicate each have a single home.
|
|
174
|
+
- Third review-round fixes: source-line scans (Turbo broadcasts, retry_on
|
|
175
|
+
options) strip comments with a string-aware walker (`SourceLine`) instead
|
|
176
|
+
of a regex, so a `#` inside a string literal (`"#comments"`, `"##{id}"`)
|
|
177
|
+
no longer truncates the line and drops real calls, while comment text
|
|
178
|
+
still never reads as code - including comments after a continuation comma
|
|
179
|
+
in multi-line `retry_on`; `def self.broadcast_*_to` definitions are not
|
|
180
|
+
counted as broadcast calls; the doctor gitignore check now models git's
|
|
181
|
+
negation semantics (dir-only `!*/` entries, no re-inclusion under an
|
|
182
|
+
excluded parent, negations must match the file itself) - verified against
|
|
183
|
+
`git check-ignore` on a 16-case battery; the database.yml block lookup
|
|
184
|
+
no longer bleeds across whitespace-only lines into a sibling database's
|
|
185
|
+
adapter; and a custom tool whose body references a missing constant that
|
|
186
|
+
shares a name segment with the entry is no longer misreported as
|
|
187
|
+
"class not found".
|
|
188
|
+
- Fourth review-round fixes: the gitignore check now evaluates paths the
|
|
189
|
+
way git does (ancestor-directory recursion with last-match-wins instead
|
|
190
|
+
of descend globbing), closing a false-SAFE case where `config/` followed
|
|
191
|
+
by `!config/` read as still covering config/master.key - verified 25/25
|
|
192
|
+
against `git check-ignore`, including `**/`, whitelist preludes, and
|
|
193
|
+
re-included directories; it also honors case-insensitive filesystems the
|
|
194
|
+
way core.ignorecase does. `SourceLine.strip_comment` gained %-literal
|
|
195
|
+
support (`%w[cs#billing]` is content, `n % 2` is not a literal), an
|
|
196
|
+
O(n) walk (a 400k-char multibyte line dropped from 27s to 51ms), and a
|
|
197
|
+
no-`#` fast path. Broadcast def-blanking handles constant receivers
|
|
198
|
+
(`def User.broadcast_updates_to`) and multiple defs per line; multi-line
|
|
199
|
+
broadcast extraction reads comment-stripped context; `::`-prefixed
|
|
200
|
+
custom_tools entries classify correctly; config/database.yml is read
|
|
201
|
+
once, CRLF-normalized, per introspection run.
|
|
202
|
+
- Sixth-through-eighth review-round fixes (call-site scanning converged
|
|
203
|
+
after three more single-finding rounds): classic one-line methods
|
|
204
|
+
(`def x; broadcast_y_to(...); end`) contribute their body while
|
|
205
|
+
visibility-prefixed definitions (`private def`, `private_class_method
|
|
206
|
+
def`) contribute nothing; `;`/`=` inside string keyword defaults
|
|
207
|
+
(`sep: "; "`, `label: "a=b"`) and inside `#{...}` interpolation (with
|
|
208
|
+
nested quoted strings) no longer delimit a signature. A final review
|
|
209
|
+
round at a realistic-Rails bar returned zero findings.
|
|
210
|
+
- Fifth review-round fixes: comment stripping is now a whole-fragment
|
|
211
|
+
state machine (`SourceLine.strip_comments`) - literal state carries
|
|
212
|
+
across newlines, so a comment mid-way through a multi-line broadcast
|
|
213
|
+
call is removed while a string spanning lines keeps its tail; backtick
|
|
214
|
+
command literals (with interpolation) are modeled; singleton broadcast
|
|
215
|
+
definitions on any receiver (`def record.broadcast_status_to`,
|
|
216
|
+
`def self::...`) are excluded from broadcast detection; database.yml
|
|
217
|
+
without a final newline still resolves its last block's adapter; and
|
|
218
|
+
the case-insensitivity probe works for app roots with caseless names
|
|
219
|
+
(all-digit directories).
|
|
220
|
+
|
|
221
|
+
### Changed
|
|
222
|
+
|
|
223
|
+
- Standalone `doctor` writes its report to stdout (previously stderr), so
|
|
224
|
+
`doctor > report.txt` captures it; update scripts that redirected `2>`.
|
|
225
|
+
- Custom tools resolved through the CLI are now validated as `MCP::Tool`
|
|
226
|
+
subclasses, matching what the MCP server has always enforced; duck-typed
|
|
227
|
+
tool classes that only ever worked via `rails-ai-context tool` are
|
|
228
|
+
warn-skipped with the reason.
|
|
229
|
+
|
|
230
|
+
## [5.15.0] - 2026-07-11
|
|
231
|
+
|
|
232
|
+
### Added
|
|
233
|
+
|
|
234
|
+
- Static tier: `serve` and `tool` fall back to source-file analysis when the
|
|
235
|
+
app cannot boot, instead of exiting. Routes (new `config/routes.rb` Prism
|
|
236
|
+
walker), models, controllers, schema, and migrations answer statically;
|
|
237
|
+
other sections report `[UNAVAILABLE]` with the reason; every response
|
|
238
|
+
carries a tier banner.
|
|
239
|
+
- CLI flags: `--no-boot` (serve/tool), `--app-path` (all app-reading
|
|
240
|
+
commands), and `--environment` (all commands).
|
|
241
|
+
- `[STATIC]` and `[UNAVAILABLE: <reason>]` confidence tags.
|
|
242
|
+
- structure.sql parsing is dialect-aware: MySQL (backticks, inline
|
|
243
|
+
KEY/CONSTRAINT definitions, ENGINE trailers) and SQLite (quoted
|
|
244
|
+
identifiers, IF NOT EXISTS) now parse; output gains a `dialect` key.
|
|
245
|
+
- Shape-aware discovery: models and controllers are found in packwerk
|
|
246
|
+
`packs/`, in-repo `engines/`, and configured `extra_app_paths`.
|
|
247
|
+
- Multi-database schema dumps (`db/*_schema.rb`, `db/*_structure.sql`)
|
|
248
|
+
reported under `secondary_databases`.
|
|
249
|
+
- Mongoid detection: honest `[UNAVAILABLE]` schema signal and basic static
|
|
250
|
+
model extraction (fields, embeds, store_in) for Mongoid apps.
|
|
251
|
+
- API-only apps get explicit "not applicable" answers from view/frontend
|
|
252
|
+
tools instead of empty listings.
|
|
253
|
+
- **Loud Rails 9 warning in the standalone CLI.** In-Gemfile installs already
|
|
254
|
+
fail Bundler resolution against Rails 9 (`railties < 9.0`); the standalone
|
|
255
|
+
CLI has no such check, so it used to boot silently against an untested
|
|
256
|
+
Rails version. It now prints a stderr warning naming the installed Rails
|
|
257
|
+
version and pointing at checking for a newer gem release.
|
|
258
|
+
- **`docs/COMPATIBILITY.md`** publishes the supported-version matrix, the CI
|
|
259
|
+
version grid, the RUNTIME/STATIC tier contract with the confidence-tag
|
|
260
|
+
vocabulary, and a shape-by-introspector matrix that cites the spec or QA
|
|
261
|
+
run proving every cell.
|
|
262
|
+
|
|
263
|
+
### Changed
|
|
264
|
+
|
|
265
|
+
- Boot failure in `tool`/`serve` no longer exits 1; it degrades to the
|
|
266
|
+
static tier with a stderr notice. `doctor` keeps requiring a bootable app.
|
|
267
|
+
- CI runs Rails 8.x on Ruby 3.2 again. The exclusion assumed Rails 8
|
|
268
|
+
required Ruby 3.3+; both railties 8.0.5 and 8.1.3 declare
|
|
269
|
+
`required_ruby_version >= 3.2.0`.
|
|
270
|
+
|
|
271
|
+
### Fixed
|
|
272
|
+
|
|
273
|
+
- `config.auto_mount = true` set in a user initializer now takes effect
|
|
274
|
+
(the middleware initializer ran before user initializers loaded).
|
|
275
|
+
- `prism` and `concurrent-ruby` are now capped below their next majors
|
|
276
|
+
(`< 2.0`, `< 3.0`) instead of an open-ended floor.
|
|
277
|
+
- A syntax error in one model file now costs exactly that model - the rest
|
|
278
|
+
of the models section, and every other section, still answers normally
|
|
279
|
+
(verified end-to-end in the static tier).
|
|
280
|
+
|
|
8
281
|
## [5.14.0] - 2026-07-11
|
|
9
282
|
|
|
10
283
|
### Added
|
data/README.md
CHANGED
|
@@ -448,6 +448,44 @@ 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
|
| | |
|
|
@@ -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
|
|
|
@@ -484,7 +523,7 @@ class RailsGetBusinessMetrics < MCP::Tool
|
|
|
484
523
|
end
|
|
485
524
|
|
|
486
525
|
# config/initializers/rails_ai_context.rb
|
|
487
|
-
config.custom_tools = [RailsGetBusinessMetrics]
|
|
526
|
+
config.custom_tools = ["RailsGetBusinessMetrics"]
|
|
488
527
|
```
|
|
489
528
|
|
|
490
529
|
Test with the built-in `TestHelper` (works with RSpec and Minitest):
|
|
@@ -523,7 +562,8 @@ Every MCP tool call fires an `ActiveSupport::Notifications` event:
|
|
|
523
562
|
|
|
524
563
|
```ruby
|
|
525
564
|
ActiveSupport::Notifications.subscribe("rails_ai_context.tools.call") do |event|
|
|
526
|
-
|
|
565
|
+
ms = (event.payload[:duration].to_f * 1000).round
|
|
566
|
+
Rails.logger.info "[MCP] #{event.payload[:tool_name]} - #{ms}ms"
|
|
527
567
|
end
|
|
528
568
|
```
|
|
529
569
|
|
data/config/routes.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
RailsAiContext::Engine.routes.draw do
|
|
4
|
-
|
|
4
|
+
# The engine is not namespace-isolated, so the controller must be
|
|
5
|
+
# referenced by its full path or Rails resolves a top-level McpController.
|
|
6
|
+
match "/", to: "rails_ai_context/mcp#handle", via: :all
|
|
5
7
|
end
|
|
@@ -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/CUSTOM_TOOLS.md
CHANGED
|
@@ -75,13 +75,20 @@ Add your tool classes to the configuration:
|
|
|
75
75
|
# config/initializers/rails_ai_context.rb
|
|
76
76
|
if defined?(RailsAiContext)
|
|
77
77
|
RailsAiContext.configure do |config|
|
|
78
|
-
config.custom_tools = [RailsGetBusinessMetrics]
|
|
78
|
+
config.custom_tools = ["RailsGetBusinessMetrics"]
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Custom tools appear alongside built-in tools in the MCP server and CLI.
|
|
84
84
|
|
|
85
|
+
Use the class *name* as a string: classes under `app/` (including
|
|
86
|
+
`app/mcp_tools/`) are not autoloadable while initializers run, so a bare
|
|
87
|
+
constant reference there aborts boot with a `NameError`. String entries are
|
|
88
|
+
resolved after the app finishes booting. A bare constant still works for
|
|
89
|
+
classes already loaded when the initializer runs (e.g. defined in `lib/`
|
|
90
|
+
and required manually).
|
|
91
|
+
|
|
85
92
|
## Using BaseTool features
|
|
86
93
|
|
|
87
94
|
For access to caching, pagination, and helper methods, you can use `RailsAiContext::Tools::BaseTool` utilities directly:
|
|
@@ -111,7 +118,14 @@ end
|
|
|
111
118
|
|
|
112
119
|
## Testing custom tools
|
|
113
120
|
|
|
114
|
-
Use the built-in `TestHelper` module
|
|
121
|
+
Use the built-in `TestHelper` module.
|
|
122
|
+
|
|
123
|
+
With the gem in `group: :development`, the initializer's guard makes
|
|
124
|
+
`config.custom_tools` a no-op in the test environment, so name-based lookup
|
|
125
|
+
(`execute_tool("rails_get_business_metrics")`) won't find custom tools
|
|
126
|
+
there. Either install the gem with `group: %i[development test]`, or pass
|
|
127
|
+
the class itself: `execute_tool(RailsGetBusinessMetrics)`. Built-in tools
|
|
128
|
+
resolve by name in any environment.
|
|
115
129
|
|
|
116
130
|
### With RSpec
|
|
117
131
|
|
|
@@ -191,7 +205,7 @@ If a custom tool replaces a built-in one, exclude the original:
|
|
|
191
205
|
|
|
192
206
|
```ruby
|
|
193
207
|
RailsAiContext.configure do |config|
|
|
194
|
-
config.custom_tools = [MyBetterSecurityScan]
|
|
208
|
+
config.custom_tools = ["MyBetterSecurityScan"]
|
|
195
209
|
config.skip_tools = %w[rails_security_scan]
|
|
196
210
|
end
|
|
197
211
|
```
|
data/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -100,6 +100,23 @@ For Codex CLI specifically, the env section in `.codex/config.toml` must match y
|
|
|
100
100
|
2. Check schema exists: `ls db/schema.rb` or `ls db/structure.sql`
|
|
101
101
|
3. Run doctor: `rails ai:doctor`
|
|
102
102
|
|
|
103
|
+
### MCP client reports a JSON parse error on the first line ("Resolving dependencies...")
|
|
104
|
+
|
|
105
|
+
RubyGems can print `Resolving dependencies...` to stdout while activating the
|
|
106
|
+
standalone `rails-ai-context` binary - before any gem code runs - which breaks
|
|
107
|
+
the pure-JSON stdio framing. This happens when gem activation needs the full
|
|
108
|
+
dependency resolver: typically a `GEM_PATH` that is missing part of the
|
|
109
|
+
dependency graph, or launching from inside another Bundler project's
|
|
110
|
+
directory. Fixes:
|
|
111
|
+
|
|
112
|
+
1. Launch `rails-ai-context serve` with the app root as the working directory
|
|
113
|
+
(the generated MCP configs do this by default).
|
|
114
|
+
2. Make sure `GEM_PATH` covers the complete default gem path
|
|
115
|
+
(`gem env path`), not just `gem env gemdir` - Ruby's bundled gems
|
|
116
|
+
(racc, etc.) live in a separate directory.
|
|
117
|
+
3. In-Gemfile installs are immune: `bundle exec rails-ai-context serve`
|
|
118
|
+
activates through the lockfile.
|
|
119
|
+
|
|
103
120
|
### MCP server responds slowly
|
|
104
121
|
|
|
105
122
|
- Check `config.cache_ttl` - lower values mean more frequent re-introspection
|