Cursor keeps making the same bad code practices (or at least bad practices for my setup) over and over again.
e.g. instead of relying on foreign key constraints, Cursor seems to like to check a row with the correct id exists first, before updating all three columns that need to be updated, in individual SQL queries.
I can correct that each time…but is there a way to tell Cursor “when you are writing SQL queries, you should rely on the foreign keys, and do updates in a single call, with varying numbers of arguments”?
I’m planning on adding that to the CodeView extension I’m playing around with. But thought I should probably ask if I’m missing a built in feature.
Hey, yeah, there’s a built-in feature for this, so you don’t need to ship a separate extension. It’s called Cursor Rules.
For your use case, a scoped Project Rule is the best fit. Create .cursor/rules/sql.mdc in your repo with frontmatter that targets SQL files:
---
globs: ["*.sql"]
---
When writing SQL requests: rely on foreign key constraints instead of first
checking that a referenced row exists. Perform multi-column updates in a
single UPDATE statement rather than one request per column.
With globs, the rule is only pulled in when matching files are in context. Other options:
User Rules in Settings > Rules, global for your personal preferences across all projects
AGENTS.md at the repo root, the simplest option if you don’t want to deal with frontmatter
alwaysApply: true in the frontmatter if you want the rule to apply to every request
Thanks. Just to check shouldn’t that be “**/*.sql”?
*.ts
All .ts files in the root
**/*.ts
All .ts files in any directory
And as I’m defining the SQL queries in a PHP file, I guess
globs: ["**/*.sql","**/*.php", ]
*“rules strongly nudge the model, but they don’t guarantee perfect compliance. Short, specific, scoped rules using globs usually work best.”
*
Yes. I’d actually forgotten that I’ve seen the rules before, but then experienced some frustration from having them being treated more like guidelines than what you’d actually call “rules”.
That’s why I’m continuing to prototype an extension to be able to make it easy to view how the different parts of the app fit together, be able to select which bits need to be worked on, and then directly give instructions to the agent on how to write and debug code, where which instructions are put into the prompt depend on which files are selected.
And then the extension also enforces me checking in and committing a small amount of code, to avoid the problem of me forgetting to check in, and then having a few days of different random things to try to draft a commit message for.
Yeah, that’s correct. *.sql matches only files in the repo root, **/*.sql matches .sql files in any folder.
One important detail for your case: since your SQL requests live inside .php files, the agent will have a .php file in context, not a .sql file. So a .sql glob won’t match anything. You need **/*.php.
For the glob syntax, it’s safer to use a YAML list one value per line since it parses reliably:
---
globs:
- "**/*.php"
- "**/*.sql"
---
When writing SQL: rely on foreign key constraints instead of first checking
that a referenced row exists. Perform multi-column updates in a single UPDATE
statement rather than one request per column.
Or you can do it as a single quoted string with commas inside: globs: "**/*.php, **/*.sql". What you should avoid is: globs: "**/*.php", "**/*.sql" two separately quoted strings on the same line since that can match incorrectly.
One more thing that explains why it feels like a guideline: a glob-scoped rule with alwaysApply: false doesn’t show up at the start of the chat. It only gets pulled in when the agent reads a matching file during the work. In most agent workflows that happens naturally, but if the agent edits a file without reading it first, the rule might not apply for that step. If you want the rule always in context, set alwaysApply: true, but even then it’s still more of a strong nudge than a hard guarantee.
Your idea about an extension that pulls instructions based on selected files and forces regular commits sounds interesting. Let me know how the prototype goes.