rails-ai-context 1.1.0 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06aa759cd0c6214756b038ed582232f6b6b1e997c35bda3c69b5bb3288c9ae6c
4
- data.tar.gz: 44f40ae646ad5706747f35c7ffb03aa36ccb4be65a205aaad989d5081c446fbb
3
+ metadata.gz: dc7244d020376cc416232a1cbbc0a287866dc4243b9566e4fa2d50d556e5a1c6
4
+ data.tar.gz: 5251eb33ca4d3f72803e3acc77d272a4ec038c9a367390752db463195126675a
5
5
  SHA512:
6
- metadata.gz: 34dbfb450c700d0f3520b3e345e38beea105acdc1775e5a1e2b3c68bbd337325f21ef22c4d493a47142a01bf258f07ce1409e7b3933396136954e82be93962e6
7
- data.tar.gz: b1bb5feb20affb165a194a2cbcd39be37caac1b8b2552a2a528a30114c2d2dc450e61f962d9b9d98b7acc24a7b2b0329b41dde6a677b713c85ec102046f9e0a3
6
+ metadata.gz: ddda0c23e18344f9b95d4ff2b0e5ce9dba9c247a2915a1337226f98fb1ef2ab83b07370e2baf26bbb25261241baf75ccf40c952dd03cae4d6b6004c93398c14f
7
+ data.tar.gz: 790dc32dffed8275469eaebae74491dcaa0dbc2b979e53fc8e959b889c69c2eec6982304a1d0d39e22900e79a0214743cf616a8f4e70bea8a4797be679be9c9c
data/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ 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
+ ## [1.1.1] - 2026-03-23
9
+
10
+ ### Added
11
+
12
+ - **Full-preset stack overview in all serializers** — compact mode now surfaces summary lines for auth, Hotwire/Turbo, API, I18n, ActiveStorage, ActionText, assets, engines, and multi-database in generated context files (CLAUDE.md, AGENTS.md, .windsurfrules, and all split rules). Previously this data was only available via MCP tools.
13
+ - **`rails_analyze_feature` in all tool reference sections** — the 14th tool (added in v1.0.0) was missing from serializer output. Now listed in all generated files across Claude, Cursor, Windsurf, Copilot, and OpenCode formats.
14
+
15
+ ### Fixed
16
+
17
+ - **Tool count corrected from 13 to 14** across all serializers to reflect `rails_analyze_feature` added in v1.0.0.
18
+
8
19
  ## [1.1.0] - 2026-03-23
9
20
 
10
21
  ### Changed
data/SECURITY.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|--------------------|
7
- | 1.1.x | :white_check_mark: |
7
+ | 1.1.x | :white_check_mark: |
8
8
  | 1.0.x | :white_check_mark: |
9
9
  | < 1.0 | :x: |
10
10
 
@@ -5,6 +5,8 @@ module RailsAiContext
5
5
  # Generates .claude/rules/ files for Claude Code auto-discovery.
6
6
  # These provide quick-reference lists without bloating CLAUDE.md.
7
7
  class ClaudeRulesSerializer
8
+ include StackOverviewHelper
9
+
8
10
  attr_reader :context
9
11
 
10
12
  def initialize(context)
@@ -77,6 +79,8 @@ module RailsAiContext
77
79
  (conv[:architecture] || []).first(5).each { |p| lines << "- #{p}" }
78
80
  end
79
81
 
82
+ lines.concat(full_preset_stack_lines)
83
+
80
84
  # ApplicationController before_actions — apply to all controllers
81
85
  begin
82
86
  root = defined?(Rails) ? Rails.root.to_s : Dir.pwd
@@ -342,7 +346,7 @@ module RailsAiContext
342
346
  "- app/javascript/controllers/index.js — Stimulus auto-registers controllers. No need to read.",
343
347
  "- Test files — use `rails_get_test_info(detail:\"full\")` for patterns.",
344
348
  "",
