rails-ai-context 5.9.0 → 5.10.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 +58 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +5 -5
- data/Rakefile +28 -0
- data/docs/ARCHITECTURE.md +2 -2
- data/docs/CONFIGURATION.md +2 -2
- data/docs/FAQ.md +1 -1
- data/docs/GUIDE.md +9 -9
- data/docs/INTROSPECTORS.md +22 -4
- data/docs/TROUBLESHOOTING.md +1 -1
- data/docs/index.md +1 -1
- data/lib/rails_ai_context/configuration.rb +2 -1
- data/lib/rails_ai_context/introspector.rb +9 -1
- data/lib/rails_ai_context/introspectors/active_support_introspector.rb +173 -0
- data/lib/rails_ai_context/introspectors/autoload_introspector.rb +135 -0
- data/lib/rails_ai_context/introspectors/connection_pool_introspector.rb +139 -0
- data/lib/rails_ai_context/introspectors/credentials_introspector.rb +137 -0
- data/lib/rails_ai_context/introspectors/env_introspector.rb +141 -0
- data/lib/rails_ai_context/introspectors/initializer_introspector.rb +113 -0
- data/lib/rails_ai_context/introspectors/listeners/base_listener.rb +3 -3
- data/lib/rails_ai_context/introspectors/observability_introspector.rb +208 -0
- data/lib/rails_ai_context/introspectors/security_introspector.rb +179 -0
- data/lib/rails_ai_context/serializers/stack_overview_helper.rb +3 -3
- data/lib/rails_ai_context/tools/base_tool.rb +1 -1
- data/lib/rails_ai_context/tools/diagnose.rb +5 -5
- data/lib/rails_ai_context/tools/get_concern.rb +5 -3
- data/lib/rails_ai_context/tools/review_changes.rb +4 -4
- data/lib/rails_ai_context/tools/validate.rb +3 -3
- data/lib/rails_ai_context/version.rb +1 -1
- data/server.json +2 -2
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b022525ce9aa13c5b6d5f21a46f13db5ae84dac04f346adc40eeb2b315f9fcaa
|
|
4
|
+
data.tar.gz: 9e9f7bd60ecdab6a5d1599e560762c8c7d95a8973f5d001c4573d00f55b2a594
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7dca71cfea9fb5106b40f5eaebd119e36bcd1cbcbdd41b8dcd8ba4b11eb802dbb647d0c90db88b4d21a98d303ed2ed42f605ca7278a245a76155fc57cb0366cf
|
|
7
|
+
data.tar.gz: baaeaa8a5eec91ba86c6aee32f5a0d459ebcc7f14c1eaec64e7110afe7368985e005b92dcdee960606525806d652cd63f5eb1092773765cbe831857cffde75a1
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,64 @@ 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.10.0] — 2026-04-20
|
|
9
|
+
|
|
10
|
+
### Added — 8 new introspectors closing RAILS_NERVOUS_SYSTEM.md gaps
|
|
11
|
+
|
|
12
|
+
An audit against [`RAILS_NERVOUS_SYSTEM.md`](RAILS_NERVOUS_SYSTEM.md) identified 9 framework sections where introspection was missing or partial. This release ships the 8 introspectors needed to close them (the 9th — §11 Query interface — was already covered by `:conventions` via `load_async` / `.async_*` scanning). All are wired into `PRESETS[:full]`. The gem now exposes **39 introspectors** (up from 31).
|
|
13
|
+
|
|
14
|
+
- **`InitializerIntrospector` (`:initializers`, §2).** Enumerates `Rails.application.initializers` — every initializer's name, owner, declared `before:` / `after:` ordering edges, and the `source_location` of its block. Also summarizes each `config/initializers/*.rb` file (initializer count + the top `config.*` setters it touches) so AI can jump straight to user-owned boot code.
|
|
15
|
+
- **`AutoloadIntrospector` (`:autoload`, §3).** Zeitwerk presence, both `Rails.autoloaders.main` and `.once` with their collapsed dirs + ignored paths + root dirs, raw `autoload_paths` / `autoload_once_paths` / `eager_load_paths`, the resolved `eager_load` boolean, plus custom inflection rules extracted from `config/initializers/*.rb` (`inflect.acronym` / `.plural` / `.singular` / `.irregular` / `.uncountable` / `.human`). Paths are root-relative.
|
|
16
|
+
- **`ConnectionPoolIntrospector` (`:connection_pool`, §10).** Per-database adapter config: `pool`, `checkout_timeout`, `idle_timeout`, `reaping_frequency`, `prepared_statements`, `advisory_locks`, replica flag, role, connection-handler pool counts per role (`:writing` / `:reading`), and automatic shard selector detection (Rails 7.1+ `ActiveRecord::Middleware::ShardSelector`). Complements `:database_stats` (which only returns row counts).
|
|
17
|
+
- **`ActiveSupportIntrospector` (`:active_support`, §17).** Covers ActiveSupport runtime surface other introspectors leave untouched: Concerns in `app/**/concerns/` (with `ActiveSupport::Concern` / `included do` / `class_methods do` flags), `Rails.application.deprecators` registry keys, MessageEncryptor + MessageVerifier usage scan across `lib/` + `app/`, TaggedLogging configuration (`config.log_tags` + initializer-based `ActiveSupport::TaggedLogging.new`), active on-load hooks, and cache-store options.
|
|
18
|
+
- **`CredentialsIntrospector` (`:credentials`, §30).** Default `config/credentials.yml.enc` + every per-env `config/credentials/<env>.yml.enc`, master-key source resolution (`env:RAILS_MASTER_KEY` / `file:config/master.key` / `missing`), `config.require_master_key` flag, arbitrary encrypted configs (`config/<name>.yml.enc` pairs), and top-level credential **key names only** — decrypted hash is inspected for `.keys` and nothing more. A regression spec asserts no known credential value appears in the output.
|
|
19
|
+
- **`SecurityIntrospector` (`:security`, §32).** Framework-level security controls `auth_introspector` doesn't cover: `config.force_ssl`, SSL options (HSTS `expires` / `subdomains` / `preload`, `redirect`, `secure_cookies`), `config.hosts` + `host_authorization` options, `ContentSecurityPolicy` directives (including `report_only`), `PermissionsPolicy` directives, CSRF config (`protect_from_forgery` declaration, `per_form_csrf_tokens`, `forgery_protection_origin_check`), cookie session options (`:key`, `:secure`, `:httponly`, `:same_site`, `:domain`, `:path`, `:expire_after`), and Rails 7.2+ `allow_browser` calls per controller.
|
|
20
|
+
- **`ObservabilityIntrospector` (`:observability`, §34 + §38).** `ActiveSupport::LogSubscriber.log_subscribers` catalog (class + namespace), full `ActiveSupport::Notifications` subscriber registry walked via `@string_subscribers` / `@other_subscribers` / legacy `@subscribers` (handles Rails 7.0/7.1/8.x variants — grouped by pattern with subscriber count + sample class name), `ActionDispatch::ServerTiming` middleware detection + `config.server_timing` flag, Rails 8.1 `event_reporter` availability, log level + tags + `colorize_logging`, and a static catalog of 60+ canonical Rails event names across 10 subsystems (`action_controller`, `action_view`, `active_record`, `active_job`, `action_mailer`, `action_mailbox`, `action_cable`, `active_support`, `active_storage`, `railties`).
|
|
21
|
+
- **`EnvIntrospector` (`:env`, §36).** Curated catalog of 30+ Rails-related ENV vars (core, server, bundler, assets, boot, secrets, database, cache, deploy, platform, observability, testing) partitioned into `set` / `unset`. Safe vars (`RAILS_ENV`, `RAILS_MAX_THREADS`, `PORT`, etc.) return their value. Sensitive vars (`SECRET_KEY_BASE`, `RAILS_MASTER_KEY`, `DATABASE_URL`, `REDIS_URL`, `KAMAL_REGISTRY_PASSWORD`, etc.) return `redacted: true` only — the value never leaves the process. Also scans `config/`/`app/`/`lib/` for app-specific `ENV["X"]` / `ENV.fetch("X")` references beyond the catalog.
|
|
22
|
+
|
|
23
|
+
### Why these specifically
|
|
24
|
+
|
|
25
|
+
Each corresponds to a `RAILS_NERVOUS_SYSTEM.md` section the audit flagged as uncovered. Partial-coverage sections (§2 filenames, §3 Zeitwerk presence, §17 CurrentAttributes, §27 Solid Trifecta, §30 boolean, §32 CORS/CSP/force_ssl, §34 N+1 anti-patterns, §36 puma/procfile) are preserved — the new introspectors complement rather than replace existing ones.
|
|
26
|
+
|
|
27
|
+
### Preset wiring
|
|
28
|
+
|
|
29
|
+
All 8 are in `PRESETS[:full]`. None are in `PRESETS[:standard]` — they're framework-runtime data most valuable in `full` mode where comprehensive context outranks boot speed. `config.introspectors.size` for `:full` is now `39` (from `31`). The configuration spec was updated accordingly.
|
|
30
|
+
|
|
31
|
+
### Tests
|
|
32
|
+
|
|
33
|
+
Every new introspector ships with a unit spec under `spec/lib/rails_ai_context/introspectors/*_introspector_spec.rb`, plus an orchestrator-level assertion in `spec/lib/rails_ai_context/introspector_spec.rb` that all 8 keys land in the context hash, plus a real-Rails-app e2e spec at `spec/e2e/nervous_system_introspectors_spec.rb` (8 examples, run via `E2E=1`). Specs assert shape guarantees, error absence, category-specific invariants, and the `CredentialsIntrospector` / `EnvIntrospector` specs include explicit sentinel-value leak assertions (a secret is injected, then the output hash is searched for the sentinel string). The full non-e2e suite runs **2154 examples, 0 failures**.
|
|
34
|
+
|
|
35
|
+
### Fixed — post-review hardening
|
|
36
|
+
|
|
37
|
+
Three parallel code reviews (security/data-leak, Rails-version correctness, CLAUDE.md invariant compliance) surfaced the following issues, all addressed in this release:
|
|
38
|
+
|
|
39
|
+
- **`ObservabilityIntrospector#detect_event_reporter` crashed on Rails 8.1.** The original code called `reporter.tagged` without a block to read "registered tags". `ActiveSupport::EventReporter#tagged` delegates unconditionally to `TagStack#with_tags(&block)` which `yield`s — a blockless call raises `LocalJumpError`. The outer `rescue` caught it and returned `{ available: false }`, which silently misreported Rails 8.1 apps as lacking the event reporter and dropped the `subscriber_count` too. The `entry[:tags]` line was removed; `tagged` is stack-scoped context, not an introspectable keyspace. `subscriber_count` is still reported.
|
|
40
|
+
- **`ObservabilityIntrospector#extract_subscribers_from_notifier` had dead code.** A "legacy `@subscribers` (array)" fallback claimed to support Rails 7.0, but Rails 7.0 already used `@string_subscribers` + `@other_subscribers`. The flat `@subscribers` ivar hasn't existed since Rails ≤ 5.x, so the branch was unreachable across the entire 7.1 / 7.2 / 8.0 CI matrix. Removed. Also: the `subscriber_raw_pattern` helper now unwraps `ActiveSupport::Notifications::Fanout::Subscribers::Matcher` one level deep so Regexp-pattern subscribers surface as `"pattern.source"` instead of `"#<…::Matcher:0x…>"`.
|
|
41
|
+
- **`CredentialsIntrospector` leaked paths via `e.message`.** Both the top-level `rescue` and `inspect_default_credentials`'s rescue returned `{ error: e.message }` in the output hash. OS-level errors (`Errno::EACCES`, `Errno::ENOENT`) and OpenSSL decryption failures include absolute paths with the OS username in their message — credentials-adjacent data that shouldn't leave the process. Both rescues now return `{ error: "…failed", exception_class: e.class.name }`; `e.message` stays in the `ENV["DEBUG"]`-gated stderr log where it's fine. New regression specs inject a `Errno::EACCES` with a path containing `/Users/alice/secret/master.key` and assert neither `"/Users/alice"` nor `"alice/secret"` appears anywhere in the output.
|
|
42
|
+
- **`EnvIntrospector` classified `BUNDLE_PATH` and `BUNDLE_GEMFILE` as safe-to-return.** Both are absolute filesystem paths that usually contain the OS username (e.g. `/Users/alice/.bundle`). Flipped to `safe: false` so only presence is reported, matching the treatment of other path-containing vars (`DATABASE_URL`, `REDIS_URL`, etc.).
|
|
43
|
+
- **`ActiveSupportIntrospector` + `EnvIntrospector` had non-deterministic directory walks.** Both called `Dir.glob(...).first(2000)` to cap traversal on large monorepos, but `Dir.glob` ordering is filesystem-dependent, so the selected 2000-file slice could differ run-to-run. Both now call `Dir.glob(...).sort.first(2000)`, matching the `.sort` already used by the other new introspectors.
|
|
44
|
+
- **Spec-coverage gaps on ivar-derived paths.** The reviewers flagged that three silently-failing paths had no assertions: initializer `:source` capture (via `@block.source_location`), connection_pool `:pool_config` shape, and the `@other_subscribers` Regexp-pattern branch in the Fanout walk. All three now have targeted assertions — a silent drift if any of these ivars is renamed upstream will now fail CI.
|
|
45
|
+
|
|
46
|
+
## [5.9.1] — 2026-04-20
|
|
47
|
+
|
|
48
|
+
### Fixed — `GetConcern` missed plural concern names (#78)
|
|
49
|
+
|
|
50
|
+
Thanks to [@johan--](https://github.com/johan--) for the report and fix.
|
|
51
|
+
|
|
52
|
+
`rails_get_concern`'s includer search built its include-pattern regex via `String#classify`, which singularizes its input. Concerns with intentionally plural module names — `WorksheetImports`, `PaperTrailEvents`, `SoftDeletables`, etc. — got demodulized to `WorksheetImport` and the `include WorksheetImports` line in the model never matched. The tool reported no includers even when the concern was in use.
|
|
53
|
+
|
|
54
|
+
Switched to `String#camelize`, which normalizes case (so lowercase input like `plan_limitable` → `PlanLimitable` still works) **without** singularizing. This also restores consistency with the three other `.camelize` calls already used in `get_concern.rb` for the same "file basename / module name → class name" conversion. Covered by a new spec in `get_concern_spec.rb` that exercises the plural-name case end-to-end.
|
|
55
|
+
|
|
56
|
+
### Fixed — internal invariant compliance
|
|
57
|
+
|
|
58
|
+
- **`validate.rb` now routes all Prism parses through `AstCache`.** The Ruby syntax validator was calling `Prism.parse_file` directly and the ERB + semantic-visitor paths `Prism.parse` on string input, bypassing the cache entirely for the first and violating the "all Prism parses must flow through `RailsAiContext::AstCache`" invariant (`.results/3-identify-architecture.json:33`) for all three. Now uses `AstCache.parse(path)` for on-disk sources (picking up the existing size-cap + content-hash caching) and `AstCache.parse_string(source)` for synthetic strings. Note: `AstCache.parse` enforces a 5 MB `MAX_PARSE_SIZE` cap; Ruby files above that size fall through the existing `rescue` to the `ruby -c` subprocess validator, which returns errors but not Prism warnings — a graceful degradation that affects only pathologically large source files.
|
|
59
|
+
- **`Listeners::BaseListener` uses `Confidence::INFERRED` constant.** Replaced three hardcoded `"[INFERRED]"` literals in `extract_first_symbol`, `extract_key`, and `extract_value` with `RailsAiContext::Confidence::INFERRED`. Value is identical; constant reference prevents drift if the marker string is ever versioned.
|
|
60
|
+
- **Diagnostic `$stderr.puts` in `rescue` blocks now `ENV["DEBUG"]`-gated.** 12 previously-unconditional stderr writes across `tools/diagnose.rb` (5), `tools/review_changes.rb` (4), and `serializers/stack_overview_helper.rb` (3) were logging under normal operation whenever an optional context-enrichment step failed. These were never visible to most users but polluted stderr in MCP/CLI logs. Now silent unless `DEBUG=1`, matching the convention used everywhere else in the gem.
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **Prism-discipline regression spec** (`spec/lib/rails_ai_context/ast_cache_discipline_spec.rb`). Scans every `lib/**/*.rb` file (excluding `ast_cache.rb`) for direct `Prism.parse` / `Prism.parse_file` / `Prism.parse_string` calls and fails if any are found. Prevents re-introduction of the bypass that `validate.rb` had.
|
|
65
|
+
|
|
8
66
|
## [5.9.0] — 2026-04-16
|
|
9
67
|
|
|
10
68
|
### Fixed — Cursor chat agent didn't detect rules
|
data/CONTRIBUTING.md
CHANGED
|
@@ -19,7 +19,7 @@ The test suite uses [Combustion](https://github.com/pat/combustion) to boot a mi
|
|
|
19
19
|
```
|
|
20
20
|
lib/rails_ai_context/
|
|
21
21
|
├── cli/ # CLI tool runner (tool_runner.rb) — executes MCP tools from rake/Thor
|
|
22
|
-
├── introspectors/ #
|
|
22
|
+
├── introspectors/ # 39 introspectors (schema, models, routes, etc.)
|
|
23
23
|
├── tools/ # 38 MCP tools with detail levels and pagination
|
|
24
24
|
├── serializers/ # Per-assistant formatters + shared ToolGuideHelper
|
|
25
25
|
├── server.rb # MCP server setup (stdio + HTTP)
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<br>
|
|
22
22
|
[](https://github.com/crisnahine/rails-ai-context)
|
|
23
23
|
[](https://github.com/crisnahine/rails-ai-context)
|
|
24
|
-
[](https://github.com/crisnahine/rails-ai-context/actions)
|
|
25
25
|
[](LICENSE)
|
|
26
26
|
|
|
27
27
|
</div>
|
|
@@ -397,7 +397,7 @@ Enabled by default. Disable with `config.anti_hallucination_rules = false` if yo
|
|
|
397
397
|
|
|
398
398
|
```mermaid
|
|
399
399
|
graph TD
|
|
400
|
-
A["Your Rails App\nmodels + schema + routes + controllers + views + jobs"] -->|"
|
|
400
|
+
A["Your Rails App\nmodels + schema + routes + controllers + views + jobs"] -->|"39 introspectors"| B
|
|
401
401
|
|
|
402
402
|
B["rails-ai-context\nPrism AST parsing · Cached · Confidence-tagged\nVFS: rails-ai-context:// URIs introspected fresh"]
|
|
403
403
|
|
|
@@ -459,7 +459,7 @@ Both paths ask which AI tools you use (Claude Code, Cursor, GitHub Copilot, Open
|
|
|
459
459
|
| **[Configuration](docs/CONFIGURATION.md)** | 40+ config options with defaults |
|
|
460
460
|
| **[AI Tool Setup](docs/SETUP.md)** | Claude, Cursor, Copilot, OpenCode, Codex |
|
|
461
461
|
| **[Architecture](docs/ARCHITECTURE.md)** | System design and internals |
|
|
462
|
-
| **[Introspectors](docs/INTROSPECTORS.md)** | All
|
|
462
|
+
| **[Introspectors](docs/INTROSPECTORS.md)** | All 39 introspectors and AST engine |
|
|
463
463
|
| **[Security](docs/SECURITY.md)** | 4-layer SQL safety and file blocking |
|
|
464
464
|
| **[CLI Reference](docs/CLI.md)** | Commands and argument syntax |
|
|
465
465
|
| **[Standalone](docs/STANDALONE.md)** | Use without Gemfile entry |
|
|
@@ -508,7 +508,7 @@ if defined?(RailsAiContext)
|
|
|
508
508
|
RailsAiContext.configure do |config|
|
|
509
509
|
config.ai_tools = %i[claude cursor] # Which AI tools to generate for
|
|
510
510
|
config.tool_mode = :mcp # :mcp (default) or :cli
|
|
511
|
-
config.preset = :full # :full (
|
|
511
|
+
config.preset = :full # :full (39 introspectors) or :standard (17)
|
|
512
512
|
end
|
|
513
513
|
end
|
|
514
514
|
```
|
|
@@ -543,7 +543,7 @@ end
|
|
|
543
543
|
## About
|
|
544
544
|
|
|
545
545
|
Built by a Rails developer with 10+ years of production experience.<br>
|
|
546
|
-
|
|
546
|
+
2154 tests + 100-example e2e harness. 38 tools. 5 resource templates. 39 introspectors. Standalone or in-Gemfile.<br>
|
|
547
547
|
MIT licensed. [Contributions welcome.](CONTRIBUTING.md)
|
|
548
548
|
|
|
549
549
|
<br>
|
data/Rakefile
CHANGED
|
@@ -43,4 +43,32 @@ RSpec::Core::RakeTask.new(:e2e) do |t|
|
|
|
43
43
|
ENV["E2E"] = "1"
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
namespace :e2e do
|
|
47
|
+
desc "Run e2e specs in parallel via parallel_tests (opt-in; uses all CPUs)"
|
|
48
|
+
task :parallel do
|
|
49
|
+
# Opt-in parallelization. Each parallel_tests worker is a separate
|
|
50
|
+
# process — it will rebuild its own shared-fixture memoization and
|
|
51
|
+
# its own built .gem artefact. Still a net win because rspec-level
|
|
52
|
+
# wall-clock scales with the slowest worker, not the sum.
|
|
53
|
+
#
|
|
54
|
+
# Caveats:
|
|
55
|
+
# - postgres_install_spec is excluded here because its DB name is
|
|
56
|
+
# derived from E2E_DB_SUFFIX and workers would collide unless you
|
|
57
|
+
# pre-create per-worker databases. Run `rake e2e` separately if
|
|
58
|
+
# you need postgres coverage.
|
|
59
|
+
# - Shared BUNDLE_PATH is NOT used in parallel mode — two workers
|
|
60
|
+
# writing to the same bundle path can corrupt native extension
|
|
61
|
+
# builds. Each worker gets its own implicit bundle directory.
|
|
62
|
+
require "shellwords"
|
|
63
|
+
spec_files = Dir["spec/e2e/**/*_spec.rb"].reject { |p| p.include?("postgres_install_spec") }
|
|
64
|
+
cmd = [
|
|
65
|
+
"bundle", "exec", "parallel_rspec",
|
|
66
|
+
"--serialize-stdout",
|
|
67
|
+
"--"
|
|
68
|
+
] + spec_files
|
|
69
|
+
env = { "E2E" => "1", "BUNDLE_PATH" => nil }
|
|
70
|
+
sh(env, *cmd) { |ok, _res| abort("parallel_rspec failed") unless ok }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
46
74
|
task default: :spec
|
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -18,7 +18,7 @@ graph TD
|
|
|
18
18
|
A["models + schema + routes + controllers + views + jobs + config"]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
A -->|"
|
|
21
|
+
A -->|"39 introspectors"| gem
|
|
22
22
|
|
|
23
23
|
subgraph gem["rails-ai-context"]
|
|
24
24
|
direction TB
|
|
@@ -101,7 +101,7 @@ flowchart LR
|
|
|
101
101
|
|
|
102
102
|
### Introspectors (`lib/rails_ai_context/introspectors/`)
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
39 modules that extract structured data from your Rails app. Each introspector:
|
|
105
105
|
|
|
106
106
|
- Returns a Hash (never raises — wraps errors in `{ error: msg }`)
|
|
107
107
|
- Is registered in `INTROSPECTOR_MAP` with a symbol key
|
data/docs/CONFIGURATION.md
CHANGED
|
@@ -56,7 +56,7 @@ preset: full
|
|
|
56
56
|
|
|
57
57
|
| Option | Type | Default | Description |
|
|
58
58
|
|:-------|:-----|:--------|:------------|
|
|
59
|
-
| `preset` | Symbol | `:full` | `:full` (
|
|
59
|
+
| `preset` | Symbol | `:full` | `:full` (39 introspectors) or `:standard` (17 introspectors) |
|
|
60
60
|
| `context_mode` | Symbol | `:compact` | `:compact` (context files capped at ~150 lines) or `:full` (no line cap) |
|
|
61
61
|
| `introspectors` | Array of symbols | (from preset) | Override the introspector list directly |
|
|
62
62
|
| `generate_root_files` | Boolean | `true` | Set `false` to generate split rules only, no root CLAUDE.md/AGENTS.md |
|
|
@@ -155,7 +155,7 @@ preset: full
|
|
|
155
155
|
|
|
156
156
|
## Presets
|
|
157
157
|
|
|
158
|
-
### `:full` (default) —
|
|
158
|
+
### `:full` (default) — 39 introspectors
|
|
159
159
|
|
|
160
160
|
All available introspectors. Best for comprehensive context.
|
|
161
161
|
|
data/docs/FAQ.md
CHANGED
|
@@ -99,7 +99,7 @@ PostgreSQL, MySQL, and SQLite. Each gets database-specific safety mechanisms (re
|
|
|
99
99
|
|
|
100
100
|
### What's the difference between `:full` and `:standard` preset?
|
|
101
101
|
|
|
102
|
-
- **`:full`** (default) —
|
|
102
|
+
- **`:full`** (default) — 39 introspectors. Comprehensive context for every aspect of your app.
|
|
103
103
|
- **`:standard`** — 17 introspectors. Faster, covers the essentials (schema, models, routes, controllers, tests, etc.).
|
|
104
104
|
|
|
105
105
|
### What's `:compact` vs `:full` context mode?
|
data/docs/GUIDE.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
| [Configuration](CONFIGURATION.md) | Every config option |
|
|
25
25
|
| [AI Tool Setup](SETUP.md) | Per-editor setup |
|
|
26
26
|
| [Architecture](ARCHITECTURE.md) | System design and internals |
|
|
27
|
-
| [Introspectors](INTROSPECTORS.md) | All
|
|
27
|
+
| [Introspectors](INTROSPECTORS.md) | All 39 introspectors |
|
|
28
28
|
| [Security](SECURITY.md) | Security model and SQL safety |
|
|
29
29
|
| [CLI Reference](CLI.md) | All commands and argument syntax |
|
|
30
30
|
| [Standalone Mode](STANDALONE.md) | Use without Gemfile |
|
|
@@ -1192,7 +1192,7 @@ if defined?(RailsAiContext)
|
|
|
1192
1192
|
RailsAiContext.configure do |config|
|
|
1193
1193
|
# --- Introspectors ---
|
|
1194
1194
|
|
|
1195
|
-
# Presets: :full (
|
|
1195
|
+
# Presets: :full (39 introspectors, default) or :standard (17)
|
|
1196
1196
|
config.preset = :full
|
|
1197
1197
|
|
|
1198
1198
|
# Cherry-pick on top of a preset
|
|
@@ -1311,7 +1311,7 @@ end
|
|
|
1311
1311
|
| Option | Type | Default | Description |
|
|
1312
1312
|
|--------|------|---------|-------------|
|
|
1313
1313
|
| `preset` | Symbol | `:full` | Introspector preset (`:full` or `:standard`) |
|
|
1314
|
-
| `introspectors` | Array |
|
|
1314
|
+
| `introspectors` | Array | 39 (full preset) | Which introspectors to run |
|
|
1315
1315
|
| `context_mode` | Symbol | `:compact` | `:compact` or `:full` |
|
|
1316
1316
|
| `claude_max_lines` | Integer | `150` | Max lines for CLAUDE.md in compact mode |
|
|
1317
1317
|
| `max_tool_response_chars` | Integer | `200_000` | Safety cap for MCP tool responses |
|
|
@@ -1398,7 +1398,7 @@ Core Rails structure only. Use `config.preset = :standard` for a lighter footpri
|
|
|
1398
1398
|
| `performance` | N+1 query risks, missing counter_cache, missing FK indexes, Model.all anti-patterns, eager load candidates. |
|
|
1399
1399
|
| `i18n` | Default locale, available locales, locale files with key counts, backend class, parse errors. |
|
|
1400
1400
|
|
|
1401
|
-
### Full preset (
|
|
1401
|
+
### Full preset (39 introspectors) — default
|
|
1402
1402
|
|
|
1403
1403
|
Includes all standard introspectors plus:
|
|
1404
1404
|
|
|
@@ -1534,11 +1534,11 @@ OpenCode uses **per-directory lazy-loading**: when the agent reads a file, it wa
|
|
|
1534
1534
|
|
|
1535
1535
|
| Setup | Coverage | Notes |
|
|
1536
1536
|
|-------|----------|-------|
|
|
1537
|
-
| Rails full-stack (ERB + Hotwire) |
|
|
1538
|
-
| Rails + Inertia.js (React/Vue) | ~
|
|
1539
|
-
| Rails API + React/Next.js SPA | ~
|
|
1540
|
-
| Rails API + mobile app | ~
|
|
1541
|
-
| Rails engine (mountable gem) | ~
|
|
1537
|
+
| Rails full-stack (ERB + Hotwire) | 39/39 | All introspectors relevant |
|
|
1538
|
+
| Rails + Inertia.js (React/Vue) | ~33/39 | Views/Turbo partially useful, backend fully covered |
|
|
1539
|
+
| Rails API + React/Next.js SPA | ~31/39 | Schema, models, routes, API, auth, jobs — all covered |
|
|
1540
|
+
| Rails API + mobile app | ~31/39 | Same as SPA — backend introspection is identical |
|
|
1541
|
+
| Rails engine (mountable gem) | ~26/39 | Core introspectors (schema, models, routes, gems) work |
|
|
1542
1542
|
|
|
1543
1543
|
Frontend introspectors (views, Turbo, Stimulus, assets) degrade gracefully — they report nothing when those features aren't present.
|
|
1544
1544
|
|
data/docs/INTROSPECTORS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Introspectors
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**39 modules that extract structured data from your Rails application.**
|
|
6
6
|
|
|
7
7
|
[Architecture](ARCHITECTURE.md) · [Configuration](CONFIGURATION.md) · [Tools Reference](TOOLS.md) · [Security](SECURITY.md)
|
|
8
8
|
|
|
@@ -21,7 +21,7 @@ Each introspector:
|
|
|
21
21
|
|
|
22
22
|
## Presets
|
|
23
23
|
|
|
24
|
-
### `:full` (default) — all
|
|
24
|
+
### `:full` (default) — all 39 introspectors
|
|
25
25
|
|
|
26
26
|
Best for comprehensive AI context. Covers every aspect of your app.
|
|
27
27
|
|
|
@@ -49,13 +49,16 @@ graph LR
|
|
|
49
49
|
S16["performance"] ~~~ S17["i18n"]
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
subgraph full_only["Full Preset adds +
|
|
52
|
+
subgraph full_only["Full Preset adds +22"]
|
|
53
53
|
direction TB
|
|
54
54
|
F1["views"] ~~~ F2["database_stats"] ~~~ F3["api"]
|
|
55
55
|
F4["active_storage"] ~~~ F5["action_text"] ~~~ F6["action_mailbox"]
|
|
56
56
|
F7["rake_tasks"] ~~~ F8["assets"] ~~~ F9["devops"]
|
|
57
57
|
F10["seeds"] ~~~ F11["middleware"] ~~~ F12["engines"]
|
|
58
58
|
F13["multi_database"] ~~~ F14["frontend_frameworks"]
|
|
59
|
+
F15["initializers"] ~~~ F16["autoload"] ~~~ F17["connection_pool"]
|
|
60
|
+
F18["active_support"] ~~~ F19["credentials"] ~~~ F20["security"]
|
|
61
|
+
F21["observability"] ~~~ F22["env"]
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
standard --> full_only
|
|
@@ -74,7 +77,7 @@ end
|
|
|
74
77
|
|
|
75
78
|
---
|
|
76
79
|
|
|
77
|
-
## All
|
|
80
|
+
## All 39 introspectors
|
|
78
81
|
|
|
79
82
|
### Core
|
|
80
83
|
|
|
@@ -148,6 +151,21 @@ end
|
|
|
148
151
|
| TestIntrospector | `:tests` | Test framework, file counts, coverage hints |
|
|
149
152
|
| PerformanceIntrospector | `:performance` | N+1 risks, missing indexes, counter_cache hints |
|
|
150
153
|
|
|
154
|
+
### Runtime & Framework Internals
|
|
155
|
+
|
|
156
|
+
These introspectors map directly onto [`RAILS_NERVOUS_SYSTEM.md`](../RAILS_NERVOUS_SYSTEM.md) sections and capture framework-level surface that `:config`, `:auth`, and `:middleware` don't.
|
|
157
|
+
|
|
158
|
+
| Introspector | Key | Nervous-system § | What it extracts |
|
|
159
|
+
|:-------------|:----|:---|:-----------------|
|
|
160
|
+
| InitializerIntrospector | `:initializers` | §2 | `Rails.application.initializers` graph: name, owner, `before:`/`after:` edges, block `source_location`, per-file `config/initializers/*.rb` summary |
|
|
161
|
+
| AutoloadIntrospector | `:autoload` | §3 | Zeitwerk presence, autoloaders (`:main` / `:once`) with collapsed + ignored dirs, `autoload_paths`, `eager_load_paths`, custom inflections (`acronym`, `plural`, `singular`, `irregular`) |
|
|
162
|
+
| ConnectionPoolIntrospector | `:connection_pool` | §10 | Per-database adapter config: pool size, `checkout_timeout`, `reaping_frequency`, `prepared_statements`, `advisory_locks`, replica flag, connection-handler roles, automatic shard selector detection |
|
|
163
|
+
| ActiveSupportIntrospector | `:active_support` | §17 | Concerns in `app/**/concerns/` (ActiveSupport::Concern flags, `included do`/`class_methods do` blocks), deprecators registry, MessageEncryptor/Verifier usage, TaggedLogging config, common on-load hooks, cache store options |
|
|
164
|
+
| CredentialsIntrospector | `:credentials` | §30 | Default + per-env encrypted files, master-key source (`env:RAILS_MASTER_KEY` vs `file:config/master.key` vs missing), `require_master_key` flag, arbitrary encrypted configs (`config/*.yml.enc`), top-level key **names only** (never values) |
|
|
165
|
+
| SecurityIntrospector | `:security` | §32 | `force_ssl`, SSL options (HSTS `expires`/`subdomains`/`preload`), `host_authorization` hosts, ContentSecurityPolicy directives + `report_only`, PermissionsPolicy directives, CSRF config (`protect_from_forgery`, `per_form_csrf_tokens`, `origin_check`), cookie session options, Rails 7.2+ `allow_browser` usage |
|
|
166
|
+
| ObservabilityIntrospector | `:observability` | §34 + §38 | `ActiveSupport::LogSubscriber.log_subscribers` catalog, AS::Notifications subscriber registry (pattern + count + sample class), `ActionDispatch::ServerTiming` middleware detection, Rails 8.1 `event_reporter` availability, log level + tags, canonical Rails event-name catalog (10 subsystems) |
|
|
167
|
+
| EnvIntrospector | `:env` | §36 | Catalog of 30+ Rails-related ENV vars partitioned into `set` / `unset`; safe vars (`RAILS_ENV`, `RAILS_MAX_THREADS`, etc.) return values, sensitive vars (`SECRET_KEY_BASE`, `DATABASE_URL`, `RAILS_MASTER_KEY`, etc.) return `redacted: true` only; scans `config/`/`app/`/`lib/` for app-specific `ENV["X"]` references |
|
|
168
|
+
|
|
151
169
|
---
|
|
152
170
|
|
|
153
171
|
## AST-based introspection
|
data/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -247,7 +247,7 @@ Without it, the tool reports "not installed" but the gem works fine otherwise.
|
|
|
247
247
|
|
|
248
248
|
### "Introspection is slow"
|
|
249
249
|
|
|
250
|
-
1. Use `:standard` preset (17 introspectors vs
|
|
250
|
+
1. Use `:standard` preset (17 introspectors vs 39)
|
|
251
251
|
2. Increase cache TTL: `config.cache_ttl = 300`
|
|
252
252
|
3. Check schema file size: `rails ai:doctor` warns if too large
|
|
253
253
|
4. Check view count: many views slow down view introspection
|
data/docs/index.md
CHANGED
|
@@ -25,7 +25,7 @@ Works with Claude Code, Cursor, GitHub Copilot, OpenCode, and Codex CLI.
|
|
|
25
25
|
| **[Tools Reference](TOOLS.md)** | All 38 tools with every parameter |
|
|
26
26
|
| **[Configuration](CONFIGURATION.md)** | 40+ config options with defaults |
|
|
27
27
|
| **[CLI Reference](CLI.md)** | Commands and argument syntax |
|
|
28
|
-
| **[Introspectors](INTROSPECTORS.md)** | All
|
|
28
|
+
| **[Introspectors](INTROSPECTORS.md)** | All 39 introspectors and AST engine |
|
|
29
29
|
|
|
30
30
|
## Learn
|
|
31
31
|
|
|
@@ -78,7 +78,8 @@ module RailsAiContext
|
|
|
78
78
|
full: %i[schema models routes jobs gems conventions stimulus database_stats controllers views view_templates turbo
|
|
79
79
|
i18n config active_storage action_text auth api tests rake_tasks assets
|
|
80
80
|
devops action_mailbox migrations seeds middleware engines multi_database
|
|
81
|
-
components performance frontend_frameworks
|
|
81
|
+
components performance frontend_frameworks
|
|
82
|
+
initializers autoload connection_pool active_support credentials security observability env]
|
|
82
83
|
}.freeze
|
|
83
84
|
|
|
84
85
|
# MCP server settings
|
|
@@ -80,7 +80,15 @@ module RailsAiContext
|
|
|
80
80
|
multi_database: Introspectors::MultiDatabaseIntrospector,
|
|
81
81
|
components: Introspectors::ComponentIntrospector,
|
|
82
82
|
performance: Introspectors::PerformanceIntrospector,
|
|
83
|
-
frontend_frameworks: Introspectors::FrontendFrameworkIntrospector
|
|
83
|
+
frontend_frameworks: Introspectors::FrontendFrameworkIntrospector,
|
|
84
|
+
initializers: Introspectors::InitializerIntrospector,
|
|
85
|
+
autoload: Introspectors::AutoloadIntrospector,
|
|
86
|
+
connection_pool: Introspectors::ConnectionPoolIntrospector,
|
|
87
|
+
active_support: Introspectors::ActiveSupportIntrospector,
|
|
88
|
+
credentials: Introspectors::CredentialsIntrospector,
|
|
89
|
+
security: Introspectors::SecurityIntrospector,
|
|
90
|
+
observability: Introspectors::ObservabilityIntrospector,
|
|
91
|
+
env: Introspectors::EnvIntrospector
|
|
84
92
|
}.freeze
|
|
85
93
|
|
|
86
94
|
private
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAiContext
|
|
4
|
+
module Introspectors
|
|
5
|
+
# Extracts ActiveSupport runtime surface that other introspectors don't
|
|
6
|
+
# cover: Concerns registry (`app/**/concerns`), Deprecators registry,
|
|
7
|
+
# MessageEncryptor/MessageVerifier usage, and TaggedLogging tags.
|
|
8
|
+
# Covers RAILS_NERVOUS_SYSTEM.md §17 (ActiveSupport).
|
|
9
|
+
class ActiveSupportIntrospector
|
|
10
|
+
attr_reader :app
|
|
11
|
+
|
|
12
|
+
def initialize(app)
|
|
13
|
+
@app = app
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
{
|
|
18
|
+
concerns: extract_concerns,
|
|
19
|
+
deprecators: extract_deprecators,
|
|
20
|
+
message_verifier_usage: extract_message_verifier_usage,
|
|
21
|
+
tagged_logging: detect_tagged_logging,
|
|
22
|
+
on_load_hooks: common_on_load_hooks,
|
|
23
|
+
cache_usage: detect_cache_usage
|
|
24
|
+
}
|
|
25
|
+
rescue => e
|
|
26
|
+
$stderr.puts "[rails-ai-context] ActiveSupportIntrospector#call failed: #{e.message}" if ENV["DEBUG"]
|
|
27
|
+
{ error: e.message }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def root
|
|
33
|
+
app.root.to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
CONCERN_DIRS = %w[
|
|
37
|
+
app/models/concerns
|
|
38
|
+
app/controllers/concerns
|
|
39
|
+
app/jobs/concerns
|
|
40
|
+
app/mailers/concerns
|
|
41
|
+
app/channels/concerns
|
|
42
|
+
].freeze
|
|
43
|
+
|
|
44
|
+
def extract_concerns
|
|
45
|
+
result = {}
|
|
46
|
+
CONCERN_DIRS.each do |rel_dir|
|
|
47
|
+
dir = File.join(root, rel_dir)
|
|
48
|
+
next unless Dir.exist?(dir)
|
|
49
|
+
|
|
50
|
+
modules = Dir.glob(File.join(dir, "**/*.rb")).sort.filter_map do |path|
|
|
51
|
+
content = RailsAiContext::SafeFile.read(path) or next
|
|
52
|
+
mod_name = File.basename(path, ".rb").camelize
|
|
53
|
+
entry = { name: mod_name, file: path.sub("#{root}/", "") }
|
|
54
|
+
entry[:uses_active_support_concern] = true if content.include?("ActiveSupport::Concern")
|
|
55
|
+
entry[:included_blocks] = content.scan(/^\s*included\s+do\b/).size
|
|
56
|
+
entry[:class_methods_block] = content.include?("class_methods do")
|
|
57
|
+
entry
|
|
58
|
+
end
|
|
59
|
+
result[rel_dir] = modules if modules.any?
|
|
60
|
+
end
|
|
61
|
+
result
|
|
62
|
+
rescue => e
|
|
63
|
+
$stderr.puts "[rails-ai-context] extract_concerns failed: #{e.message}" if ENV["DEBUG"]
|
|
64
|
+
{}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Rails 7.1+ registers deprecators per gem/component via
|
|
68
|
+
# `Rails.application.deprecators`. Return the registered keys.
|
|
69
|
+
def extract_deprecators
|
|
70
|
+
return [] unless app.respond_to?(:deprecators)
|
|
71
|
+
registry = app.deprecators
|
|
72
|
+
return [] unless registry
|
|
73
|
+
|
|
74
|
+
keys = if registry.respond_to?(:each) && registry.respond_to?(:map)
|
|
75
|
+
registry.map { |name, _d| name.to_s }
|
|
76
|
+
elsif registry.instance_variable_defined?(:@deprecators)
|
|
77
|
+
(registry.instance_variable_get(:@deprecators) || {}).keys.map(&:to_s)
|
|
78
|
+
else
|
|
79
|
+
[]
|
|
80
|
+
end
|
|
81
|
+
keys.compact.sort.uniq
|
|
82
|
+
rescue => e
|
|
83
|
+
$stderr.puts "[rails-ai-context] extract_deprecators failed: #{e.message}" if ENV["DEBUG"]
|
|
84
|
+
[]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Scan `lib/` + `app/` for calls into ActiveSupport::MessageEncryptor and
|
|
88
|
+
# ActiveSupport::MessageVerifier. Used for tokens, signed IDs, etc.
|
|
89
|
+
def extract_message_verifier_usage
|
|
90
|
+
hits = []
|
|
91
|
+
%w[lib app].each do |rel|
|
|
92
|
+
dir = File.join(root, rel)
|
|
93
|
+
next unless Dir.exist?(dir)
|
|
94
|
+
|
|
95
|
+
# Sort before slicing — Dir.glob ordering is filesystem-dependent
|
|
96
|
+
# and would produce non-deterministic output on large monorepos.
|
|
97
|
+
Dir.glob(File.join(dir, "**/*.rb")).sort.first(2000).each do |path|
|
|
98
|
+
content = RailsAiContext::SafeFile.read(path) or next
|
|
99
|
+
next unless content.match?(/MessageEncryptor|MessageVerifier/)
|
|
100
|
+
relative = path.sub("#{root}/", "")
|
|
101
|
+
hits << { file: relative, encryptor: content.include?("MessageEncryptor"), verifier: content.include?("MessageVerifier") }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
hits
|
|
105
|
+
rescue => e
|
|
106
|
+
$stderr.puts "[rails-ai-context] extract_message_verifier_usage failed: #{e.message}" if ENV["DEBUG"]
|
|
107
|
+
[]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def detect_tagged_logging
|
|
111
|
+
result = { configured: false }
|
|
112
|
+
config_logger = app.config.log_tags
|
|
113
|
+
if config_logger.is_a?(Array) && config_logger.any?
|
|
114
|
+
result[:configured] = true
|
|
115
|
+
result[:tags] = config_logger.map(&:to_s)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Initializer pattern: Rails.logger = ActiveSupport::TaggedLogging.new(…)
|
|
119
|
+
Dir.glob(File.join(root, "config/initializers/*.rb")).each do |path|
|
|
120
|
+
content = RailsAiContext::SafeFile.read(path) or next
|
|
121
|
+
if content.include?("ActiveSupport::TaggedLogging")
|
|
122
|
+
result[:configured] = true
|
|
123
|
+
result[:initializer] = path.sub("#{root}/", "")
|
|
124
|
+
break
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
result
|
|
128
|
+
rescue => e
|
|
129
|
+
$stderr.puts "[rails-ai-context] detect_tagged_logging failed: #{e.message}" if ENV["DEBUG"]
|
|
130
|
+
{ configured: false }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The canonical lazy hooks that Railties expose. Report which have at
|
|
134
|
+
# least one subscriber attached so AI can reason about load-order.
|
|
135
|
+
COMMON_HOOKS = %i[
|
|
136
|
+
active_record before_initialize after_initialize
|
|
137
|
+
action_controller action_controller_base action_controller_api
|
|
138
|
+
action_view action_mailer active_job active_storage
|
|
139
|
+
action_cable action_text action_mailbox
|
|
140
|
+
].freeze
|
|
141
|
+
|
|
142
|
+
def common_on_load_hooks
|
|
143
|
+
return [] unless defined?(ActiveSupport) && ActiveSupport.respond_to?(:on_load)
|
|
144
|
+
registry = ActiveSupport.instance_variable_get(:@load_hooks)
|
|
145
|
+
return [] unless registry.respond_to?(:each_key)
|
|
146
|
+
|
|
147
|
+
registry.each_key.filter_map do |name|
|
|
148
|
+
next unless COMMON_HOOKS.include?(name)
|
|
149
|
+
callbacks = registry[name]
|
|
150
|
+
callback_count = callbacks.respond_to?(:size) ? callbacks.size : 0
|
|
151
|
+
{ hook: name.to_s, callbacks: callback_count } if callback_count > 0
|
|
152
|
+
end.sort_by { |h| h[:hook] }
|
|
153
|
+
rescue => e
|
|
154
|
+
$stderr.puts "[rails-ai-context] common_on_load_hooks failed: #{e.message}" if ENV["DEBUG"]
|
|
155
|
+
[]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def detect_cache_usage
|
|
159
|
+
store = app.config.cache_store
|
|
160
|
+
entry = {
|
|
161
|
+
store: store.is_a?(Array) ? store.first.to_s : store.to_s
|
|
162
|
+
}
|
|
163
|
+
if store.is_a?(Array) && store.last.is_a?(Hash)
|
|
164
|
+
entry[:options] = store.last.keys.map(&:to_s)
|
|
165
|
+
end
|
|
166
|
+
entry
|
|
167
|
+
rescue => e
|
|
168
|
+
$stderr.puts "[rails-ai-context] detect_cache_usage failed: #{e.message}" if ENV["DEBUG"]
|
|
169
|
+
{}
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|