Problem
Self-hosters and the upstream maintainer both apply schema changes by:
- Cloning the repo (or reading the file in the npm package)
- Opening Supabase → SQL Editor
- Pasting
migrations/00X_*.sql
- Clicking Run
This is fragile and easy to miss. PR #7 shipped migrations/002_loot_titles.sql and the cloud silently 400'd on the new fields until someone remembered to run it. Forks inherit this same papercut every time we add a migration.
Proposal
A claudexp cloud migrate subcommand that:
- Reads
migrations/*.sql from the installed package, sorted lexicographically
- Tracks applied versions in a
_claudexp_migrations table (one row per filename + applied_at timestamp)
- Connects to Supabase via the configured anon key + a separate service role key (operator only — the anon key can't run DDL)
- For each unapplied file: prints filename, runs in a transaction, records the row
- Idempotent — safe to re-run
CLI shape:
claudexp cloud migrate --dry-run — list pending migrations, don't apply
claudexp cloud migrate — apply all pending
claudexp cloud migrate status — show applied vs pending
Service role key can come from SUPABASE_SERVICE_ROLE_KEY env var; never written to disk. Print a warning if invoked without it.
Acceptance criteria
claudexp cloud migrate status on a fresh fork shows 001, 002 pending
claudexp cloud migrate applies them and they don't re-apply on second run
- Forks adding
migrations/003_*.sql get a one-command upgrade path
- README's self-host section drops the "go paste this in SQL Editor" step
Problem
Self-hosters and the upstream maintainer both apply schema changes by:
migrations/00X_*.sqlThis is fragile and easy to miss. PR #7 shipped
migrations/002_loot_titles.sqland the cloud silently 400'd on the new fields until someone remembered to run it. Forks inherit this same papercut every time we add a migration.Proposal
A
claudexp cloud migratesubcommand that:migrations/*.sqlfrom the installed package, sorted lexicographically_claudexp_migrationstable (one row per filename + applied_at timestamp)CLI shape:
claudexp cloud migrate --dry-run— list pending migrations, don't applyclaudexp cloud migrate— apply all pendingclaudexp cloud migrate status— show applied vs pendingService role key can come from
SUPABASE_SERVICE_ROLE_KEYenv var; never written to disk. Print a warning if invoked without it.Acceptance criteria
claudexp cloud migrate statuson a fresh fork shows001,002pendingclaudexp cloud migrateapplies them and they don't re-apply on second runmigrations/003_*.sqlget a one-command upgrade path