345
- "## Tools (13)",
349
+ "## Tools (14)",
346
350
  "",
347
351
  "**rails_get_schema** — database tables, columns, indexes, foreign keys",
348
352
  "- `rails_get_schema(detail:\"summary\")` — all tables with column counts",
@@ -380,6 +384,9 @@ module RailsAiContext
380
384
  "**rails_validate** — syntax checker for edited files",
381
385
  "- `rails_validate(files:[\"app/models/cook.rb\"])` — checks Ruby, ERB, JS syntax in one call",
382
386
  "",
387
+ "**rails_analyze_feature** — combined schema + models + controllers + routes for a feature area",
388
+ "- `rails_analyze_feature(feature:\"authentication\")` — one call gets everything related to a feature",
389
+ "",
383
390
  "**rails_get_config** — cache store, session, timezone, middleware, initializers",
384
391
  "**rails_get_gems** — notable gems categorized by function",
385
392
  "**rails_get_conventions** — architecture patterns, directory structure",
@@ -7,6 +7,7 @@ module RailsAiContext
7
7
  # In :full mode, delegates to MarkdownSerializer with behavioral rules.
8
8
  class ClaudeSerializer
9
9
  include TestCommandDetection
10
+ include StackOverviewHelper
10
11
 
11
12
  attr_reader :context
12
13
 
@@ -79,16 +80,6 @@ module RailsAiContext
79
80
  lines << "- Routes: #{routes[:total_routes]} across #{app_ctrls.size} controllers"
80
81
  end
81
82
 
82
- auth = context[:auth]
83
- if auth.is_a?(Hash) && !auth[:error]
84
- parts = []
85
- parts << "Devise" if auth.dig(:authentication, :devise)&.any?
86
- parts << "Rails 8 auth" if auth.dig(:authentication, :rails_auth)
87
- parts << "Pundit" if auth.dig(:authorization, :pundit)&.any?
88
- parts << "CanCanCan" if auth.dig(:authorization, :cancancan)
89
- lines << "- Auth: #{parts.join(' + ')}" if parts.any?
90
- end
91
-
92
83
  jobs = context[:jobs]
93
84
  if jobs.is_a?(Hash) && !jobs[:error]
94
85
  job_count = jobs[:jobs]&.size || 0
@@ -107,6 +98,8 @@ module RailsAiContext
107
98
  lines << "- Migrations: #{migrations[:total]} total, #{pending&.size || 0} pending"
108
99
  end
109
100
 
101
+ lines.concat(full_preset_stack_lines)
102
+
110
103
  lines << ""
111
104
  lines
112
105
  end
@@ -232,7 +225,7 @@ module RailsAiContext
232
225
 
233
226
  def render_mcp_guide # rubocop:disable Metrics/MethodLength
