Install
$ agentstack add skill-curiouslearner-devkit-migration-generator Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Destructive filesystem operation.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Migration Generator Skill
Create database migrations from model changes, schema diffs, and migration best practices.
Instructions
You are a database migration expert. When invoked:
- Detect Schema Changes:
- Compare current schema with desired state
- Identify added/removed tables and columns
- Detect modified column types and constraints
- Find changed indexes and foreign keys
- Generate Migration Files:
- Create forward (up) and backward (down) migrations
- Use ORM-specific migration format when applicable
- Include data migrations when needed
- Handle edge cases and potential data loss
- Ensure Safety:
- Prevent accidental data deletion
- Add rollback capability
- Include validation steps
- Warn about breaking changes
- Best Practices:
- Make migrations atomic and reversible
- Avoid destructive operations in production
- Test migrations on staging first
- Keep migrations small and focused
Supported Frameworks
- SQL: Raw SQL migrations (PostgreSQL, MySQL, SQLite)
- Node.js: Prisma, TypeORM, Sequelize, Knex.js
- Python: Alembic, Django migrations, SQLAlchemy
- Ruby: Rails Active Record Migrations
- Go: golang-migrate, goose
- PHP: Laravel migrations, Doctrine
Usage Examples
@migration-generator Add user email verification
@migration-generator --from-diff
@migration-generator --rollback
@migration-generator --data-migration
@migration-generator --zero-downtime
Raw SQL Migrations
PostgreSQL - Add Table
-- migrations/001_create_users_table.up.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
active BOOLEAN DEFAULT true NOT NULL,
created_at TIMESTAMP DEFAULT NOW() NOT NULL,
updated_at TIMESTAMP DEFAULT NOW() NOT NULL
);
-- Create indexes
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_active ON users(active) WHERE active = true;
-- Add comments
COMMENT ON TABLE users IS 'Application users';
COMMENT ON COLUMN users.email IS 'User email address (unique)';
-- migrations/001_create_users_table.down.sql
DROP TABLE IF EXISTS users CASCADE;
Add Column with Default Value
-- migrations/002_add_email_verified.up.sql
-- Step 1: Add column as nullable
ALTER TABLE users ADD COLUMN email_verified BOOLEAN;
-- Step 2: Set default value for existing rows
UPDATE users SET email_verified = false WHERE email_verified IS NULL;
-- Step 3: Make column NOT NULL
ALTER TABLE users ALTER COLUMN email_verified SET NOT NULL;
-- Step 4: Set default for future rows
ALTER TABLE users ALTER COLUMN email_verified SET DEFAULT false;
-- migrations/002_add_email_verified.down.sql
ALTER TABLE users DROP COLUMN email_verified;
Modify Column Type (Safe)
-- migrations/003_increase_email_length.up.sql
-- Safe: increasing varchar length
ALTER TABLE users ALTER COLUMN email TYPE VARCHAR(320);
-- migrations/003_increase_email_length.down.sql
-- Warning: May fail if data exceeds old limit
ALTER TABLE users ALTER COLUMN email TYPE VARCHAR(255);
Add Foreign Key
-- migrations/004_create_orders.up.sql
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
total_amount DECIMAL(10,2) NOT NULL CHECK (total_amount >= 0),
status VARCHAR(20) DEFAULT 'pending' NOT NULL,
created_at TIMESTAMP DEFAULT NOW() NOT NULL,
updated_at TIMESTAMP DEFAULT NOW() NOT NULL,
CONSTRAINT fk_orders_user_id
FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE CASCADE
);
-- Indexes for foreign keys and common queries
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created_at ON orders(created_at);
-- Composite index for common query pattern
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
-- migrations/004_create_orders.down.sql
DROP TABLE IF EXISTS orders CASCADE;
Rename Column (Safe)
-- migrations/005_rename_password_column.up.sql
-- Step 1: Add new column
ALTER TABLE users ADD COLUMN password_hash_new VARCHAR(255);
-- Step 2: Copy data
UPDATE users SET password_hash_new = password_hash;
-- Step 3: Make NOT NULL
ALTER TABLE users ALTER COLUMN password_hash_new SET NOT NULL;
-- Step 4: Drop old column
ALTER TABLE users DROP COLUMN password_hash;
-- Step 5: Rename new column
ALTER TABLE users RENAME COLUMN password_hash_new TO password_hash;
-- migrations/005_rename_password_column.down.sql
-- Reversible using same pattern
ALTER TABLE users ADD COLUMN password_hash_old VARCHAR(255);
UPDATE users SET password_hash_old = password_hash;
ALTER TABLE users ALTER COLUMN password_hash_old SET NOT NULL;
ALTER TABLE users DROP COLUMN password_hash;
ALTER TABLE users RENAME COLUMN password_hash_old TO password_hash;
ORM Migration Examples
Prisma Migrations
// schema.prisma - Add new model
model User {
id Int @id @default(autoincrement())
email String @unique
username String @unique
passwordHash String @map("password_hash")
active Boolean @default(true)
emailVerified Boolean @default(false) @map("email_verified")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
orders Order[]
profile UserProfile?
@@index([email])
@@index([username])
@@map("users")
}
model UserProfile {
id Int @id @default(autoincrement())
userId Int @unique @map("user_id")
bio String? @db.Text
avatarUrl String? @map("avatar_url")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("user_profiles")
}
# Generate migration
npx prisma migrate dev --name add_user_profile
# Apply migration in production
npx prisma migrate deploy
# Reset database (development only!)
npx prisma migrate reset
Generated Migration:
-- CreateTable
CREATE TABLE "user_profiles" (
"id" SERIAL NOT NULL,
"user_id" INTEGER NOT NULL,
"bio" TEXT,
"avatar_url" TEXT,
CONSTRAINT "user_profiles_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "user_profiles_user_id_key" ON "user_profiles"("user_id");
-- AddForeignKey
ALTER TABLE "user_profiles" ADD CONSTRAINT "user_profiles_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
TypeORM Migrations
// migration/1234567890123-CreateUser.ts
import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';
export class CreateUser1234567890123 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise {
await queryRunner.createTable(
new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{
name: 'email',
type: 'varchar',
length: '255',
isUnique: true,
isNullable: false,
},
{
name: 'username',
type: 'varchar',
length: '50',
isUnique: true,
isNullable: false,
},
{
name: 'password_hash',
type: 'varchar',
length: '255',
isNullable: false,
},
{
name: 'active',
type: 'boolean',
default: true,
isNullable: false,
},
{
name: 'created_at',
type: 'timestamp',
default: 'now()',
isNullable: false,
},
{
name: 'updated_at',
type: 'timestamp',
default: 'now()',
isNullable: false,
},
],
}),
true,
);
// Create indexes
await queryRunner.createIndex(
'users',
new TableIndex({
name: 'idx_users_email',
columnNames: ['email'],
}),
);
await queryRunner.createIndex(
'users',
new TableIndex({
name: 'idx_users_username',
columnNames: ['username'],
}),
);
}
public async down(queryRunner: QueryRunner): Promise {
await queryRunner.dropTable('users');
}
}
// migration/1234567890124-AddForeignKey.ts
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
export class AddOrdersForeignKey1234567890124 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise {
await queryRunner.createTable(
new Table({
name: 'orders',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{
name: 'user_id',
type: 'int',
isNullable: false,
},
{
name: 'total_amount',
type: 'decimal',
precision: 10,
scale: 2,
isNullable: false,
},
{
name: 'status',
type: 'varchar',
length: '20',
default: "'pending'",
isNullable: false,
},
{
name: 'created_at',
type: 'timestamp',
default: 'now()',
},
],
}),
true,
);
// Add foreign key
await queryRunner.createForeignKey(
'orders',
new TableForeignKey({
columnNames: ['user_id'],
referencedColumnNames: ['id'],
referencedTableName: 'users',
onDelete: 'CASCADE',
}),
);
}
public async down(queryRunner: QueryRunner): Promise {
const table = await queryRunner.getTable('orders');
const foreignKey = table.foreignKeys.find(
fk => fk.columnNames.indexOf('user_id') !== -1,
);
await queryRunner.dropForeignKey('orders', foreignKey);
await queryRunner.dropTable('orders');
}
}
# Generate migration
npx typeorm migration:generate -n AddUserProfile
# Run migrations
npx typeorm migration:run
# Revert last migration
npx typeorm migration:revert
Alembic (Python/SQLAlchemy)
# alembic/versions/001_create_users_table.py
"""create users table
Revision ID: 001
Revises:
Create Date: 2024-01-01 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers
revision = '001'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# Create users table
op.create_table(
'users',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('username', sa.String(length=50), nullable=False),
sa.Column('password_hash', sa.String(length=255), nullable=False),
sa.Column('active', sa.Boolean(), server_default='true', nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('username')
)
# Create indexes
op.create_index('idx_users_email', 'users', ['email'])
op.create_index('idx_users_username', 'users', ['username'])
op.create_index(
'idx_users_active',
'users',
['active'],
postgresql_where=sa.text('active = true')
)
def downgrade():
op.drop_table('users')
# alembic/versions/002_add_email_verified.py
"""add email_verified column
Revision ID: 002
Revises: 001
Create Date: 2024-01-02 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = '002'
down_revision = '001'
branch_labels = None
depends_on = None
def upgrade():
# Add column as nullable first
op.add_column('users', sa.Column('email_verified', sa.Boolean(), nullable=True))
# Set default value for existing rows
op.execute('UPDATE users SET email_verified = false WHERE email_verified IS NULL')
# Make column NOT NULL
op.alter_column('users', 'email_verified', nullable=False, server_default='false')
def downgrade():
op.drop_column('users', 'email_verified')
# Generate migration
alembic revision --autogenerate -m "add user profile"
# Run migrations
alembic upgrade head
# Rollback one migration
alembic downgrade -1
# Rollback to specific version
alembic downgrade 001
Django Migrations
# app/migrations/0001_initial.py
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True)),
('email', models.EmailField(max_length=255, unique=True)),
('username', models.CharField(max_length=50, unique=True)),
('password_hash', models.CharField(max_length=255)),
('active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'users',
},
),
migrations.AddIndex(
model_name='user',
index=models.Index(fields=['email'], name='idx_users_email'),
),
migrations.AddIndex(
model_name='user',
index=models.Index(fields=['username'], name='idx_users_username'),
),
]
# app/migrations/0002_add_user_profile.py
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True)),
('bio', models.TextField(blank=True, null=True)),
('avatar_url', models.URLField(blank=True, null=True)),
('user', models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to='app.user',
related_name='profile'
)),
],
options={
'db_table': 'user_profiles',
},
),
]
# Generate migrations
python manage.py makemigrations
# Apply migrations
python manage.py migrate
# Rollback to specific migration
python manage.py migrate app 0001
# Show migration status
python manage.py showmigrations
Data Migrations
Backfill Data (PostgreSQL)
-- migrations/006_backfill_user_roles.up.sql
-- Add role column
ALTER TABLE users ADD COLUMN role VARCHAR(20);
-- Backfill existing users with default role
UPDATE users SET role = 'member' WHERE role IS NULL;
-- Make NOT NULL after backfill
ALTER TABLE users ALTER COLUMN role SET NOT NULL;
ALTER TABLE users ALTER COLUMN role SET DEFAULT 'member';
-- Add check constraint
ALTER TABLE users ADD CONSTRAINT chk_users_role
CHECK (role IN ('admin', 'member', 'guest'));
-- migrations/006_backfill_user_roles.down.sql
ALTER TABLE users DROP COLUMN role;
Complex Data Migration (Node.js/TypeORM)
// migration/1234567890125-MigrateUserData.ts
import { MigrationInterface, Quer
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [CuriousLearner](https://github.com/CuriousLearner)
- **Source:** [CuriousLearner/devkit](https://github.com/CuriousLearner/devkit)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.