A Bun TypeScript CLI tool that reads PostgreSQL migration files from disk, applies them sequentially in-memory, and outputs the final schema state as LLM-optimised markdown or JSON.
LLM agents working with codebases need to understand the database schema. Today that means reading every migration file and burning tokens reconstructing the state mentally. migration-state does that once and produces a clean schema summary the agent can reference.
No existing tool reconstructs PostgreSQL schema state purely from migration files on disk without a live database connection.
bunx migration-state ./db/migrationsmigration-state <dir>
Arguments:
dir Path to migration directory
Options:
--tool <tool> Migration tool (auto-detected if omitted)
--tables <tables> Comma-separated table names to filter
--schemas <schemas> Comma-separated schema names to filter
--format <format> Output format: markdown (default) or json
--no-views Exclude views from output
--no-functions Exclude functions from output
--no-sequences Exclude sequences from output
--quiet Suppress warnings
--help Show help
Auto-detected from file patterns:
- Flyway (
V{version}__{desc}.sql) - golang-migrate (
{version}_{title}.up.sql) - goose (
-- +goose Upmarkers) - dbmate (
-- migrate:upmarkers) - sql-migrate (
-- +migrate Upmarkers) - Prisma (
{timestamp}_{name}/migration.sql) - Drizzle (directory +
snapshot.json) - Atlas (
atlas.sumpresent) - Generic (fallback, sorts by filename)
Handles 46 PostgreSQL DDL statement types including:
- Tables (CREATE, ALTER, DROP with all sub-commands)
- Constraints (PK, FK, UNIQUE, CHECK, EXCLUDE)
- Indices (btree, gin, gist, partial, expression, multi-column)
- Types (ENUM, composite, domain)
- Triggers and functions (including dollar-quoted bodies)
- Views (CREATE, CREATE OR REPLACE)
- Schemas, sequences, extensions, comments
- Partitioned tables (PARTITION BY, PARTITION OF)
- Row-level security (ENABLE RLS, CREATE POLICY)
- Generated columns, table inheritance
Uses @supabase/pg-parser (the real PostgreSQL 17 C parser compiled to WASM). Selected over pgsql-ast-parser based on a spike comparing both:
| Metric | @supabase/pg-parser | pgsql-ast-parser |
|---|---|---|
| DDL Coverage | 46/46 (100%) | 36/46 (78%) |
| Mandatory DDL | 34/34 | 31/34 |
| Parse Speed | 2.19ms/iter | 7.98ms/iter |
| Bundle Size | 5.1 MB | 2.2 MB |
Full spike results in spike/results/comparison.md.
- Runtime: Bun
- Parser: @supabase/pg-parser (WASM)
- Language: TypeScript
- PostgreSQL only, SQL-file migrations only
bun install # install dependencies
bun test ./test/ # run tests (213 tests)
bun run typecheck # type check src/
bun run build # build dist/index.js for npm
bun run src/index.ts # run from source (dev)CI automatically publishes to npm when a PR is merged to main, but only if the version in package.json has changed. No version bump = no publish.
- Bump the version in
package.json(e.g.,0.1.0→0.2.0) - Merge to
main - CI runs typecheck + tests, then publishes to npm
The first merge that includes the npm token setup will publish 0.1.0 automatically (since the package doesn't exist on npm yet, any version counts as new).
Publishing uses npm Trusted Publishing via OIDC — no tokens to manage or rotate.
First publish (creates the package on npm):
bun run build
npm login
npm publish --access publicThen enable Trusted Publishing:
- Go to npmjs.com → package
migration-state→ Settings → Publishing access - Add trusted publisher:
- Provider: GitHub Actions
- Repository owner:
mec07 - Repository name:
migration-state - Workflow filename:
ci.yml - Environment: (leave blank)
After this, all subsequent publishes happen automatically via CI with no tokens needed.
| Trigger | Jobs |
|---|---|
Push to main |
Typecheck → Test → Publish (if version changed) |
Pull request to main |
Typecheck → Test |
MIT