Skip to content

Utility Script Standards

Cursor.cursor/rules/*.mdcCoding

Rules for one-off and operational scripts requiring idempotency, a default dry-run mode, structured output, and meaningful exit codes.

Use when: Attach to the scripts directory of any Node/TypeScript repo so data fixes, backfills, and ops scripts are safe to rerun and easy to audit.

@library0 upvotes0 remixes0 comments

Get this skill

Add the skill file with the CLI, by hand, or over MCP.

Save this to your project or home directory so Cursor can load it.

npx goodprompts add node-scripts-rules
Saves to./.cursor/rules/node-scripts-rules.mdc
npx goodprompts add node-scripts-rules
Saves to ./.cursor/rules/node-scripts-rules.mdc

Skill file

.cursor/rules/*.mdc3,348 characters
---
description: Safety and output conventions for files under scripts/
globs: "scripts/**"
alwaysApply: false
---
# Script rules
Scripts here touch real data and run unattended in CI or by tired humans at 2am. Every script follows these rules — no exceptions for "quick one-offs", because one-offs get rerun.
## Idempotency
- Running a script twice must produce the same end state as running it once. Design for it explicitly:
- inserts use upserts or existence checks, never blind `INSERT`;
- file generation writes to a temp path then renames atomically;
- external calls (emails, webhooks, charges) check a recorded marker before sending and record completion after.
- Process records in stable order with a resumable cursor (last processed id), so a crash mid-run can resume instead of restarting. Print the cursor on exit.
- Batch mutations: chunks of ≤ 500 with progress output per chunk. Never one transaction around the whole run.
## Dry-run by default
- Mutating scripts run in dry-run mode unless `--apply` (or `--no-dry-run`) is passed. Dry-run executes the full read path and prints exactly what WOULD change: counts, sample ids, before→after values.
- The first line of output always states the mode: `mode=dry-run` or `mode=APPLY`. In apply mode against a production `DATABASE_URL`, require `--yes` or an interactive confirmation naming the target (`Type "production" to continue`).
- Parse flags with `node:util` `parseArgs` (or the repo's CLI lib) — no hand-rolled `process.argv[2]` positional guessing. Every script supports `--help` listing flags, env vars read, and an example invocation.
## Structured output
- Progress and results go to stdout as one JSON object per line: `{"level":"info","event":"chunk_done","processed":500,"failed":0}`. Human-only decoration goes to stderr.
- Always end with a single summary line: `{"event":"summary","mode":"dry-run","scanned":12000,"changed":340,"failed":2,"duration_ms":8120}` — this is what CI greps and what gets pasted into the incident channel.
- Collect per-record failures (id + reason) and report them in the summary; do not abort the whole run on the first bad record unless `--fail-fast` is passed.
- No bare `console.log("done")`, no `console.table` in scripts meant for CI.
## Exit codes
- `0` — completed, zero failures. `1` — completed with per-record failures (summary still printed). `2` — bad usage: unknown flag, missing env var, failed precondition (exit before touching anything). `3+` — fatal runtime error mid-run.
- Validate every required env var and connectivity precondition up front; fail with exit 2 and a message naming the missing variable before any mutation starts.
- Handle `SIGINT`/`SIGTERM`: stop taking new chunks, print the summary with current cursor, exit `130`. A Ctrl-C must never leave a half-applied chunk uncommitted-but-unknown.
- Top-level code is wrapped in `main().catch(err => { print error event; process.exit(3); })` — an unhandled rejection that exits 0 is the worst possible outcome.
## Misc
- Scripts are TypeScript, run via the repo's runner (`tsx`/`bun`), and import shared db/env modules from `src/` — never duplicate a connection string or schema definition inside a script.
- Each script starts with a comment header: purpose, example invocation, owner, and date — delete the script when its purpose is done.
Comments

Comments

Sign in to commentSign in

Remixes

Loading remixes…