rails-agent-kit 0.1.0 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4204d99ae173d2d33485290dabf6cd45f769bf718fee6d9abe5bc6de4783dbc4
4
- data.tar.gz: 4b1697ce2c95405f50edfb4106ae145b8d6aa9c6957fa9be92c08749d1c591cf
3
+ metadata.gz: a76627b569e56f98f6e73636bcad9c04907c5b78a8efecec7989a65111ee1045
4
+ data.tar.gz: e8330c54dec1898177123c346e237b6b0cc4265aeebc337208b8bdecc00d2d5e
5
5
  SHA512:
6
- metadata.gz: b099152c12dc8939cdb776a0f582c2c6df5c36110a6e8c3af9607d42496505b1d6bba2d6492fcc060801add37c6e3bcbf53ab13d787ef8b8110deafbee3fa481
7
- data.tar.gz: 7757d654bf17f8824a7d59168acbb9911fdf1086db0ab3ef1a85e3301da3a6727fa20b05345f81e966e57d2aa38754c15d34b7c27f357e52815fff64d64d9483
6
+ metadata.gz: cfe28fdedbe2391c946c06ce4c0265bc735572b6f61de61546c52d577c346faa5d4bdddf2bd23c97551c1a9b917760e2b0cd5acef6e0a231f71d8205eaff1e39
7
+ data.tar.gz: 8d6978a64aa800220d23748be96bf1ba8ad219673b2d1e06311999df8f04a336270371fa1608bbf882ac5fc54dedcc6c9af794c79a99999fb98df80c0c5784d4
data/CHANGELOG.md CHANGED
@@ -2,10 +2,69 @@
2
2
 
3
3
  All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
