rails-ai-context 5.32.2 → 5.34.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 +1491 -0
- data/CONTEXT.md +27 -3
- data/README.md +16 -12
- data/Rakefile +6 -0
- data/app/controllers/rails_ai_context/mcp_controller.rb +112 -84
- data/config/routes.rb +2 -3
- data/docs/ARCHITECTURE.md +7 -5
- data/docs/CLI.md +79 -6
- data/docs/COMPATIBILITY.md +75 -1
- data/docs/CONFIGURATION.md +30 -11
- data/docs/CUSTOM_TOOLS.md +2 -1
- data/docs/FAQ.md +16 -6
- data/docs/GUIDE.md +117 -71
- data/docs/INTROSPECTORS.md +10 -8
- data/docs/QUICKSTART.md +3 -3
- data/docs/RECIPES.md +1 -1
- data/docs/SECURITY.md +109 -25
- data/docs/SETUP.md +30 -6
- data/docs/STANDALONE.md +10 -3
- data/docs/TOOLS.md +152 -34
- data/docs/TROUBLESHOOTING.md +90 -12
- data/docs/adr/0003-shared-tool-cache-semantics.md +8 -0
- data/docs/adr/0005-workspace-mcp-entries.md +39 -0
- data/exe/rails-ai-context +733 -104
- data/lib/generators/rails_ai_context/install/install_generator.rb +341 -125
- data/lib/rails_ai_context/app_kind.rb +18 -5
- data/lib/rails_ai_context/ast_cache.rb +7 -0
- data/lib/rails_ai_context/base_mixins.rb +8 -8
- data/lib/rails_ai_context/boot_manager.rb +22 -3
- data/lib/rails_ai_context/change_watch.rb +14 -11
- data/lib/rails_ai_context/cli/app_root.rb +401 -0
- data/lib/rails_ai_context/cli/entry_boot.rb +58 -22
- data/lib/rails_ai_context/cli/tool_runner.rb +47 -21
- data/lib/rails_ai_context/code_reloader.rb +115 -1
- data/lib/rails_ai_context/concern_membership.rb +3 -1
- data/lib/rails_ai_context/concern_paths.rb +8 -3
- data/lib/rails_ai_context/configuration.rb +41 -9
- data/lib/rails_ai_context/context_file_report.rb +19 -2
- data/lib/rails_ai_context/database_file.rb +26 -0
- data/lib/rails_ai_context/doctor.rb +1122 -184
- data/lib/rails_ai_context/engine.rb +18 -1
- data/lib/rails_ai_context/erb_source.rb +19 -0
- data/lib/rails_ai_context/executable.rb +21 -0
- data/lib/rails_ai_context/facts_formatter.rb +5 -2
- data/lib/rails_ai_context/file_walk.rb +61 -10
- data/lib/rails_ai_context/fingerprinter/snapshot.rb +234 -0
- data/lib/rails_ai_context/fingerprinter.rb +40 -12
- data/lib/rails_ai_context/gem_lock.rb +186 -27
- data/lib/rails_ai_context/git_ignore.rb +22 -0
- data/lib/rails_ai_context/install/ai_tool.rb +31 -5
- data/lib/rails_ai_context/install/cleanup.rb +21 -0
- data/lib/rails_ai_context/install/program.rb +209 -35
- data/lib/rails_ai_context/install/selection_record.rb +258 -97
- data/lib/rails_ai_context/install/surface.rb +6 -1
- data/lib/rails_ai_context/install/validation_hook.rb +282 -0
- data/lib/rails_ai_context/install/workspace.rb +166 -0
- data/lib/rails_ai_context/install_mode.rb +71 -13
- data/lib/rails_ai_context/introspectors/action_mailbox_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/action_presence.rb +11 -6
- data/lib/rails_ai_context/introspectors/action_source.rb +182 -0
- data/lib/rails_ai_context/introspectors/action_text_introspector.rb +1 -2
- data/lib/rails_ai_context/introspectors/active_record_settings.rb +1 -1
- data/lib/rails_ai_context/introspectors/active_storage_introspector.rb +1 -3
- data/lib/rails_ai_context/introspectors/active_support_introspector.rb +3 -3
- data/lib/rails_ai_context/introspectors/admin_resources.rb +1 -1
- data/lib/rails_ai_context/introspectors/anyway_configs.rb +1 -1
- data/lib/rails_ai_context/introspectors/api_introspector.rb +3 -3
- data/lib/rails_ai_context/introspectors/auth_introspector.rb +15 -10
- data/lib/rails_ai_context/introspectors/call_site_expansion.rb +24 -1
- data/lib/rails_ai_context/introspectors/component_introspector.rb +3 -2
- data/lib/rails_ai_context/introspectors/controller_introspector.rb +387 -60
- data/lib/rails_ai_context/introspectors/convention_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/database_stats_introspector.rb +15 -3
- data/lib/rails_ai_context/introspectors/devops_introspector.rb +3 -1
- data/lib/rails_ai_context/introspectors/eager_load.rb +201 -9
- data/lib/rails_ai_context/introspectors/engine_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/env_config_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/env_references.rb +10 -1
- data/lib/rails_ai_context/introspectors/frontend_framework_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/gem_introspector.rb +23 -10
- data/lib/rails_ai_context/introspectors/grape_endpoints.rb +1 -1
- data/lib/rails_ai_context/introspectors/habtm_join_tables.rb +1 -1
- data/lib/rails_ai_context/introspectors/helper_names.rb +1 -1
- data/lib/rails_ai_context/introspectors/i18n_introspector.rb +3 -3
- data/lib/rails_ai_context/introspectors/job_introspector.rb +8 -6
- data/lib/rails_ai_context/introspectors/listeners/base_listener.rb +12 -8
- data/lib/rails_ai_context/introspectors/listeners/callbacks_listener.rb +78 -4
- data/lib/rails_ai_context/introspectors/listeners/config_assignment_listener.rb +1 -0
- data/lib/rails_ai_context/introspectors/listeners/env_access_listener.rb +10 -1
- data/lib/rails_ai_context/introspectors/listeners/foreign_evals_listener.rb +61 -0
- data/lib/rails_ai_context/introspectors/listeners/macros_listener.rb +13 -4
- data/lib/rails_ai_context/introspectors/listeners/migration_dsl_listener.rb +2 -1
- data/lib/rails_ai_context/introspectors/listeners/migration_replay_listener.rb +3 -2
- data/lib/rails_ai_context/introspectors/listeners/routes_dsl_listener.rb +29 -8
- data/lib/rails_ai_context/introspectors/listeners/schema_dsl_listener.rb +2 -2
- data/lib/rails_ai_context/introspectors/listeners/scopes_listener.rb +88 -12
- data/lib/rails_ai_context/introspectors/middleware_introspector.rb +2 -2
- data/lib/rails_ai_context/introspectors/migration_introspector.rb +27 -0
- data/lib/rails_ai_context/introspectors/migration_replay/helpers.rb +1 -1
- data/lib/rails_ai_context/introspectors/migration_replay/statements.rb +83 -27
- data/lib/rails_ai_context/introspectors/migration_replay.rb +15 -14
- data/lib/rails_ai_context/introspectors/model_introspector.rb +231 -23
- data/lib/rails_ai_context/introspectors/n_plus_one_scan.rb +620 -0
- data/lib/rails_ai_context/introspectors/performance_introspector.rb +11 -174
- data/lib/rails_ai_context/introspectors/route_introspector.rb +10 -1
- data/lib/rails_ai_context/introspectors/schema_conventions.rb +23 -3
- data/lib/rails_ai_context/introspectors/schema_introspector.rb +66 -16
- data/lib/rails_ai_context/introspectors/schema_reader.rb +4 -3
- data/lib/rails_ai_context/introspectors/seeds_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/source_scan.rb +39 -9
- data/lib/rails_ai_context/introspectors/stimulus_introspector.rb +15 -6
- data/lib/rails_ai_context/introspectors/test_introspector.rb +1 -1
- data/lib/rails_ai_context/introspectors/turbo_introspector.rb +131 -12
- data/lib/rails_ai_context/introspectors/view_introspector.rb +4 -4
- data/lib/rails_ai_context/introspectors/view_template_introspector.rb +15 -1
- data/lib/rails_ai_context/legacy_cleanup.rb +17 -4
- data/lib/rails_ai_context/live_reload.rb +20 -20
- data/lib/rails_ai_context/mcp_config_generator.rb +916 -109
- data/lib/rails_ai_context/mcp_edge.rb +182 -32
- data/lib/rails_ai_context/middleware.rb +5 -1
- data/lib/rails_ai_context/migration_status.rb +88 -25
- data/lib/rails_ai_context/output_guard.rb +48 -2
- data/lib/rails_ai_context/package_json.rb +1 -1
- data/lib/rails_ai_context/path_resolver.rb +46 -13
- data/lib/rails_ai_context/payload.rb +92 -8
- data/lib/rails_ai_context/pending_migrations.rb +5 -1
- data/lib/rails_ai_context/portable_path.rb +3 -2
- data/lib/rails_ai_context/presets.rb +15 -11
- data/lib/rails_ai_context/redaction.rb +262 -51
- data/lib/rails_ai_context/resources.rb +55 -20
- data/lib/rails_ai_context/route_coverage.rb +8 -0
- data/lib/rails_ai_context/safe_file.rb +14 -1
- data/lib/rails_ai_context/safe_path.rb +101 -4
- data/lib/rails_ai_context/schema_adapter.rb +8 -0
- data/lib/rails_ai_context/schema_version.rb +9 -0
- data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +88 -33
- data/lib/rails_ai_context/serializers/compact_serializer_helper.rb +36 -34
- data/lib/rails_ai_context/serializers/context_file_serializer.rb +21 -29
- data/lib/rails_ai_context/serializers/copilot_instructions_serializer.rb +8 -6
- data/lib/rails_ai_context/serializers/copilot_serializer.rb +2 -7
- data/lib/rails_ai_context/serializers/cursor_rules_serializer.rb +19 -16
- data/lib/rails_ai_context/serializers/guide_examples.rb +142 -0
- data/lib/rails_ai_context/serializers/markdown_serializer.rb +146 -30
- data/lib/rails_ai_context/serializers/opencode_rules_serializer.rb +38 -15
- data/lib/rails_ai_context/serializers/section_facts.rb +44 -1
- data/lib/rails_ai_context/serializers/section_marker_writer.rb +94 -12
- data/lib/rails_ai_context/serializers/stack_overview_helper.rb +24 -12
- data/lib/rails_ai_context/serializers/tool_guide_helper.rb +244 -129
- data/lib/rails_ai_context/server.rb +304 -71
- data/lib/rails_ai_context/tasks/rails_ai_context.rake +447 -372
- data/lib/rails_ai_context/tools/analyze_feature.rb +72 -18
- data/lib/rails_ai_context/tools/argument_schema.rb +49 -0
- data/lib/rails_ai_context/tools/base_tool.rb +367 -46
- data/lib/rails_ai_context/tools/brakeman_guard.rb +24 -0
- data/lib/rails_ai_context/tools/dependency_graph.rb +63 -28
- data/lib/rails_ai_context/tools/diagnose.rb +218 -11
- data/lib/rails_ai_context/tools/generate_test.rb +339 -39
- data/lib/rails_ai_context/tools/get_callbacks.rb +24 -13
- data/lib/rails_ai_context/tools/get_component_catalog.rb +1 -1
- data/lib/rails_ai_context/tools/get_concern.rb +91 -27
- data/lib/rails_ai_context/tools/get_config.rb +13 -4
- data/lib/rails_ai_context/tools/get_context.rb +71 -14
- data/lib/rails_ai_context/tools/get_controllers.rb +179 -44
- data/lib/rails_ai_context/tools/get_conventions.rb +28 -6
- data/lib/rails_ai_context/tools/get_edit_context.rb +27 -14
- data/lib/rails_ai_context/tools/get_env.rb +18 -9
- data/lib/rails_ai_context/tools/get_frontend_stack.rb +3 -1
- data/lib/rails_ai_context/tools/get_gems.rb +1 -1
- data/lib/rails_ai_context/tools/get_helper_methods.rb +10 -6
- data/lib/rails_ai_context/tools/get_i18n.rb +1 -1
- data/lib/rails_ai_context/tools/get_job_pattern.rb +3 -2
- data/lib/rails_ai_context/tools/get_mailers.rb +3 -3
- data/lib/rails_ai_context/tools/get_model_details.rb +30 -10
- data/lib/rails_ai_context/tools/get_partial_interface.rb +242 -23
- data/lib/rails_ai_context/tools/get_routes.rb +29 -8
- data/lib/rails_ai_context/tools/get_schema.rb +52 -19
- data/lib/rails_ai_context/tools/get_service_pattern.rb +24 -10
- data/lib/rails_ai_context/tools/get_stimulus.rb +37 -1
- data/lib/rails_ai_context/tools/get_test_info.rb +18 -4
- data/lib/rails_ai_context/tools/get_turbo_map.rb +221 -58
- data/lib/rails_ai_context/tools/get_view.rb +14 -14
- data/lib/rails_ai_context/tools/migration_advisor.rb +355 -48
- data/lib/rails_ai_context/tools/onboard.rb +52 -8
- data/lib/rails_ai_context/tools/performance_check.rb +5 -3
- data/lib/rails_ai_context/tools/query.rb +914 -86
- data/lib/rails_ai_context/tools/read_logs.rb +69 -15
- data/lib/rails_ai_context/tools/review_changes.rb +215 -53
- data/lib/rails_ai_context/tools/runtime_info.rb +51 -1
- data/lib/rails_ai_context/tools/safe_call.rb +35 -14
- data/lib/rails_ai_context/tools/search_code.rb +243 -67
- data/lib/rails_ai_context/tools/search_docs.rb +4 -4
- data/lib/rails_ai_context/tools/security_scan.rb +143 -33
- data/lib/rails_ai_context/tools/session_context.rb +39 -5
- data/lib/rails_ai_context/tools/validate.rb +259 -40
- data/lib/rails_ai_context/tools/validate_semantics.rb +130 -56
- data/lib/rails_ai_context/version.rb +1 -1
- data/lib/rails_ai_context/vfs.rb +67 -40
- data/lib/rails_ai_context/view_file.rb +41 -3
- data/lib/rails_ai_context/watcher.rb +51 -13
- data/lib/rails_ai_context.rb +21 -0
- data/server.json +1 -1
- metadata +14 -1
data/docs/CLI.md
CHANGED
|
@@ -40,6 +40,10 @@ rails-ai-context serve --transport http --port 6029 # HTTP transport
|
|
|
40
40
|
| `--port` | `http_port` from config, else `6029` | HTTP listen port |
|
|
41
41
|
| `--no-boot` | off | Skip booting the app; answer from source alone |
|
|
42
42
|
|
|
43
|
+
Ctrl-C or SIGTERM stops the HTTP server and exits 0. Requests still running get 3 seconds, then Puma's own grace period of 5, before they are cut off, and the process exits within 15 seconds of the signal whatever else is still running.
|
|
44
|
+
|
|
45
|
+
The server announces itself to the client as `server_name` from config, else `rails-ai-context`. The `RAILS_AI_CONTEXT_SERVER_NAME` environment variable takes the place of that default for one process; a `server_name` the app sets itself still wins. A [workspace](#init-standalone-only) entry sets the variable to put its app first, since VS Code names every tool after the announced name and keeps 13 characters of it.
|
|
46
|
+
|
|
43
47
|
### `tool`
|
|
44
48
|
|
|
45
49
|
Run any of the 45 MCP tools from the terminal.
|
|
@@ -67,7 +71,18 @@ Global options may be typed before or after the command name:
|
|
|
67
71
|
`rails-ai-context --app-path /srv/app doctor` and
|
|
68
72
|
`rails-ai-context doctor --app-path /srv/app` run the same check.
|
|
69
73
|
|
|
70
|
-
`--app-path` names the Rails root,
|
|
74
|
+
`--app-path` names the Rails root, taken as given. Left out, every command
|
|
75
|
+
that reads an app finds it the way `bin/rails` and Bundler do: the working
|
|
76
|
+
directory if it is an app, else the nearest app root above it (a directory with
|
|
77
|
+
`config/application.rb`, `config/environment.rb`, or a `bin/rails` that boots an
|
|
78
|
+
app or an engine; never `$HOME`, `node_modules` or a gem install), else the one
|
|
79
|
+
app a level or two below it - looked for everywhere but `/`, `$HOME` and the
|
|
80
|
+
directories above it, which hold too much to search. A walk names the app it chose on stderr, as
|
|
81
|
+
`[rails-ai-context] using app at a/`, so `serve` keeps stdout clean. A folder
|
|
82
|
+
holding several apps is refused with one `--app-path` command per app, and
|
|
83
|
+
`tool --list` there lists the tools without an app. The chosen root is the
|
|
84
|
+
process's working directory for the rest of the run, so `init`, the config and
|
|
85
|
+
every file written land in it.
|
|
71
86
|
`--environment` names the `RAILS_ENV` to boot under. Left out, it resolves the
|
|
72
87
|
way Rails resolves its own environment: the ambient `RAILS_ENV`, then
|
|
73
88
|
`RACK_ENV`, then `development`, reading an empty value as unset. So an app
|
|
@@ -114,7 +129,13 @@ from source alone, which is what you want on a repo you have just cloned, on an
|
|
|
114
129
|
app whose boot is broken, and in CI where booting costs more than the answer.
|
|
115
130
|
|
|
116
131
|
The same tier is entered automatically when a boot fails, so you get an answer
|
|
117
|
-
either way.
|
|
132
|
+
either way. A boot that hangs (an initializer waiting on a service that is
|
|
133
|
+
down) counts as failed after `RAILS_AI_CONTEXT_BOOT_TIMEOUT` seconds: 20 for
|
|
134
|
+
`serve` over stdio, whose client waits about 30 seconds for its first answer
|
|
135
|
+
and then gives up, and 60 for every other command. Raise it for an app that
|
|
136
|
+
is healthy but slow to boot, and for stdio also raise the client's own limit
|
|
137
|
+
(Claude Code reads `MCP_TIMEOUT`, in milliseconds), or the client stops
|
|
138
|
+
waiting first. Answers that need a running app are marked `[UNAVAILABLE: ...]`
|
|
118
139
|
rather than guessed, and everything else is tagged `[STATIC]` instead of
|
|
119
140
|
`[VERIFIED]`. `docs/COMPATIBILITY.md` lists which of the 40 introspectors answer
|
|
120
141
|
in which tier.
|
|
@@ -161,6 +182,8 @@ rails-ai-context context --format claude # Specific format
|
|
|
161
182
|
|:-------|:--------|:------------|
|
|
162
183
|
| `--format` | all configured | `claude`, `cursor`, `copilot`, `opencode`, `codex`, `json`, `all` |
|
|
163
184
|
|
|
185
|
+
Without `--format`, `rails ai:context` and `rails-ai-context context` also bring the selected tools' MCP configs up to date, in the command form the install takes now - after `bundle remove rails-ai-context` that is the bare binary. A tool whose config a [workspace](#init-standalone-only) above already serves is left to it. `rails ai:context:<tool>` writes that tool's config along with its files.
|
|
186
|
+
|
|
164
187
|
The rake tasks `ai:context`, `ai:context:<format>` and `ai:context_for` also read
|
|
165
188
|
`CONTEXT_MODE`: `CONTEXT_MODE=full rails ai:context` overrides `config.context_mode`
|
|
166
189
|
for that run. The standalone binary does not read it.
|
|
@@ -176,7 +199,11 @@ rails-ai-context doctor --strict # exit 1 when any check fails (CI gate)
|
|
|
176
199
|
rails ai:doctor STRICT=1 # rake equivalent
|
|
177
200
|
```
|
|
178
201
|
|
|
179
|
-
Checks include: schema existence, pending migrations, model files, routes, MCP config
|
|
202
|
+
Checks include: schema existence, pending migrations in every database (and a database that does not exist or does not answer), model files, routes, whether the command each MCP config holds can start, the Codex env snapshot, whether a context run would rewrite the context files, introspector health, ripgrep availability, the Prism and mcp gems, Brakeman gem, listen gem, secret files and .gitignore, auto_mount security, schema size, view count, and more. The [Guide](GUIDE.md#diagnostics) lists every check.
|
|
203
|
+
|
|
204
|
+
At a mountable engine's root, which boots no app of its own, `bundle exec rails-ai-context doctor` (or `rails-ai-context doctor` for a standalone install) diagnoses the dummy app the engine's `bin/rails` boots - `test/dummy` or `spec/dummy` - and reads the install at the engine's root: its `.rails-ai-context.yml`, MCP configs and context files, the ones an editor opened there uses. `bin/rails app:ai:doctor` reads them there too. Its fixes name the commands that run at the engine's root, such as `bundle exec rails-ai-context context`, and its context check runs that command into a copy of the files to see which it would rewrite.
|
|
205
|
+
|
|
206
|
+
Every path the report names is named from where the command was typed, and every command a fix gives runs there. Typed in an app's parent folder (`rails-ai-context --app-path shop doctor`), the app's files are named `shop/...`, and a command of the app's goes there first: `rails-ai-context --app-path shop context`, `cd shop && bin/rails db:migrate`, `git -C shop rm --cached config/master.key`. A line for `.gitignore` names the app's own and is written the way that file reads it, from the app's root: Add to shop/.gitignore: `.env.local`. Typed in a folder inside the app (`app/models`), the report names the app's files from its root, as the app names them, while a fix still works where it was typed: `cd ../.. && bin/rails db:migrate`, `git -C ../.. rm --cached .env`, `rails-ai-context --app-path ../.. context`, and Add to ../../.gitignore: `.env`. `rails ai:doctor` reads that folder from the shell's `PWD`, since Rails' `rails` command and rake move up to the app's root before the task runs. Typed at an engine's root, the dummy app's files read `test/dummy/db/schema.rb` and the engine's own `Gemfile.lock` and `app/views`, a command of the engine's runs "at the engine's root" (`bundle install` there needs no folder), and one of the dummy app's goes to `test/dummy` first; typed inside `test/dummy`, the engine's files read `../../Gemfile.lock` and its commands run "in the engine at ../..". A fix that starts with `cd` leaves the shell in that folder, so type the next one from where doctor ran.
|
|
180
207
|
|
|
181
208
|
### `watch`
|
|
182
209
|
|
|
@@ -187,7 +214,7 @@ rails ai:watch
|
|
|
187
214
|
rails-ai-context watch
|
|
188
215
|
```
|
|
189
216
|
|
|
190
|
-
Requires the `listen` gem. Watches `app/`, `config/`, `db/`, `lib/`, `rakelib/`, `test/`, `spec/`.
|
|
217
|
+
Requires the `listen` gem. `rails ai:watch` needs it in the app's Gemfile; the standalone binary uses the app's when its bundle locks one, and otherwise one installed with `gem install listen`. Watches `app/`, `config/`, `db/`, `lib/`, `rakelib/`, `test/`, `spec/`.
|
|
191
218
|
|
|
192
219
|
### `init` (standalone only)
|
|
193
220
|
|
|
@@ -198,7 +225,48 @@ rails-ai-context init
|
|
|
198
225
|
rails-ai-context init --mcp-only # MCP config only, no context files
|
|
199
226
|
```
|
|
200
227
|
|
|
201
|
-
Asks which AI tools to configure and what to write: MCP config and context files, context files only (CLI mode), or MCP config only. Creates `.rails-ai-context.yml`, the MCP config files (except in CLI mode) and the context files (except with MCP config only). `--mcp-only` skips the second question.
|
|
228
|
+
Asks which AI tools to configure and what to write: MCP config and context files, context files only (CLI mode), or MCP config only. Creates `.rails-ai-context.yml`, the MCP config files (except in CLI mode) and the context files (except with MCP config only). `--mcp-only` skips the second question. Every question - including a re-run's cleanup of dropped tools, and in CLI mode whether to take the gem's entries out of existing MCP configs - is asked before the app boots and anything is written; with MCP config only, the app is not booted at all.
|
|
229
|
+
|
|
230
|
+
Run inside an app, `init` sets up that app (found the way every command finds it: the [app root](#tool) above a subdirectory, or the one `--app-path` names).
|
|
231
|
+
|
|
232
|
+
Run in a folder that is no app but holds apps one or two levels down - a **workspace**, the folder an editor or agent is often opened at - it sets the folder up as a whole:
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
work/ <- run `rails-ai-context init` here
|
|
236
|
+
.mcp.json one server per app: rails-ai-context-api, rails-ai-context-web
|
|
237
|
+
.cursor/mcp.json ... (every selected AI tool's MCP config, in the folder it reads)
|
|
238
|
+
api/ .rails-ai-context.yml, CLAUDE.md, ... each app keeps its own config and context files
|
|
239
|
+
web/ .rails-ai-context.yml, CLAUDE.md, ...
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The AI tools and the setup are asked once. Each app gets its own `.rails-ai-context.yml` and context files, because its server reads them from its own root; a child process generates them with the install that app's server will run. The folder's MCP configs get one server per app, because a client reads its project config from the folder it opened. Each entry is named `rails-ai-context-<app folder>` (the whole path where two apps share a folder name; a short hash where the name would pass 30 characters, since Cursor drops a tool whose server and tool names together pass 60) and points `--app-path` at its app:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"mcpServers": {
|
|
247
|
+
"rails-ai-context-api": {
|
|
248
|
+
"command": "rails-ai-context",
|
|
249
|
+
"args": ["serve", "--app-path", "api"],
|
|
250
|
+
"env": { "RAILS_AI_CONTEXT_SERVER_NAME": "api-rails-ai-context" }
|
|
251
|
+
},
|
|
252
|
+
"rails-ai-context-web": {
|
|
253
|
+
"command": "bundle",
|
|
254
|
+
"args": ["exec", "rails-ai-context", "serve", "--app-path", "web"],
|
|
255
|
+
"env": { "BUNDLE_GEMFILE": "web/Gemfile", "RAILS_AI_CONTEXT_SERVER_NAME": "web-rails-ai-context" }
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
- **Paths stay relative**, so a committed file means the same thing on every machine. Claude Code, Codex and OpenCode start a server in the folder they were launched in, so their configs name the app from the workspace (`api`). Cursor and VS Code have a name for the folder their config sits in, `${workspaceFolder}`, so their configs name the app from it (`${workspaceFolder}/api`); Cursor does not promise a working directory.
|
|
262
|
+
- **The command form is each app's own**: the bare binary where the gem is not in the app's Gemfile.lock, `bundle exec` where it is, with `BUNDLE_GEMFILE` naming the Gemfile the app's bundle reads - its own (a linked one by the link's name, so its lockfile stays beside it), else the one its `config/boot.rb` names, as a monorepo's apps share one, else the one `bundle exec` run inside the app would find - since `bundle exec` started in the workspace would look for one there.
|
|
263
|
+
- **`RAILS_AI_CONTEXT_SERVER_NAME` puts the app first** in the name the server announces. VS Code names every tool after that name and keeps 13 characters of it, so without it every app's tools would start `rails-ai-cont`. It is a variable rather than a flag so that an app whose bundle pins an older gem ignores it and starts.
|
|
264
|
+
- A bare `rails-ai-context` entry left in the folder is replaced, since it would serve the folder itself, and so is an entry under a name the gem gives an app in this folder (its folder name or path) when that app's folder is gone, or the app now goes by another name because an app with the same folder name arrived. An entry named `rails-ai-context-...` that runs `rails-ai-context serve` is the gem's: dropping an AI tool removes it from that tool's config. One that runs no command, such as an HTTP entry of your own under either name, is left alone, and so is a second entry you named yourself for an app (`rails-ai-context-api-2` included), one that names its app through a variable the tool expands (`${userHome}`), and one naming an app outside the folder.
|
|
265
|
+
- A config that does not parse as JSON, or holds comments a rewrite would drop, is left as it is, with a warning that names the entry to add by hand. Trailing commas, which VS Code's and OpenCode's take, are read under json 2.9 or later; Claude Code's `.mcp.json` and Cursor's `.cursor/mcp.json` are held to plain JSON, so a comment or trailing comma there is reported as one the client cannot read. An empty one is filled, and a byte order mark at the head of one stays.
|
|
266
|
+
- `--app-path` naming a folder of apps sets that folder up the same way; every other command lists the apps below it.
|
|
267
|
+
- An app whose bundle is not installed yet (a Gemfile and no lockfile) is served by this binary unless its Gemfile names the gem, or pulls gems in from other files (`eval_gemfile`, `gemspec`) that could.
|
|
268
|
+
|
|
269
|
+
Start the client in the workspace folder. A client started inside one of the apps (Claude Code, Codex and OpenCode still read a config above it) runs each workspace server from there, where the relative path misses; the server says which folder the path was written for. To open an app on its own, run `init` inside it as well. `init` warns when an app declares a Ruby other than the one the workspace runs, since every server it starts runs under that one. `doctor` inside one of the apps finds the workspace's config one or two levels up.
|
|
202
270
|
|
|
203
271
|
### `version`
|
|
204
272
|
|
|
@@ -239,7 +307,8 @@ rails 'ai:preset[architecture]'
|
|
|
239
307
|
### `tree`
|
|
240
308
|
|
|
241
309
|
Print a tree of every command. `rails-ai-context help <command>` lists that
|
|
242
|
-
command's options
|
|
310
|
+
command's options, and so do `rails-ai-context <command> --help` and `-h`
|
|
311
|
+
(after a tool's name, `--help` is that tool's: `tool schema --help`).
|
|
243
312
|
|
|
244
313
|
```bash
|
|
245
314
|
rails-ai-context tree
|
|
@@ -306,6 +375,10 @@ rails-ai-context tool schema --table users --json
|
|
|
306
375
|
JSON=1 rails 'ai:tool[schema]' table=users
|
|
307
376
|
```
|
|
308
377
|
|
|
378
|
+
stdout then holds the envelope (`tool`, `output`, `error`) and nothing else.
|
|
379
|
+
Whatever the app prints while it boots or while the tool runs - an
|
|
380
|
+
initializer's `puts`, Sidekiq's Redis connection line - goes to stderr.
|
|
381
|
+
|
|
309
382
|
---
|
|
310
383
|
|
|
311
384
|
## Common workflows
|
data/docs/COMPATIBILITY.md
CHANGED
|
@@ -30,7 +30,11 @@ document is aspirational.
|
|
|
30
30
|
quirks_mode` inside Rails itself. Rails 8.1 dropped the keyword and resolves
|
|
31
31
|
json 3 cleanly. The test bundle pins `json < 3` below 8.1 for that reason; an
|
|
32
32
|
app on those lines has the same choice to make, and it is between its own
|
|
33
|
-
Rails and json rather than anything here.
|
|
33
|
+
Rails and json rather than anything here. In such an app the stdio server
|
|
34
|
+
still works, because the gem writes its stdio replies with `JSON.generate`.
|
|
35
|
+
Over HTTP the MCP SDK serializes each reply with `to_json`, so every request
|
|
36
|
+
answers with a JSON-RPC error naming `unknown keyword: quirks_mode` until
|
|
37
|
+
the pair is fixed.
|
|
34
38
|
|
|
35
39
|
The gemspec's `railties` bound is wider than the CI matrix: point releases inside
|
|
36
40
|
7.0-8.1 and any future 8.x minor satisfy Bundler's constraint without a gem
|
|
@@ -60,6 +64,31 @@ Source: `.github/workflows/unit-matrix.yml`, which both `ci.yml` and
|
|
|
60
64
|
a second run inside the matrix (Ruby 3.3 / Rails 8.0), since `release.yml`
|
|
61
65
|
has no `lint` job of its own.
|
|
62
66
|
|
|
67
|
+
### Operating systems
|
|
68
|
+
|
|
69
|
+
Linux, macOS and Windows. CI runs the unit suite on Linux for every cell
|
|
70
|
+
above, and on macOS (Ruby 3.4 / Rails 8.1 and Ruby 3.1 / Rails 7.0) and
|
|
71
|
+
Windows (Ruby 3.4 / Rails 8.1). The E2E harness, which builds real apps and
|
|
72
|
+
drives the CLI, the rake tasks and both transports, runs on Linux for six
|
|
73
|
+
Ruby / Rails pairs and on macOS and Windows for Ruby 3.4 / Rails 8.1. A
|
|
74
|
+
release waits for all of them.
|
|
75
|
+
|
|
76
|
+
On Windows:
|
|
77
|
+
|
|
78
|
+
- The MCP configs start the server through `cmd /c`, since a gem's
|
|
79
|
+
executables there are batch files that a client starting its server
|
|
80
|
+
without a shell does not run. See [GUIDE.md](GUIDE.md#mcp-server-setup)
|
|
81
|
+
for teams on both Windows and macOS or Linux.
|
|
82
|
+
- `rails_query` on SQLite runs without its time limit: the limit runs the
|
|
83
|
+
query in a forked child, and Windows has no `fork`. The answer says so.
|
|
84
|
+
- The pre-commit hook runs under the sh that Git for Windows runs hooks with.
|
|
85
|
+
- A checkout whose lines end in CRLF, Git for Windows' default, is read and
|
|
86
|
+
written in them: `rails_search_code` ends a line before its CRLF, so `$`
|
|
87
|
+
anchors as it does on LF, and the context files' managed block and the
|
|
88
|
+
initializer's `config.ai_tools` line keep the file's own line endings.
|
|
89
|
+
- ripgrep installs with `winget install BurntSushi.ripgrep.MSVC`; without it
|
|
90
|
+
`rails_search_code` takes the slower Ruby path.
|
|
91
|
+
|
|
63
92
|
### Rails 9
|
|
64
93
|
|
|
65
94
|
Not supported, and the two install paths fail differently:
|
|
@@ -245,6 +274,25 @@ Proof sources:
|
|
|
245
274
|
limit; the installer's cleanup on a small Rails 8.1 app holding hand-written
|
|
246
275
|
rule files; and the unit suite on Ruby 3.1 with Rails 7.0 through Ruby 4.0
|
|
247
276
|
with Rails 8.1.
|
|
277
|
+
In the v5.33.0 release QA: the release gem in a fresh app on Ruby 3.1 with
|
|
278
|
+
Rails 7.0 and 7.1, Ruby 3.2 with Rails 8.0, and Rails 8.1 on Ruby 3.3, 3.4
|
|
279
|
+
and 4.0, each through the standalone and in-Gemfile installs, the rake
|
|
280
|
+
tasks and all 45 tools over MCP stdio; a Rails 8.1 app reached through a
|
|
281
|
+
symlinked path, as macOS spells its temporary folders; a Rails 8.1 and a
|
|
282
|
+
Rails 7.0 app mounting the engine, six clients calling while a model
|
|
283
|
+
changed every 0.3 seconds; the pre-commit hook installed and run by real
|
|
284
|
+
commits in an app's own repository, a monorepo's folder and a submodule;
|
|
285
|
+
and the unit suite and the E2E harness on Linux, macOS and Windows.
|
|
286
|
+
In the v5.34.0 release QA: the 21 apps of the v5.33.0 acceptance run
|
|
287
|
+
(Mastodon, OpenProject, Discourse, Canvas, Forem, Whitehall, Consul, Open
|
|
288
|
+
Food Network, Diaspora, Errbit, Huginn, Plots2, a private app and the small
|
|
289
|
+
blogs), 11 booted and 10 static, their answers compared with v5.33.0's,
|
|
290
|
+
and their strong params and model tables compared again after the last
|
|
291
|
+
fix; a fresh Rails 8.1 and a fresh Rails 7.0 app through the install,
|
|
292
|
+
`ai:context` twice, `zeitwerk:check` with the engine mounted, MCP over the
|
|
293
|
+
mounted HTTP endpoint and stdio, and the standalone CLI; an app that loads
|
|
294
|
+
a gem before `bundler/setup`; and the unit suite and the E2E harness on
|
|
295
|
+
Linux, macOS and Windows.
|
|
248
296
|
2. Non-crash coverage for every built-in tool including `get_view` in
|
|
249
297
|
`spec/e2e/in_gemfile_install_spec.rb`'s full-tool sweep; output correctness
|
|
250
298
|
(ivar cross-check, render-form detection, partial interfaces) verified
|
|
@@ -403,6 +451,32 @@ Postgres instance in `spec/e2e/postgres_install_spec.rb`, opt-in via
|
|
|
403
451
|
under `config/initializers` adds with `Rails.application.routes.prepend` or
|
|
404
452
|
`.append` are read, before and after the draw as Rails orders them. Routes Rails' own
|
|
405
453
|
engines and gems draw into the app's table are read only with the app booted.
|
|
454
|
+
- **A folder of apps is served from the folder.** `init` run in a folder of
|
|
455
|
+
apps writes entries that name each app relative to that folder, because
|
|
456
|
+
Claude Code, Codex and OpenCode start a server in the folder they were
|
|
457
|
+
launched in, and Cursor and VS Code expand `${workspaceFolder}`. A tool
|
|
458
|
+
launched inside one of the apps still reads the folder's config above it,
|
|
459
|
+
and its servers then start where the relative path misses. Every server in
|
|
460
|
+
the folder also runs under the Ruby the version manager picks in the folder,
|
|
461
|
+
so an app that declares another may answer from the static tier, and its
|
|
462
|
+
`bundle exec` entry may not start; `init` warns when it sees one.
|
|
463
|
+
`spec/e2e/workspace_install_spec.rb` starts each entry the way its tool does,
|
|
464
|
+
on a standalone and an in-Gemfile app. ADR-0005 records the per-tool facts.
|
|
465
|
+
- **A file saved twice within one second at the same size can reload as the
|
|
466
|
+
first save.** Bootsnap's compile cache, which `config/boot.rb` sets up in a
|
|
467
|
+
new Rails app, knows a Ruby file by its size and the second of its mtime.
|
|
468
|
+
A file written again within that second at the same size gets the code
|
|
469
|
+
compiled from the earlier write back from it: on the reload the write sets
|
|
470
|
+
off, and in every process that loads the file after, the app's own server
|
|
471
|
+
and tests included, until the file is written in another second. Bootsnap
|
|
472
|
+
has no way to drop one entry, and the server does not touch the app's files
|
|
473
|
+
to move their stat, so a server that reloads code names such a file at the
|
|
474
|
+
end of every answer (under `_stale_code` in a JSON body), from the call
|
|
475
|
+
that sees the save until the file is saved again. What reflection reads,
|
|
476
|
+
such as associations and enums, may be the earlier save's meanwhile.
|
|
477
|
+
Saving the file again a second later puts the edit through, and
|
|
478
|
+
`DISABLE_BOOTSNAP_COMPILE_CACHE=1` in the server's environment turns the
|
|
479
|
+
compile cache off for that process.
|
|
406
480
|
|
|
407
481
|
<br>
|
|
408
482
|
|
data/docs/CONFIGURATION.md
CHANGED
|
@@ -50,7 +50,7 @@ preset: full
|
|
|
50
50
|
| Option | Type | Default | Description |
|
|
51
51
|
|:-------|:-----|:--------|:------------|
|
|
52
52
|
| `ai_tools` | Array of symbols | `nil` (all five) | Which AI tools to generate context for. Options: `:claude`, `:cursor`, `:copilot`, `:opencode`, `:codex`. Unset, it reads the selection the installer recorded, and all five when there is none |
|
|
53
|
-
| `tool_mode` | Symbol | `:mcp` | `:mcp` (MCP server primary, CLI fallback) or `:cli` (CLI only, no MCP server) |
|
|
53
|
+
| `tool_mode` | Symbol | `:mcp` | `:mcp` (MCP server primary, CLI fallback) or `:cli` (CLI only, no MCP server). Any other value is refused. Under `:cli` the tools' own next-step hints name the command (`rails-ai-context tool schema table=posts`, or the rake task when the gem is in the Gemfile) rather than an MCP call |
|
|
54
54
|
| `context_files` | Boolean | `true` | Set `false` for MCP-only: the server and the CLI still answer, and no context file is written or touched |
|
|
55
55
|
|
|
56
56
|
#### MCP only
|
|
@@ -86,25 +86,26 @@ empty list wrote every tool's files.
|
|
|
86
86
|
| Option | Type | Default | Description |
|
|
87
87
|
|:-------|:-----|:--------|:------------|
|
|
88
88
|
| `preset` | Symbol | `:full` | `:full` (40 introspectors) or `:standard` (17 introspectors) |
|
|
89
|
-
| `context_mode` | Symbol | `:compact` | `:compact` (context files capped at ~150 lines) or `:full` (no line cap) |
|
|
89
|
+
| `context_mode` | Symbol | `:compact` | `:compact` (context files capped at ~150 lines) or `:full` (no line cap). Any other value is refused, and so is any other `CONTEXT_MODE` on a rake task |
|
|
90
90
|
| `introspectors` | Array of symbols | (from preset) | Override the introspector list directly |
|
|
91
91
|
| `generate_root_files` | Boolean | `true` | Set `false` to generate split rules only: no CLAUDE.md, AGENTS.md, .cursorrules or copilot-instructions.md (`.ai-context.json` is still written) |
|
|
92
92
|
| `anti_hallucination_rules` | Boolean | `true` | Embed 6-rule verification protocol in generated context files |
|
|
93
|
-
| `claude_max_lines` | Integer | `150` |
|
|
93
|
+
| `claude_max_lines` | Integer | `150` | Budget for the non-blank lines of a compact context file's gem-managed block, the `<!-- BEGIN/END rails-ai-context -->` markers included. Over budget, the data sections (Stack, Key models, Gems, Architecture) are cut first, down to none; the title and the Commands, Warnings, Rules and Tools sections are never cut, so they set the smallest file a budget can give (about 110 lines with the anti-hallucination protocol). Must be positive |
|
|
94
94
|
|
|
95
95
|
### MCP Server
|
|
96
96
|
|
|
97
97
|
| Option | Type | Default | Validation | Description |
|
|
98
98
|
|:-------|:-----|:--------|:-----------|:------------|
|
|
99
|
-
| `server_name` | String | `"rails-ai-context"` | - | MCP server name |
|
|
99
|
+
| `server_name` | String | `"rails-ai-context"` | - | The MCP server name the server announces. When it is left at the default, the `RAILS_AI_CONTEXT_SERVER_NAME` environment variable takes its place for one process, which is how a workspace's entries announce their app |
|
|
100
100
|
| `cache_ttl` | Integer | `60` | Must be positive | Cache time-to-live in seconds |
|
|
101
101
|
| `max_tool_response_chars` | Integer | `200_000` | Must be positive | Safety cap for tool responses and MCP resource payloads. An over-cap resource keeps its JSON shape: whole elements are dropped and reported under a `_truncated` key |
|
|
102
|
-
| `live_reload` | Symbol/Boolean | `:auto` | - | `:auto` (uses `listen` gem if available), `true`, or `false
|
|
102
|
+
| `live_reload` | Symbol/Boolean | `:auto` | - | `:auto` (uses `listen` gem if available), `true`, or `false`. The watch tells clients when files change; every server checks the app's files at each tool call either way, so its answers follow edits with or without it |
|
|
103
103
|
| `live_reload_debounce` | Float | `1.5` | - | Seconds to wait before processing file changes |
|
|
104
104
|
| `auto_mount` | Boolean | `false` | - | Auto-mount Rack middleware for HTTP transport |
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
105
|
+
| `allow_http_in_production` | Boolean | `false` | - | Let the mounted engine and `auto_mount` answer in production and every other environment but development and test. Off, they refuse there with a 403 and a JSON-RPC error saying how to opt in. Every tool answers whoever reaches the endpoint, so set it only with the endpoint behind your app's authentication (see [SECURITY.md](SECURITY.md#mcp-http-transport)). The endpoint answers the hosts the app's `config.hosts` allows |
|
|
106
|
+
| `http_path` | String | `"/mcp"` | - | HTTP endpoint path, for `auto_mount` and the standalone HTTP server (the engine answers where you mount it) |
|
|
107
|
+
| `http_bind` | String | `"127.0.0.1"` | - | Address the standalone HTTP server (`serve --transport http`, `rails ai:serve_http`) binds. Another address opens the port to the network with no authentication, but the MCP SDK answers only requests whose `Host` header is `127.0.0.1`, `::1` or `localhost`: a client that uses the machine's IP or name gets 403 "Invalid Host header", and one that sends `Host: localhost` gets every tool. It suits a container whose port is published to the host. The engine and `auto_mount` use the app's own server and ignore it |
|
|
108
|
+
| `http_port` | Integer | `6029` | 1 to 65535 | Port of the standalone HTTP server. The engine and `auto_mount` use the app's own server and ignore it |
|
|
108
109
|
|
|
109
110
|
### Cross-Tool Hydration
|
|
110
111
|
|
|
@@ -119,7 +120,7 @@ empty list wrote every tool's files.
|
|
|
119
120
|
|:-------|:-----|:--------|:------------|
|
|
120
121
|
| `excluded_models` | Array | 8 framework models | Models to skip during introspection |
|
|
121
122
|
| `excluded_controllers` | Array | 2 framework controllers | Controllers to skip |
|
|
122
|
-
| `excluded_route_prefixes` | Array |
|
|
123
|
+
| `excluded_route_prefixes` | Array | 7 framework prefixes | Route controller prefixes `rails_get_routes` hides with `app_only`: the routes Rails' engines and gems draw on their own. A route the app's route files declare (`devise_for`, the health check) is not one |
|
|
123
124
|
| `excluded_filters` | Array | 5 framework filters | Controller filters to skip |
|
|
124
125
|
| `excluded_middleware` | Array | 25 framework middleware | Middleware to skip in listing |
|
|
125
126
|
| `excluded_paths` | Array | `["node_modules", "tmp", "log", "vendor", ".git", "doc", "docs"]` | Paths excluded from search |
|
|
@@ -223,13 +224,31 @@ its exact name blocks it; see [SECURITY.md](SECURITY.md).
|
|
|
223
224
|
| Option | Type | Default | Description |
|
|
224
225
|
|:-------|:-----|:--------|:------------|
|
|
225
226
|
| `custom_tools` | Array | `[]` | Additional MCP::Tool classes to register (initializer only - a class reference cannot be written in YAML) |
|
|
226
|
-
| `skip_tools` | Array | `[]` | Built-in tool names to exclude (e.g., `%w[rails_security_scan]`) |
|
|
227
|
+
| `skip_tools` | Array | `[]` | Built-in tool names to exclude (e.g., `%w[rails_security_scan]`). A skipped tool is left out of every list and of the `_Next:` hints other tools end with, and a call that names it anyway - from the CLI, rake or an MCP client - says it is turned off here |
|
|
227
228
|
|
|
228
229
|
### Output
|
|
229
230
|
|
|
230
231
|
| Option | Type | Default | Description |
|
|
231
232
|
|:-------|:-----|:--------|:------------|
|
|
232
|
-
| `output_dir` | String | Rails.root | Directory for generated context files |
|
|
233
|
+
| `output_dir` | String | Rails.root | Directory for generated context files. OpenCode reads `app/models/AGENTS.md` and `app/controllers/AGENTS.md` beside the code, so those two are written only where the directory exists under it, and named `no app/models under output_dir` otherwise |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Environment variables
|
|
238
|
+
|
|
239
|
+
Read from the environment of the process, so an MCP config sets them in its
|
|
240
|
+
server entry's `env`.
|
|
241
|
+
|
|
242
|
+
| Variable | Read by | Effect |
|
|
243
|
+
|:---------|:--------|:-------|
|
|
244
|
+
| `RAILS_AI_CONTEXT_BOOT_TIMEOUT` | Every command that boots the app, and the `ai:serve`, `ai:serve_http` and `ai:tool` rake tasks | Seconds a boot may take before it counts as failed; a command that can then answers from the static tier. Default 20 for `serve` over stdio and `rails ai:serve`, whose client waits about 30 seconds for its first answer, and 60 for everything else. A value that is not a whole number warns and keeps the default |
|
|
245
|
+
| `RAILS_AI_CONTEXT_SERVER_NAME` | The MCP server | The name it announces for one process, in place of the default `server_name`; a `server_name` the app sets wins. A workspace's MCP entries set it |
|
|
246
|
+
| `DEBUG` | Every command and the MCP server | Any value prints the backtrace of a failed boot, a failed command and an exception that escapes a tool, and names the errors a fallback swallows |
|
|
247
|
+
| `RAILS_ENV`, `RACK_ENV` | Every command | The Rails environment to boot, read the way Rails reads it; `--environment` sets it for one command. Default `development` |
|
|
248
|
+
| `JSON` | `rails 'ai:tool[NAME]'` | `JSON=1` prints the JSON envelope, as `--json` does |
|
|
249
|
+
| `CONTEXT_MODE` | `rails ai:context`, `ai:context:<format>` and `ai:context_for` | `compact` or `full` for one run, in place of `config.context_mode`. Any other value stops the task |
|
|
250
|
+
| `FORMAT` | `rails ai:context_for` | The format when the task is given none: `FORMAT=cursor rails ai:context_for`. Default `claude` |
|
|
251
|
+
| `STRICT` | `rails ai:doctor` | `STRICT=1` (or `true`, `yes`) exits 1 when a check fails, as `doctor --strict` does |
|
|
233
252
|
|
|
234
253
|
---
|
|
235
254
|
|
data/docs/CUSTOM_TOOLS.md
CHANGED
|
@@ -80,7 +80,7 @@ if defined?(RailsAiContext) && RailsAiContext.respond_to?(:configure)
|
|
|
80
80
|
end
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Custom tools appear alongside built-in tools in the MCP server and CLI.
|
|
83
|
+
Custom tools appear alongside built-in tools in the MCP server and CLI, and in the tool counts and lists of the generated context files. The tools table gives a custom tool its own row, described by the first sentence of its `description`.
|
|
84
84
|
|
|
85
85
|
Use the class *name* as a string: classes under `app/` (including
|
|
86
86
|
`app/mcp_tools/`) are not autoloadable while initializers run, so a bare
|
|
@@ -216,6 +216,7 @@ end
|
|
|
216
216
|
- Keep tools read-only when possible. MCP tools annotated as non-destructive build more trust with AI clients.
|
|
217
217
|
- Return `MCP::Tool::Response` objects with `type: "text"` content blocks.
|
|
218
218
|
- A response you build with `MCP::Tool::Response.new` is sent as written. The `config.max_tool_response_chars` cap (default: 200,000) is applied by the built-in tools' `text_response`, so keep a custom tool's output short yourself.
|
|
219
|
+
- Through the mounted engine a call's answer is sent whole when the call is over. Progress and log notifications a tool sends (`server_context.report_progress`, `server_context.notify_log_message`) arrive together with its answer, and a request it makes of the client (sampling, elicitation, roots) fails at once, saying why. stdio and the standalone HTTP server deliver both as they are sent.
|
|
219
220
|
|
|
220
221
|
---
|
|
221
222
|
|
data/docs/FAQ.md
CHANGED
|
@@ -47,7 +47,11 @@ Yes. The gem gracefully degrades - it parses `db/schema.rb` as text when no data
|
|
|
47
47
|
|
|
48
48
|
### Can I switch between Gemfile and standalone?
|
|
49
49
|
|
|
50
|
-
Yes, freely. Both
|
|
50
|
+
Yes, freely. Both write the same context files and serve the same 45 tools; only the commands those files name follow the install (`rails ai:context` and `rails 'ai:tool[...]'` in the Gemfile, `rails-ai-context context` and `rails-ai-context tool ...` standalone). Re-run the install or `init` after switching: it updates the MCP config files and regenerates the context files with the new commands.
|
|
51
|
+
|
|
52
|
+
### My editor opens a folder that holds several apps. Does that work?
|
|
53
|
+
|
|
54
|
+
Yes. Run `rails-ai-context init` in that folder. It asks once, gives each app its own `.rails-ai-context.yml` and context files, and writes one MCP server per app into the folder's configs, `rails-ai-context-<app folder>`, so your AI tool sees every app and each app's tools are named after it. Start the tool in that folder: the entries name each app relative to it. See [`init`](CLI.md#init-standalone-only).
|
|
51
55
|
|
|
52
56
|
### Do I need to commit the generated files?
|
|
53
57
|
|
|
@@ -83,7 +87,7 @@ Start with `rails_onboard` for an app overview, `rails_analyze_feature` for feat
|
|
|
83
87
|
|
|
84
88
|
### Can I add my own tools?
|
|
85
89
|
|
|
86
|
-
Yes. See [Custom Tools](CUSTOM_TOOLS.md). Create an `MCP::Tool` subclass, register it via `config.custom_tools`, and it appears alongside the 45 built-in tools.
|
|
90
|
+
Yes. See [Custom Tools](CUSTOM_TOOLS.md). Create an `MCP::Tool` subclass, register it via `config.custom_tools`, and it appears alongside the 45 built-in tools, in the generated context files' tool counts and lists too.
|
|
87
91
|
|
|
88
92
|
### Can I remove built-in tools?
|
|
89
93
|
|
|
@@ -93,6 +97,8 @@ Yes. Use `config.skip_tools`:
|
|
|
93
97
|
config.skip_tools = %w[rails_security_scan rails_query]
|
|
94
98
|
```
|
|
95
99
|
|
|
100
|
+
The server, the CLI and the generated context files then leave them out alike, so no context file tells the AI to call a tool that answers "Unknown tool".
|
|
101
|
+
|
|
96
102
|
### What's the `detail` parameter?
|
|
97
103
|
|
|
98
104
|
Individual lookup tools accept `detail`: `summary` (compact), `standard` (default), `full` (everything). Start with summary and drill down as needed. This keeps AI context windows lean. Composite tools (`rails_get_context`, `rails_analyze_feature`) do not accept `detail` - they always return their full bundled output.
|
|
@@ -172,15 +178,19 @@ Yes. Add content outside the `<!-- BEGIN/END rails-ai-context -->` markers. The
|
|
|
172
178
|
|
|
173
179
|
### Is introspection slow?
|
|
174
180
|
|
|
175
|
-
Introspection results are cached with TTL (default: 60s) and fingerprint invalidation. The first call is slower; subsequent calls use cache. Prism AST parsing uses a single-pass Dispatcher - all 8 listeners run in one tree walk.
|
|
181
|
+
Introspection results are cached with TTL (default: 60s) and fingerprint invalidation. The first call is slower; subsequent calls use cache until a watched file changes. Prism AST parsing uses a single-pass Dispatcher - all 8 listeners run in one tree walk.
|
|
176
182
|
|
|
177
183
|
### Does this affect my app's performance?
|
|
178
184
|
|
|
179
|
-
No, as long as the gem sits in the Gemfile's `:development` group, where `bundle add rails-ai-context --group development` puts it: Bundler then never loads it in production.
|
|
185
|
+
No, as long as the gem sits in the Gemfile's `:development` group, where `bundle add rails-ai-context --group development` puts it: Bundler then never loads it in production. Where the gem is loaded in production, it refuses two things there by default: `rails_query`, and MCP over HTTP from inside the app (the mounted engine and `auto_mount`). Tools execute on demand (not continuously). The MCP server is a separate process (stdio), or an HTTP endpoint that exists only when you mount it or set `auto_mount`.
|
|
180
186
|
|
|
181
187
|
### How does live reload work?
|
|
182
188
|
|
|
183
|
-
The `listen` gem watches `app/`, `config/`, `db/`, `lib/`, `rakelib/`, `test/`, `spec
|
|
189
|
+
The `listen` gem watches `app/`, `config/`, `db/`, `lib/`, `rakelib/`, `test/`, `spec/`, and when files change, MCP clients are notified. Debounce interval: 1.5s (configurable).
|
|
190
|
+
|
|
191
|
+
Answers do not depend on it. Every tool call first checks those files, with `listen` or without it (a new Rails 8 app does not bundle it), and when one changed it reloads the app's code and drops the caches, so a call sees an edit made a moment before it. The check costs about a millisecond on a small app and about 30 ms at 10,000 files.
|
|
192
|
+
|
|
193
|
+
Under `RAILS_ENV=test`, or in production with eager loading, the app does not reload code, so what reflection reads (associations, enums) stays as it was at boot; once app code changes, every answer ends with a note naming the files, and a restart brings it up to date.
|
|
184
194
|
|
|
185
195
|
---
|
|
186
196
|
|
|
@@ -228,7 +238,7 @@ Those are AI coding interfaces. This gem is a data layer that makes *any* of the
|
|
|
228
238
|
|
|
229
239
|
### My AI tool doesn't see the MCP server
|
|
230
240
|
|
|
231
|
-
Run `rails ai:doctor` - it checks MCP config
|
|
241
|
+
Run `rails ai:doctor` - it checks each configured tool's MCP config file, and whether the command it holds can start. See [Troubleshooting](TROUBLESHOOTING.md) for detailed steps.
|
|
232
242
|
|
|
233
243
|
### Tools return empty results
|
|
234
244
|
|