rails-ai-context 0.8.3 → 0.8.4

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: 850a9ebc40beb305b127b34872a6b7d9e82f67279629b43ba639ed434ae1f62f
4
- data.tar.gz: 517f855b24c3d2b1c581e2b7c7f7d2597c360d7ce7ec749a7fe57301c39db1e8
3
+ metadata.gz: '08211bf465556a4185faaebc25d66fffcf4bd2e54ba335708b82c5e8d81f11f2'
4
+ data.tar.gz: 2ab947951eb10d48acc6d674c31cea3b2ab9104a6ca9a9f6e9c409ffa1f96a7a
5
5
  SHA512:
6
- metadata.gz: 75446004fe5acc29bfd5ecfa6c3ef603ca00e698a9a59f81f3bf17a67f8ae94bfecbfc03fe5c1e34c5a2f1f931dd9ea6ff3e46bef501994a5935c4641f86b56a
7
- data.tar.gz: 73fce749f524c13a913dbe928fe47f6b9ee66071ddc8a6218f5c52baad360a135100b52d4deed80c453bff9613f538761565760cbd9e9e81e6f8c4399a894c29
6
+ metadata.gz: 4757bd5de1b215938543d138b4c4a077ff7d1cda9dc25164e03251acd81afe26cd65cae9855cf879950b7a77e1f8aa78fb86e9a02d2e640e10569a9a5fc2bb45
7
+ data.tar.gz: b2ebb6584d2886ae60c587954fdd54687c9a23a74422c395ee3d1cec063d9c499c4094ea611f1b053100b27cdaa4a47c85da006d99354437e52b6459575aab7c
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ 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
+ ## [0.8.4] - 2026-03-19
9
+
10
+ ### Added
11
+
12
+ - **`structure.sql` support** — the schema introspector now parses `db/structure.sql` when no `db/schema.rb` exists and no database connection is available. Extracts tables, columns (with SQL type normalization), indexes, and foreign keys from PostgreSQL dump format. Prefers `schema.rb` when both exist.
13
+ - **Fingerprinter watches `db/structure.sql`** — file changes to `structure.sql` now trigger cache invalidation and live reload.
14
+
8
15
  ## [0.8.3] - 2026-03-19
9
16
 
10
17
  ### Changed
data/CLAUDE.md CHANGED
@@ -39,7 +39,7 @@ structure to AI assistants via the Model Context Protocol (MCP).
39
39
  ## Testing
40
40
 
41
41
  ```bash
42
- bundle exec rspec # Run specs (385 examples)
42
+ bundle exec rspec # Run specs (392 examples)
43
43
  bundle exec rubocop # Lint
44
44
  ```
45
45
 
data/README.md CHANGED
@@ -353,7 +353,7 @@ The gem parses `db/schema.rb` as text when no database is connected. Works in CI
353
353
  ```bash
354
354
  git clone https://github.com/crisnahine/rails-ai-context.git
355
355
  cd rails-ai-context && bundle install
356
- bundle exec rspec # 385 examples
356
+ bundle exec rspec # 392 examples
357
357
  bundle exec rubocop # Lint
358
358
  ```
359
359
 
data/demo_script.sh CHANGED
@@ -8,7 +8,7 @@ echo 'Fetching gem metadata from https://rubygems.org...'
8
8
  sleep 0.3
9
9
  echo 'Resolving dependencies...'
10
10
  sleep 0.3
11
- echo 'Installing rails-ai-context 0.8.3'
11
+ echo 'Installing rails-ai-context 0.8.4'
12
12
  echo ''
13
13
  sleep 1
14
14
 
@@ -8,6 +8,7 @@ module RailsAiContext
8
8
  class Fingerprinter
9
9
  WATCHED_FILES = %w[
10
10
  db/schema.rb
11
+ db/structure.sql
11
12
  config/routes.rb
12
13
  config/database.yml
13
14
  Gemfile.lock
@@ -109,12 +109,24 @@ module RailsAiContext
109
109
  File.join(app.root, "db", "schema.rb")
110
110
  end
111
111
 
112
- # Fallback: parse db/schema.rb as text when DB isn't connected
112
+ def structure_file_path
113
+ File.join(app.root, "db", "structure.sql")
114
+ end
115
+
116
+ # Fallback: parse schema file as text when DB isn't connected.
117
+ # Tries db/schema.rb first, then db/structure.sql.
113
118
  # This enables introspection in CI, Claude Code, etc.
114
119
  def static_schema_parse
115
- path = schema_file_path
116
- return { error: "No schema.rb found at #{path}" } unless File.exist?(path)
120
+ if File.exist?(schema_file_path)
121
+ parse_schema_rb(schema_file_path)
122
+ elsif File.exist?(structure_file_path)
123
+ parse_structure_sql(structure_file_path)
124
+ else
125
+ { error: "No db/schema.rb or db/structure.sql found" }
126
+ end
127
+ end
117
128
 
129
+ def parse_schema_rb(path)
118
130
  content = File.read(path)
119
131
  tables = {}
120
132
  current_table = nil
@@ -138,6 +150,81 @@ module RailsAiContext
138
150
  note: "Parsed from db/schema.rb (no DB connection)"
139
151
  }