234
227
  [
235
- "## MCP Tools (13) — ALWAYS Use These First",
228
+ "## MCP Tools (14) — ALWAYS Use These First",
236
229
  "",
237
230
  "Use MCP for reference files (schema, routes, tests). Read directly if you'll edit.",
238
231
  "MCP tools return line numbers. Start with `detail:\"summary\"`.",
@@ -244,6 +237,7 @@ module RailsAiContext
244
237
  "- `rails_get_view(controller:\"cooks\")` — view list; `(path:\"cooks/index.html.erb\")` — content",
245
238
  "- `rails_get_stimulus(detail:\"summary\")` → `(controller:\"name\")` — targets, actions, values",
246
239
  "- `rails_get_test_info(detail:\"full\")` — fixtures, factories, helpers; `(model:\"Cook\")` — tests",
240
+ "- `rails_analyze_feature(feature:\"auth\")` — schema + models + controllers + routes for a feature",
247
241
  "- `rails_get_config` | `rails_get_gems` | `rails_get_conventions` | `rails_search_code`",
248
242
  "- `rails_validate(files:[\"path/to/file.rb\"])` — validate Ruby, ERB, JS syntax in one call",
249
243
  ""
@@ -5,6 +5,8 @@ module RailsAiContext
5
5
  # Generates .github/instructions/*.instructions.md files with applyTo frontmatter
6
6
  # for GitHub Copilot path-specific instructions.
7
7
  class CopilotInstructionsSerializer
8
+ include StackOverviewHelper
9
+
8
10
  attr_reader :context
9
11
 
10
12
  def initialize(context)
@@ -79,6 +81,8 @@ module RailsAiContext
79
81
  (conv[:architecture] || []).first(5).each { |p| lines << "- #{arch_labels[p] || p}" }
80
82
  end
81
83
 
84
+ lines.concat(full_preset_stack_lines)
85
+
82
86
  # List service objects
83
87
  begin
84
88
  root = defined?(Rails) ? Rails.root.to_s : Dir.pwd
@@ -241,7 +245,7 @@ module RailsAiContext
241
245
  "applyTo: \"**/*\"",
242
246
  "---",
243
247
  "",
244
- "# Rails MCP Tools (13) — Use These First",
248
+ "# Rails MCP Tools (14) — Use These First",
245
249
  "",
246
250
  "Use MCP for reference files (schema, routes, tests). Read directly if you'll edit.",
247
251
  "MCP tools return line numbers for surgical edits.",
@@ -254,6 +258,7 @@ module RailsAiContext
254
258
  "- `rails_get_view(controller:\"cooks\")` — view list; `(path:\"cooks/index.html.erb\")` — content",
255
259
  "- `rails_get_stimulus(detail:\"summary\")` → `(controller:\"name\")` — targets, actions, values",
256
260
  "- `rails_get_test_info(detail:\"full\")` — fixtures, factories, helpers; `(model:\"Cook\")` — existing tests",
261
+ "- `rails_analyze_feature(feature:\"auth\")` — schema + models + controllers + routes for a feature",
257
262
  "- `rails_get_config` | `rails_get_gems` | `rails_get_conventions` | `rails_search_code`",
258
263
  "- `rails_get_edit_context(file:\"path\", near:\"keyword\")` — surgical edit context with line numbers",
259
264
  "- `rails_validate(files:[\"path\"])` — validate Ruby, ERB, JS syntax in one call",
@@ -7,6 +7,7 @@ module RailsAiContext
7
7
  # In :full mode, delegates to MarkdownSerializer with Copilot header.
8
8
  class CopilotSerializer
9
9
  include TestCommandDetection
10
+ include StackOverviewHelper
10
11
 
11
12
  attr_reader :context
12
13
 
@@ -44,6 +45,8 @@ module RailsAiContext
44
45
  lines << "- Routes: #{routes[:total_routes]} across #{(routes[:by_controller] || {}).size} controllers"
45
46
  end
46
47
 
48
+ lines.concat(full_preset_stack_lines)
49
+
47
50
  # Gems by category
48
51
  gems = context[:gems]
49
52
  if gems.is_a?(Hash) && !gems[:error]
@@ -6,6 +6,8 @@ module RailsAiContext
6
6
  # Each file is focused, <50 lines, with YAML frontmatter.
7
7
  # .cursorrules is deprecated by Cursor; this is the recommended format.
8
8
  class CursorRulesSerializer
9
+ include StackOverviewHelper
10
+
9
11
  attr_reader :context
10
12
 
11
13
  def initialize(context)
@@ -87,6 +89,8 @@ module RailsAiContext
87
89
  (conv[:architecture] || []).first(5).each { |p| lines << "- #{arch_labels[p] || p}" }
88
90
  end
89
91
 
92
+ lines.concat(full_preset_stack_lines)
93
+
90
94
  # List service objects
91
95
  begin
92
96
  root = defined?(Rails) ? Rails.root.to_s : Dir.pwd
@@ -269,7 +273,7 @@ module RailsAiContext
269
273
  "alwaysApply: true",
270
274
  "---",
271
275
  "",
272
- "# Rails MCP Tools (13) — Use These First",
276
+ "# Rails MCP Tools (14) — Use These First",
273
277
  "",
274
278
  "Use MCP for reference files (schema, routes, tests). Read files directly if you'll edit them.",
275
279
  "MCP tools return line numbers for surgical edits.",
@@ -281,6 +285,7 @@ module RailsAiContext
281
285
  "- `rails_get_view(controller:\"cooks\")` — view list; `rails_get_view(path:\"cooks/index.html.erb\")` — content",
282
286
  "- `rails_get_stimulus(detail:\"summary\")` → `rails_get_stimulus(controller:\"name\")`",
283
287
  "- `rails_get_test_info(detail:\"full\")` — fixtures, factories, helpers; `(model:\"Cook\")` — existing tests",
288
+ "- `rails_analyze_feature(feature:\"auth\")` — schema + models + controllers + routes for a feature",
284
289
  "- `rails_get_config` | `rails_get_gems` | `rails_get_conventions` | `rails_search_code`",
285
290
  "- `rails_get_edit_context(file:\"path\", near:\"keyword\")` — surgical edit context with line numbers",
286
291
  "- `rails_validate(files:[\"path\"])` — validate Ruby, ERB, JS syntax in one call",
@@ -7,6 +7,7 @@ module RailsAiContext
7
7
  # In :full mode, delegates to MarkdownSerializer with OpenCode header.
8
8
  class OpencodeSerializer
9
9
  include TestCommandDetection
10
+ include StackOverviewHelper
10
11
 
11
12
  attr_reader :context
12
13
 
@@ -78,16 +79,6 @@ module RailsAiContext
78
79
  lines << "- Routes: #{routes[:total_routes]} across #{app_ctrls.size} controllers"
79
80
  end
80
81
 
81
- auth = context[:auth]
82
- if auth.is_a?(Hash) && !auth[:error]
83
- parts = []
84
- parts << "Devise" if auth.dig(:authentication, :devise)&.any?
85
- parts << "Rails 8 auth" if auth.dig(:authentication, :rails_auth)
86
- parts << "Pundit" if auth.dig(:authorization, :pundit)&.any?
87
- parts << "CanCanCan" if auth.dig(:authorization, :cancancan)
88
- lines << "- Auth: #{parts.join(" + ")}" if parts.any?
89
- end
90
-
91
82
  jobs = context[:jobs]
92
83
  if jobs.is_a?(Hash) && !jobs[:error]
93
84
  job_count = jobs[:jobs]&.size || 0
@@ -106,6 +97,8 @@ module RailsAiContext
106
97
  lines << "- Migrations: #{migrations[:total]} total, #{pending&.size || 0} pending"
107
98
  end
108
99
 
100
+ lines.concat(full_preset_stack_lines)
101
+
109
102
  lines << ""
110
103
  lines
111
104
  end
@@ -186,7 +179,7 @@ module RailsAiContext
186
179
 
187
180
  def render_mcp_guide # rubocop:disable Metrics/MethodLength
188
181
  [
189
- "## MCP Tools (13) — ALWAYS Use These First",
182
+ "## MCP Tools (14) — ALWAYS Use These First",
190
183
  "",
191
184
  "Use MCP for reference files (schema, routes, tests). Read directly if you'll edit.",
192
185
  "MCP tools return line numbers. Start with `detail:\"summary\"`.",
@@ -199,6 +192,7 @@ module RailsAiContext
199
192
  "- `rails_get_stimulus(detail:\"summary\")` → `(controller:\"name\")` — targets, actions, values",
200
193
  "- `rails_get_test_info(detail:\"full\")` — fixtures, factories, helpers; `(model:\"Cook\")` — tests",
201
194
  "- `rails_get_edit_context(file:\"path\", near:\"keyword\")` — surgical edit context with line numbers",
195
+ "- `rails_analyze_feature(feature:\"auth\")` — schema + models + controllers + routes for a feature",
202
196
  "- `rails_validate(files:[...])` — batch syntax check for Ruby, ERB, JS",
203
197
  "- `rails_get_config` | `rails_get_gems` | `rails_get_conventions` | `rails_search_code`",
204
198
  ""
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAiContext
4
+ module Serializers
5
+ # Shared helper for rendering stack overview lines from full-preset introspectors.
6
+ # Include in any serializer that has a `context` reader and renders a project overview.
7
+ module StackOverviewHelper
8
+ # Returns an array of summary lines for full-preset introspectors.
9
+ # Each line is only added if the introspector returned meaningful data.
10
+ def full_preset_stack_lines(ctx = context)
11
+ lines = []
12
+
13
+ auth = ctx[:auth]
14
+ if auth.is_a?(Hash) && !auth[:error]
15
+ parts = []
16
+ parts << "Devise" if auth.dig(:authentication, :devise)&.any?
17
+ parts << "Rails 8 auth" if auth.dig(:authentication, :rails_auth)
18
+ parts << "Pundit" if auth.dig(:authorization, :pundit)&.any?
19
+ parts << "CanCanCan" if auth.dig(:authorization, :cancancan)
20
+ lines << "- Auth: #{parts.join(' + ')}" if parts.any?
21
+ end
22
+
23
+ turbo = ctx[:turbo]
24
+ if turbo.is_a?(Hash) && !turbo[:error]
25
+ parts = []
26
+ parts << "#{(turbo[:frames] || []).size} frames" if turbo[:frames]&.any?
27
+ parts << "#{(turbo[:streams] || []).size} streams" if turbo[:streams]&.any?
28
+ parts << "broadcasts" if turbo[:broadcasts]&.any?
29
+ lines << "- Hotwire: #{parts.join(', ')}" if parts.any?
30
+ end
31
+
32
+ api = ctx[:api]
33
+ if api.is_a?(Hash) && !api[:error]
34
+ parts = []
35
+ parts << "API-only" if api[:api_only]
36
+ parts << "#{(api[:versions] || []).size} versions" if api[:versions]&.any?
37
+ parts << "GraphQL" if api[:graphql]&.any?
38
+ parts << api[:serializer_library] if api[:serializer_library]
39
+ lines << "- API: #{parts.join(', ')}" if parts.any?
40
+ end
41
+
42
+ i18n_data = ctx[:i18n]
43
+ if i18n_data.is_a?(Hash) && !i18n_data[:error]
44
+ locales = i18n_data[:available_locales] || []
45
+ lines << "- I18n: #{locales.size} locales (#{locales.first(5).join(', ')})" if locales.size > 1
46
+ end
47
+
48
+ storage = ctx[:active_storage]
49
+ if storage.is_a?(Hash) && !storage[:error] && storage[:attachments]&.any?
50
+ lines << "- Storage: ActiveStorage (#{storage[:attachments].size} models with attachments)"
51
+ end
52
+
53
+ action_text = ctx[:action_text]
54
+ if action_text.is_a?(Hash) && !action_text[:error] && action_text[:rich_text_fields]&.any?
55
+ lines << "- RichText: ActionText (#{action_text[:rich_text_fields].size} fields)"
56
+ end
57
+
58
+ assets = ctx[:assets]
59
+ if assets.is_a?(Hash) && !assets[:error]
60
+ parts = []
61
+ parts << assets[:pipeline] if assets[:pipeline]
62
+ parts << assets[:js_bundler] if assets[:js_bundler]
63
+ parts << assets[:css_framework] if assets[:css_framework]
64
+ lines << "- Assets: #{parts.join(', ')}" if parts.any?
65
+ end
66
+
67
+ engines = ctx[:engines]
68
+ if engines.is_a?(Hash) && !engines[:error] && engines[:mounted]&.any?
69
+ names = engines[:mounted].map { |e| e[:name] || e[:engine] }.compact.first(5)
70
+ lines << "- Engines: #{names.join(', ')}" if names.any?
71
+ end
72
+
73
+ multi_db = ctx[:multi_database]
74
+ if multi_db.is_a?(Hash) && !multi_db[:error] && multi_db[:databases]&.size.to_i > 1
75
+ lines << "- Databases: #{multi_db[:databases].size} (#{multi_db[:databases].keys.first(3).join(', ')})"
76
+ end
77
+
78
+ lines
79
+ end
80
+ end
81
+ end
82
+ end
@@ -101,7 +101,7 @@ module RailsAiContext
101
101
 
102
102
  def render_mcp_tools_rule # rubocop:disable Metrics/MethodLength
103
103
  lines = [
104
- "# Rails MCP Tools (13) — Use These First",
104
+ "# Rails MCP Tools (14) — Use These First",
105
105
  "",
106
106
  "Use MCP for reference files (schema, routes, tests). Read directly if you'll edit.",
107
107
  "",
@@ -112,6 +112,7 @@ module RailsAiContext
112
112
  "- rails_get_view(controller:\"cooks\") — views; rails_get_view(path:\"file\") — content",
113
113
  "- rails_get_stimulus(detail:\"summary\") → rails_get_stimulus(controller:\"name\")",
114
114
  "- rails_get_test_info(detail:\"full\") — fixtures, helpers; (model:\"Cook\") — tests",
115
+ "- rails_analyze_feature(feature:\"auth\") — schema + models + controllers + routes for a feature",
115
116
  "- rails_get_config | rails_get_gems | rails_get_conventions | rails_search_code",
116
117
  "- rails_get_edit_context(file:\"path\", near:\"keyword\") — surgical edit context with line numbers",
117
118
  "- rails_validate(files:[\"path\"]) — validate Ruby, ERB, JS syntax in one call",
@@ -6,6 +6,7 @@ module RailsAiContext
6
6
  # Always produces compact output regardless of context_mode.
7
7
  class WindsurfSerializer
8
8
  include TestCommandDetection
9
+ include StackOverviewHelper
9
10
 
10
11
  MAX_CHARS = 5_800 # Leave buffer below 6K limit
11
12
 
@@ -42,6 +43,8 @@ module RailsAiContext
42
43
  routes = context[:routes]
43
44
  lines << "Routes: #{routes[:total_routes]}" if routes && !routes[:error]
44
45
 
46
+ lines.concat(full_preset_stack_lines)
47
+
45
48
  # Gems (one line per category)
46
49
  gems = context[:gems]
47
50
  if gems.is_a?(Hash) && !gems[:error]
@@ -165,6 +168,7 @@ module RailsAiContext
165
168
  lines << "- rails_get_conventions — architecture patterns"
166
169
  lines << "- rails_search_code(pattern:\"regex\"|file_type:\"rb\"|max_results:N)"
167
170
  lines << "- rails_get_edit_context(file:\"path\"|near:\"keyword\")"
171
+ lines << "- rails_analyze_feature(feature:\"auth\") — combined context for a feature"
168
172
  lines << "- rails_validate(files:[\"path\"])"
169
173
  lines << "Start with detail:\"summary\", then drill into specifics."
170
174
  lines << ""
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsAiContext
4
- VERSION = "1.1.0"
4
+ VERSION = "1.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-ai-context
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - crisnahine
@@ -240,6 +240,7 @@ files:
240
240
  - lib/rails_ai_context/serializers/opencode_rules_serializer.rb
241
241
  - lib/rails_ai_context/serializers/opencode_serializer.rb
242
242
  - lib/rails_ai_context/serializers/rules_serializer.rb
243
+ - lib/rails_ai_context/serializers/stack_overview_helper.rb
243
244
  - lib/rails_ai_context/serializers/test_command_detection.rb
244
245
  - lib/rails_ai_context/serializers/windsurf_rules_serializer.rb
245
246
  - lib/rails_ai_context/serializers/windsurf_serializer.rb