rails-ai-bridge 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +26 -0
  3. data/.rubocop.yml +9 -0
  4. data/AGENTS.md +5 -5
  5. data/CHANGELOG.md +27 -0
  6. data/CLAUDE.md +5 -5
  7. data/CONTRIBUTING.md +2 -2
  8. data/README.md +10 -9
  9. data/Rakefile +2 -0
  10. data/SECURITY.md +4 -4
  11. data/docs/02-port-registry-resolution.md +1 -1
  12. data/docs/devin-setup.md +2 -2
  13. data/docs/port-registry-resolution.md +1 -1
  14. data/docs/registry-resolution.md +3 -3
  15. data/docs/skill-registry-guide.md +8 -8
  16. data/lib/rails_ai_bridge/config/registry.rb +47 -4
  17. data/lib/rails_ai_bridge/context_provider.rb +1 -1
  18. data/lib/rails_ai_bridge/doctor/checkers/registry_checker.rb +135 -0
  19. data/lib/rails_ai_bridge/doctor.rb +2 -1
  20. data/lib/rails_ai_bridge/engine.rb +1 -0
  21. data/lib/rails_ai_bridge/instrumentation.rb +30 -1
  22. data/lib/rails_ai_bridge/introspectors/action_mailbox_introspector.rb +6 -0
  23. data/lib/rails_ai_bridge/introspectors/asset_pipeline_introspector.rb +7 -0
  24. data/lib/rails_ai_bridge/introspectors/database_stats_introspector.rb +8 -0
  25. data/lib/rails_ai_bridge/introspectors/devops_introspector.rb +7 -0
  26. data/lib/rails_ai_bridge/introspectors/i18n_introspector.rb +7 -0
  27. data/lib/rails_ai_bridge/introspectors/rake_task_introspector.rb +7 -0
  28. data/lib/rails_ai_bridge/introspectors/test_introspector.rb +8 -0
  29. data/lib/rails_ai_bridge/registry.rb +61 -1
  30. data/lib/rails_ai_bridge/resources.rb +87 -3
  31. data/lib/rails_ai_bridge/serializers/README.md +0 -1
  32. data/lib/rails_ai_bridge/serializers/json_serializer.rb +4 -0
  33. data/lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb +1 -1
  34. data/lib/rails_ai_bridge/server.rb +2 -1
  35. data/lib/rails_ai_bridge/tasks/rails_ai_bridge.rake +1 -1
  36. data/lib/rails_ai_bridge/tools/list_context_providers.rb +132 -0
  37. data/lib/rails_ai_bridge/tools/list_registry.rb +3 -3
  38. data/lib/rails_ai_bridge/tools/search_code.rb +31 -0
  39. data/lib/rails_ai_bridge/version.rb +1 -1
  40. data/lib/tasks/perf.rake +27 -0
  41. metadata +5 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f7810f4efa4400b60d813b2344b82f38e0a731ccc89e68a3be51aabe2526a3d
4
- data.tar.gz: 7faa7fe7f2f731d572806596a3f60d756d85b589ba46b6dfa7ef2e614bea8749
3
+ metadata.gz: 24c9dd6461240b9d6864b35c171c465635f9fdacc3c76195915f43b995a27f18
4
+ data.tar.gz: e97756c625478c854f78f661393d26043ba36b07b8941bed7a2a2c09b3a62a4b
5
5
  SHA512:
6
- metadata.gz: 1d4cf0bdcf2dcf203a30b112ac940709dcf3222af18efaeb10625d08594197f38d49cfb23f5edc328fce6a4fd6242b02aa545cb6075c5c142ab81b3a5b89d1d5
7
- data.tar.gz: 2f7dad1ecef89cc739b7a8031f16e753b0b4a55bdabbc52f8692f6b42f05f0cf7a14acc80700d819f401649eb384fc7109d98569372462f651eee2ea608d7767
6
+ metadata.gz: f453227c44348be19356eecb728dac22328abcb20a99cf0430f441c68557346d93576527716764cae4f68f404fcab524dd44c4454b27a4f9078955632be8937f
7
+ data.tar.gz: 10e975098f252c37f37108e04d0e370b03f469829f9e2520334b180e83a8844a8276b9f4ed60ae650e0f9bd47dd7361b42885dcbdf87880e37d56c04152720fa
data/.mutant.yml ADDED
@@ -0,0 +1,26 @@
1
+ ---
2
+ # Mutant configuration for rails-ai-bridge critical paths.
3
+ #
4
+ # Run with: BUNDLE_GEMFILE=Gemfile-mutation bundle exec mutant run [subject expressions]
5
+ # Example: BUNDLE_GEMFILE=Gemfile-mutation bundle exec mutant run RailsAiBridge::Registry::FrontmatterParser
6
+ #
7
+ # The open-source usage mode is used because rails-ai-bridge is an
8
+ # open-source project. A commercial license is required for private
9
+ # repositories or advanced features (full operator set, isolation).
10
+ #
11
+ # Target: >80% mutation coverage on registry/tools.
12
+ #
13
+ # Subject expressions are passed on the CLI to target critical paths:
14
+ # RailsAiBridge::Registry — registry resolution
15
+ # RailsAiBridge::Tools — MCP tools
16
+ # RailsAiBridge::Serializers — output serializers
17
+ usage: opensource
18
+ integration: rspec
19
+ includes:
20
+ - lib
21
+ - spec
22
+ requires:
23
+ - spec_helper
24
+ fail_fast: false
25
+ mutation:
26
+ timeout: 5.0
data/.rubocop.yml CHANGED
@@ -9,6 +9,7 @@ AllCops:
9
9
  - "vendor/**/*"
10
10
  - "spec/internal/**/*"
11
11
  - "gemfiles/**/*"
12
+ - ".mutant.yml"
12
13
  Naming/FileName:
13
14
  Exclude:
