@dennisrongo/skills 0.5.0 → 0.6.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.
- package/README.md +1 -0
- package/package.json +1 -1
- package/skills/sql-review/SKILL.md +233 -0
package/README.md
CHANGED
|
@@ -104,6 +104,7 @@ node bin/claude-skills.js list
|
|
|
104
104
|
| [`plan-and-build`](./skills/plan-and-build/SKILL.md) | Plan-first feature builder. Grills the user about the feature (à la `grill-with-docs`) until the design is unambiguous, detects the project's stack and conventions, presents a plan, and gates on `ExitPlanMode` approval before writing any code. When Phase 3 hits a genuine design fork (service-layer vs. CQRS, new table vs. nullable columns, sync vs. background job, etc.) and Phase 2 didn't settle it, runs **Design It Twice**: two parallel sub-agents each draft the BEST plan for one side of the named axis, then an inline debate names three wins for each and recommends one — the user sees a single recommended plan with a one-line "considered alternative" note so they can redirect with one sentence. Skipped when an existing pattern in the repo already dictates the approach. Builds TDD-first with NUnit when a .NET API changes — appending to the matching test class if one already exists rather than forking a parallel one — reuses existing patterns, keeps comments minimal, and generates EF Core / migration files **without ever** running `dotnet ef database update` or any DDL/SQL against the user's database. Triggers on "build/add/implement a feature", "/plan-and-build", or a pasted feature spec. |
|
|
105
105
|
| [`pr-review`](./skills/pr-review/SKILL.md) | Structured review of a local branch, **grouped per `#NNN` task** referenced in commit messages, prioritized correctness → design → tests → security → performance → readability, with categorized feedback (`blocking` / `suggestion` / `question` / `nit` / `praise`). On non-trivial tasks, convenes a **per-task lens council**: parallel `Explore` sub-agents through distinct lenses (correctness / design / security / tests) on that task's diff only, followed by an **adversarial critique round** that demotes false-positive blockers when another lens defuses them, surfaces contradictions as `question` items for the user, and promotes findings raised by multiple lenses independently. Small tasks skip the council. The council never crosses task boundaries — each `#NNN` gets its own verdict. Triggers on "review my PR", "review the diff", "review my branch", or `/pr-review`. |
|
|
106
106
|
| [`ship-it`](./skills/ship-it/SKILL.md) | Pre-launch **operational-readiness** gate for a feature, release, or branch — the complement to [`code-review`](./skills/code-review/SKILL.md). Walks a fixed 10-category checklist (logging, error handling, telemetry, feature flags, migrations, rollback strategy, secrets, local-first storage, auth, update strategy) against the named scope and produces a structured report with PASS / GAP / N/A per item — every PASS backed by a `file:line` citation, every GAP labelled `no evidence found at <path>`, every N/A justified in one line. The final verdict groups findings as **Blocking** (secrets in code, missing authz on a new endpoint, destructive migration without rollback, no way to disable the change in prod) / **Should-fix** / **N/A with reason** / **Passing**, then asks per-blocker whether to draft a fix. **Never edits code unprompted** and **forces scope before auditing** — a PR, a flag, a release tag, or a module — so the output stays actionable. Distinct from `code-review` (diff quality) and `pr-review` (branch / per-task review): `ship-it` is the operational gate that catches what diff-level reviews don't surface. Triggers on "is this ready to ship?", "ship-it check", "production checklist", "pre-launch checklist", "release readiness", or `/ship-it`. |
|
|
107
|
+
| [`sql-review`](./skills/sql-review/SKILL.md) | Pre-commit SQL code review for uncommitted `.sql` changes (staged + unstaged). Detects 17 antipattern classes that map to **real production incident causes** — `sp_send_dbmail` in CATCH blocks (masks the real exception as a misleading permission denial), broken retry patterns (`@retry` declared without a surrounding `WHILE` loop), swallowing CATCH blocks (no `THROW`/`RAISERROR`/log), **new tables created without a primary key or any index** (the silent perf-then-deadlock killer), parameter-vs-column type mismatches (8152 truncation risk), `EXEC()` string concatenation without `sp_executesql` parameters (SQL injection), `NOLOCK` inside transactional write paths, `UPDATE`/`DELETE` without `WHERE`, cursors without `READ_ONLY FORWARD_ONLY LOCAL`, hardcoded environment values (emails, server names, paths, linked servers), cross-DB references like `msdb.dbo.*`, missing `SET NOCOUNT ON`, missing `GRANT EXECUTE` on `CREATE PROC`, `BEGIN TRANSACTION` outside `TRY`/`CATCH` with `XACT_STATE` handling, and vestigial control-flow comments hinting at refactor leftovers (e.g. `-- end while loop` with no `WHILE`). **Scope-aware** — full-file scan for new files, diff-only scan for modified files so legacy antipatterns in untouched parts of a large SP don't flood the report. Categorizes findings as `BLOCKER` / `WARN` / `INFO` with `file:line` citations and per-finding fix recommendations. **Never edits SQL unprompted** — produces the report, then asks per fix. Distinct from [`code-review`](./skills/code-review/SKILL.md), which carries the general best-practice catalog without SQL-specific patterns. Triggers on `/sql-review`, "review my SQL", "review the SQL diff", "lint the SQL", "check my SQL changes", "SQL pre-commit check", or "audit my stored proc". |
|
|
107
108
|
| [`task-executor`](./skills/task-executor/SKILL.md) | Disciplined execution loop for a single, already-defined task — Understand → Inspect → Plan → Execute incrementally → Validate after every change → Track assumptions → Update progress. Forces a strict per-turn output format with six fixed sections (**Goal** / **Current understanding** / **Files to inspect** / **Plan** / **Progress** / **Risks** / **Assumptions**) so the work stays legible and resumable instead of devolving into ad-hoc edits across turns. When the inspection working set spans multiple layers (controller + service + persistence + tests), Phase 2 convenes an **inspection council**: parallel `Explore` sub-agents — one per layer slice — each map their area and return `file:line`-cited findings on existing patterns, wiring points, and sibling test classes; the main session aggregates the results into `Current understanding` so the working context window stays free for the strict per-turn output the executional phase keeps emitting. Small inspection sets (≤ ~5 files, one layer) skip the council and read inline. Enters Plan Mode after inspection and gates on `ExitPlanMode` approval before writing any code; every plan step is then one logical change followed by an immediate validation (test, build, type-check, curl, page load) before the next checkbox ticks. Assumptions are tracked explicitly until confirmed by code or the user — and promoted into `Current understanding` or killed, never silently carried. Composes downward from [`plan-and-build`](./skills/plan-and-build/SKILL.md) (which interviews the user to design the feature) and routes back to it when the user invokes this skill on a fuzzy spec. Triggers on `/task-executor`, "Work on task: …", or any concrete, already-defined task handed to Claude for execution. |
|
|
108
109
|
| [`tauri-2-app`](./skills/tauri-2-app/SKILL.md) | Scaffold a new Tauri 2 desktop app (Rust backend + TypeScript/React frontend) using a thin-frontend / rich-Rust-backend architecture with modular `commands/`, `state/`, `storage/`, `platform/` traits, `error/` macros, single-instance + updater plugins wired correctly, capability JSON per window, encrypted secrets at rest, `spawn_blocking` for sync work, and typed frontend command hooks — while forbidding common pitfalls (committed `.backup`/`.orig`/`.temp` files, plaintext API keys in `settings.json`, tokens in `localStorage`, `cfg!(target_os)` in command bodies instead of trait-based platform code, hand-rolled date math instead of `chrono`, raw `std::fs` bypassing capability checks, blocking I/O inside async commands, missing `windows_subsystem = "windows"` in `main.rs`, `devtools: true` in release, hardcoded bundle identifiers / updater pubkeys / CDN URLs). Three modes — full project scaffold, add-a-command end-to-end, add-a-Rust-module slice. Resolves Cargo + npm versions at scaffold time (not hard-coded). |
|
|
109
110
|
| [`write-a-skill`](./skills/write-a-skill/SKILL.md) | Author a new Claude Code skill — interview-driven scaffolding that produces a properly-structured `SKILL.md` (trigger-rich YAML description, "When to use", workflow, examples, anti-patterns), drops it in the right location (library `skills/`, project `./.claude/skills/`, or global `~/.claude/skills/`), updates the README skills table when extending this library, and runs a review checklist focused on the failure mode that matters most — under-triggering descriptions. Triggers on "create/write/add a skill", "/write-a-skill", or a pasted SKILL.md URL with "one like this". |
|
package/package.json
CHANGED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sql-review
|
|
3
|
+
description: Pre-commit SQL code review for uncommitted database changes. Detects antipatterns that cause real production incidents — `sp_send_dbmail` in CATCH blocks (masks the real error as a misleading permission denial), broken retry patterns (`@retry` declared without a surrounding `WHILE` loop), swallowing CATCH blocks (no `THROW`/`RAISERROR`/log), new tables created without a primary key or any index (silent perf-then-deadlock killer), parameter-vs-column type mismatches (8152 truncation risk), `EXEC()` string concatenation without `sp_executesql` parameters (SQL injection), `NOLOCK` in write paths, `UPDATE`/`DELETE` without `WHERE`, cursors without `READ_ONLY FORWARD_ONLY LOCAL`, hardcoded env values (emails, server names, paths), cross-DB references like `msdb.dbo.*`, missing `SET NOCOUNT ON`, missing `GRANT EXECUTE` on `CREATE PROC`, `BEGIN TRANSACTION` outside `TRY`/`CATCH`, and vestigial control-flow comments hinting at refactor leftovers (e.g. `-- end while loop` with no `WHILE`). Reports findings as `BLOCKER` / `WARN` / `INFO` with `file:line` citations and per-finding fix recommendations. **Never edits SQL** — surfaces findings for human review. Use this skill whenever the user says "/sql-review", "review my SQL", "review the SQL diff", "lint the SQL", "check my SQL changes", "SQL pre-commit check", "audit my stored proc", or "any SQL antipatterns in this diff" — even if they don't explicitly say "SQL review skill". Distinct from `code-review` (general best-practice review) — this carries SQL-specific patterns that surface DB-layer production incidents.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SQL Review
|
|
7
|
+
|
|
8
|
+
Review uncommitted SQL changes against a catalog of patterns that cause real production incidents — masked errors, unindexed new tables, silent truncation, SQL injection, deadlock-prone CATCH blocks — and surface findings as recommendations the user can act on, **without editing SQL unprompted**.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- "/sql-review" / "review my SQL" / "review the SQL diff"
|
|
13
|
+
- "lint the SQL" / "SQL pre-commit check" / "check my SQL changes"
|
|
14
|
+
- "audit my stored proc" / "any SQL antipatterns in this diff"
|
|
15
|
+
- Before committing a `.sql` change touching a stored procedure, function, or table
|
|
16
|
+
|
|
17
|
+
Do **not** auto-trigger when the user is asking for a *general* code review on a mixed diff — defer to [`code-review`](../code-review/SKILL.md) for that. This skill is specifically for SQL-heavy diffs where the antipattern catalog applies.
|
|
18
|
+
|
|
19
|
+
## Hard rule: surface, don't refactor
|
|
20
|
+
|
|
21
|
+
If you find issues, **do not start editing the SQL**. Produce the findings report first. After delivering it, ask the user per finding: *"Want me to fix #N?"* — and wait for an explicit yes. Pre-authorization ("fix them all") proceeds through the list; absent that, default to ask-per-fix.
|
|
22
|
+
|
|
23
|
+
SQL changes are particularly sensitive because they often run against production data in a single non-reversible deployment. A drive-by refactor mid-review is more dangerous here than in application code.
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
1. **Snapshot the SQL diff.** Run in parallel:
|
|
28
|
+
- `git status --short` — see what's modified, staged, untracked.
|
|
29
|
+
- `git diff -- '*.sql'` — unstaged SQL changes.
|
|
30
|
+
- `git diff --staged -- '*.sql'` — staged SQL changes.
|
|
31
|
+
- If user passed an explicit range (e.g. `main..HEAD`), use that instead: `git diff <range> -- '*.sql'`.
|
|
32
|
+
2. **Triage files into two buckets:**
|
|
33
|
+
- **New files** (status `A` or `??`) — review the entire file. The whole file is new code; pre-existing antipatterns elsewhere don't apply.
|
|
34
|
+
- **Modified files** (status `M`) — review only the changed hunks. The rest of the file may have legacy antipatterns the user isn't introducing (e.g. existing repos commonly have dozens of SPs with the `sp_send_dbmail`-in-CATCH pattern; flagging all of them on an unrelated edit creates noise).
|
|
35
|
+
3. **For each check in [What to check](#what-to-check), look for the pattern** in the relevant scope (whole file for new files, diff hunks for modified files). Use ripgrep — don't try to fully parse SQL.
|
|
36
|
+
4. **Categorize each finding** as `BLOCKER` / `WARN` / `INFO` (see [Categories](#categories)).
|
|
37
|
+
5. **Cite `file:line`** for every finding. Line numbers come from the diff or the current file content.
|
|
38
|
+
6. **Produce the report** in the [Output format](#output-format).
|
|
39
|
+
7. **Offer to fix** the suggested findings. Wait for the user's selection before editing.
|
|
40
|
+
|
|
41
|
+
## Categories
|
|
42
|
+
|
|
43
|
+
- **`BLOCKER`** — would cause a production incident: silent data loss, masked errors, SQL injection, unindexed table that will grow, broken transaction handling. Fix before commit.
|
|
44
|
+
- **`WARN`** — likely a bug or maintenance hazard: NOLOCK in write paths, hardcoded environment values, missing `SET NOCOUNT ON`, cross-DB references. Should fix but won't necessarily incident.
|
|
45
|
+
- **`INFO`** — style / convention / hygiene: vestigial comments, cursor defaults, missing `GRANT EXECUTE` where the project's pattern exists.
|
|
46
|
+
|
|
47
|
+
Lead each finding with the *why*. "This swallows the original error and surfaces a misleading permission denial" beats "add `;THROW;`".
|
|
48
|
+
|
|
49
|
+
## What to check
|
|
50
|
+
|
|
51
|
+
Use ripgrep for detection. The patterns below are starting points — adapt to the actual diff text.
|
|
52
|
+
|
|
53
|
+
### Error-handling antipatterns (BLOCKER class)
|
|
54
|
+
|
|
55
|
+
1. **`sp_send_dbmail` inside `CATCH`** — masks the real exception with a permission/configuration error when the executing login lacks `EXECUTE` on `msdb.dbo.sp_send_dbmail`. The real underlying error is silently lost.
|
|
56
|
+
- Pattern: `rg -n -B 20 'sp_send_dbmail' <file>` → check whether the surrounding context is a `BEGIN CATCH`.
|
|
57
|
+
- **Fix:** Replace with `;THROW;` to re-raise, or write the error info to a local log table. Never `sp_send_dbmail` from inside operational SPs.
|
|
58
|
+
|
|
59
|
+
2. **Broken retry pattern** — `DECLARE @retry INT = N;` exists, but no `WHILE @retry` loop wraps the `TRY`/`CATCH`. The retry variable is decremented in the CATCH but never read, so the SP gives up on the first deadlock instead of retrying.
|
|
60
|
+
- Pattern: `rg -n '@retry' <file>` → confirm there's a matching `WHILE.*@retry` somewhere in the same SP body.
|
|
61
|
+
- **Fix:** Add the missing `WHILE @retry > 0 BEGIN ... END` around the TRY/CATCH, or remove the vestigial variable entirely.
|
|
62
|
+
|
|
63
|
+
3. **Empty / swallowing `CATCH` blocks** — `BEGIN CATCH ... END CATCH` with no `THROW`, no `RAISERROR`, and no `INSERT INTO <error_log_table>`. Silent failure → silent data loss.
|
|
64
|
+
- Pattern: `rg -n -A 30 'BEGIN CATCH' <file>` → check each match for at least one of `THROW`, `RAISERROR`, or `INSERT INTO`.
|
|
65
|
+
- **Fix:** End the CATCH with `;THROW;` unless there's a deliberate reason to suppress (and document that reason).
|
|
66
|
+
|
|
67
|
+
4. **`BEGIN TRANSACTION` without `TRY`/`CATCH` + `XACT_STATE` handling** — an error mid-transaction leaves an open transaction on the connection.
|
|
68
|
+
- Pattern: `rg -n 'BEGIN TRAN' <file>` → confirm the same SP has `BEGIN TRY` and `XACT_STATE()` checks in CATCH.
|
|
69
|
+
- **Fix:** Wrap in `BEGIN TRY / BEGIN TRANSACTION ... COMMIT TRANSACTION / END TRY / BEGIN CATCH / IF XACT_STATE() = -1 ROLLBACK; IF XACT_STATE() = 1 COMMIT; ;THROW; / END CATCH`.
|
|
70
|
+
|
|
71
|
+
5. **Vestigial control-flow comments** — `-- end while loop` with no `WHILE` keyword, `-- retry on deadlock` with no retry loop, etc. Strong signal that a refactor left dead code behind.
|
|
72
|
+
- Pattern: `rg -n -- '-- end (while|for|repeat)' <file>` → check for matching opening keyword.
|
|
73
|
+
- **Fix:** Either restore the missing loop or delete the misleading comment.
|
|
74
|
+
|
|
75
|
+
### Schema / performance antipatterns (BLOCKER for new tables, WARN otherwise)
|
|
76
|
+
|
|
77
|
+
6. **New `CREATE TABLE` without a primary key, clustered index, or any index** — heap tables will deadlock-and-table-scan once they grow. This is the exact pattern that caused real production deadlocks.
|
|
78
|
+
- Pattern: `rg -n -A 50 'CREATE TABLE' <file>` → confirm at least one of `PRIMARY KEY`, `CLUSTERED INDEX`, or `CREATE INDEX` exists on that table within the diff or in a sibling file.
|
|
79
|
+
- **Fix:** Add `PRIMARY KEY CLUSTERED` on the natural ID column. If the table is append-mostly with no natural ID, add a clustered index on the column the populating SPs filter by in `WHERE`.
|
|
80
|
+
|
|
81
|
+
7. **Parameter-to-column type-width mismatches** — passing `@x VARCHAR(50)` into a column declared `VARCHAR(3)` causes silent truncation (or runtime error 8152 depending on `ANSI_WARNINGS`).
|
|
82
|
+
- Pattern: cross-reference the SP's `@param VARCHAR(N)` declarations against the target table's column widths. Flag any narrowing.
|
|
83
|
+
- **Fix:** Match widths between parameter and column, or add explicit `LEFT(@x, 3)` truncation at the boundary so it's intentional and visible.
|
|
84
|
+
|
|
85
|
+
8. **Implicit type conversions in `JOIN` / `WHERE`** — `WHERE intCol = @varcharParam` defeats indexes and produces table scans.
|
|
86
|
+
- Pattern: `rg -n -i 'WHERE\s+\w+\s*=\s*@\w+' <file>` → cross-check parameter type vs. column type.
|
|
87
|
+
- **Fix:** Match the parameter type to the column type. If they're authoritatively different, cast the *parameter*, not the column (casting the column kills the index).
|
|
88
|
+
|
|
89
|
+
### Security antipatterns (BLOCKER)
|
|
90
|
+
|
|
91
|
+
9. **Dynamic SQL via string concatenation passed to `EXEC()`** without `sp_executesql @stmt, @params` parameterization — SQL injection.
|
|
92
|
+
- Pattern: `rg -n 'EXEC\s*\(' <file>` → confirm any string-concat dynamic SQL nearby uses `sp_executesql` with parameters, not `EXEC(@strSQL)` with values inlined.
|
|
93
|
+
- **Fix:** Switch to `EXEC sp_executesql @stmt = @strSQL, @params = N'@p1 INT, @p2 VARCHAR(50)', @p1 = @value1, @p2 = @value2;`.
|
|
94
|
+
|
|
95
|
+
10. **Hardcoded environment-specific values** — email addresses, server names, file paths, IP addresses, connection strings.
|
|
96
|
+
- Pattern: `rg -nE "'[\w.-]+@[\w.-]+\.\w+'|@server_name\s*=\s*'\w+'|\\\\\\\\[\\w.-]+\\\\" <file>` (emails, linked servers, UNC paths).
|
|
97
|
+
- **Fix:** Move env-specific values into a configuration table or pass them as parameters from the application layer.
|
|
98
|
+
|
|
99
|
+
11. **Cross-database references** — `msdb.dbo.*`, `master.dbo.*`, linked server `[srv].db.dbo.*`. These require permissions that may not exist in all environments (the canonical example: a SP calling `msdb.dbo.sp_send_dbmail` from a login that lacks EXECUTE on it).
|
|
100
|
+
- Pattern: `rg -n 'msdb\.|master\.dbo\.|\[\w+\]\.\w+\.\w+\.' <file>`.
|
|
101
|
+
- **Fix:** Move the cross-DB call out of operational SPs into infrastructure-owned code. If it must stay, document the required permission in a comment so deployments to new environments include the GRANT.
|
|
102
|
+
|
|
103
|
+
### Correctness / data-safety antipatterns (BLOCKER)
|
|
104
|
+
|
|
105
|
+
12. **`UPDATE` or `DELETE` without a `WHERE` clause** — almost always a bug; will affect every row.
|
|
106
|
+
- Pattern: `rg -n -B 0 -A 20 'UPDATE\s+\w+' <file>` → confirm a `WHERE` clause is present and not commented out.
|
|
107
|
+
- Pattern: `rg -n -B 0 -A 10 'DELETE\s+FROM' <file>` → same.
|
|
108
|
+
- **Fix:** Add the `WHERE` clause. If "every row" really is intended (TRUNCATE-style), use `TRUNCATE TABLE` and add a comment explaining why.
|
|
109
|
+
|
|
110
|
+
13. **`NOLOCK` / `READ UNCOMMITTED` inside transactional write paths** — `WITH (NOLOCK)` is fine for ad-hoc reporting reads, but in an `UPDATE`/`INSERT`/`DELETE` SP it can read uncommitted data and corrupt the write.
|
|
111
|
+
- Pattern: `rg -n -i 'NOLOCK|READ UNCOMMITTED' <file>` → check whether the same SP has `INSERT`, `UPDATE`, or `DELETE` against the same tables.
|
|
112
|
+
- **Fix:** Remove the hint on tables being modified. Consider `SNAPSHOT` isolation if the goal is to avoid blocking.
|
|
113
|
+
|
|
114
|
+
14. **`TRUNCATE TABLE` or `DROP` without `IF EXISTS`** in deploy scripts that are supposed to be idempotent (re-runnable). Many repos use flat idempotent `Deploy_*.sql` files where re-runs must not fail on missing objects.
|
|
115
|
+
- Pattern: `rg -n -i 'DROP (TABLE|PROC|FUNCTION|VIEW)' <file>` → confirm `IF EXISTS` or `OBJECT_ID(...) IS NOT NULL` guard.
|
|
116
|
+
- **Fix:** `IF OBJECT_ID('dbo.X', 'U') IS NOT NULL DROP TABLE dbo.X;` or `DROP TABLE IF EXISTS dbo.X;` (SQL 2016+).
|
|
117
|
+
|
|
118
|
+
### Style / convention (WARN/INFO)
|
|
119
|
+
|
|
120
|
+
15. **Missing `SET NOCOUNT ON`** at the top of a new `CREATE PROC` — causes extra round-trips to the client and breaks some ORM drivers that don't handle row-count messages.
|
|
121
|
+
- Pattern: `rg -n -A 5 'CREATE PROC' <file>` → confirm `SET NOCOUNT ON` appears within the first ~10 lines of the procedure body.
|
|
122
|
+
- **Fix:** Add `SET NOCOUNT ON;` as the first statement after `AS BEGIN`.
|
|
123
|
+
|
|
124
|
+
16. **Cursors without `READ_ONLY FORWARD_ONLY LOCAL`** — default cursors are slow and persist beyond the current batch.
|
|
125
|
+
- Pattern: `rg -n 'DECLARE\s+\w+\s+CURSOR\b' <file>` → confirm `READ_ONLY FORWARD_ONLY LOCAL` (or `FAST_FORWARD`) is specified.
|
|
126
|
+
- **Fix:** `DECLARE c CURSOR READ_ONLY FORWARD_ONLY LOCAL FOR SELECT ...`.
|
|
127
|
+
|
|
128
|
+
17. **Missing `GRANT EXECUTE` at end of `CREATE PROC`** — only flag when the rest of the repo has a consistent `GRANT EXECUTE ON ... TO <role>` pattern at the end of every SP file. Detect the pattern by sampling a few neighboring `.sql` files; if every existing SP grants to the same role and this new SP doesn't, flag it.
|
|
129
|
+
- Pattern: `rg -n 'GRANT\s+EXECUTE' <file>` → confirm present if the repo convention requires it.
|
|
130
|
+
- **Fix:** Append `GRANT EXECUTE ON [dbo].[<proc>] TO [<app_role>] GO` matching the role the rest of the repo grants to.
|
|
131
|
+
|
|
132
|
+
## Output format
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
# SQL review — uncommitted changes
|
|
136
|
+
|
|
137
|
+
**SQL files changed:** <N> (<S new>, <M modified>)
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Verdict
|
|
142
|
+
**Ship / Fix blockers first**
|
|
143
|
+
|
|
144
|
+
<one-paragraph summary of the change and overall state>
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Blockers (<N>)
|
|
149
|
+
|
|
150
|
+
### B1. <short title> — `path/to/file.sql:<line>`
|
|
151
|
+
**Why:** <root cause / production consequence — one line>
|
|
152
|
+
**Fix:** <concrete recommendation — one line>
|
|
153
|
+
|
|
154
|
+
### B2. ...
|
|
155
|
+
|
|
156
|
+
## Warnings (<N>)
|
|
157
|
+
|
|
158
|
+
### W1. <short title> — `path/to/file.sql:<line>`
|
|
159
|
+
**Why:** ...
|
|
160
|
+
**Fix:** ...
|
|
161
|
+
|
|
162
|
+
## Info (<N>)
|
|
163
|
+
- `path:line` — <one-liner>
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Summary
|
|
168
|
+
- BLOCKER: <count>
|
|
169
|
+
- WARN: <count>
|
|
170
|
+
- INFO: <count>
|
|
171
|
+
|
|
172
|
+
## Fixes I can apply
|
|
173
|
+
|
|
174
|
+
If you want, I can apply any of: B1, B2, W1. Which? (or "all", or "none")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
End with the offer. Wait for the user's choice.
|
|
178
|
+
|
|
179
|
+
## Examples
|
|
180
|
+
|
|
181
|
+
### Example 1: New stored proc with mixed issues
|
|
182
|
+
|
|
183
|
+
**User:** "/sql-review"
|
|
184
|
+
|
|
185
|
+
**Claude:**
|
|
186
|
+
- Runs `git diff --staged -- '*.sql'` and `git diff -- '*.sql'` in parallel.
|
|
187
|
+
- Finds one new file `Stored Procedures/dbo.usp_NewSave.sql`.
|
|
188
|
+
- Spots:
|
|
189
|
+
- `B1` — `BEGIN CATCH` without `;THROW;` (line 88) — swallowing.
|
|
190
|
+
- `B2` — calls `EXEC msdb.dbo.sp_send_dbmail` inside that CATCH (line 92) — masking risk.
|
|
191
|
+
- `W1` — missing `SET NOCOUNT ON` at line 6.
|
|
192
|
+
- `W2` — `WITH (NOLOCK)` on the table being UPDATEd (line 45).
|
|
193
|
+
- `I1` — missing `GRANT EXECUTE` at end of file (the repo's other SPs all end with it).
|
|
194
|
+
- Reports, then asks: "Want me to fix B1, B2, W1, W2, I1?"
|
|
195
|
+
|
|
196
|
+
### Example 2: Adding a new table — the canonical incident pattern
|
|
197
|
+
|
|
198
|
+
**User:** "review the SQL diff before I push"
|
|
199
|
+
|
|
200
|
+
**Claude:**
|
|
201
|
+
- Spots `Tables/dbo.NewAuditLog.sql` (new file) — `CREATE TABLE` declared but **no PRIMARY KEY, no CLUSTERED INDEX, no CREATE INDEX**.
|
|
202
|
+
- Flags as `B1` — "Heap table will table-scan on every read/UPDATE. Once the table grows past a few hundred thousand rows under concurrent writes, SQL Server's optimizer picks parallel plans and the workers deadlock on heap pages — a recurring production incident pattern."
|
|
203
|
+
- Fix: "Add `PRIMARY KEY CLUSTERED (id)` or a clustered index on the lookup column."
|
|
204
|
+
- Also flags the SP that populates the new table (`B2` — implicit type conversion `WHERE recordID = @recordID` where the new table's `recordID` is `INT` but the param is declared `VARCHAR`).
|
|
205
|
+
|
|
206
|
+
### Example 3: Modified existing SP — flag only new badness, not legacy
|
|
207
|
+
|
|
208
|
+
**User:** "lint the SQL — just the staged hunks"
|
|
209
|
+
|
|
210
|
+
**Claude:**
|
|
211
|
+
- The file `usp_UpdateBigOldSP.sql` is modified but huge. The user's diff added a 20-line block that does `UPDATE dbo.Customers SET ...` without a `WHERE` clause.
|
|
212
|
+
- **Only flags the new block's `UPDATE` without `WHERE` as `B1`** — does NOT flag the legacy `sp_send_dbmail` call elsewhere in the file (which was already there before the user's edit).
|
|
213
|
+
- Notes in the report footer: "1 pre-existing antipattern observed in this file but outside the diff — flagging suppressed per scope. Full-file audit available on request."
|
|
214
|
+
|
|
215
|
+
## Anti-patterns
|
|
216
|
+
|
|
217
|
+
- ❌ Trying to parse SQL with a full grammar — use ripgrep on the diff text. The skill is heuristic by design.
|
|
218
|
+
- ❌ Flagging every existing antipattern in a modified file. Stay scoped to the diff for modified files; full-file scan only for new files.
|
|
219
|
+
- ❌ Editing SQL during the review pass. Report first, **always**.
|
|
220
|
+
- ❌ Suggesting autofixes for `BLOCKER` items without asking. SQL deploys to production; user confirmation gates the change.
|
|
221
|
+
- ❌ Listing a `nit` when there's a `BLOCKER` next to it. Lead with severity.
|
|
222
|
+
- ❌ Hand-wavy findings without `file:line`.
|
|
223
|
+
- ❌ Inventing antipatterns not in [What to check](#what-to-check). This is a fixed catalog — extending it is a `write-a-skill` change, not a per-run improvisation.
|
|
224
|
+
- ❌ Running on non-SQL files. The skill is `.sql`-scoped; mixed diffs route to `code-review` for the non-SQL parts.
|
|
225
|
+
- ✅ Diff-scoped scan for modified files, full scan for new files, findings categorized + cited + offered as fixes.
|
|
226
|
+
|
|
227
|
+
## Notes
|
|
228
|
+
|
|
229
|
+
- The check catalog is intentionally finite — 17 checks chosen because each one maps to a real production incident class. Don't try to be a full SQL linter (e.g. SQLFluff or sqlcheck) — those exist and run from CI; this skill is the pre-commit human-pass complement focused on bug classes that linters miss.
|
|
230
|
+
- For repos that use flat idempotent `Deploy_*.sql` files (every deploy is a full re-run), check #14 (`DROP` / `TRUNCATE` without `IF EXISTS`) is critical — the deploy will fail on second run otherwise. For repos that use forward-only migrations, this check is lower priority.
|
|
231
|
+
- The "broken retry" pattern (check #2) is sneakier than it sounds: a `DECLARE @retry INT = 5;` followed by `SET @retry = @retry - 1;` in CATCH but no `WHILE` loop *looks* like deadlock handling. It isn't. Multiple SPs in real codebases have this bug from removed-loop refactors.
|
|
232
|
+
- If the user wants the broader cleanup (refactor every legacy SP in the repo with one of these issues), recommend opening a tracked task rather than doing it in the current review — these fixes ripple through deploy pipelines and need coordinated review.
|
|
233
|
+
- This skill composes downstream with [`code-review`](../code-review/SKILL.md) — on a mixed diff (`.cs` + `.sql`), run `code-review` for the application code and `sql-review` for the database changes.
|