skills/supabase/SKILL.md
--- name: supabase description: Manage Supabase — create/list projects via the Management API, link a project, wire @supabase/supabase-js + @supabase/ssr into a Next.js (or any TS) app, run migrations (db push), generate TypeScript types, scaffold and review Row Level Security (RLS) policies, and run the local dev stack. Reads SUPABASE_ACCESS_TOKEN from ~/.claude/.env. Use when the user wants Postgres + Auth + storage with RLS, multi-role apps, or is building on the Supabase stack (e.g. Next + S
npx skillsauth add RonanCodes/ronan-skills skills/supabaseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
CLI + Management-API ops for Supabase: projects, linking, migrations, TS types, RLS, and local dev. Postgres + Auth + Row Level Security in one. Reach for this over neon when you want auth and storage bundled with the database and RLS-enforced multi-tenant / multi-role access, or when the target stack is Supabase (e.g. a Next.js + Supabase + Vercel app).
/ro:supabase install # wire @supabase/supabase-js (+ @supabase/ssr for Next) into the current app
/ro:supabase project list
/ro:supabase project create <name> --org <id> # create a hosted project, print URL + anon/service keys
/ro:supabase link --project <ref> # link the cwd app to a hosted project
/ro:supabase migrate new <name> # create a new migration file
/ro:supabase migrate up # apply migrations locally
/ro:supabase db-push # push local migrations to the linked hosted project
/ro:supabase types # generate src/types/database.ts from the schema
/ro:supabase rls # scaffold / review RLS policies (multi-role pattern)
/ro:supabase start | stop # local dev stack (Docker)
SUPABASE_ACCESS_TOKEN in ~/.claude/.env (personal token, sbp_...). Create/rotate at https://supabase.com/dashboard/account/tokens. Always grep ~/.claude/.env for it before asking the user to paste one.supabase CLI: brew install supabase/tap/supabase (check which supabase first; install only with the user's ok).start / local dev: Docker running.Load the token into the shell for any CLI/API call:
export SUPABASE_ACCESS_TOKEN=$(grep -E '^SUPABASE_ACCESS_TOKEN=' ~/.claude/.env | cut -d= -f2-)
Base https://api.supabase.com/v1, auth Authorization: Bearer $SUPABASE_ACCESS_TOKEN.
# list projects
curl -s https://api.supabase.com/v1/projects -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" | jq -r '.[] | "\(.id) \(.name) \(.region)"'
# list orgs (need the org id to create a project)
curl -s https://api.supabase.com/v1/organizations -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" | jq
# create a project (EU region for personal/Simplicity work; prompt for a db password)
curl -s -X POST https://api.supabase.com/v1/projects \
-H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"<name>","organization_id":"<org>","region":"eu-central-1","db_pass":"<generated>","plan":"free"}' | jq
Before any of these, prompt the user (AskUserQuestion) with what will happen, since they never invoke the skill internals directly:
pnpm add,db-push (mutates the remote schema),supabase start (spins up Docker containers).Never run db-push against a linked production project without an explicit confirm.
pnpm add @supabase/supabase-js @supabase/ssrsupabase init (creates supabase/ with config + migrations)..env.local): NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, and server-only SUPABASE_SERVICE_ROLE_KEY. Never expose the service-role key to the client.@supabase/ssr createServerClient / createBrowserClient) and middleware for auth session refresh./ro:supabase types to generate typed rows.RLS is Supabase's headline feature and the right way to enforce per-role and per-tenant access in the database, not the app layer. Default deny, then add policies.
alter table bids enable row level security;
-- adviser: full access to bids on their own listings
create policy "adviser_reads_own_listing_bids" on bids for select
using (
exists (select 1 from listings l
where l.id = bids.listing_id and l.adviser_id = auth.uid())
);
-- bidder (client): only their own bid
create policy "bidder_reads_own_bid" on bids for select
using (bidder_id = auth.uid());
-- admin: all rows in their agency
create policy "admin_reads_agency_bids" on bids for select
using (
exists (select 1 from profiles p
where p.id = auth.uid() and p.role = 'admin'
and p.agency_id = (select agency_id from listings where id = bids.listing_id))
);
Pattern notes:
role and agency_id on a profiles table keyed by auth.uid(); policies read from it.supabase local + a seeded user per role.supabase gen types typescript --linked > src/types/database.ts # or --project-id <ref>
When tables were applied directly to the live DB (e.g. DDL run against the direct
host db.<ref>.supabase.co:5432 because the pooler rejects DDL) they never enter your
migration history or ORM schema file — so the repo's "source of truth" silently drifts
from production. Symptom: an app where some tables exist in code (Drizzle schema.ts /
migrations) and others only live in Supabase. Reconcile BEFORE writing the next
migration, or you'll generate a diff against a stale baseline.
# 1. Capture the live schema as the truth
supabase db pull # writes a migration reflecting live state
# — or, if Drizzle is the schema source of truth:
npx drizzle-kit pull # introspects the live DB → regenerates schema.ts
# 2. Regenerate TS types from the reconciled schema
supabase gen types typescript --linked > src/types/database.ts
# 3. Inspect anything code never touches (columns/constraints/RPCs added out of band)
pg_dump --schema-only "postgresql://...@db.<ref>.supabase.co:5432/postgres"
RLS policies and Postgres functions/RPCs (e.g. *_rebuild_* procedures) live only in
SQL — db pull captures them into a migration, but drizzle-kit pull does NOT model
them, so keep those as SQL migrations regardless. Only after reconciling is the next
migrate new diff trustworthy.
VERCEL_TOKEN in ~/.claude/.env).testing
--- name: linear-pipeline description: The Fable orchestrator for a single dispatched Linear ticket. Holds almost no context itself; it receives `--issue <ID> --detached`, decides the stage sequence, and fans out a sub-agent per stage, passing forward only each stage's artifact (never re-derived, never inlined into its own context). Step zero, before any planning or stage routing, is a boundary triage against `canon/security-boundary.md` (#199): a match tags Ronan Connolly and stops the run, no
development
--- name: in-your-face description: Capture a chat-only answer into a durable artifact (markdown + HTML, PDF when cheap) and launch it automatically so the user cannot miss it. Use when user says "in your face", "don't let me lose this", "save that answer", "make that durable", or right after answering a substantive side question (a recipe, comparison, how-to, or generated prompt) that would otherwise die with the context. category: workflow argument-hint: [--no-open] [--vault <short>] [hint of
tools
One-shot headless OpenAI Codex CLI calls for background/admin AI tasks — summaries, classification, extraction, admin glue. The default engine for anything that runs AI constantly in the background (daemon-driven, per-event), because it bills the flat ChatGPT subscription instead of Claude usage or per-token API spend, and it keeps working while Claude is rate-limited. NEVER for coding — coding stays Claude. Use when a skill or daemon needs a cheap always-on AI call, when the user says "use codex", "ask codex", "codex as backup", or when building a background summarizer/classifier into a listener or loop. Reads auth from ~/.codex/auth.json (ChatGPT account, no API key).
research
Turn a warranty rejection, repair quote, or RMA email into a cited decision brief — legal read (NL/EU consumer law), is the part user-serviceable, live part and new-unit prices, repair-vs-DIY-vs-new economics, before-you-send-it checklist, deadlines. Use when the user pastes or screenshots a repair quote, warranty rejection, "not covered" email, onderzoekskosten fee, or asks "should I repair or replace this".