140
152
  end
153
+
154
+ def parse_structure_sql(path) # rubocop:disable Metrics/MethodLength
155
+ content = File.read(path)
156
+ tables = {}
157
+
158
+ # Match CREATE TABLE blocks
159
+ content.scan(/CREATE TABLE (?:public\.)?(\w+)\s*\((.*?)\);/m) do |table_name, body|
160
+ next if table_name.start_with?("ar_internal_metadata", "schema_migrations")
161
+
162
+ columns = parse_sql_columns(body)
163
+ tables[table_name] = { columns: columns, indexes: [], foreign_keys: [] }
164
+ end
165
+
166
+ # Match CREATE INDEX / CREATE UNIQUE INDEX
167
+ content.scan(/CREATE (?:UNIQUE )?INDEX (\w+) ON (?:public\.)?(\w+).*?\((.+?)\)/m) do |idx_name, table, cols|
168
+ col_list = cols.scan(/\w+/).first
169
+ tables[table]&.dig(:indexes)&.push({ name: idx_name, columns: col_list })
170
+ end
171
+
172
+ # Match ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY (handles multi-line)
173
+ content.scan(/ALTER TABLE\s+(?:ONLY\s+)?(?:public\.)?(\w+)\s+ADD CONSTRAINT.*?FOREIGN KEY\s*\((\w+)\)\s*REFERENCES\s+(?:public\.)?(\w+)\((\w+)\)/m) do |from, col, to, pk|
174
+ tables[from]&.dig(:foreign_keys)&.push({ from_table: from, to_table: to, column: col, primary_key: pk })
175
+ end
176
+
177
+ {
178
+ adapter: "static_parse",
179
+ tables: tables,
180
+ total_tables: tables.size,
181
+ note: "Parsed from db/structure.sql (no DB connection)"
182
+ }
183
+ end
184
+
185
+ # Parse column definitions from a CREATE TABLE body
186
+ def parse_sql_columns(body)
187
+ columns = []
188
+ body.each_line do |line|
189
+ line = line.strip.chomp(",").strip
190
+ next if line.empty?
191
+ next if line.match?(/\A(PRIMARY|CONSTRAINT|CHECK|UNIQUE|EXCLUDE|FOREIGN)\b/i)
192
+
193
+ # Match: column_name type_with_params [constraints]
194
+ if (match = line.match(/\A"?(\w+)"?\s+(.+)/))
195
+ col_name = match[1]
196
+ rest = match[2]
197
+ # Extract type: everything before NOT NULL, NULL, DEFAULT, etc.
198
+ col_type = rest.split(/\s+(?:NOT\s+NULL|NULL|DEFAULT|PRIMARY|UNIQUE|CONSTRAINT|CHECK)\b/i).first&.strip&.downcase
199
+ next unless col_type && !col_type.empty?
200
+ columns << { name: col_name, type: normalize_sql_type(col_type) }
201
+ end
202
+ end
203
+ columns
204
+ end
205
+
206
+ def normalize_sql_type(type)
207
+ case type
208
+ when /\Ainteger\z/i, /\Aint\z/i, /\Aint4\z/i then "integer"
209
+ when /\Abigint\z/i, /\Aint8\z/i then "bigint"
210
+ when /\Asmallint\z/i, /\Aint2\z/i then "smallint"
211
+ when /\Acharacter varying\z/i, /\Avarchar\z/i then "string"
212
+ when /\Atext\z/i then "text"
213
+ when /\Aboolean\z/i, /\Abool\z/i then "boolean"
214
+ when /\Atimestamp/i then "datetime"
215
+ when /\Adate\z/i then "date"
216
+ when /\Atime\z/i then "time"
217
+ when /\Anumeric\z/i, /\Adecimal\z/i then "decimal"
218
+ when /\Afloat/i, /\Adouble/i then "float"
219
+ when /\Ajsonb?\z/i then "json"
220
+ when /\Auuid\z/i then "uuid"
221
+ when /\Ainet\z/i then "inet"
222
+ when /\Acitext\z/i then "citext"
223
+ when /\Aarray\z/i then "array"
224
+ when /\Ahstore\z/i then "hstore"
225
+ else type
226
+ end
227
+ end
141
228
  end
142
229
  end
143
230
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsAiContext
4
- VERSION = "0.8.3"
4
+ VERSION = "0.8.4"
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": "0.8.3",
10
+ "version": "0.8.4",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "mcpb",
14
- "identifier": "https://github.com/crisnahine/rails-ai-context/releases/download/v0.8.3/rails-ai-context-mcp.mcpb",
14
+ "identifier": "https://github.com/crisnahine/rails-ai-context/releases/download/v0.8.4/rails-ai-context-mcp.mcpb",
15
15
  "fileSha256": "dd711a0ad6c4de943ae4da94eaf59a6dc9494b9d57f726e24649ed4e2f156990",
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: 0.8.3
4
+ version: 0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - crisnahine