rails-ai-context 5.7.0 → 5.7.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: 313744756e1399207abeb841866440c3de88da5863f92163fd7d344b60c479c5
4
- data.tar.gz: 4de97686b1d62c8444a76ed78fc709aade9e0e004adae1a7a655c7d0eae8ce23
3
+ metadata.gz: e613f50465df28b1b7830acae8a75665fb6c5ce1fc2058e9c4e33f0dbc7e5e47
4
+ data.tar.gz: cc04cd3316cba5728ee08c5324ee546aedf60eea9dd656aa942754edccbbb58d
5
5
  SHA512:
6
- metadata.gz: 0df4cd4cd7c0e99f8c1e662baae65a960819ed8bea551e8bb536bf06cf2b1ba6c42fee462668e990c2bf48572d5b65b66b6f38c730f643c2cd6e19b751ab7a9c
7
- data.tar.gz: 88df4ee906be73bb71212bf47acc88e09efe0ac82a87abef786a3f9a829c5aed57bdb63d19c58d6f5a4361d8ebf5955b3ce29a09235a84aa01e9c204e7ee9471
6
+ metadata.gz: d6950c03b5b7ad58f2ae1df320d698e82dc244976b1140b19fe8c0aad9a37b7430db2504d990a9bb794fc2278ff591e4416b68c0c6027df7df6001198d5fe55f
7
+ data.tar.gz: e80e37aba8ea2602a9142490c69fb35fa574669e234cf9b7c71093846d57687b7c4b355dbdf3fd295459e74b0b4e19fc4316cb69fc009608c8e28b19e9558409
data/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ 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.7.1] — 2026-04-09
9
+
10
+ ### Changed — SLOP Cleanup
11
+
12
+ Internal code quality improvements — no API changes, no new features.
13
+
14
+ - **Extract `safe_read`, `max_file_size`, `sensitive_file?` to BaseTool** — removed 16 duplicate one-liner methods across 8 tool files (get_env, get_job_pattern, get_service_pattern, get_turbo_map, get_partial_interface, get_view, get_edit_context, get_model_details, search_code)
15
+ - **Extract `FullSerializerBehavior` module** — deduplicated identical `footer` and `architecture_summary` methods from FullClaudeSerializer and FullOpencodeSerializer
16
+ - **Derive `tools_name_list` from `TOOL_ROWS`** — replaced hardcoded 38-tool name array with derivation from single source of truth in ToolGuideHelper
17
+ - **Fix `notable_gems_list` bypass** — copilot_instructions_serializer and markdown_serializer now use the triple-fallback helper instead of raw hash access
18
+ - **Narrow bare `rescue` to `rescue StandardError`** — 4 sites in get_config and i18n_introspector no longer catch `SignalException`/`NoMemoryError`
19
+ - **Delete dead `SENSITIVE_PATTERNS = nil` constant** — vestigial from get_edit_context
20
+
8
21
  ## [5.7.0] — 2026-04-09
9
22
 
10
23
  ### Quickstart — Two commands. Problem gone.
@@ -100,7 +100,7 @@ module RailsAiContext
100
100
  next 0 unless content
101
101
  data = YAML.safe_load(content, permitted_classes: [ Symbol ])
102
102
  count_nested_keys(data)
103
- rescue
103
+ rescue StandardError
104
104
  0
105
105
  end
106
106
  rescue => e
@@ -104,6 +104,8 @@ module RailsAiContext
104
104
 
105
105
  # Internal: full-mode Claude serializer (wraps MarkdownSerializer with behavioral rules)
106
106
  class FullClaudeSerializer < MarkdownSerializer
107
+ include FullSerializerBehavior
108
+
107
109
  private
108
110
 
109
111
  def header
@@ -119,27 +121,6 @@ module RailsAiContext
119
121
  that matches this project's style.
120
122
  MD
121
123
  end