14
15
  - "lib/rails-ai-bridge.rb"
@@ -39,6 +40,8 @@ Layout/LineLength:
39
40
  Max: 180
40
41
  Metrics/ModuleLength:
41
42
  Max: 240
43
+ Exclude:
44
+ - "lib/rails_ai_bridge/resources.rb"
42
45
  Metrics/MethodLength:
43
46
  Max: 70
44
47
  RSpec/NestedGroups:
@@ -57,6 +60,12 @@ RSpec/MultipleDescribes:
57
60
  Enabled: false
58
61
  RSpec/MessageSpies:
59
62
  Enabled: false
63
+ RSpec/Output:
64
+ Exclude:
65
+ - "spec/support/perf_baseline.rb"
66
+ Rails/RakeEnvironment:
67
+ Exclude:
68
+ - "lib/tasks/*.rake"
60
69
  Metrics/PerceivedComplexity:
61
70
  Max: 35
62
71
  Exclude:
data/AGENTS.md CHANGED
@@ -8,9 +8,9 @@ structure to AI assistants via the Model Context Protocol (MCP).
8
8
  - `lib/rails_ai_bridge.rb` — Main entry point, public API (Zeitwerk autoloaded)
9
9
  - `lib/rails_ai_bridge/configuration.rb` — User-facing config with presets (:standard, :full)
10
10
  - `lib/rails_ai_bridge/introspector.rb` — Orchestrates sub-introspectors
11
- - `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset runs **9**, `:full` runs **26** (see `Configuration::PRESETS`). Registry: `Introspector::BUILTIN_INTROSPECTORS` (includes opt-in symbols such as `database_stats`, `non_ar_models` not listed in those presets).
12
- - `lib/rails_ai_bridge/tools/` — 16 built-in MCP tools using the official mcp SDK (hosts can add more via `additional_tools`)
13
- - `lib/rails_ai_bridge/serializers/` — Output formatters (Codex, claude_rules, cursor_rules, devin, devin_rules, copilot, copilot_instructions, rules, markdown, JSON)
11
+ - `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset runs **9**, `:full` runs **27** (see `Configuration::PRESETS`). Registry: `Introspector::BUILTIN_INTROSPECTORS` (includes opt-in symbols such as `database_stats`, `non_ar_models` not listed in those presets).
12
+ - `lib/rails_ai_bridge/tools/` — 17 built-in MCP tools using the official mcp SDK (hosts can add more via `additional_tools`)
13
+ - `lib/rails_ai_bridge/serializers/` — Output formatters (claude, claude_rules, codex, cursor_rules, devin, devin_rules, copilot, copilot_instructions, gemini, rules, markdown, JSON)
14
14
  - `lib/rails_ai_bridge/resources.rb` — MCP resources (static data AI clients read directly)
15
15
  - `lib/rails_ai_bridge/server.rb` — MCP server configuration (stdio + HTTP transports)
16
16
  - `lib/rails_ai_bridge/middleware.rb` — Rack middleware for auto-mounting MCP HTTP endpoint
@@ -30,10 +30,10 @@ structure to AI assistants via the Model Context Protocol (MCP).
30
30
  6. **Diff-aware** — context regeneration skips unchanged files
31
31
  7. **Per-assistant serializers** — each AI tool gets tailored output format
32
32
  8. **Zeitwerk autoloading** — files loaded on-demand, not all upfront
33
- 9. **Introspector presets** — `:standard` (9 core) default, `:full` (26 introspectors; optional extras such as `database_stats`, `non_ar_models`) for power users
33
+ 9. **Introspector presets** — `:standard` (9 core) default, `:full` (27 introspectors; optional extras such as `database_stats`, `non_ar_models`) for power users
34
34
  10. **MCP auto-discovery** — `.mcp.json` generated by install generator
35
35
  11. **Compact by default** — context files ≤150 lines, MCP tools use `detail` parameter (summary/standard/full)
36
- 12. **Per-tool split rules** — `.Codex/rules/`, `.cursor/rules/`, `.devin/rules/`, `.github/instructions/`
36
+ 12. **Per-tool split rules** — `.codex/`, `.cursor/rules/`, `.devin/rules/`, `.github/instructions/`
37
37
 
38
38
  ## Testing
39
39
 
