⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
rails-query
|
Choose how to use this skill
You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.
1. Native installer
This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.
Do not guess an installer command or replace an existing version without reviewing the diff.
2. Complete package recommended
Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.
No complete ProSkills package is published for this listing yet.3. Prompt-only
Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.
Need only the instruction file?
Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.
No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.
// RATINGS
Not yet listed on ClawHub or SkillsMP
// README
rails-query-command
A Claude Code plugin that teaches Claude to answer data questions about a Rails application using the rails query command — locally, or against a deployed environment through named remotes you configure in a config file (Heroku, SSH, docker, kubectl, …).
It's a fork of lewispb/rails-query-skill, adapted to:
- Work on Rails 7.2, 8.0 and 8.1 via the
rails-query-commandgem (and on Rails 8.2+ natively). - Run remotely through named command templates in your app's
.rails_query_remote. The skill is Markdown only: no scripts to install, justbashon Claude's side.
What it does
When you ask Claude something like:
- "How many active users do we have in production?"
- "Show me the orders table structure."
- "What models reference
accounts?" - "Explain the query plan for this scope."
…Claude reaches for rails query instead of opening heroku run rails console, SSHing to a server, or writing a one-off script. Queries are structured (JSON output), read-only by construction (writes blocked at the connection level), and safe to run against production with a read replica.
Installation
claude plugin marketplace add lazaronixon/rails-query-command-skill
claude plugin install rails-query-command@rails-query-command-skill
Or, from inside a Claude Code session:
/plugin marketplace add lazaronixon/rails-query-command-skill
/plugin install rails-query-command@rails-query-command-skill
/reload-plugins
Requirements
-
rails queryin the app. Rails 8.2+ has it built in. On Rails 7.2–8.1 add the gem and deploy:gem "rails-query-command"
Remote setup
Create .rails_query_remote at the root of your project (the Rails app, next to bin/rails), with one name: command per line. Each project has its own: in a monorepo, each Rails app (e.g. apps/api) gets its own file. Each command is the full command that runs rails query, taken literally (everything after the first : ), and it ends in bin/rails query {args}:
# Example
production: heroku run -a platform-api-main -x "apps/api/bin/rails query {args}"
beta: heroku run -a platform-api-beta -x "apps/api/bin/rails query {args}"
# Other runners
ssh: ssh [email protected] "/srv/app/current/bin/rails query {args}"
k8s: kubectl exec -i deploy/web -- sh -c "bin/rails query {args}"
docker: docker exec -i app sh -c "bin/rails query {args}"
Blank lines and lines starting with # are ignored. Commit it to share remotes with your team, or add it to .gitignore to keep them personal.
Name remotes after the environment (production, beta) so Claude can match "in beta" or "in production" to the right one. Keep bin/rails query {args} at the end of the command string, and let the command pass its stdin through: no -n on ssh, -i on docker exec and kubectl exec.
Claude reads the file, picks the remote for the environment you named and replaces {args} (- for a Ruby expression, --sql -, explain -, schema users, …). It writes the expression to a file in tmp/query/, named after the question, and runs the command with that file on stdin:
# tmp/query/recent_users.rb
recent = User.where(created_at: 30.days.ago..)
{
obrien: recent.where(last_name: "O'Brien").count,
total: recent.count
}
heroku run -a platform-api-beta -x "apps/api/bin/rails query -" < tmp/query/recent_users.rb
rails query - reads the expression from stdin, so it never passes through a shell: quotes, $, parens and newlines arrive intact, and expressions can span several lines. Keep the -: some runners (like heroku run) give the command a terminal, and without - it then ignores stdin and fails with No query expression provided. The query files stay in tmp/query/, which Rails ignores in git by default, so you can read or rerun the query behind any answer.
Output is left alone: runner banners, boot logs and the JSON result come through as the runner prints them, with its exit status.
Auto mode
If auto mode blocks production runs with [Production Reads], add this to ~/.claude/settings.json:
{
"autoMode": {
"allow": [
"$defaults",
"Rails Query: `bin/rails query` is read-only, so running it isn't a production read."
]
}
}
How triggering works
The skill is described broadly so Claude invokes it whenever a task would naturally be solved by querying a Rails database. Try:
"How many users registered last week in production?"
"What's in the
mailboxestable?"
"Explain this query:
Post.published.limit(100)"
Further reading
- rails/rails#57156 — the PR that added
rails query. - rails-query-command — the backport gem.
- lewispb/rails-query-skill — the original skill this is forked from.
License
MIT
// HOW IT'S BUILT
// FILES
2 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.
skills/rails-query/SKILL.md9.0 KBd2d56ba0dc1eupstream ↗
--- name: rails-query description: | Answer questions needing live data from a Rails app's database with `rails query` (Rails 8.2+, or 7.2–8.1 with the rails-query-command gem): counts, lookups, aggregates, investigation, schema/model/enum exploration, EXPLAIN plans, locally or on a deployed environment (production, beta, staging…) via remotes in `.rails_query_remote`. Trigger whenever the user asks for Rails database data, says "in production" / "in beta" about data, mentions `rails query`, wants to inspect tables, columns or models, or run SQL or ActiveRecord. Prefer it over a console, server shell or one-off script: read-only, JSON output. --- # rails-query `rails query` runs one read-only expression against the app's database and prints one JSON object. Writes are blocked at the connection level and a configured read replica is used, so it is safe on production. Rails 8.2+ ships it; 7.2–8.1 needs `gem "rails-query-command"`. Use it instead of a console, runner script or server shell. Never go around the app with a database client and credentials from `DATABASE_URL`, `.env` or `database.yml`: that skips the read-only guard and model logic (enums, scopes, encryption). If `rails query` can't run, say why instead of falling back to a raw connection. ## How to run it Run from the directory with `bin/rails` (in a monorepo, the app's subdirectory); `.rails_query_remote` and `tmp/` live there too. Write the expression to a file in `tmp/query/` with your file-writing tool, then pass the file on stdin, locally and on remotes alike: ```ruby # tmp/query/recent_users.rb recent = User.where(created_at: 30.days.ago..) { obrien: recent.where(last_name: "O'Brien").count, dollar: recent.where("email LIKE ?", "%$%").count } ``` ```bash bin/rails query - < tmp/query/recent_users.rb ``` - **`-` reads the expression from stdin**, byte for byte, so quotes, `$` and newlines need no escaping. Never put the expression in the command string, where an apostrophe like `O'Brien` breaks the quoting. - **Name the file after the question** (`pets_by_month.sql`, `orders_missing_address.rb`). To fix or tweak a query, edit that file and rerun it rather than making copies, so it always holds the query behind the answer. - **The result is the last line** starting with `{"columns"`, `{"table"`, `{"error"` or `[{"model"`. Logs often surround it (SQL debug logs locally; runner banners, boot output or an echo of the expression on remotes). No such line means the command failed (boot crash, auth, wrong path): read the rest of the output. Flags and subcommands go before the `-`. `schema` and `models` take no expression: they replace the `-` and get `< /dev/null` (`schema -` would look for a table named `-`). | Query | Command | stdin | |---|---|---| | Ruby (default) | `bin/rails query -` | `< tmp/query/NAME.rb` | | SQL | `bin/rails query --sql -` | `< tmp/query/NAME.sql` | | Flags | `bin/rails query --per 500 -`, `bin/rails query --db primary_replica -` | the query file | | EXPLAIN | `bin/rails query explain -`, `bin/rails query --sql explain -` | the query file | | Schema, models | `bin/rails query schema`, `bin/rails query schema users`, `bin/rails query models` | `< /dev/null` | ## Choosing the environment No environment named: run locally. Environment named ("in beta", "on prod"): read `.rails_query_remote` and pick the matching remote (`beta`, `app_beta`, `api-beta` all match "beta"). Query only that environment. If none matches, or two could, ask: the wrong environment gives a confidently wrong answer. If the file doesn't exist, explain how to add one (see "Remote setup") rather than improvising remote commands. ### Running a remote Replace `{args}` in the remote's command with what follows `bin/rails query` in the table above, and run it otherwise exactly as written, with the query file on stdin (`<runner>` is the part before the quoted `rails query`): ```bash <runner> "bin/rails query -" < tmp/query/users_by_plan.rb ``` Leave the rest (runner, flags, path) alone. If it looks wrong or doesn't end in `bin/rails query {args}`, say so rather than fixing it silently. Each remote run boots the app, often 10–30 seconds, so: - Answer several numbers in **one** expression returning a Hash, as in the first example. - Prefer the local schema file and `app/models` over a remote `schema` call when the code answers the question; deployed code is usually close to the checked-out branch. ## Writing the expression **Ruby (ActiveRecord) by default.** It is `eval`'d in the app, so scopes, associations, enums and encryption apply. Multi-line is fine; the last value is the result. **Keep it simple and readable.** Write it the way you'd write it in the app: idiomatic ActiveRecord or plain SQL, named variables for repeated pieces, one clause per line when it grows, no clever one-liners. The user may read or rerun it. **`--sql`** for raw tables, joins without models, or aggregates awkward in AR. `SyntaxError: unexpected *` means SQL was parsed as Ruby; add `--sql`. **Check an unfamiliar schema first**, or a guessed column costs a round trip. Read `db/schema.rb` (or `db/structure.sql`) and the model, or run `schema TABLE`, which also lists associations and `enums` (absent when there are none or no model). For enums use `User.where(status: "active")`; `status: 0` may silently misfire. **Time zones.** Ruby ranges like `30.days.ago..` use the app's zone; SQL dates (`DATE(created_at)`) use the database's, usually UTC. Daily counts can differ near midnight. ### Pagination and large results Only **relations** and **`--sql` queries** are paginated: `LIMIT per+1` is appended (`per` default 100, max 10000) and `meta.has_more` is `true` when more rows exist. Use `--page N` or a larger `--per`, and order explicitly so pages don't overlap. Everything else (`pluck`, `to_a`, `map`, `group(...).count`, other Hashes and Arrays) comes back whole with `has_more: false`, however big. On a large table, add `.limit` or count first. Your own `LIMIT` disables pagination (fine for "the 10 most recent"). In `--sql`, `LIMIT` anywhere, even a subquery, counts as yours, and a trailing `-- comment` swallows the appended `LIMIT`. ### EXPLAIN Run `explain` before heavy production queries (full scans, unindexed filters, large joins). In Ruby the expression must be a **relation**: `User.where(active: true)` works, `.count` on it fails with `undefined method 'to_sql' for ... Integer`. Explain the relation, then count. ## Output shapes ```json {"columns": ["id", "email"], "rows": [[1, "[email protected]"]], "meta": {"row_count": 1, "query_time_ms": 4.2, "page": 1, "per_page": 100, "has_more": false, "sql": "SELECT ..."}} ``` Records and relations give their columns, one row each. A Hash gives `["key", "value"]`, one row per pair; a scalar gives `["result"]` and `[[value]]`; other Arrays give `column_0`, …. ## Reporting the answer - **Lead with the answer and its environment**: "In beta, 25 pets were created in the last 30 days." Call local results development data. - **Several values go in a small table**, not raw JSON, with keys and enum values in plain words. - **Name the query file** so the user can read or rerun it. - **Add only caveats that change the reading**: the time zone of date buckets, a partial first or last period, `has_more: true`. ## Safety model - Writes raise `ActiveRecord::ReadOnlyError` (`reading` role if configured, else `while_preventing_writes`). If the user wants data changed, say `rails query` is the wrong tool; don't work around the guard. - Ruby is still evaluated like `rails runner`; protection covers the database only, so keep to queries: no mailers, jobs, HTTP calls or cache writes. - Don't read `ENV`, credentials or secrets: the result lands in the transcript. ## Remote setup `.rails_query_remote` sits next to `bin/rails`. One `name: command` per line; everything after the first `: ` is a shell command ending in `bin/rails query {args}`. Blank lines and `#` comments are ignored. ``` production: <runner for production> "bin/rails query {args}" beta: <runner for beta> "bin/rails query {args}" ``` `<runner>` runs a command where the app is deployed. It must forward stdin to `bin/rails query` and close it at the end (some runners need a flag for that; some have one that disables it). Name remotes after environments. ## Troubleshooting | Symptom | Cause | Fix | |---------|-------|-----| | `"rows":[[null]]` with `"sql":""`, or with `--sql` an error near ` LIMIT 101` | Nothing arrived on stdin: an empty expression | Check the file isn't empty and is redirected with `<` on the same command, and that the runner forwards stdin | | `No such file or directory` for the query file | Command run from another directory | `cd` to the directory with `bin/rails`, where `tmp/query/` is | | Remote hangs with no result | Runner keeps stdin open | Check the runner's stdin flags; for `schema`/`models` use `< /dev/null` | | `ConnectionNotEstablished` with `--db` | Name not in `database.yml` | Use the exact key from `database.yml` | | `rails query` unknown command | Rails < 8.2 without the gem, or not deployed | Add `gem "rails-query-command"` and deploy |README.mdupstream ↗
Contents are not inlined in the catalog. Open the upstream file.
Plain-text copy, no login: /skill-bundles/lazaronixon-rails-query-command-skill-skills-rails-query.md