122
-
123
- def footer
124
- rules = []
125
- rules << "## Behavioral Rules"
126
- rules << ""
127
- rules << "When working in this codebase:"
128
- rules << "- Follow existing patterns and conventions detected above"
129
- rules << "- Use the database schema as the source of truth for column names and types"
130
- rules << "- Respect existing associations and validations when modifying models"
131
- rules << "- Match the project's architecture style (#{architecture_summary})" if architecture_summary
132
- test_cmd = detect_test_command
133
- rules << "- Run `#{test_cmd}` after making changes to verify correctness"
134
- rules << ""
135
- rules << super
136
- rules.join("\n")
137
- end
138
-
139
- def architecture_summary
140
- arch = context.dig(:conventions, :architecture)
141
- arch&.any? ? arch.join(", ") : nil
142
- end
143
124
  end
144
125
  end
145
126
  end
@@ -56,7 +56,7 @@ module RailsAiContext
56
56
 
57
57
  gems = context[:gems]
58
58
  if gems.is_a?(Hash) && !gems[:error]
59
- notable = gems[:notable_gems] || []
59
+ notable = notable_gems_list(gems)
60
60
  notable.group_by { |g| g[:category]&.to_s || "other" }.first(6).each do |cat, gem_list|
61
61
  lines << "- #{cat}: #{gem_list.map { |g| g[:name] }.join(', ')}"
62
62
  end
@@ -153,7 +153,7 @@ module RailsAiContext
153
153
  gems = context[:gems]
154
154
  return if gems[:error]
155
155
 
156
- notable = gems[:notable_gems] || []
156
+ notable = notable_gems_list(gems)
157
157
  return if notable.empty?
158
158
 
159
159
  lines = [ "## Notable Gems" ]
@@ -528,5 +528,32 @@ module RailsAiContext
528
528
  text.to_s.gsub(MARKDOWN_SPECIAL_CHARS, '\\\\\1')
529
529
  end
530
530
  end
531
+
532
+ # Shared behavior for full-mode serializers (FullClaudeSerializer, FullOpencodeSerializer).
533
+ # Provides behavioral rules footer and architecture summary.
534
+ module FullSerializerBehavior
535
+ private
536
+
537
+ def footer
538
+ rules = []
539
+ rules << "## Behavioral Rules"
540
+ rules << ""
541
+ rules << "When working in this codebase:"
542
+ rules << "- Follow existing patterns and conventions detected above"
543
+ rules << "- Use the database schema as the source of truth for column names and types"
544
+ rules << "- Respect existing associations and validations when modifying models"
545
+ rules << "- Match the project's architecture style (#{architecture_summary})" if architecture_summary
546
+ test_cmd = detect_test_command
547
+ rules << "- Run `#{test_cmd}` after making changes to verify correctness"
548
+ rules << ""
549
+ rules << super
550
+ rules.join("\n")
551
+ end
552
+
553
+ def architecture_summary
554
+ arch = context.dig(:conventions, :architecture)
555
+ arch&.any? ? arch.join(", ") : nil
556
+ end
557
+ end
531
558
  end
532
559
  end
@@ -73,6 +73,8 @@ module RailsAiContext
73
73
 
74
74
  # Internal: full-mode OpenCode serializer (wraps MarkdownSerializer)
75
75
  class FullOpencodeSerializer < MarkdownSerializer
76
+ include FullSerializerBehavior
77
+
76
78
  private
77
79
 
78
80
  def header
@@ -87,27 +89,6 @@ module RailsAiContext
87
89
  structure, patterns, and conventions.
88
90
  MD
89
91
  end
90
-
91
- def footer
92
- rules = []
93
- rules << "## Behavioral Rules"
94
- rules << ""
95
- rules << "When working in this codebase:"
96
- rules << "- Follow existing patterns and conventions detected above"
97
- rules << "- Use the database schema as the source of truth for column names and types"
98
- rules << "- Respect existing associations and validations when modifying models"
99
- rules << "- Match the project's architecture style (#{architecture_summary})" if architecture_summary
100
- test_cmd = detect_test_command
101
- rules << "- Run `#{test_cmd}` after making changes to verify correctness"
102
- rules << ""
103
- rules << super
104
- rules.join("\n")
105
- end
106
-
107
- def architecture_summary
108
- arch = context.dig(:conventions, :architecture)
109
- arch&.any? ? arch.join(", ") : nil
110
- end
111
92
  end
112
93
  end
113
94
  end
@@ -289,22 +289,9 @@ module RailsAiContext
289
289
  lines
