rails-ai-context 5.24.0 → 5.25.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 +251 -0
- data/CONTEXT.md +18 -0
- data/CONTRIBUTING.md +8 -0
- data/README.md +195 -467
- data/docs/ARCHITECTURE.md +26 -4
- data/docs/COMPATIBILITY.md +9 -6
- data/docs/CONFIGURATION.md +3 -3
- data/docs/FAQ.md +1 -1
- data/docs/GUIDE.md +8 -6
- data/docs/INTROSPECTORS.md +7 -6
- data/docs/TOOLS.md +4 -1
- data/exe/rails-ai-context +45 -260
- data/gemfiles/mcp_floor.gemfile +13 -0
- data/gemfiles/prism_floor.gemfile +1 -10
- data/gemfiles/prism_head.gemfile +1 -10
- data/gemfiles/rails.gemfile +34 -0
- data/lib/generators/rails_ai_context/install/install_generator.rb +2 -6
- data/lib/rails_ai_context/action_filters.rb +145 -0
- data/lib/rails_ai_context/app_kind.rb +2 -7
- data/lib/rails_ai_context/ast_cache.rb +0 -8
- data/lib/rails_ai_context/change_watch.rb +6 -15
- data/lib/rails_ai_context/cli/entry_boot.rb +214 -0
- data/lib/rails_ai_context/cli/tool_runner.rb +1 -1
- data/lib/rails_ai_context/concern_membership.rb +8 -1
- data/lib/rails_ai_context/configuration.rb +64 -9
- data/lib/rails_ai_context/doctor.rb +24 -17
- data/lib/rails_ai_context/engine.rb +6 -5
- data/lib/rails_ai_context/facts_formatter.rb +8 -11
- data/lib/rails_ai_context/fingerprinter.rb +48 -24
- data/lib/rails_ai_context/gem_lock.rb +97 -0
- data/lib/rails_ai_context/hydrators/controller_hydrator.rb +2 -9
- data/lib/rails_ai_context/hydrators/model_hints.rb +28 -0
- data/lib/rails_ai_context/hydrators/schema_hint_builder.rb +1 -8
- data/lib/rails_ai_context/hydrators/view_hydrator.rb +2 -9
- data/lib/rails_ai_context/install_mode.rb +3 -7
- data/lib/rails_ai_context/introspector.rb +21 -2
- data/lib/rails_ai_context/introspectors/action_resolver.rb +119 -2
- data/lib/rails_ai_context/introspectors/action_text_introspector.rb +2 -6
- data/lib/rails_ai_context/introspectors/active_storage_introspector.rb +6 -18
- data/lib/rails_ai_context/introspectors/active_support_introspector.rb +1 -0
- data/lib/rails_ai_context/introspectors/api_introspector.rb +26 -36
- data/lib/rails_ai_context/introspectors/asset_pipeline_introspector.rb +9 -13
- data/lib/rails_ai_context/introspectors/auth_introspector.rb +18 -45
- data/lib/rails_ai_context/introspectors/config_introspector.rb +17 -22
- data/lib/rails_ai_context/introspectors/controller_introspector.rb +29 -55
- data/lib/rails_ai_context/introspectors/convention_introspector.rb +34 -74
- data/lib/rails_ai_context/introspectors/declared_constant.rb +18 -2
- data/lib/rails_ai_context/introspectors/eager_load.rb +65 -0
- data/lib/rails_ai_context/introspectors/frontend_framework_introspector.rb +6 -4
- data/lib/rails_ai_context/introspectors/gem_introspector.rb +12 -35
- data/lib/rails_ai_context/introspectors/job_introspector.rb +51 -138
- data/lib/rails_ai_context/introspectors/listeners/method_call_listener.rb +31 -0
- data/lib/rails_ai_context/introspectors/listeners/methods_listener.rb +37 -5
- data/lib/rails_ai_context/introspectors/middleware_introspector.rb +14 -1
- data/lib/rails_ai_context/introspectors/migration_introspector.rb +32 -35
- data/lib/rails_ai_context/introspectors/model_introspector.rb +109 -74
- data/lib/rails_ai_context/introspectors/multi_database_introspector.rb +13 -21
- data/lib/rails_ai_context/introspectors/performance_introspector.rb +13 -39
- data/lib/rails_ai_context/introspectors/schema_introspector.rb +12 -38
- data/lib/rails_ai_context/introspectors/security_introspector.rb +3 -6
- data/lib/rails_ai_context/introspectors/source_scan.rb +80 -0
- data/lib/rails_ai_context/introspectors/test_introspector.rb +7 -17
- data/lib/rails_ai_context/introspectors/turbo_introspector.rb +276 -120
- data/lib/rails_ai_context/introspectors/view_template_introspector.rb +19 -0
- data/lib/rails_ai_context/mcp_config_generator.rb +2 -2
- data/lib/rails_ai_context/payload.rb +76 -11
- data/lib/rails_ai_context/pending_migrations.rb +60 -0
- data/lib/rails_ai_context/presets.rb +34 -4
- data/lib/rails_ai_context/redaction.rb +32 -1
- data/lib/rails_ai_context/resources.rb +6 -13
- data/lib/rails_ai_context/route_coverage.rb +1 -1
- data/lib/rails_ai_context/safe_path.rb +72 -0
- data/lib/rails_ai_context/schema_version.rb +13 -0
- data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +8 -9
- data/lib/rails_ai_context/serializers/compact_serializer_helper.rb +21 -23
- data/lib/rails_ai_context/serializers/context_file_serializer.rb +15 -1
- data/lib/rails_ai_context/serializers/copilot_instructions_serializer.rb +9 -13
- data/lib/rails_ai_context/serializers/copilot_serializer.rb +9 -11
- data/lib/rails_ai_context/serializers/cursor_rules_serializer.rb +9 -13
- data/lib/rails_ai_context/serializers/markdown_serializer.rb +64 -87
- data/lib/rails_ai_context/serializers/opencode_rules_serializer.rb +4 -10
- data/lib/rails_ai_context/serializers/section_facts.rb +26 -3
- data/lib/rails_ai_context/serializers/section_marker_writer.rb +11 -1
- data/lib/rails_ai_context/serializers/stack_overview_helper.rb +4 -21
- data/lib/rails_ai_context/serializers/test_command_detection.rb +1 -2
- data/lib/rails_ai_context/serializers/tool_guide_helper.rb +1 -2
- data/lib/rails_ai_context/tasks/rails_ai_context.rake +15 -53
- data/lib/rails_ai_context/tools/analyze_feature.rb +18 -43
- data/lib/rails_ai_context/tools/base_tool.rb +68 -24
- data/lib/rails_ai_context/tools/dependency_graph.rb +3 -4
- data/lib/rails_ai_context/tools/diagnose.rb +27 -21
- data/lib/rails_ai_context/tools/get_api.rb +65 -12
- data/lib/rails_ai_context/tools/get_concern.rb +36 -136
- data/lib/rails_ai_context/tools/get_config.rb +16 -14
- data/lib/rails_ai_context/tools/get_context.rb +60 -107
- data/lib/rails_ai_context/tools/get_controllers.rb +46 -182
- data/lib/rails_ai_context/tools/get_edit_context.rb +9 -37
- data/lib/rails_ai_context/tools/get_env.rb +6 -35
- data/lib/rails_ai_context/tools/get_frontend_stack.rb +9 -14
- data/lib/rails_ai_context/tools/get_helper_methods.rb +2 -23
- data/lib/rails_ai_context/tools/get_job_pattern.rb +96 -165
- data/lib/rails_ai_context/tools/get_model_details.rb +15 -60
- data/lib/rails_ai_context/tools/get_partial_interface.rb +16 -40
- data/lib/rails_ai_context/tools/get_routes.rb +1 -1
- data/lib/rails_ai_context/tools/get_schema.rb +19 -16
- data/lib/rails_ai_context/tools/get_stimulus.rb +1 -17
- data/lib/rails_ai_context/tools/get_test_info.rb +38 -22
- data/lib/rails_ai_context/tools/get_turbo_map.rb +113 -454
- data/lib/rails_ai_context/tools/get_view.rb +27 -142
- data/lib/rails_ai_context/tools/migration_advisor.rb +6 -10
- data/lib/rails_ai_context/tools/onboard.rb +68 -76
- data/lib/rails_ai_context/tools/performance_check.rb +5 -3
- data/lib/rails_ai_context/tools/read_logs.rb +13 -43
- data/lib/rails_ai_context/tools/review_changes.rb +4 -7
- data/lib/rails_ai_context/tools/runtime_info.rb +2 -6
- data/lib/rails_ai_context/tools/safe_call.rb +4 -4
- data/lib/rails_ai_context/tools/search_code.rb +6 -6
- data/lib/rails_ai_context/tools/search_docs.rb +3 -8
- data/lib/rails_ai_context/tools/validate.rb +8 -45
- data/lib/rails_ai_context/tools/validate_semantics.rb +20 -20
- data/lib/rails_ai_context/version.rb +1 -1
- data/lib/rails_ai_context/vfs.rb +44 -102
- data/lib/rails_ai_context/view_file.rb +59 -0
- data/lib/rails_ai_context.rb +12 -8
- data/server.json +1 -1
- metadata +15 -4
- data/lib/rails_ai_context/source_line.rb +0 -208
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d80b8bd17147a84fb1f8ce3028b7e1291af63c0d639663704eec171069e8f1b7
|
|
4
|
+
data.tar.gz: 4ce32bd09b90f16b62f70621642d6db35258c3a47892196e1fdb6d329366b4ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53ecb39c98209ddd59050431dca51b448c848e7f6e4d93fe092523ee87b014e6279aa25d97b283e57c66368eb0b315baaa4b75d0ea349d21bc6b0b1109ec0d2d
|
|
7
|
+
data.tar.gz: 25c7854ce8e4e4c1e16996e13a2031dc35d6cfee245d46515a48833c4fdcc48d35eb3a38b5365cfa4518847096340a104dc79dbfda2dfc314565a1508ea5dd7b
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,257 @@ 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.25.0] - 2026-09-03
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`sprockets-rails`, `sprockets`, `sorcery` and `clearance` join the notable
|
|
13
|
+
gem table**, so `rails_get_gems` lists them and the `rails_get_config` assets
|
|
14
|
+
and auth lines name them from the lockfile rather than from a loaded
|
|
15
|
+
constant.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
Defects found by a second survey round over the whole surface, and the
|
|
20
|
+
duplicated mechanisms behind them.
|
|
21
|
+
|
|
22
|
+
- **`max_view_total_size` and `max_view_file_size` said they capped view
|
|
23
|
+
reads.** Only `doctor` reads either one, as the threshold for its view-size
|
|
24
|
+
warning; the config comments and the docs rows now say that.
|
|
25
|
+
- **`excluded_concerns` hid a concern from a model's list but not from the
|
|
26
|
+
catalogue.** `rails_get_concern` and the `rails_get_active_support` concern
|
|
27
|
+
registry still listed and counted a concern the key names; all three now
|
|
28
|
+
apply the same predicate.
|
|
29
|
+
- **An in-Gemfile app's `.rails-ai-context.yml` was inert.** The generated
|
|
30
|
+
initializer holds a `configure` block, and the YAML was skipped whenever one
|
|
31
|
+
had run, so a booted command took the defaults while `--no-boot` read the
|
|
32
|
+
file and the two disagreed (45 tools against 43 with `skip_tools` set).
|
|
33
|
+
Precedence is a merge: the file is applied once, before
|
|
34
|
+
`config/initializers`, so an initializer may assign a key or edit it in
|
|
35
|
+
place and both survive; a later call from the standalone binary or the
|
|
36
|
+
CLI's boot path is a no-op. A block wins the keys it assigns wherever it
|
|
37
|
+
lives, so one in `config/application.rb` or an environment file, which runs
|
|
38
|
+
before the load, keeps them too.
|
|
39
|
+
- **The static tier named the app after its directory.** `onboard` and every
|
|
40
|
+
generated context file were headed "mastodon" for an app that declares
|
|
41
|
+
`module Mastodon`. The name now comes from the module enclosing
|
|
42
|
+
`class Application < Rails::Application` in `config/application.rb`, and
|
|
43
|
+
falls back to the directory only when that file names nothing.
|
|
44
|
+
- **The static tier counted every class under `app/models` as a model.**
|
|
45
|
+
Namespace modules, form objects, filters and plain service classes were
|
|
46
|
+
listed and rendered as models of a table (Mastodon answered 195 models
|
|
47
|
+
against 117 booted). A static model is now a class whose superclass chain
|
|
48
|
+
reaches `ApplicationRecord`, a namespaced `*ApplicationRecord` or
|
|
49
|
+
`ActiveRecord::Base`, STI subclasses included.
|
|
50
|
+
- **An unreadable `.rails-ai-context.yml` aborted the boot.** A directory or a
|
|
51
|
+
file the process cannot read at that path raised out of the engine
|
|
52
|
+
initializer; it now warns and keeps the defaults, the way broken YAML does.
|
|
53
|
+
- **A model under a per-connection abstract base was missing from the static
|
|
54
|
+
list.** The multi-database shape - `class AnimalsRecord < ApplicationRecord;
|
|
55
|
+
self.abstract_class = true` in its own file, then `class Dog <
|
|
56
|
+
AnimalsRecord` - dropped every model on that connection from the static
|
|
57
|
+
list, the count and the schema listing. An abstract base is still walked so
|
|
58
|
+
chains resolve through it; it is left out of the result, as before.
|
|
59
|
+
- **`rails_get_api` stated a filesystem finding for a section nobody had
|
|
60
|
+
read.** In the static tier the whole section but the mode was declared
|
|
61
|
+
unavailable, and nothing read that declaration, so an app with
|
|
62
|
+
`app/controllers/api/v1/` was told "not detected (no app/controllers/api/v*
|
|
63
|
+
directories)". Every detection in that section is a file read, so the
|
|
64
|
+
static tier now answers all of them, and a key a section does name as
|
|
65
|
+
unanswered renders as `[UNAVAILABLE: ...]` rather than as a negative
|
|
66
|
+
finding.
|
|
67
|
+
- **An existing `.ai-context.json` holding anything but a JSON object
|
|
68
|
+
aborted the whole generation run.** The skip check reads it as a file to
|
|
69
|
+
replace.
|
|
70
|
+
- **`rails_get_schema`'s table heading named every model on the table**, so
|
|
71
|
+
an STI table with thirty subclasses filled the cheap summary with one
|
|
72
|
+
heading. It names five and counts the rest, the way the file's other lists
|
|
73
|
+
do.
|
|
74
|
+
- **`rails_get_test_info` blamed the app root for a name it refused as
|
|
75
|
+
sensitive.** The refusal names what the check covers: the name leaves the
|
|
76
|
+
app root or names a sensitive file.
|
|
77
|
+
- **`rails_onboard`'s Getting Started block told the reader to `cd` into the
|
|
78
|
+
app's class name underscored**, which is not the directory the clone lands
|
|
79
|
+
in. It names the app directory.
|
|
80
|
+
- **`init` refused a tree with app source but no `config/environment.rb`
|
|
81
|
+
after it had already written the config files**, leaving it half set up
|
|
82
|
+
with no `CLAUDE.md`. Every command that can serve the static tier now
|
|
83
|
+
reads such a tree, the way `--no-boot` already did.
|
|
84
|
+
- **A tree with app source but no `config/environment.rb` printed a boot
|
|
85
|
+
failure before falling back to static analysis.** No boot can succeed
|
|
86
|
+
without that file, so the static tier now takes over at once and its banner
|
|
87
|
+
names the missing file; `doctor` refuses the same tree with that reason.
|
|
88
|
+
The banner calls that tree static mode rather than a boot failure, since
|
|
89
|
+
no boot ran.
|
|
90
|
+
- **`rails_runtime_info` and `rails_query` told a tree with no
|
|
91
|
+
`config/environment.rb` to fix a boot failure or drop `--no-boot`**, neither
|
|
92
|
+
of which had happened. Such a tree now reads "This tree has no
|
|
93
|
+
`config/environment.rb`; add one (or run from the app root) for runtime
|
|
94
|
+
data."
|
|
95
|
+
- **A source path was contained without a separator and against an
|
|
96
|
+
unresolved root.** The frontend framework introspector's own containment
|
|
97
|
+
check let `/app-old` pass for `/app`. The pre-v5.8.1 bug, still in one
|
|
98
|
+
place.
|
|
99
|
+
- **`rails_get_env` printed Dockerfile `ENV` and `ARG` values verbatim at
|
|
100
|
+
detail full.** Every default now leaves through the redaction gate, and a
|
|
101
|
+
surrounding quote pair is stripped, so `ENV FOO="bar"` prints `bar`.
|
|
102
|
+
- **`rails_read_logs` matched the `search` term before redacting**, so a
|
|
103
|
+
hidden value could be probed a character at a time. The search runs on the
|
|
104
|
+
redacted text now.
|
|
105
|
+
- **The middleware introspector fabricated an empty stack in the static
|
|
106
|
+
tier.** It declares an alternate source and answers only its file facts,
|
|
107
|
+
rather than reporting an app with no middleware.
|
|
108
|
+
- **`rails_get_config` and `rails-ai-context facts` read gem keys no
|
|
109
|
+
introspector emits**, so the Auth line and the Key Dependencies section
|
|
110
|
+
never rendered at all.
|
|
111
|
+
- **Jobs and mailers carry `file:`.** `rails_get_job_pattern` reads the
|
|
112
|
+
carried file instead of camelizing a basename, and its listing comes from
|
|
113
|
+
the payload, so a job in a pack lists.
|
|
114
|
+
- **The auth introspector keyed Devise models by basename**, so
|
|
115
|
+
`app/models/admin/user.rb` overwrote `User`.
|
|
116
|
+
- **An out-of-order migration merge was reported as none pending** by the
|
|
117
|
+
migrations section while the schema section counted it. Both derive from
|
|
118
|
+
one applied set now, and an unknown applied set answers no pending key
|
|
119
|
+
rather than "everything" or "nothing".
|
|
120
|
+
- **Substring lockfile scans reported `bugsnag-capistrano` as Bugsnag** and
|
|
121
|
+
`database_cleaner-redis` as database_cleaner. One lockfile reader with
|
|
122
|
+
exact names answers every gem question, across the GEM, GIT and PATH
|
|
123
|
+
sections.
|
|
124
|
+
- **The hydrators warned about a model they had just resolved in another
|
|
125
|
+
spelling**, and emitted one hint block twice.
|
|
126
|
+
- **The channel and mailer eager loads had no per-constant recovery**, so a
|
|
127
|
+
single unloadable file emptied the list.
|
|
128
|
+
- **`YAML.safe_load` refused the `&default` anchors** every stock webpacker
|
|
129
|
+
or shakapacker config carries, so the source path fell back to the
|
|
130
|
+
convention.
|
|
131
|
+
- **VFS: `controllers/admin/posts` fell into the action handler** and
|
|
132
|
+
`routes/PostsController` returned zero routes.
|
|
133
|
+
- **`Payload.section` accepted a refused (`unavailable`) section**, so a
|
|
134
|
+
static-tier context file rendered a bare heading.
|
|
135
|
+
- **Every generated file counts the same controller set.**
|
|
136
|
+
`config.excluded_controllers` is honoured everywhere, not only by
|
|
137
|
+
`rails_get_controllers`.
|
|
138
|
+
- **The doctor counted models and controllers under `app/` only.** Packs and
|
|
139
|
+
engines count, and the freshness check reads the one watch scope.
|
|
140
|
+
- **The watcher stopped noticing `config/routes.rb` and `db/schema.rb`.** The
|
|
141
|
+
fingerprint, the watcher and the doctor share one scope covering `config`
|
|
142
|
+
and `db` whole, and the fingerprint mark is taken before the read it
|
|
143
|
+
protects.
|
|
144
|
+
- **The diagnose tool's git probe ran outside a repository** and leaked its
|
|
145
|
+
stderr into the response.
|
|
146
|
+
- **`rails_get_stimulus` re-read every controller file** to recompute a
|
|
147
|
+
lifecycle the payload already carried.
|
|
148
|
+
- **MCP config removal wrote non-atomically**, the dead `AstCache.invalidate`
|
|
149
|
+
is gone, and the doctor checks that `.codex/config.toml` is gitignored.
|
|
150
|
+
- **Composing tools decided whether a sub-tool answered by scraping its prose
|
|
151
|
+
for "not found".** A real answer whose body mentioned those words was
|
|
152
|
+
dropped. Responses carry an empty marker in MCP `meta` now.
|
|
153
|
+
- **`rails_get_test_info` guarded a caller-supplied name with its own
|
|
154
|
+
containment.** It reads through the shared guard.
|
|
155
|
+
- **The asset pipeline introspector's literal `none` was rendered as if it
|
|
156
|
+
named a pipeline**, so every generated context file carried
|
|
157
|
+
`- Assets: none`. The line shows only the parts that name something, and
|
|
158
|
+
disappears when there are none.
|
|
159
|
+
- **`rails_get_view`'s ivar list counted an `@` inside an email address and a
|
|
160
|
+
`@@class_variable`.** Neither is an instance variable.
|
|
161
|
+
- **A controller ivar compared, not assigned, was reported as set.** `return
|
|
162
|
+
unless @post == current_user` no longer names `@post`; `||=`, `+=`, `<<=` and
|
|
163
|
+
`>>=` still count.
|
|
164
|
+
- **`rails_get_context` resolved an action name case-sensitively** while
|
|
165
|
+
`rails_get_controllers` did not, so `action: "Show"` skipped the ivar
|
|
166
|
+
cross-check.
|
|
167
|
+
- **The ivar cross-check ran with no view templates section**, reporting every
|
|
168
|
+
controller ivar as unused in the view. It is skipped instead.
|
|
169
|
+
- **`rails_get_turbo_map` printed a Turbo Stream response as a Ruby hash.**
|
|
170
|
+
It renders `PostsController#create`, the way the file's other sections name
|
|
171
|
+
a controller action.
|
|
172
|
+
- **A second `context` run rewrote the generated files** for their timestamp
|
|
173
|
+
alone, so a repo that commits the context files saw a diff from a run that
|
|
174
|
+
found nothing new. One rule now covers them all: a file that differs only
|
|
175
|
+
in its `generated_at` key, or in the full-mode header's `> Generated:`
|
|
176
|
+
line, is skipped.
|
|
177
|
+
- **The schema listing named one model per table.** A table an STI child or a
|
|
178
|
+
namespaced second model shares listed whichever came first in the payload,
|
|
179
|
+
which could be the emptier one. Every model on the table is listed now,
|
|
180
|
+
the one carrying the most detail first.
|
|
181
|
+
- **Every static-tier context file carried `Rails [UNAVAILABLE: app not
|
|
182
|
+
booted]` mid-sentence.** The version comes from the lockfile, and the
|
|
183
|
+
marker is left for a tree whose lockfile does not carry rails.
|
|
184
|
+
- **`rails-ai-context preset " FULL "` exited 1 with the listing.** A preset
|
|
185
|
+
name is matched whatever its case or padding.
|
|
186
|
+
- **A `--no-boot` run that found no app printed the doctor hint**, which asks
|
|
187
|
+
for a boot error that never happened. `doctor` and `init` also accept a
|
|
188
|
+
tree that has app source but no `config/environment.rb`, and the refusal
|
|
189
|
+
names that file instead of telling the user to go to the app root they are
|
|
190
|
+
standing in.
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
|
|
194
|
+
- **`RailsAiContext.generate_context(format: nil)` means the recorded AI-tool
|
|
195
|
+
selection**, and all of them when nothing is recorded. It was `:all`.
|
|
196
|
+
- **Static-tier migration names are the class-style name** (`CreatePosts`),
|
|
197
|
+
matching what the booted tier reports.
|
|
198
|
+
- **Names in the turbo, auth, attachments and multi-database sections carry
|
|
199
|
+
their namespace** (`Admin::User`, not `User`), and packs and in-repo
|
|
200
|
+
engines count everywhere the app's source is walked.
|
|
201
|
+
- **The `.env.example` reader judges a value by its shape and length only.** A
|
|
202
|
+
default in Ruby source or a Dockerfile is condemned by a secret-shaped name
|
|
203
|
+
as well.
|
|
204
|
+
- **Turbo wiring lives in the introspector payload with file and line** -
|
|
205
|
+
frames, model broadcasts, explicit broadcasts and stream subscriptions -
|
|
206
|
+
and `rails_get_turbo_map` renders it. A `turbo_frame_tag dom_id(@post,
|
|
207
|
+
:edit)` frame renders as that call, and a symbol `turbo_stream_from :posts`
|
|
208
|
+
now pairs with its broadcast. A subscription argument that carries its own
|
|
209
|
+
commas renders whole: `turbo_stream_from [current_user, :notifications]` and
|
|
210
|
+
`dom_id(@post, :x)` keep every character they were written with.
|
|
211
|
+
- **The public-methods lists come from the parser.** `rails_get_concern`,
|
|
212
|
+
`rails_get_helper_methods`, `rails_get_model_details` and
|
|
213
|
+
`rails_get_controllers` read `private def` and `class_methods do`
|
|
214
|
+
correctly.
|
|
215
|
+
- **The standalone binary boots through `CLI::EntryBoot`**, and presets run
|
|
216
|
+
through `Presets.run` in both the binary and the rake task.
|
|
217
|
+
- **`rails_get_controllers` and the controller-action resource decide which
|
|
218
|
+
filters apply through one module, `ActionFilters`.** Every inherited filter
|
|
219
|
+
carries the `(from Parent)` annotation, after_action and
|
|
220
|
+
except-constrained ones included; a filter constrained with `except:` shows
|
|
221
|
+
that constraint; `skip_after_action` and `skip_around_action` count as
|
|
222
|
+
skips, and a skipped filter is struck through in the whole-controller view
|
|
223
|
+
as well as the per-action one.
|
|
224
|
+
- **A config value longer than 40 characters is filtered on every path.** The
|
|
225
|
+
Ruby-source and Dockerfile readers capped at 30 before; `.env.example` was
|
|
226
|
+
already 40.
|
|
227
|
+
- **`schema[:pending_migrations]`, reachable through the `rails://schema`
|
|
228
|
+
resource, is a list of `{ version:, name: }` entries** rather than version
|
|
229
|
+
strings.
|
|
230
|
+
- **`rails-ai-context://routes/{controller}` prefers an exact route-key
|
|
231
|
+
match**, so `routes/posts` no longer also returns `admin/posts`.
|
|
232
|
+
- **`unavailable_sections`, where a section carries it, is a list of
|
|
233
|
+
section keys**, not a reason string. The middleware section's static
|
|
234
|
+
answer is the one that still emits it.
|
|
235
|
+
- **The models resource template advertises
|
|
236
|
+
`rails-ai-context://models/{name}`**, and every scheme resolves through the
|
|
237
|
+
VFS rather than an exact-match legacy reader. `rails://models/{name}` is
|
|
238
|
+
still accepted.
|
|
239
|
+
- **`controllers[:controllers]` can carry `{ error: "unreadable" }` entries**
|
|
240
|
+
for a file over the size cap or otherwise unreadable, and the listing and
|
|
241
|
+
the count include them.
|
|
242
|
+
|
|
243
|
+
### Removed
|
|
244
|
+
|
|
245
|
+
- **`SourceLine`, the hand-rolled Ruby lexer.** Every source read goes through
|
|
246
|
+
Prism.
|
|
247
|
+
- **`Fingerprinter.reset_gem_lib_fingerprint!` and `AstCache.invalidate`** -
|
|
248
|
+
no callers.
|
|
249
|
+
- **Support for the mcp gem below 0.13.** The gemspec floor is `>= 0.13`, the
|
|
250
|
+
first release whose tool responses carry `meta`.
|
|
251
|
+
- **`Presets.names` and `Presets.fetch`** - the listing and the run read
|
|
252
|
+
`DEFINITIONS` directly.
|
|
253
|
+
- **`SchemaHintBuilder.build_many`** - no callers.
|
|
254
|
+
- **`Fingerprinter.changed?`** - no callers; `stale?` answers the question.
|
|
255
|
+
- **The public constant `ChangeWatch::WATCH_DIRS`.**
|
|
256
|
+
- **`RailsAiContext.configured_via_block?`** - no callers once the config file
|
|
257
|
+
applies once per configuration and a block wins its keys by name.
|
|
258
|
+
|
|
8
259
|
## [5.24.0] - 2026-08-17
|
|
9
260
|
|
|
10
261
|
### Fixed
|
data/CONTEXT.md
CHANGED
|
@@ -28,6 +28,24 @@ Two senses, one per module, and neither is bare "path" in a name.
|
|
|
28
28
|
|
|
29
29
|
**How a path is written down** - `PortablePath` rewrites one so it means the same thing on another machine, because what it touches ends up in `.ai-context.json` and the app commits that file. App paths go app-relative, gem paths keep the gem and version and drop the install prefix. "Relativize" always means this.
|
|
30
30
|
|
|
31
|
+
## Safe path
|
|
32
|
+
|
|
33
|
+
A caller-supplied path resolved once, through `SafePath`, before anything reads it. Its refusal order is the contract, not an implementation detail: traversal, sensitive name, realpath, containment, sensitive realpath, file, size. A caller that reorders those checks, or repeats one of them itself, gets a different answer on a symlink or a dotfile than every other tool does, which is the divergence the module exists to end. `safe_glob` is the globbed-path form: the same guard applied to each path a pattern yields.
|
|
34
|
+
|
|
35
|
+
Not the same as a path that merely looks harmless, and not a caller's own containment check - "the tool guards this parameter" means it hands the parameter to `SafePath` and renders whatever refusal comes back.
|
|
36
|
+
|
|
37
|
+
## Carried path
|
|
38
|
+
|
|
39
|
+
A path the payload already holds because the gem's own walk found it - a controller's or a model's `file:`. It is re-read with `SafeFile.read(File.join(root, relative))`, which applies the size cap and nothing else. The reason is the walk: `SourceScan` deliberately keeps the spelling the app uses rather than the realpath, so a pack or an in-repo engine symlinked out of the root is spelled inside it, and `SafePath`'s realpath containment would refuse the very file the payload just named - the source comes back nil and a section silently empties.
|
|
40
|
+
|
|
41
|
+
Not an exception to the **safe path** rule, the other side of it: a caller-supplied path is untrusted and goes through `SafePath`; a carried path was produced by this gem and only needs the cap.
|
|
42
|
+
|
|
43
|
+
## Source scan
|
|
44
|
+
|
|
45
|
+
The one walk over a kind of app source, across every directory `PathResolver` resolves for it: conventional layout, packs, in-repo engines. `SourceScan.paths` stats only, `each` reads the source on top of it, and `classes` names each file by its declared constant. An introspector that globs `app/<kind>` itself is the mistake this entry exists to name: it misses every pack and engine, and it names files by their path rather than by what they declare.
|
|
46
|
+
|
|
47
|
+
Distinct from the **eager load** of the booted tier (`EagerLoad`), which makes constants exist rather than reading files, and from a **targeted walk** - one named file handed to `SourceIntrospector` with the listeners that file needs.
|
|
48
|
+
|
|
31
49
|
## Declared constant
|
|
32
50
|
|
|
33
51
|
What a source file calls its own class, as opposed to the **path name** - the constant its path camelizes to. The two differ wherever the app registers an inflection, because Zeitwerk resolves a path through the app's own inflector and the static tier has never loaded it: `app/controllers/activitypub/` is `ActivityPub` in Mastodon, and `Oauth` is a constant nothing defines. `DeclaredConstant` reads the class the source declares, and since an inflection only ever changes case, the declaration that names a file is the one equal to the path name ignoring case. Anything else - a second class in the file, a nested error class, a tree Prism recovered from a syntax error - is not this file's class, and there the path name stays the answer: it is the only thing carrying the namespace when the source does not.
|
data/CONTRIBUTING.md
CHANGED
|
@@ -82,6 +82,14 @@ bundle exec rspec spec/lib/ # Just lib specs
|
|
|
82
82
|
bundle exec rubocop --parallel # Lint check
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
### Static fixture
|
|
86
|
+
|
|
87
|
+
`spec/fixtures/static_app` is the app the static tier is exercised against, and
|
|
88
|
+
`spec/lib/rails_ai_context/serializers/real_shape_smoke_spec.rb` pins its model
|
|
89
|
+
count in two places (`Models 7` and `Models (7)`).
|
|
90
|
+
|
|
91
|
+
Adding or removing a fixture model means changing that spec in the same commit.
|
|
92
|
+
|
|
85
93
|
## Pull Request Process
|
|
86
94
|
|
|
87
95
|
1. Fork the repo and create your branch from `main`
|