data/CHANGELOG.md CHANGED
@@ -5,6 +5,33 @@ 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
+ ## [4.2.0] - 2026-08-13
9
+
10
+ ### Added
11
+
12
+ - **`rails_list_context_providers` MCP tool and dynamic resources** (#147) — new tool reads the registry manifest's `context_providers` section and returns formatted markdown with provider name, type, endpoint, optional flag, and tool specs. Context providers are also registered as dynamic MCP resources (URI template `rails://context-providers/{name}`), bringing the tool count from 16 to 17.
13
+ - **Registry health checker for Doctor** (#145) — new `Doctor::Checkers::RegistryChecker` validates manifest existence, JSON parsing, `RegistryManifest.validate!`, resolver construction, and lockfile presence (when configured). Returns pass/warn/fail with actionable fix hints. Doctor check count: 17.
14
+ - **Request-level resolver memoization** (#159) — `Registry.with_request_resolver` wraps each tool invocation in a thread-local request scope so `build_resolver` is called once per request instead of once per tool. Nil results are never memoized; thread-local storage is always cleaned up (even on error).
15
+ - **Dependabot configuration** (#153) — `.github/dependabot.yml` with grouped updates for bundler, github-actions, and gomod ecosystems, plus automatic PR labeling.
16
+ - **Rails 8.1 in release workflow matrix** (#164) — `.github/workflows/release.yml` now includes Rails 8.1 alongside 7.1, 7.2, and 8.0.
17
+ - **Skunk score threshold CI job** (#157) — advisory CI job runs `skunk lib/` after tests generate coverage data, fails if the SkunkScore average exceeds 30. Threshold to be ratcheted down over time.
18
+ - **Performance regression baseline** (#160) — `spec/support/perf_baseline.json` with three metrics (introspection, context generation, MCP tool response). `rake perf:compare` fails on >20% regression. CI uploads the baseline as a 30-day artifact.
19
+ - **Mutation testing for critical paths** (#163) — `mutant-rspec` via separate `Gemfile-mutation` (eval'd from the main Gemfile) to avoid breaking Ruby 3.2 CI resolution. Advisory CI job targets `Registry::Resolver`, `FrontmatterParser`, `RegistryManifest`, `PackResolver`, `SkillSourceResolver`, and key tools/serializers. Initial coverage: 94.39% on `FrontmatterParser`.
20
+ - **YARD documentation for 9 uncovered files** (#156) — `@param`/`@return` tags added to `search_code.rb`, all 6 introspectors, `json_serializer.rb`, and `usage_formatter.rb`. `yard stats` now 100% for `lib/`.
21
+ - **Dedicated `UsageFormatter` spec** (#151) — comprehensive spec coverage for `Tools::UsageFormatter` formatting logic.
22
+ - **Registry server specs and integration tests** (#149, #150) — `server_spec.rb` extended with registry tool assertions; new `mcp/registry_integration_spec.rb` covers `rails_list_registry`, `rails_resolve_skill`, and `rails_use_skill` end-to-end through the MCP server.
23
+ - **Documentation parity guard spec** — `spec/lib/rails_ai_bridge/doc_parity_spec.rb` fails when README/AGENTS/CLAUDE tool or introspector counts drift from the actual constants.
24
+
25
+ ### Changed
26
+
27
+ - **Standardized registry manifest and lockfile paths** (#155) — defaults changed from `config/rails_ai_bridge_registry.json` / `config/rails_ai_bridge_registry.lock` to `config/rails_ai_bridge/registry.json` / `config/rails_ai_bridge/registry.lock`. Backward-compat fallback: when the new path doesn't exist but the legacy path does, the getter returns the legacy path. Custom user paths are never overridden.
28
+ - **Documentation humanization and Windsurf purge** (#144, #148) — README, AGENTS.md, CLAUDE.md, CONTRIBUTING.md, and SECURITY.md updated: tool count 16→17, introspector count 26→27, serializer list humanized (alphabetical with Claude/Codex/Gemini), bogus `.Codex/rules/` path fixed to `.codex/`, Windsurf support claims removed (no serializer will be implemented), Mermaid diagrams added to docs.
29
+
30
+ ### Fixed
31
+
32
+ - **`Instrumentation::InstrumentedTool` server_context parameter translation** — tools without caching received `server_context:` but expected `_server_context:`, causing `ArgumentError` when caching was disabled. `InstrumentedTool` now detects the tool's parameter name and translates accordingly.
33
+ - **`ContextProvider` cache key fork safety** (#162) — replaced `object_id`-based cache key with a stable digest, preventing stale cache hits after `Process.fork`.
34
+
8
35
  ## [4.1.0] - 2026-08-11
9
36
 
10
37
  ### Added
data/CLAUDE.md CHANGED
@@ -9,14 +9,14 @@ structure to AI assistants via the Model Context Protocol (MCP).
9
9
  - `lib/rails_ai_bridge/configuration.rb` — User-facing config with presets (:standard, :full)
10
10
  - `lib/rails_ai_bridge/introspector.rb` — Orchestrates sub-introspectors
11
11
  - `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset
12
- runs **9**, `:full` runs **26** (see `Configuration::PRESETS`). Registry:
12
+ runs **9**, `:full` runs **27** (see `Configuration::PRESETS`). Registry:
13
13
  `Introspector::BUILTIN_INTROSPECTORS` (includes opt-in symbols such as
14
14
  `database_stats`, `non_ar_models` not listed in those presets).
15
- - `lib/rails_ai_bridge/tools/` — 16 built-in MCP tools using the official mcp SDK
15
+ - `lib/rails_ai_bridge/tools/` — 17 built-in MCP tools using the official mcp SDK
16
16
  (hosts can add more via `additional_tools`)
17
17
  - `lib/rails_ai_bridge/serializers/` — Output formatters (claude, claude_rules,
18
- cursor_rules, devin, devin_rules, copilot, copilot_instructions, rules,
19
- markdown, JSON)
18
+ codex, cursor_rules, devin, devin_rules, copilot, copilot_instructions,
19
+ gemini, rules, markdown, JSON)
20
20
  - `lib/rails_ai_bridge/resources.rb` — MCP resources (static data AI clients read directly)
21
21
  - `lib/rails_ai_bridge/server.rb` — MCP server configuration (stdio + HTTP transports)
22
22
  - `lib/rails_ai_bridge/middleware.rb` — Rack middleware for auto-mounting MCP HTTP endpoint
@@ -36,7 +36,7 @@ structure to AI assistants via the Model Context Protocol (MCP).
36
36
  6. **Diff-aware** — context regeneration skips unchanged files
37
37
  7. **Per-assistant serializers** — each AI tool gets tailored output format
38
38
  8. **Zeitwerk autoloading** — files loaded on-demand, not all upfront
39
- 9. **Introspector presets** — `:standard` (9 core) default, `:full` (26 introspectors; optional extras such as `database_stats`, `non_ar_models`) for power users
39
+ 9. **Introspector presets** — `:standard` (9 core) default, `:full` (27 introspectors; optional extras such as `database_stats`, `non_ar_models`) for power users
40
40
  10. **MCP auto-discovery** — `.mcp.json` generated by install generator
41
41
  11. **Compact by default** — context files ≤150 lines, MCP tools use `detail` parameter (summary/standard/full)
42
42
  12. **Per-tool split rules** — `.claude/rules/`, `.cursor/rules/`, `.devin/rules/`, `.github/instructions/`
data/CONTRIBUTING.md CHANGED
@@ -19,13 +19,13 @@ The test suite uses [Combustion](https://github.com/pat/combustion) to boot a mi
19
19
  ```
20
20
  lib/rails_ai_bridge/
21
21
  ├── introspectors/ # Built-in introspectors (schema, models, non_ar_models, routes, …)
22
- ├── tools/ # 11 built-in MCP tools (detail levels, pagination, extensible)
22
+ ├── tools/ # 17 built-in MCP tools (detail levels, pagination, extensible)
23
23
  ├── rubydex_adapter.rb # Rubydex API wrapper (singleton + query interface + stats)
24
24
  ├── rubydex_adapter/ # Extracted collaborators (one concern each)
25
25
  │ ├── serializer.rb # Hash serialization (declaration_to_hash, definition_to_hash, …)
26
26
  │ ├── indexer.rb # Graph building + source file scanning
27
27
  │ └── method_counter.rb # Flat method counting pipeline (no nested conditionals)
28
- ├── serializers/ # Per-assistant formatters (claude, cursor, windsurf, copilot, JSON)
28
+ ├── serializers/ # Per-assistant formatters (claude, codex, cursor, devin, copilot, gemini, JSON)
29
29
  ├── server.rb # MCP server setup (stdio + HTTP)
30
30
  ├── engine.rb # Rails Engine for auto-integration
31
31
  └── configuration.rb # User-facing config (presets, context_mode, limits)
data/README.md CHANGED
@@ -83,7 +83,7 @@ This creates two complementary layers:
83
83
  | Static files | Give the assistant passive project orientation at session start | Fewer cold starts and fewer generic assumptions |
84
84
  | MCP tools | Return exact live details when requested | Less context bloat and fewer schema/route hallucinations |
85
85
 
86
- Compact files are ordered for usefulness: primary domain models, busy endpoints, recently migrated tables, and optional hot-table signals appear before lower-signal supporting details.
86
+ Compact files are ordered for usefulness: primary domain models, busy endpoints, recently migrated tables, and optional hot-table hints appear before less important details.
87
87
 
88
88
  ## Safety model
89
89
 
@@ -159,7 +159,7 @@ Optional: `gem install rails-ai-bridge` installs the gem into your Ruby environm
159
159
  | Zero config | Yes — Railtie + install generator | No — per-project `projects.yml` | No |
160
160
  | Token optimization | Yes — compact files + `detail:"summary"` workflow | Varies | No |
161
161
  | Codex-oriented repo files | Yes — `AGENTS.md`, `.codex/README.md` | No | DIY |
162
- | Live MCP tools | Yes — 13 read-only `rails_*` tools (extensible) | Yes | No |
162
+ | Live MCP tools | Yes — 17 read-only `rails_*` tools (extensible) | Yes | No |
163
163
  | Auto-introspection | Yes — up to **27** domains (`:full`) | No — server points at projects you configure | DIY |
164
164
 
165
165
  *Comparison reflects typical documented setups; verify against each project before treating any row as absolute.*
@@ -251,7 +251,7 @@ This keeps context focused and avoids unnecessary token usage while still allowi
251
251
 
252
252
  ## MCP Tools
253
253
 
254
- The gem exposes **16 built-in tools** via MCP that AI clients call on-demand (hosts can append more via `config.additional_tools`):
254
+ The gem exposes **17 built-in tools** via MCP that AI clients call on-demand (hosts can append more via `config.additional_tools`):
255
255
 
256
256
  | Tool | What it returns |
257
257
  |------|----------------|
@@ -267,10 +267,11 @@ The gem exposes **16 built-in tools** via MCP that AI clients call on-demand (ho
267
267
  | `rails_get_view` | View layouts, templates, partials; optional per-file detail under the configured `app/views` path |
268
268
  | `rails_search_semantic` | Semantic code search using rubydex — find declarations by name with types, locations, and relationships |
269
269
  | `rails_get_stimulus` | Stimulus controllers: targets, values, actions, outlets (requires `:stimulus` introspector) |
270
- | `rails_list_registry` | Skill pack catalog — list skills, agents, or active packs; requires `config/rails_ai_bridge_registry.json` |
270
+ | `rails_list_registry` | Skill pack catalog — list skills, agents, or active packs; requires `config/rails_ai_bridge/registry.json` |
271
271
  | `rails_resolve_skill` | Full content of a named skill or agent from the registry (priority ordering + deprecation redirects); optional `pack=` pin and `type=agent` |
272
272
  | `rails_use_skill` | Loads a skill framed as an application directive (apply it step by step to the current task) |
273
273
  | `rails_use_agent` | Loads an agent/workflow framed as an activation directive (follow it end to end) |
274
+ | `rails_list_context_providers` | Context providers declared in the registry manifest — external services (e.g. MCP servers) the bridge can query for project context; shows type, endpoint, optional flag, and tool specs |
274
275
 
275
276
  All tools are **read-only** — they never modify your application or database.
276
277
 
@@ -307,7 +308,7 @@ A safety net (`max_tool_response_chars`, default 120K) truncates oversized respo
307
308
 
308
309
  Early project-level trials suggest the biggest improvement is not always dramatic token reduction by itself. In several runs, `rails-ai-bridge` led to faster, more focused responses even when total token usage only dropped modestly.
309
310
 
310
- This is expected: compact assistant-specific files and the summary-first MCP workflow reduce orientation overhead, help the model navigate the codebase earlier, and improve the quality of the initial context.
311
+ This makes sense: compact assistant-specific files and the summary-first MCP workflow help the model find relevant code faster and start with better context.
311
312
 
312
313
  Observed benefits so far:
313
314
  - Less exploratory reading before the assistant reaches the relevant files
@@ -543,7 +544,7 @@ end
543
544
  | `parallel_introspection` | `false` | Run introspectors concurrently (requires `concurrent-ruby`, which is already a Rails dependency) |
544
545
  | `parallel_pool_size` | `4` | Max threads in the parallel pool; capped at the number of active introspectors so no idle threads are created |
545
546
  | `parallel_timeout_seconds` | `10` | Per-introspector future timeout (seconds); timed-out introspectors return `{ error: "timed out after Ns" }` without blocking the others |
546
- | `registry.registry_manifest_path` | `"config/rails_ai_bridge_registry.json"` | Path to the registry manifest JSON file for skill pack resolution |
547
+ | `registry.registry_manifest_path` | `"config/rails_ai_bridge/registry.json"` | Path to the registry manifest JSON file for skill pack resolution |
547
548
  | `registry.skill_cache_dir` | `"~/.rails-ai-bridge/cache"` | Directory for caching git repositories containing skill packs |
548
549
  | `registry.skill_packs` | `nil` | Explicit pack names to load, or `nil` for auto-detection based on framework |
549
550
  | `registry.local_registry_paths` | `[]` | Local directory paths (must contain `directory.json`) loaded at priority 0 |
@@ -686,10 +687,10 @@ To customize it in your initializer (`config/initializers/rails_ai_bridge.rb`):
686
687
  rails-ai-bridge can load **skill packs** — shared collections of agent instructions — from versioned git repositories and surface them through `rails_list_registry` and rake tasks.
687
688
 
688
689
  ```ruby
689
- config.registry.registry_manifest_path = "config/rails_ai_bridge_registry.json"
690
+ config.registry.registry_manifest_path = "config/rails_ai_bridge/registry.json"
690
691
  ```
691
692
 
692
- Quick example manifest (`config/rails_ai_bridge_registry.json`):
693
+ Quick example manifest (`config/rails_ai_bridge/registry.json`):
693
694
 
694
695
  ```json
695
696
  {
@@ -835,7 +836,7 @@ Bug reports and pull requests: [github.com/igmarin/rails-ai-bridge/issues](https
835
836
 
836
837
  ## Acknowledgments & Origins
837
838
 
838
- This gem ships as **rails-ai-bridge** (Ruby **`RailsAiBridge`**, version **4.1.0**). Earlier iterations of the same codebase were distributed as `rails-ai-context`.
839
+ This gem ships as **rails-ai-bridge** (Ruby **`RailsAiBridge`**, version **4.2.0**). Earlier iterations of the same codebase were distributed as `rails-ai-context`.
839
840
 
840
841
  RailsMCP evolved from
841
842
  [crisnahine/rails-ai-context](https://github.com/crisnahine/rails-ai-context),
data/Rakefile CHANGED
@@ -5,4 +5,6 @@ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
+ Dir.glob('lib/tasks/*.rake').each { |task| load task }
9
+
8
10
  task default: :spec
data/SECURITY.md CHANGED
@@ -4,10 +4,10 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|--------------------|
7
- | 3.6.x | :white_check_mark: |
8
- | 3.5.x | :white_check_mark: |
9
- | 3.0.x–3.4.x | :white_check_mark: (security fixes best-effort) |
10
- | 2.x | :x: (end of life — upgrade to 3.x) |
7
+ | 4.x | :white_check_mark: |
8
+ | 3.6.x | :white_check_mark: (security fixes best-effort) |
9
+ | 3.0.x–3.5.x | :warning: (best-effort) |
10
+ | 2.x | :x: (end of life — upgrade to 4.x) |
11
11
  | 1.x | :x: |
12
12
  | < 1.0 | :x: |
13
13
 
@@ -96,7 +96,7 @@ end
96
96
  ### 3. Configuration
97
97
 
98
98
  **Add to `lib/rails_ai_bridge/configuration.rb`:**
99
- - `registry_manifest_path` — Path to registry.json (default: `config/rails_ai_bridge_registry.json`)
99
+ - `registry_manifest_path` — Path to registry.json (default: `config/rails_ai_bridge/registry.json`)
100
100
  - `skill_cache_dir` — Path to git cache directory (default: `~/.rails-ai-bridge/cache`)
101
101
  - `skill_packs` — Array of explicit pack names to load (optional)
102
102
  - `local_registry_paths` — Array of local registry directories (optional)
data/docs/devin-setup.md CHANGED
@@ -226,7 +226,7 @@ rails-ai-bridge supports a skill registry — a collection of reusable AI skills
226
226
 
227
227
  The `rails_list_registry` tool returns all available skills. The `rails_resolve_skill` tool retrieves the full content of a named skill so Devin can apply it.
228
228
 
229
- Skills are configured via `config/rails_ai_bridge_registry.json` in your Rails application. A minimal registry file looks like:
229
+ Skills are configured via `config/rails_ai_bridge/registry.json` in your Rails application. A minimal registry file looks like:
230
230
 
231
231
  ```json
232
232
  {
@@ -261,4 +261,4 @@ Devin will call `rails_list_registry`, review the options, and call `rails_resol
261
261
  | `.devin/rules/rails-mcp-tools.md` | `rails ai:bridge:devin` | Yes |
262
262
  | `AGENTS.md` | `rails ai:bridge:devin` | Yes |
263
263
  | `.devin/mcp.json` | Manual | Yes |
264
- | `config/rails_ai_bridge_registry.json` | Manual | Yes |
264
+ | `config/rails_ai_bridge/registry.json` | Manual | Yes |
@@ -119,7 +119,7 @@ necessary for the future skill compiler feature.
119
119
 
120
120
  **Files:**
121
121
  - `lib/rails_ai_bridge/config/registry.rb` — `Config::Registry` sub-object
122
- - `registry_manifest_path` (default: `config/rails_ai_bridge_registry.json`)
122
+ - `registry_manifest_path` (default: `config/rails_ai_bridge/registry.json`)
123
123
  - `skill_cache_dir` (default: `~/.rails-ai-bridge/cache`)
124
124
  - `skill_packs` (default: `nil` — triggers auto-detection)
125
125
  - `local_registry_paths` (default: `[]`)
@@ -16,7 +16,7 @@ When a pack is loaded, its skills and agents appear in `rails_list_registry`, an
16
16
 
17
17
  ## Quick start
18
18
 
19
- **1. Create the registry manifest** at `config/rails_ai_bridge_registry.json`:
19
+ **1. Create the registry manifest** at `config/rails_ai_bridge/registry.json`:
20
20
 
21
21
  ```json
22
22
  {
@@ -39,7 +39,7 @@ When a pack is loaded, its skills and agents appear in `rails_list_registry`, an
39
39
 
40
40
  ```ruby
41
41
  RailsAiBridge.configure do |config|
42
- config.registry.registry_manifest_path = "config/rails_ai_bridge_registry.json"
42
+ config.registry.registry_manifest_path = "config/rails_ai_bridge/registry.json"
43
43
  end
44
44
  ```
45
45
 
@@ -102,7 +102,7 @@ All options live under `config.registry.*`:
102
102
 
103
103
  | Option | Default | Description |
104
104
  |--------|---------|-------------|
105
- | `registry_manifest_path` | `"config/rails_ai_bridge_registry.json"` | Path to the registry manifest JSON |
105
+ | `registry_manifest_path` | `"config/rails_ai_bridge/registry.json"` | Path to the registry manifest JSON |
106
106
  | `skill_cache_dir` | `~/.rails-ai-bridge/cache` | Directory for caching cloned git repositories |
107
107
  | `skill_packs` | `nil` | Explicit list of pack names to load, or `nil` for auto-detection |
108
108
  | `local_registry_paths` | `[]` | Local directory paths containing a `directory.json` (loaded at priority 0) |
@@ -18,7 +18,7 @@ A **skill pack** is a git repository containing:
18
18
  - **Markdown files** for each skill (e.g. `skills/code-review.md`)
19
19
  - Optional **deprecation redirects** when skills are renamed
20
20
 
21
- The skill registry in your Rails app is a **`config/rails_ai_bridge_registry.json`** manifest that tells rails-ai-bridge which packs to load, where to find them, and how to prioritize them when two packs define a skill with the same name.
21
+ The skill registry in your Rails app is a **`config/rails_ai_bridge/registry.json`** manifest that tells rails-ai-bridge which packs to load, where to find them, and how to prioritize them when two packs define a skill with the same name.
22
22
 
23
23
  ---
24
24
 
@@ -26,7 +26,7 @@ The skill registry in your Rails app is a **`config/rails_ai_bridge_registry.jso
26
26
 
27
27
  ### Step 1 — Create the registry manifest
28
28
 
29
- Create `config/rails_ai_bridge_registry.json` in your Rails app:
29
+ Create `config/rails_ai_bridge/registry.json` in your Rails app:
30
30
 
31
31
  ```json
32
32
  {
@@ -78,7 +78,7 @@ In `config/initializers/rails_ai_bridge.rb`:
78
78
 
79
79
  ```ruby
80
80
  RailsAiBridge.configure do |config|
81
- config.registry.registry_manifest_path = "config/rails_ai_bridge_registry.json"
81
+ config.registry.registry_manifest_path = "config/rails_ai_bridge/registry.json"
82
82
  end
83
83
  ```
84
84
 
@@ -195,13 +195,13 @@ This protects against:
195
195
  rails ai:registry:lockfile
196
196
  ```
197
197
 
198
- The file is written to `config/rails_ai_bridge/directory.lock` by default. Commit it alongside your manifest.
198
+ The file is written to `config/rails_ai_bridge/registry.lock` by default. Commit it alongside your manifest.
199
199
 
200
200
  ### Configuration
201
201
 
202
202
  ```ruby
203
203
  RailsAiBridge.configure do |config|
204
- config.registry.lockfile_path = "config/rails_ai_bridge/directory.lock"
204
+ config.registry.lockfile_path = "config/rails_ai_bridge/registry.lock"
205
205
  config.registry.lockfile_verification = :strict # :strict (default), :warn, or :disabled
206
206
  end
207
207
  ```
@@ -438,7 +438,7 @@ initializer changes take effect immediately.
438
438
 
439
439
  | Symptom | Likely cause | Fix |
440
440
  |---------|-------------|-----|
441
- | `rails ai:skills:list` shows "No registry manifest found" | Manifest file does not exist or path is wrong | Create `config/rails_ai_bridge_registry.json` or check `config.registry.registry_manifest_path` |
441
+ | `rails ai:skills:list` shows "No registry manifest found" | Manifest file does not exist or path is wrong | Create `config/rails_ai_bridge/registry.json` or check `config.registry.registry_manifest_path` |
442
442
  | Git clone fails with "not found" | Pack source URL is wrong or repo is private | Check the `source` field; for private repos use a full SSH URL `git@github.com:org/repo.git` |
443
443
  | `Invalid source format` error mentioning `http://` | Pack source uses plain HTTP | Change `source` to `https://` or `git@` — plain HTTP is not accepted |
444
444
  | Pack loads but shows 0 skills | `directory.json` is missing or has wrong path | Check the root of the cloned pack for `directory.json`; set `tile:` field if the file is elsewhere |
@@ -446,7 +446,7 @@ initializer changes take effect immediately.
446
446
  | Stale skills after pack update | Resolver cache is warm or pull TTL has not elapsed | Run `rails ai:skills:clear_cache` to force a re-clone on the next build |
447
447
  | Pack is not re-fetched even after `resolver_ttl` expired | Pack was cloned recently — `git_pull_ttl` (24 h) is still fresh | Run `rails ai:skills:clear_cache` or set `git_pull_ttl: 0` temporarily |
448
448
  | `git checkout <ref>` failed | Invalid ref or detached HEAD issue | Verify the `ref` value matches a branch, tag, or SHA in the remote repo |
449
- | `Lockfile mismatch for pack '…'` | Resolved pack commit differs from `directory.lock` | Run `rails ai:registry:lockfile` to update the lockfile after reviewing the pack changes |
449
+ | `Lockfile mismatch for pack '…'` | Resolved pack commit differs from `registry.lock` | Run `rails ai:registry:lockfile` to update the lockfile after reviewing the pack changes |
450
450
  | Git operation hangs / server request times out | Slow or unreachable remote | Reduce `git_timeout` to fail faster; check network connectivity to the remote |
451
451
  | Warning: "Pack '…' depends on '…' which is not in the active pack set" | A loaded pack has an unsatisfied `depends_on` entry | Add the missing pack name to `always_loaded` or `skill_packs` in your manifest, or enable `config.registry.auto_load_dependencies` |
452
452
  | Warning: "Circular dependency detected: …" | Two or more active packs depend on each other | Break the cycle in the manifests; packs still load, but the cycle usually indicates a mistake |
@@ -462,5 +462,5 @@ initializer changes take effect immediately.
462
462
  - **Timeout protection**: all git operations are bounded by `git_timeout` (default 30 s) so a slow remote cannot block the calling thread indefinitely.
463
463
  - **Cache key sanitization**: local cache directory names are derived from a sanitized source string plus a SHA256 hash suffix to prevent filesystem collisions.
464
464
  - **Stable local pack names**: local registry pack names use a SHA256 digest of the directory path, so reordering `local_registry_paths` cannot silently shift pack identities.
465
- - **Lockfile verification**: a `directory.lock` records the expected commit SHA for each remote pack. The resolver fails closed when the cloned commit does not match, preventing a compromised or unexpectedly modified pack from injecting instructions.
465
+ - **Lockfile verification**: a `registry.lock` records the expected commit SHA for each remote pack. The resolver fails closed when the cloned commit does not match, preventing a compromised or unexpectedly modified pack from injecting instructions.
466
466
  - **Local path trust**: local paths are used directly without git operations. The path traversal guard in the Resolver still applies to file reads within a local pack.
@@ -11,8 +11,20 @@ module RailsAiBridge
11
11
  # @see RailsAiBridge::Registry::PackResolver
12
12
  # @see RailsAiBridge::Registry::Resolver
13
13
  class Registry
14
+ # Default path for the registry manifest (4.2.0+ standard location).
15
+ DEFAULT_REGISTRY_MANIFEST_PATH = 'config/rails_ai_bridge/registry.json'
16
+
17
+ # Legacy manifest path used before 4.2.0; kept for backward-compat fallback.
18
+ LEGACY_REGISTRY_MANIFEST_PATH = 'config/rails_ai_bridge_registry.json'
19
+
20
+ # Default path for the skill pack lockfile (4.2.0+ standard location).
21
+ DEFAULT_LOCKFILE_PATH = 'config/rails_ai_bridge/registry.lock'
22
+
23
+ # Legacy lockfile path used before 4.2.0; kept for backward-compat fallback.
24
+ LEGACY_LOCKFILE_PATH = 'config/rails_ai_bridge/directory.lock'
25
+
14
26
  # @return [String] path to the registry manifest JSON file
15
- attr_accessor :registry_manifest_path
27
+ attr_writer :registry_manifest_path
16
28
 
17
29
  # @return [String] directory for caching git repositories
18
30
  attr_accessor :skill_cache_dir
@@ -36,7 +48,38 @@ module RailsAiBridge
36
48
  attr_reader :git_timeout
37
49
 
38
50
  # @return [String, nil] path to the skill pack lockfile. nil disables lockfile verification.
39
- attr_accessor :lockfile_path
51
+ attr_writer :lockfile_path
52
+
53
+ # Returns the configured registry manifest path, applying a backward-compatibility
54
+ # fallback when the default path does not exist but the legacy path does.
55
+ #
56
+ # @return [String] resolved manifest path
57
+ def registry_manifest_path
58
+ return @registry_manifest_path unless @registry_manifest_path == DEFAULT_REGISTRY_MANIFEST_PATH
59
+
60
+ File.exist?(DEFAULT_REGISTRY_MANIFEST_PATH) ? DEFAULT_REGISTRY_MANIFEST_PATH : legacy_manifest_path
61
+ end
62
+
63
+ # Returns the configured lockfile path, applying a backward-compatibility
64
+ # fallback when the default path does not exist but the legacy path does.
65
+ # Returns +nil+ when lockfile verification is disabled.
66
+ #
67
+ # @return [String, nil] resolved lockfile path
68
+ def lockfile_path
69
+ return @lockfile_path unless @lockfile_path == DEFAULT_LOCKFILE_PATH
70
+
71
+ File.exist?(DEFAULT_LOCKFILE_PATH) ? DEFAULT_LOCKFILE_PATH : legacy_lockfile_path
72
+ end
73
+
74
+ # @api private
75
+ def legacy_manifest_path
76
+ File.exist?(LEGACY_REGISTRY_MANIFEST_PATH) ? LEGACY_REGISTRY_MANIFEST_PATH : DEFAULT_REGISTRY_MANIFEST_PATH
77
+ end
78
+
79
+ # @api private
80
+ def legacy_lockfile_path
81
+ File.exist?(LEGACY_LOCKFILE_PATH) ? LEGACY_LOCKFILE_PATH : DEFAULT_LOCKFILE_PATH
82
+ end
40
83
 
41
84
  # @return [Symbol] how to behave when the lockfile differs from the resolved pack:
42
85
  # :strict (default) raises, :warn logs but proceeds, :disabled skips verification.
@@ -97,14 +140,14 @@ module RailsAiBridge
97
140
  end
98
141
 
99
142
  def initialize
100
- @registry_manifest_path = 'config/rails_ai_bridge_registry.json'
143
+ @registry_manifest_path = DEFAULT_REGISTRY_MANIFEST_PATH
101
144
  @skill_cache_dir = File.expand_path('~/.rails-ai-bridge/cache')
102
145
  @skill_packs = nil
103
146
  @local_registry_paths = []
104
147
  @resolver_ttl = 1800
105
148
  @git_pull_ttl = 86_400
106
149
  @git_timeout = 30
107
- @lockfile_path = 'config/rails_ai_bridge/directory.lock'
150
+ @lockfile_path = DEFAULT_LOCKFILE_PATH
108
151
  @lockfile_verification = :strict
109
152
  @auto_load_dependencies = false
110
153
  end
@@ -108,7 +108,7 @@ module RailsAiBridge
108
108
  end
109
109
 
110
110
  def cache_key(app)
111
- app.object_id
111
+ "#{app.class.name}:#{defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : 'development'}"
112
112
  end
113
113
 
114
114
  def metadata_key?(section_name)
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAiBridge
4
+ class Doctor
5
+ module Checkers
6
+ # Verifies the registry manifest is present, valid, and resolvable.
7
+ #
8
+ # Checks (in order):
9
+ # 1. The configured +registry_manifest_path+ exists.
10
+ # 2. The manifest JSON parses successfully.
11
+ # 3. +RegistryManifest.validate!+ passes schema validation.
12
+ # 4. +Registry.build_resolver+ returns a non-nil resolver.
13
+ # 5. The lockfile exists and matches (when +lockfile_path+ is configured).
14
+ #
15
+ # Each failure short-circuits with an actionable fix hint so the user can
16
+ # resolve the first problem before chasing downstream symptoms.
17
+ class RegistryChecker < BaseChecker
18
+ # @return [Doctor::Check] +:pass+, +:warn+, or +:fail+
19
+ def call
20
+ return missing_manifest_check unless manifest_path_exists?
21
+
22
+ return invalid_json_check unless manifest_parses?
23
+
24
+ return validation_failed_check unless manifest_validates?
25
+
26
+ resolver = build_resolver
27
+ return resolver_failed_check(resolver) unless resolver
28
+
29
+ return lockfile_check if lockfile_configured? && !lockfile_exists?
30
+
31
+ new_check(name: 'Registry', status: :pass, message: 'Registry manifest is valid and resolvable', fix: nil)
32
+ end
33
+
34
+ private
35
+
36
+ def registry_config
37
+ RailsAiBridge.configuration.registry
38
+ end
39
+
40
+ def manifest_path
41
+ registry_config.registry_manifest_path
42
+ end
43
+
44
+ def manifest_path_exists?
45
+ File.exist?(manifest_path)
46
+ end
47
+
48
+ def missing_manifest_check
49
+ new_check(
50
+ name: 'Registry',
51
+ status: :warn,
52
+ message: "Registry manifest not found at `#{manifest_path}`",
53
+ fix: 'Create a registry manifest file — see docs/skill-registry-guide.md'
54
+ )
55
+ end
56
+
57
+ def manifest_parses?
58
+ parsed_manifest
59
+ true
60
+ rescue ArgumentError
61
+ false
62
+ end
63
+
64
+ def invalid_json_check
65
+ new_check(
66
+ name: 'Registry',
67
+ status: :fail,
68
+ message: "Registry manifest at `#{manifest_path}` contains invalid JSON or could not be read",
69
+ fix: "Fix the JSON syntax in the manifest file — run `ruby -rjson -e 'JSON.parse(File.read(ARGV[0]))' #{manifest_path}` to see the parse error"
70
+ )
71
+ end
72
+
73
+ def parsed_manifest
74
+ @parsed_manifest ||= RailsAiBridge::Registry::RegistryManifest.from_file(manifest_path)
75
+ end
76
+
77
+ def manifest_validates?
78
+ RailsAiBridge::Registry::RegistryManifest.validate!(raw_manifest_hash)
79
+ true
80
+ rescue RailsAiBridge::Registry::RegistryManifest::ValidationError
81
+ false
82
+ end
83
+
84
+ def raw_manifest_hash
85
+ JSON.parse(File.read(manifest_path))
86
+ end
87
+
88
+ def validation_failed_check
89
+ new_check(
90
+ name: 'Registry',
91
+ status: :fail,
92
+ message: "Registry manifest at `#{manifest_path}` failed schema validation",
93
+ fix: 'Check the manifest structure — see docs/skill-registry-guide.md for the expected schema'
94
+ )
95
+ end
96
+
97
+ def build_resolver
98
+ RailsAiBridge::Registry.build_resolver(registry_config)
99
+ end
100
+
101
+ def resolver_failed_check(resolver)
102
+ new_check(
103
+ name: 'Registry',
104
+ status: :warn,
105
+ message: "Registry manifest parsed but resolver returned nil#{resolver_reason(resolver)}",
106
+ fix: 'Check that all pack sources are reachable and git is available — see logs for details'
107
+ )
108
+ end
109
+
110
+ def resolver_reason(resolver)
111
+ return '' unless resolver.nil?
112
+
113
+ ' (manifest missing or pack sources could not be resolved)'
114
+ end
115
+
116
+ def lockfile_configured?
117
+ registry_config.lockfile_path
118
+ end
119
+
120
+ def lockfile_exists?
121
+ File.exist?(registry_config.lockfile_path)
122
+ end
123
+
124
+ def lockfile_check
125
+ new_check(
126
+ name: 'Registry',
127
+ status: :warn,
128
+ message: "Lockfile not found at `#{registry_config.lockfile_path}`",
129
+ fix: 'Run `rails ai:registry:lock` to generate the lockfile, or set lockfile_path to nil to disable verification'
130
+ )
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end