290
290
  end
291
291
 
292
- # Dense one-line-per-tool listing — fits in compact mode without the table overhead
292
+ # Dense one-line-per-tool listing — derived from TOOL_ROWS (single source of truth)
293
293
  def tools_name_list
294
- all_tools = %w[
295
- rails_get_context rails_analyze_feature rails_search_code rails_get_controllers
296
- rails_validate rails_get_schema rails_get_model_details rails_get_routes
297
- rails_get_view rails_get_stimulus rails_get_test_info
298
- rails_get_concern rails_get_callbacks rails_get_edit_context
299
- rails_get_service_pattern rails_get_job_pattern rails_get_env
300
- rails_get_partial_interface rails_get_turbo_map rails_get_helper_methods
301
- rails_get_config rails_get_gems rails_get_conventions rails_security_scan
302
- rails_get_component_catalog rails_performance_check rails_dependency_graph
303
- rails_migration_advisor rails_get_frontend_stack rails_search_docs
304
- rails_query rails_read_logs rails_generate_test rails_diagnose
305
- rails_review_changes rails_onboard
306
- rails_runtime_info rails_session_context
307
- ]
294
+ all_tools = TOOL_ROWS.map { |row| row[0][/^(rails_\w+)/, 1] }
308
295
  [
309
296
  "### All #{all_tools.size} tools",
310
297
  "`#{all_tools.join('` `')}`",
@@ -336,6 +336,27 @@ module RailsAiContext
336
336
  param_str = params.is_a?(Hash) ? params.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}:#{v}" }.join(",") : params.to_s
337
337
  "#{normalized}:#{param_str}"
338
338
  end
339
+
340
+ # Shared utility: safe file reading with size limits.
341
+ def safe_read(path)
342
+ RailsAiContext::SafeFile.read(path)
343
+ end
344
+
345
+ # Shared utility: max file size from configuration.
346
+ def max_file_size
347
+ RailsAiContext.configuration.max_file_size
348
+ end
349
+
350
+ # Shared utility: check if a relative path matches sensitive file patterns.
351
+ def sensitive_file?(relative_path)
352
+ patterns = RailsAiContext.configuration.sensitive_patterns
353
+ basename = File.basename(relative_path)
354
+ flags = File::FNM_DOTMATCH | File::FNM_CASEFOLD
355
+ patterns.any? do |pattern|
356
+ File.fnmatch(pattern, relative_path, flags) ||
357
+ File.fnmatch(pattern, basename, flags)
358
+ end
359
+ end
339
360
  end
340
361
  end
341
362
  end
@@ -162,7 +162,7 @@ module RailsAiContext
162
162
 
163
163
  adapter = content.match(/adapter:\s*(\w+)/)&.captures&.first
164
164
  adapter || "configured"
165
- rescue
165
+ rescue StandardError
166
166
  nil
167
167
  end
168
168
 
@@ -173,7 +173,7 @@ module RailsAiContext
173
173
  return nil unless service_name
174
174
 
175
175
  service_name.to_s
176
- rescue
176
+ rescue StandardError
177
177
  nil
178
178
  end
179
179
 
@@ -182,7 +182,7 @@ module RailsAiContext
182
182
  return nil unless method
183
183
 
184
184
  method.to_s
185
- rescue
185
+ rescue StandardError
186
186
  nil
187
187
  end
188
188
  end
@@ -8,10 +8,6 @@ module RailsAiContext
8
8
  "Use when: you need to edit a specific method or section without reading the entire file. " \
9
9
  "Requires file:\"app/models/user.rb\" and near:\"def activate\" to locate the code region."
10
10
 