5
- [Semantic Versioning](https://semver.org/).
5
+ [Semantic Versioning](https://semver.org/). Each version's section becomes its GitHub release notes; see
6
+ [RELEASING.md](RELEASING.md#writing-the-changelog) for the entry style.
6
7
 
7
8
  ## [Unreleased]
8
9
 
10
+ ## [0.2.0] - 2026-10-23
11
+
12
+ rails-agent-kit 0.2.0 keeps large schemas out of your agent's context and makes setup one command:
13
+ `rails_schema` starts with a compact overview and can search and fetch several tables at once,
14
+ `rails-agent-kit init` connects Cursor, Claude Code, VS Code and Codex, the Rails 8 skill now reaches every agent
15
+ that reads `AGENTS.md`, and `rails_routes` boots your app with the Ruby it pins.
16
+
17
+ ### Upgrading
18
+
19
+ `rails_schema` with no arguments now returns a compact overview instead of the whole schema. Pass
20
+ `detail: "full"` for the previous output, or `table:` for the tables you need. If you configured clients by hand,
21
+ you can keep that config; `rails-agent-kit init` only adds what's missing.
22
+
23
+ ### Added
24
+
25
+ - **Connect every agent with one command.** `rails-agent-kit init` writes `.cursor/mcp.json`, `.mcp.json` (Claude
26
+ Code) and `.vscode/mcp.json` with project-relative paths, merges into existing files without touching other
27
+ servers, chooses `bundle exec` when the app's Gemfile has the gem, installs the bundled skills, and prints the
28
+ `~/.codex/config.toml` snippet for Codex. It's idempotent; `--dry-run` previews, `--agent` picks clients and
29
+ `--force` replaces an edited entry. By @afurm. (#5)
30
+ - **Give Codex, Copilot and Gemini CLI the Rails 8 skill.** `install-skills --agent codex` adds the skill to
31
+ `AGENTS.md`, which Codex, Cursor, GitHub Copilot, Windsurf, Zed and others read; `--agent copilot` writes
32
+ `.github/copilot-instructions.md` and `--agent gemini` writes `GEMINI.md`. The text sits between
33
+ `<!-- rails-agent-kit:start -->` and `<!-- rails-agent-kit:end -->` markers, so the rest of your file is never
34
+ touched and re-running updates only that block. `init` does this for Codex. By @afurm. (#7)
35
+ - **`rails_routes` boots the app with the app's own Ruby.** It reads the pinned version from `.ruby-version`,
36
+ `.tool-versions`, `mise.toml` or the Gemfile `ruby` directive and runs the matching mise, rbenv, asdf or chruby
37
+ install directly, so it works even when the editor starts the server without your shell profile. The answer
38
+ reports the Ruby it used as `ruby`. If the pinned Ruby isn't installed, it says which version to install instead
39
+ of returning a boot error. `RAILS_AGENT_KIT_ROUTES_COMMAND` still overrides everything. By @afurm. (#6)
40
+ - **Ask for several tables at once.** `rails_schema` takes `table` as an array or a comma-separated list, so
41
+ related tables come back in one call. Names that don't exist are listed in `missing_tables`. By @afurm.
42
+ - **Find tables and columns by name.** `rails_schema` `search` is a case-insensitive match on table and column
43
+ names, for example `search: "email"`. By @afurm.
44
+ - **Choose how much schema you get.** `rails_schema` `detail` is `overview` or `full`. By @afurm.
45
+ - **A documentation site** at <https://afurm.github.io/rails-agent-kit/>: getting started, a page per tool with
46
+ example output, the CLI reference, supported agents, the agent skill and compatibility. The gemspec links to it
47
+ as `documentation_uri`. By @afurm. (#3)
48
+
49
+ ### Changed
50
+
51
+ - **`rails_schema` with no arguments returns a compact overview** instead of the whole schema: each table's
52
+ column count, non-default primary key and foreign key targets. On a generated 300-table schema the default
53
+ answer drops from about 1.2 MB to 23 KB. By @afurm.
54
+ - **Large answers stay bounded.** A `rails_schema` answer over 60 KB is replaced by the overview with
55
+ `truncated: true` and a note on how to narrow the request. By @afurm.
56
+ - **Smaller responses.** Tools return compact JSON instead of pretty-printed JSON, which roughly halves their
57
+ size, and the server instructions describe the overview, then `table:` / `search:` flow. By @afurm.
58
+ - **Tested on Ruby 4.0.** CI runs Ruby 3.2, 3.3, 3.4 and 4.0, plus `ruby-head` (allowed to fail). By @afurm.
59
+ - **Project docs and automated releases.** A rewritten README; CONTRIBUTING, CODE_OF_CONDUCT (Contributor
60
+ Covenant 2.1), SECURITY, RELEASING and ROADMAP; issue and PR templates. Releases are published from a tag with
61
+ RubyGems trusted publishing. By @afurm. (#1)
62
+
63
+ ### Known limitations
64
+
65
+ - `db/structure.sql` parsing is best effort: foreign keys and constraints aren't extracted yet.
66
+ - Multi-database apps: only the primary database's schema is read.
67
+
9
68
  ## [0.1.0] - 2026-10-17
10
69
 
11
70
  ### Added
@@ -20,5 +79,6 @@ All notable changes to this project are documented here. The format follows
20
79
  - `rails-agent-kit install-skills [--agent cursor|claude] [--force]`, which installs the bundled skills into an app.
21
80
  - CI on Ruby 3.2, 3.3 and 3.4.
22
81
 
23
- [Unreleased]: https://github.com/afurm/rails-agent-kit/compare/v0.1.0...HEAD
82
+ [Unreleased]: https://github.com/afurm/rails-agent-kit/compare/v0.2.0...HEAD
83
+ [0.2.0]: https://github.com/afurm/rails-agent-kit/compare/v0.1.0...v0.2.0
24
84
  [0.1.0]: https://github.com/afurm/rails-agent-kit/releases/tag/v0.1.0
data/README.md CHANGED
@@ -1,66 +1,192 @@
1
- # rails-agent-kit
1
+ <div align="center">
2
2
 
3
- A tiny [Model Context Protocol](https://modelcontextprotocol.io) server that gives coding agents
4
- (Cursor, Claude Code, …) **ground truth about your Rails app**: its database schema and its routes.
3
+ <h1>rails-agent-kit</h1>
5
4
 
6
- > Status: early scaffold (v0.1.0). Not published to RubyGems yet — run it from source.
5
+ <strong>Ground truth about your Rails app, for coding agents</strong>
7
6
 
8
- ## Why (the 30-second version)
7
+ <p>A read-only MCP server that gives Cursor, Claude Code, Codex and other MCP clients your app's real schema and
8
+ routes, plus a Rails 8 skill that tells the agent to check them before it writes code.</p>
9
9
 
10
- Agents writing Rails code constantly guess: *is it `user.full_name` or `user.name`? is the column nullable?
11
- is the helper `post_comments_path` or `comments_path`?* Wrong guesses become broken code and wasted review cycles.
12
- With rails-agent-kit the agent just asks:
10
+ [Documentation](https://afurm.github.io/rails-agent-kit/) · [Install](#installation) · [Connect your agent](#connect-your-agent) · [Tools](#tools) · [Changelog](CHANGELOG.md) · [Roadmap](ROADMAP.md)
13
11
 
14
- | Tool | What it returns | Boots the app? |
15
- | --- | --- | --- |
16
- | `rails_schema` | Tables, columns (type, null, default, limit, …), indexes, foreign keys. Optional `table` filter. | **No** — statically parses `db/schema.rb` (falls back to a best-effort `db/structure.sql` parse). |
17
- | `rails_routes` | Every route: name/prefix, verb, path, `controller#action`, source location. Optional `filter`. | Yes — runs `bin/rails routes --expanded` with the app's own bundle. If that fails or times out, it says why and returns the raw `config/routes.rb` instead. |
12
+ [![Gem Version](https://img.shields.io/gem/v/rails-agent-kit?color=CC342D&logo=rubygems&logoColor=white)](https://rubygems.org/gems/rails-agent-kit)
13
+ [![Gem Downloads](https://img.shields.io/gem/dt/rails-agent-kit)](https://rubygems.org/gems/rails-agent-kit)
14
+ [![CI](https://github.com/afurm/rails-agent-kit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/afurm/rails-agent-kit/actions/workflows/ci.yml)
15
+ [![Ruby](https://img.shields.io/badge/ruby-3.2%20%7C%203.3%20%7C%203.4%20%7C%204.0-CC342D?logo=ruby&logoColor=white)](#compatibility)
16
+ [![License: MIT](https://img.shields.io/github/license/afurm/rails-agent-kit)](LICENSE)
17
+
18
+ </div>
19
+
20
+ > [!NOTE]
21
+ > rails-agent-kit is early (v0.2.0), and the API may change before 1.0. Using it? Tell us how it went in
22
+ > [Discussions](https://github.com/afurm/rails-agent-kit/discussions) or [open an issue](https://github.com/afurm/rails-agent-kit/issues).
23
+
24
+ ---
25
+
26
+ ## Look it up instead of guessing
27
+
28
+ Agents writing Rails code guess all the time. *Is it `user.full_name` or `user.name`? Is the column nullable? Is
29
+ the helper `post_comments_path` or `comments_path`?* Each wrong guess is broken code and another review round.
30
+ rails-agent-kit lets the agent ask your app.
31
+
32
+ * **Schema:** tables, columns, indexes and foreign keys from `db/schema.rb`, parsed statically. It never boots
33
+ the app or connects to a database. `db/structure.sql` gets a best-effort parse.
34
+ * **Schema overview and search:** a compact overview by default, several tables
35
+ in one call, and a name search across tables and columns, so large schemas don't flood the agent's context.
36
+ * **Routes:** every route with its name, verb, path, `controller#action` and source line, from your app's own
37
+ `bin/rails routes --expanded`. Engines and route concerns resolve exactly as Rails sees them.
38
+ * **The app's own Ruby:** routes boot with the Ruby your app pins in `.ruby-version`,
39
+ `.tool-versions` or `mise.toml`, found through mise, rbenv, asdf or chruby.
40
+ * **Routes without booting:** point it at a saved routes dump, or run the routes command in Docker.
41
+ * **One-command setup:** `rails-agent-kit init` connects Cursor, Claude Code, VS Code
42
+ and Codex, and installs the skill.
43
+ * **Rails 8 conventions skill:** an Agent Skill for Cursor and Claude Code, or a managed block in `AGENTS.md`
44
+ (Codex and others), `.github/copilot-instructions.md` or `GEMINI.md`, installed with `rails-agent-kit install-skills`.
45
+ * **Read-only and local:** a stdio server your client starts. It doesn't listen on a network port.
46
+
47
+ ## See what your agent sees
48
+
49
+ The agent calls the tools itself. These are real answers for the small blog app in
50
+ [`test/fixtures/blog_app`](test/fixtures/blog_app), pretty-printed (the server sends compact JSON).
51
+
52
+ ```js
53
+ // rails_schema {} (the overview; trimmed)
54
+ {
55
+ "source": "db/schema.rb",
56
+ "table_count": 4,
57
+ "tables": [
58
+ { "name": "comments", "columns": 6, "references": ["posts", "users"] },
59
+ { "name": "posts", "columns": 10, "references": ["users"] },
60
+ { "name": "posts_tags", "columns": 2, "primary_key": false },
61
+ { "name": "users", "columns": 7 }
62
+ ]
63
+ }
64
+ ```
18
65
 
19
- Both tools are read-only. Output is JSON.
66
+ ```js
67
+ // rails_schema { "search": "email" } (trimmed)
68
+ { "match_count": 1, "matches": [{ "table": "users", "columns": [{ "name": "email", "type": "string", "null": false }] }] }
69
+ ```
20
70
 
21
- ## Install
71
+ ```js
72
+ // rails_schema { "table": "comments" } (trimmed)
73
+ {
74
+ "tables": [{
75
+ "name": "comments",
76
+ "columns": [
77
+ { "name": "post_id", "type": "bigint", "null": false },
78
+ { "name": "body", "type": "text", "null": false }
79
+ ],
80
+ "indexes": [{ "name": "index_comments_on_post_id", "columns": ["post_id"], "unique": false }]
81
+ }],
82
+ "foreign_keys": [{ "from_table": "comments", "to_table": "users", "column": "user_id", "on_delete": "cascade" }]
83
+ }
84
+ ```
22
85
 
23
- From source (today):
86
+ ```js
87
+ // rails_routes { "filter": "comments" } (trimmed)
88
+ {
89
+ "total": 7,
90
+ "count": 2,
91
+ "routes": [
92
+ { "name": "post_comments", "verb": "POST", "path": "/posts/:post_id/comments(.:format)",
93
+ "controller_action": "comments#create", "source_location": "config/routes.rb:3" }
94
+ ]
95
+ }
96
+ ```
97
+
98
+ ## Installation
24
99
 
25
100
  ```sh
26
- git clone https://github.com/afurm/rails-agent-kit
27
- cd rails-agent-kit
28
- bundle install
29
- # try it against an app:
30
- BUNDLE_GEMFILE=$PWD/Gemfile bundle exec rails-agent-kit --root /path/to/your/rails/app
101
+ gem install rails-agent-kit
31
102
  ```
32
103
 
33
- Once released (not yet): `gem install rails-agent-kit`.
104
+ or, inside your Rails app:
34
105
 
35
- Requires Ruby 3.2+. Runtime dependency: the official [`mcp`](https://github.com/modelcontextprotocol/ruby-sdk) Ruby SDK.
106
+ ```sh
107
+ bundle add rails-agent-kit --group development
108
+ ```
109
+
110
+ Requires Ruby 3.2+.
36
111
 
37
- ## Usage
112
+ ## Connect your agent
38
113
 
114
+ ### One command
115
+
116
+ From your app's root:
117
+
118
+ ```sh
119
+ bundle add rails-agent-kit --group development
120
+ bundle exec rails-agent-kit init
39
121
  ```
40
- rails-agent-kit [--root PATH] [--routes-file FILE]
41
122
 
42
- --root PATH Rails app root (default: current directory)
43
- --routes-file FILE Use a saved `bin/rails routes --expanded` dump instead of booting the app
123
+ `init` writes the MCP config for Cursor (`.cursor/mcp.json`), Claude Code (`.mcp.json`) and VS Code
124
+ (`.vscode/mcp.json`), using project-relative paths so you can commit the files for your whole team. It adds
125
+ rails-agent-kit next to the servers you already have, picks `bundle exec` or the installed gem for you, installs the
126
+ Rails 8 skill for Cursor and Claude Code, adds it to `AGENTS.md` for Codex, and prints the snippet for Codex's
127
+ global `~/.codex/config.toml`.
128
+ Running it again changes nothing. Preview with `--dry-run`, or pick clients with `--agent cursor,claude`.
129
+
130
+ ### By hand
131
+
132
+ Replace `/path/to/your/rails/app` with the absolute path to your app.
133
+
134
+ <details open>
135
+ <summary><b>Cursor</b></summary>
136
+
137
+ `.cursor/mcp.json` in your app (or `~/.cursor/mcp.json`):
138
+
139
+ ```json
140
+ {
141
+ "mcpServers": {
142
+ "rails-agent-kit": {
143
+ "command": "rails-agent-kit",
144
+ "args": ["--root", "/path/to/your/rails/app"]
145
+ }
146
+ }
147
+ }
44
148
  ```
45
149
 
46
- The server speaks MCP over stdio. Environment knobs:
150
+ </details>
151
+
152
+ <details open>
153
+ <summary><b>Claude Code</b></summary>
154
+
155
+ ```sh
156
+ claude mcp add rails-agent-kit -- rails-agent-kit --root /path/to/your/rails/app
157
+ ```
47
158
 
48
- - `RAILS_AGENT_KIT_ROUTES_TIMEOUT` — seconds to wait for `bin/rails routes` (default `60`).
49
- - `RAILS_AGENT_KIT_ROUTES_COMMAND` — override the routes command, e.g. `"bundle exec rails routes --expanded"`
50
- or `"docker compose exec -T web bin/rails routes --expanded"`. Runs in `--root`.
159
+ </details>
51
160
 
52
- If your app can't boot locally (needs a DB, secrets, Docker…), dump the routes once and point the server at the file:
161
+ <details open>
162
+ <summary><b>Codex</b></summary>
53
163
 
54
164
  ```sh
55
- bin/rails routes --expanded > tmp/routes.txt
56
- rails-agent-kit --root . --routes-file tmp/routes.txt
165
+ codex mcp add rails-agent-kit -- rails-agent-kit --root /path/to/your/rails/app
166
+ ```
167
+
168
+ or in `~/.codex/config.toml`:
169
+
170
+ ```toml
171
+ [mcp_servers.rails-agent-kit]
172
+ command = "rails-agent-kit"
173
+ args = ["--root", "/path/to/your/rails/app"]
57
174
  ```
58
175
 
59
- ## MCP client config
176
+ </details>
60
177
 
61
- Replace the paths with absolute paths on your machine.
178
+ <details>
179
+ <summary><b>Claude Desktop and other MCP clients</b></summary>
62
180
 
63
- **Cursor** — `.cursor/mcp.json` in your Rails app (or `~/.cursor/mcp.json`):
181
+ Any client that launches stdio MCP servers works. Claude Desktop's `claude_desktop_config.json` takes the same
182
+ `mcpServers` block as Cursor above.
183
+
184
+ </details>
185
+
186
+ <details>
187
+ <summary><b>Installed with <code>bundle add</code> instead of <code>gem install</code>?</b></summary>
188
+
189
+ Run it through the app's bundle:
64
190
 
65
191
  ```json
66
192
  {
@@ -68,51 +194,141 @@ Replace the paths with absolute paths on your machine.
68
194
  "rails-agent-kit": {
69
195
  "command": "bundle",
70
196
  "args": ["exec", "rails-agent-kit", "--root", "/path/to/your/rails/app"],
71
- "env": { "BUNDLE_GEMFILE": "/path/to/rails-agent-kit/Gemfile" }
197
+ "env": { "BUNDLE_GEMFILE": "/path/to/your/rails/app/Gemfile" }
72
198
  }
73
199
  }
74
200
  }
75
201
  ```
76
202
 
77
- **Claude Code**:
78
-
79
203
  ```sh
80
- claude mcp add rails-agent-kit \
81
- -e BUNDLE_GEMFILE=/path/to/rails-agent-kit/Gemfile \
204
+ claude mcp add rails-agent-kit -e BUNDLE_GEMFILE=/path/to/your/rails/app/Gemfile \
82
205
  -- bundle exec rails-agent-kit --root /path/to/your/rails/app
83
206
  ```
84
207
 
85
- **Claude Desktop** — `claude_desktop_config.json` uses the same `mcpServers` block as Cursor above.
208
+ </details>
86
209
 
87
- `rails_routes` runs the app's `bin/rails` with Bundler's environment cleared, so your app uses its own Gemfile,
88
- not rails-agent-kit's.
210
+ To also add the Rails 8 conventions skill (optional), see [Agent skill](#agent-skill-rails-8-conventions):
89
211
 
90
- ## Agent skill: Rails 8 conventions
212
+ ```sh
213
+ rails-agent-kit install-skills --root /path/to/your/rails/app # Cursor
214
+ rails-agent-kit install-skills --root /path/to/your/rails/app --agent claude # Claude Code
215
+ rails-agent-kit install-skills --root /path/to/your/rails/app --agent codex # AGENTS.md
216
+ rails-agent-kit install-skills --root /path/to/your/rails/app --agent copilot # GitHub Copilot
217
+ rails-agent-kit install-skills --root /path/to/your/rails/app --agent gemini # Gemini CLI
218
+ ```
219
+
220
+ ## Tools
221
+
222
+ Both tools are read-only and return JSON.
223
+
224
+ | Tool | Parameters | Returns | Boots the app? |
225
+ | --- | --- | --- | --- |
226
+ | `rails_schema` | All optional. `table`: one exact name, an array or a comma-separated list. `search`: case-insensitive part of a table or column name. `detail`: `overview` or `full` | With no arguments, a compact overview (each table's column count, non-default primary key and foreign key targets). With `table`, full columns (type, null, default, limit, …), indexes and foreign keys. With `search`, the matching tables and columns | **No.** Parses `db/schema.rb` statically, with a best-effort fallback to `db/structure.sql` |
227
+ | `rails_routes` | `filter` (optional, case-insensitive substring of name, path, verb or `controller#action`) | Each route's name, verb, path, `controller#action` and source location | **Yes.** Runs `bin/rails routes --expanded`. If that fails or times out, it explains why and returns the raw `config/routes.rb` |
228
+
229
+ Responses are compact (single-line) JSON. Agents usually call `rails_schema` with no arguments first, so that
230
+ answer stays small even on large apps: a generated 300-table schema gives about 23 KB, where the full dump is
231
+ over 1 MB. Any `rails_schema` answer over 60 KB comes back as the overview plus a note on how to narrow the
232
+ request (`truncated: true`).
91
233
 
92
- The gem bundles a [Cursor skill](https://cursor.com/docs/skills),
93
- [`skills/rails-8-conventions/SKILL.md`](skills/rails-8-conventions/SKILL.md). It tells the agent to check
94
- `rails_schema`, `rails_routes` and `Gemfile.lock` before it edits code, then follow Rails 8 defaults:
95
- `params.expect`, the built-in authentication generator, `rate_limit`, Solid Queue/Cache/Cable, Hotwire with
96
- importmap/Propshaft, reversible migrations, and tests in the framework the app already uses. It also covers a
97
- paired Next.js frontend. Install it into your app:
234
+ ## CLI reference
235
+
236
+ ```text
237
+ rails-agent-kit [--root PATH] [--routes-file FILE] # start the MCP server on stdio
238
+ rails-agent-kit init [--root PATH] [--agent LIST] [--dry-run] [--force] [--no-skills]
239
+ rails-agent-kit install-skills [--root PATH] [--agent AGENT] [--force] [SKILL...]
240
+ rails-agent-kit --version
241
+ ```
242
+
243
+ | Option / env var | Purpose |
244
+ | --- | --- |
245
+ | `--root PATH` | Rails app root (default: the current directory) |
246
+ | `--routes-file FILE` | Use a saved `bin/rails routes --expanded` dump instead of booting the app |
247
+ | `RAILS_AGENT_KIT_ROUTES_TIMEOUT` | Seconds to wait for `bin/rails routes` (default `60`) |
248
+ | `RAILS_AGENT_KIT_ROUTES_COMMAND` | Override the routes command, e.g. `"docker compose exec -T web bin/rails routes --expanded"`. Runs in `--root` |
249
+
250
+ If your app can't boot locally (it needs a DB, secrets or Docker), dump the routes once and point the server at
251
+ the file:
98
252
 
99
253
  ```sh
100
- rails-agent-kit install-skills --root /path/to/your/rails/app # -> .cursor/skills/rails-8-conventions/SKILL.md
101
- rails-agent-kit install-skills --root /path/to/your/rails/app --agent claude # -> .claude/skills/...
254
+ bin/rails routes --expanded > tmp/routes.txt
255
+ rails-agent-kit --root . --routes-file tmp/routes.txt
102
256
  ```
103
257
 
104
- Running it again is safe. It won't overwrite a skill you've edited unless you pass `--force`.
258
+ ## How it works
259
+
260
+ - **Transport:** a stdio MCP server built on the official [`mcp` Ruby SDK](https://github.com/modelcontextprotocol/ruby-sdk).
261
+ Your agent client starts it and talks JSON-RPC over stdin/stdout. Nothing listens on a network port.
262
+ - **Schema:** read from `db/schema.rb` with a static parser. It never loads your app or connects to a database,
263
+ so it's fast and safe on any checkout. Apps that use `db/structure.sql` get a best-effort SQL parse.
264
+ - **Routes:** generated by your app itself (`bin/rails routes --expanded`), so engines and route concerns
265
+ resolve exactly as Rails sees them. It runs with the app's pinned Ruby and with Bundler's environment cleared,
266
+ so the app uses its own Ruby and Gemfile.
267
+ - **Skill:** a Markdown [Agent Skill](https://cursor.com/docs/skills) copied into your app. It tells the agent to
268
+ call these tools before it writes model attributes, queries, URL helpers or migrations.
269
+
270
+ ## Agent skill: Rails 8 conventions
271
+
272
+ [`skills/rails-8-conventions/SKILL.md`](skills/rails-8-conventions/SKILL.md) makes the agent check `rails_schema`,
273
+ `rails_routes` and `Gemfile.lock` before it edits code. It then follows the Rails 8 defaults: `params.expect`, the
274
+ built-in authentication generator, `rate_limit`, Solid Queue/Cache/Cable, Hotwire with importmap/Propshaft,
275
+ reversible migrations, and tests in the framework the app already uses. It also covers a paired Next.js
276
+ frontend.
277
+
278
+ There's one source for every agent. `install-skills` installs it where each agent looks:
105
279
 
106
- ## Development
280
+ | `--agent` | Writes | Read by |
281
+ | --- | --- | --- |
282
+ | `cursor` (default) | `.cursor/skills/<name>/SKILL.md` | Cursor |
283
+ | `claude` | `.claude/skills/<name>/SKILL.md` | Claude Code (and Cursor, Copilot) |
284
+ | `codex` | A managed block in `AGENTS.md` | Codex, Cursor, GitHub Copilot, Windsurf, Zed and other `AGENTS.md` readers |
285
+ | `copilot` | A managed block in `.github/copilot-instructions.md` | GitHub Copilot |
286
+ | `gemini` | A managed block in `GEMINI.md` | Gemini CLI |
287
+
288
+ Running it again is safe. It never overwrites a skill file you've edited unless you pass `--force`. In
289
+ `AGENTS.md`, `copilot-instructions.md` and `GEMINI.md` it only touches the text between
290
+ `<!-- rails-agent-kit:start -->` and `<!-- rails-agent-kit:end -->`, appending the block if it isn't there yet,
291
+ so the rest of your file stays yours.
292
+
293
+ ## Compatibility
294
+
295
+ | | Versions | Status |
296
+ | --- | --- | --- |
297
+ | Ruby | 3.2, 3.3, 3.4, 4.0 | Tested in CI (`ruby-head` runs too, allowed to fail) |
298
+ | Rails (`db/schema.rb` format) | 8.0 | Covered by test fixtures |
299
+ | Rails | 7.2, 8.1 | Expected to work, but not yet covered by CI ([planned](ROADMAP.md)) |
300
+ | MCP clients | Cursor, Claude Code, Codex, Claude Desktop, or any stdio MCP client | Configuration shown above |
301
+
302
+ The bundled skill targets Rails 8 conventions. On Rails 7.x, the tools work the same, but some skill advice
303
+ (e.g. `params.expect` and Solid Queue defaults) won't apply.
304
+
305
+ ## Documentation
306
+
307
+ The full guide lives at **[afurm.github.io/rails-agent-kit](https://afurm.github.io/rails-agent-kit/)**: getting
308
+ started, a page per tool with example output, the CLI reference, the agent skill and compatibility.
309
+
310
+ [Connect your agent](#connect-your-agent) · [Tools](#tools) · [CLI reference](#cli-reference) ·
311
+ [Changelog](CHANGELOG.md) · [Roadmap](ROADMAP.md) · [Releasing](RELEASING.md) · [Security](SECURITY.md)
312
+
313
+ ## Community
314
+
315
+ * **Questions and ideas:** [GitHub Discussions](https://github.com/afurm/rails-agent-kit/discussions)
316
+ * **Bugs:** [open an issue](https://github.com/afurm/rails-agent-kit/issues/new/choose) with your Ruby, Rails
317
+ and MCP client versions
318
+ * **Vulnerabilities:** report them privately, as described in [SECURITY.md](SECURITY.md)
319
+
320
+ Everyone taking part follows the [Code of Conduct](CODE_OF_CONDUCT.md).
321
+
322
+ ## Contributing
323
+
324
+ Pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md), then:
107
325
 
108
326
  ```sh
327
+ gh repo fork afurm/rails-agent-kit --clone && cd rails-agent-kit
109
328
  bundle install
110
329
  bundle exec rake test
111
330
  ```
112
331
 
113
- Tests use small fixture apps in `test/fixtures` (a `schema.rb`, a `structure.sql`, captured `rails routes`
114
- output and a stand-in `bin/rails`), plus an end-to-end test that drives `exe/rails-agent-kit` over stdio JSON-RPC.
115
-
116
332
  ## License
117
333
 
118
- MIT — see [LICENSE](LICENSE).
334
+ [MIT](LICENSE) © Andrii Furmanets
data/exe/rails-agent-kit CHANGED
@@ -4,14 +4,99 @@
4
4
  require "optparse"
5
5
  require_relative "../lib/rails_agent_kit"
6
6
 
7
+ if ARGV.first == "init"
8
+ ARGV.shift
9
+ opts = { root: Dir.pwd, agent: "all", force: false, dry_run: false, skills: true }
10
+ init_parser = OptionParser.new do |o|
11
+ o.banner = "Usage: rails-agent-kit init [--root PATH] [--agent LIST] [--dry-run] [--force] [--no-skills]\n\n" \
12
+ "Writes the MCP config that starts rails-agent-kit for each agent and installs the bundled skills\n" \
13
+ "(skill files for Cursor and Claude Code, a managed AGENTS.md block for Codex)."
14
+ o.on("--root PATH", "Rails app root (default: current directory)") { |v| opts[:root] = v }
15
+ o.on("--agent LIST", "Comma-separated: cursor, claude, vscode, codex, or all (default: all)") { |v| opts[:agent] = v }
16
+ o.on("--dry-run", "Show what would change without writing anything") { opts[:dry_run] = true }
17
+ o.on("--force", "Replace a rails-agent-kit entry or skill you have edited") { opts[:force] = true }
18
+ o.on("--no-skills", "Only write the MCP config (no skills, no AGENTS.md block)") { opts[:skills] = false }
19
+ o.on("-h", "--help", "Show this help") do
20
+ puts o
21
+ exit
22
+ end
23
+ end
24
+
25
+ begin
26
+ init_parser.parse!(ARGV)
27
+ raise RailsAgentKit::Error, "unexpected argument #{ARGV.first.inspect}" unless ARGV.empty?
28
+
29
+ agents = RailsAgentKit::ClientConfig.parse_agents(opts[:agent])
30
+ root = File.expand_path(opts[:root])
31
+ raise RailsAgentKit::Error, "--root #{root} is not a directory" unless File.directory?(root)
32
+ rescue OptionParser::ParseError, RailsAgentKit::Error => e
33
+ warn "rails-agent-kit: #{e.message}"
34
+ exit 64
35
+ end
36
+
37
+ config = RailsAgentKit::ClientConfig.new(root, force: opts[:force], dry_run: opts[:dry_run])
38
+ relative = ->(path) { path.start_with?("#{root}/") ? path.delete_prefix("#{root}/") : path }
39
+ planned = { created: "would create", added: "would add", updated: "would update" }
40
+ label = ->(status) { ((opts[:dry_run] && planned[status]) || status.to_s).ljust(12) }
41
+
42
+ puts "rails-agent-kit init#{' (dry run, nothing written)' if opts[:dry_run]}"
43
+ puts "Command: #{config.bundled? ? 'bundle exec rails-agent-kit (found in the Gemfile)' : 'rails-agent-kit (installed gem)'}"
44
+ puts
45
+
46
+ results = config.write(agents)
47
+ results.reject { |r| r.status == :print }.each do |r|
48
+ note =
49
+ case r.status
50
+ when :skipped then " (has a different rails-agent-kit entry; use --force to replace it)"
51
+ when :error then " (#{r.message})"
52
+ else ""
53
+ end
54
+ puts "#{label.call(r.status)} #{relative.call(r.path)}#{note}"
55
+ end
56
+
57
+ skill_note = lambda do |r|
58
+ if r.message then " (#{r.message})"
59
+ elsif r.status == :skipped then " (differs from bundled copy; use --force to overwrite)"
60
+ else ""
61
+ end
62
+ end
63
+
64
+ if opts[:skills]
65
+ (agents & RailsAgentKit::SkillInstaller::TARGETS.keys).each do |agent|
66
+ installer = RailsAgentKit::SkillInstaller.new(root, agent: agent, force: opts[:force])
67
+ skill_results = opts[:dry_run] ? installer.plan : installer.install
68
+ skill_results.each do |r|
69
+ puts "#{label.call(r.status)} #{relative.call(r.path)}#{skill_note.call(r)}"
70
+ end
71
+ end
72
+ end
73
+
74
+ if (codex = results.find { |r| r.status == :print })
75
+ puts
76
+ puts "Codex reads MCP servers from the global ~/.codex/config.toml, so add this yourself:"
77
+ puts
78
+ puts codex.snippet
79
+ end
80
+
81
+ puts
82
+ if results.any? { |r| r.status == :error }
83
+ warn "rails-agent-kit: some files could not be updated; see above"
84
+ exit 65
85
+ end
86
+ puts "Done. Restart your agent (or reload its MCP servers) to pick up rails-agent-kit." unless opts[:dry_run]
87
+ exit 0
88
+ end
89
+
7
90
  if ARGV.first == "install-skills"
8
91
  ARGV.shift
9
92
  opts = { root: Dir.pwd, agent: "cursor", force: false }
10
93
  skills_parser = OptionParser.new do |o|
11
- o.banner = "Usage: rails-agent-kit install-skills [--root PATH] [--agent cursor|claude] [--force] [SKILL...]\n\n" \
12
- "Copies the bundled agent skills into the app (e.g. .cursor/skills/<name>/SKILL.md)."
94
+ o.banner = "Usage: rails-agent-kit install-skills [--root PATH] [--agent AGENT] [--force] [SKILL...]\n\n" \
95
+ "Installs the bundled agent skills into the app: as skill files for Cursor (.cursor/skills/) and\n" \
96
+ "Claude Code (.claude/skills/), or as a managed block in AGENTS.md (codex),\n" \
97
+ ".github/copilot-instructions.md (copilot) or GEMINI.md (gemini)."
13
98
  o.on("--root PATH", "Rails app root (default: current directory)") { |v| opts[:root] = v }
14
- o.on("--agent AGENT", "cursor (default) or claude") { |v| opts[:agent] = v }
99
+ o.on("--agent AGENT", "cursor (default), claude, codex, copilot or gemini") { |v| opts[:agent] = v }
15
100
  o.on("--force", "Overwrite skills you have edited locally") { opts[:force] = true }
16
101
  o.on("-h", "--help", "Show this help") do
17
102
  puts o
@@ -28,7 +113,11 @@ if ARGV.first == "install-skills"
28
113
  exit 64
29
114
  end
30
115
  results.each do |r|
31
- note = r.status == :skipped ? " (differs from bundled copy; use --force to overwrite)" : ""
116
+ note =
117
+ if r.message then " (#{r.message})"
118
+ elsif r.status == :skipped then " (differs from bundled copy; use --force to overwrite)"
119
+ else ""
120
+ end
32
121
  puts "#{r.status.to_s.ljust(9)} #{r.path}#{note}"
33
122
  end
34
123
  exit 0
@@ -37,6 +126,7 @@ end
37
126
  options = { root: Dir.pwd, routes_file: nil }
38
127
  parser = OptionParser.new do |o|
39
128
  o.banner = "Usage: rails-agent-kit [--root PATH] [--routes-file FILE]\n" \
129
+ " rails-agent-kit init --help\n" \
40
130
  " rails-agent-kit install-skills --help\n\n" \
41
131
  "Starts an MCP server on stdio exposing the Rails app's schema and routes."
42
132
  o.on("--root PATH", "Rails app root (default: current directory)") { |v| options[:root] = v }