11
- def self.max_file_size
12
- RailsAiContext.configuration.max_file_size
13
- end
14
-
15
11
  input_schema(
16
12
  properties: {
17
13
  file: {
@@ -32,9 +28,6 @@ module RailsAiContext
32
28
 
33
29
  annotations(read_only_hint: true, destructive_hint: false, idempotent_hint: true, open_world_hint: false)
34
30
 
35
- # Sensitive file patterns that should not be readable
36
- SENSITIVE_PATTERNS = nil # uses configuration.sensitive_patterns
37
-
38
31
  def self.call(file:, near:, context_lines: 5, server_context: nil)
39
32
  # Reject empty parameters
40
33
  if file.nil? || file.strip.empty?
@@ -143,15 +136,6 @@ module RailsAiContext
143
136
  text_response(output.join("\n"))
144
137
  end
145
138
 
146
- private_class_method def self.sensitive_file?(relative_path)
147
- patterns = RailsAiContext.configuration.sensitive_patterns
148
- basename = File.basename(relative_path)
149
- flags = File::FNM_DOTMATCH | File::FNM_CASEFOLD
150
- patterns.any? do |pattern|
151
- File.fnmatch(pattern, relative_path, flags) ||
152
- File.fnmatch(pattern, basename, flags)
153
- end
154
- end
155
139
 
156
140
  private_class_method def self.extract_methods(source_lines)
157
141
  methods = []
@@ -246,7 +246,7 @@ module RailsAiContext
246
246
  next unless Dir.exist?(dir)
247
247
  Dir.glob(File.join(dir, "**", "*.rb")).each do |file|
248
248
  next if File.size(file) > max_file_size
249
- next if sensitive_file?(file, root)
249
+ next if sensitive_file?(file.sub("#{root}/", ""))
250
250
 
251
251
  source = safe_read(file)
252
252
  next unless source
@@ -627,26 +627,6 @@ module RailsAiContext
627
627
  stripped
628
628
  end
629
629
  end
630
-
631
- private_class_method def self.sensitive_file?(file, root)
632
- relative = file.sub("#{root}/", "")
633
- basename = File.basename(relative)
634
- patterns = RailsAiContext.configuration.sensitive_patterns
635
- flags = File::FNM_DOTMATCH | File::FNM_CASEFOLD
636
-
637
- patterns.any? do |pattern|
638
- File.fnmatch(pattern, relative, flags) ||
639
- File.fnmatch(pattern, basename, flags)
640
- end
641
- end
642
-
643
- private_class_method def self.safe_read(path)
644
- RailsAiContext::SafeFile.read(path)
645
- end
646
-
647
- private_class_method def self.max_file_size
648
- RailsAiContext.configuration.max_file_size
649
- end
650
630
  end
651
631
  end
652
632
  end
@@ -404,14 +404,6 @@ module RailsAiContext
404
404
 
405
405
  enqueuers.to_a.sort.first(20)
406
406
  end
407
-
408
- private_class_method def self.safe_read(path)
409
- RailsAiContext::SafeFile.read(path)
410
- end
411
-
412
- private_class_method def self.max_file_size
413
- RailsAiContext.configuration.max_file_size
414
- end
415
407
  end
416
408
  end
417
409
  end
@@ -471,10 +471,6 @@ module RailsAiContext
471
471
  nil
472
472
  end
473
473
 
474
- def self.max_file_size
475
- RailsAiContext.configuration.max_file_size
476
- end
477
-
478
474
  private_class_method def self.extract_model_structure(model_name)
479
475
  path = "app/models/#{model_name.underscore}.rb"
480
476
  full_path = Rails.root.join(path)
@@ -470,14 +470,6 @@ module RailsAiContext
470
470
  $stderr.puts "[rails-ai-context] find_available_partials failed: #{e.message}" if ENV["DEBUG"]
471
471
  []
472
472
  end
473
-
474
- private_class_method def self.safe_read(path)
475
- RailsAiContext::SafeFile.read(path)
476
- end
477
-
478
- private_class_method def self.max_file_size
479
- RailsAiContext.configuration.max_file_size
480
- end
481
473
  end
482
474
  end
483
475
  end
@@ -316,14 +316,6 @@ module RailsAiContext
316
316
 
317
317
  parts.any? ? parts.join(", ") : "mixed/no dominant pattern"
318
318
  end
319
-
320
- private_class_method def self.safe_read(path)
321
- RailsAiContext::SafeFile.read(path)
322
- end
323
-
324
- private_class_method def self.max_file_size
325
- RailsAiContext.configuration.max_file_size
326
- end
327
319
  end
328
320
  end
329
321
  end
@@ -680,14 +680,6 @@ module RailsAiContext
680
680
  $stderr.puts "[rails-ai-context] extract_class_name failed: #{e.message}" if ENV["DEBUG"]
681
681
  nil
682
682
  end
683
-
684
- private_class_method def self.safe_read(path)
685
- RailsAiContext::SafeFile.read(path)
686
- end
687
-
688
- private_class_method def self.max_file_size
689
- RailsAiContext.configuration.max_file_size
690
- end
691
683
  end
692
684
  end
693
685
  end
@@ -223,10 +223,6 @@ module RailsAiContext
223
223
  text_response(lines.join("\n"))
224
224
  end
225
225
 
226
- def self.max_file_size
227
- RailsAiContext.configuration.max_file_size
228
- end
229
-
230
226
  private_class_method def self.read_view_file(path)
231
227
  # Reject path traversal attempts before any filesystem operation
232
228
  if path.include?("..") || path.start_with?("/")
@@ -233,10 +233,9 @@ module RailsAiContext
233
233
  cmd << pattern
234
234
  cmd << search_path
235
235
 
236
- sensitive = RailsAiContext.configuration.sensitive_patterns
237
236
  output, _status = Open3.capture2(*cmd, err: File::NULL)
238
237
  parse_rg_output(output, root)
239
- .reject { |r| sensitive_file?(r[:file], sensitive) }
238
+ .reject { |r| sensitive_file?(r[:file]) }
240
239
  .first(max_results)
241
240
  rescue => e
242
241
  [ { file: "error", line_number: 0, content: e.message } ]
@@ -252,14 +251,13 @@ module RailsAiContext
252
251
  extensions = RailsAiContext.configuration.search_extensions.join(",")
253
252
  glob = file_type ? "**/*.#{file_type}" : "**/*.{#{extensions}}"
254
253
  excluded = RailsAiContext.configuration.excluded_paths
255
- sensitive = RailsAiContext.configuration.sensitive_patterns
256
254
  test_dirs = %w[test/ spec/ features/]
257
255
  ai_context_files = %w[CLAUDE.md AGENTS.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ .vscode/mcp.json .codex/ .mcp.json opencode.json .ai-context.json]
258
256
 
259
257
  Dir.glob(File.join(search_path, glob)).each do |file|
260
258
  relative = file.sub("#{root}/", "")
261
259
  next if excluded.any? { |ex| relative.start_with?(ex) }
262
- next if sensitive_file?(relative, sensitive)
260
+ next if sensitive_file?(relative)
263
261
  next if ai_context_files.any? { |p| relative.start_with?(p) || relative == p }
264
262
  next if exclude_tests && test_dirs.any? { |td| relative.start_with?(td) }
265
263
 
@@ -276,14 +274,6 @@ module RailsAiContext
276
274
  results
277
275
  end
278
276
 
279
- private_class_method def self.sensitive_file?(relative_path, patterns)
280
- basename = File.basename(relative_path)
281
- flags = File::FNM_DOTMATCH | File::FNM_CASEFOLD
282
- patterns.any? do |pattern|
283
- File.fnmatch(pattern, relative_path, flags) ||
284
- File.fnmatch(pattern, basename, flags)
285
- end
286
- end
287
277
 
288
278
  # Group results by file for cleaner output
289
279
  private_class_method def self.format_grouped(results)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsAiContext
4
- VERSION = "5.7.0"
4
+ VERSION = "5.7.1"
5
5
  end
data/server.json CHANGED
@@ -7,11 +7,11 @@
7
7
  "url": "https://github.com/crisnahine/rails-ai-context",
8
8
  "source": "github"
9
9
  },
10
- "version": "5.7.0",
10
+ "version": "5.7.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "mcpb",
14
- "identifier": "https://github.com/crisnahine/rails-ai-context/releases/download/v5.7.0/rails-ai-context-mcp.mcpb",
14
+ "identifier": "https://github.com/crisnahine/rails-ai-context/releases/download/v5.7.1/rails-ai-context-mcp.mcpb",
15
15
  "fileSha256": "0000000000000000000000000000000000000000000000000000000000000000",
16
16
  "transport": {
17
17
  "type": "stdio"
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: 5.7.0
4
+ version: 5.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - crisnahine