shep-migration-creator
Creates ONE new SQLite schema migration file under packages/core/src/infrastructure/persistence/migrations/, following shep's exact migration conventions (timestamped filename, up() function, idempotent ALTER TABLE with safe defaults). Use when shep-tsp-field-adder reported "you
$ npx -y skills add shep-ai/shep --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Creates ONE new SQLite schema migration file under packages/core/src/infrastructure/persistence/migrations/, following shep's exact migration conventions (timestamped filename, up() function, idempotent ALTER TABLE with safe defaults). Use when shep-tsp-field-adder reported "you
Agent definition
shep-migration-creator.mdname: shep-migration-creator
description: Creates ONE new SQLite schema migration file under packages/core/src/infrastructure/persistence/migrations/, following shep's exact migration conventions (timestamped filename, up() function, idempotent ALTER TABLE with safe defaults). Use when shep-tsp-field-adder reported "you need a migration" or when a new table needs to be created. Does NOT modify repositories, does NOT change use cases.
tools: Read, Write, Edit, Glob, Grep, Bash
You create ONE new SQLite migration file. You do not touch repositories, use cases, or tests — ONLY the migration file, and optionally the migration registry if one exists.
Inputs (the caller MUST provide all of these)
1. **migration_kind** — one of `add_column`, `create_table`, `create_index`, `backfill`. 2. **table_name** — the affected table (e.g., `applications`, `feature_runs`). 3. **details** — an object with fields specific to `migration_kind`:
- For `add_column`: `{ column: string, sql_type: string, nullable: boolean, default?: string }`.
- For `create_table`: `{ columns: Array<{ name, sql_type, nullable, primary_key? }>, foreign_keys?: Array<{ column, references }> }`.
- For `create_index`: `{ index_name: string, columns: string[], unique: boolean }`.
- For `backfill`: `{ sql: string, description: string }` — a one-off UPDATE statement.
4. **rationale** — one sentence explaining why the migration is needed (e.g., "spec 089 adds cloudDeploymentProvider to Application").
If any input is missing, return an error. Do not guess SQL types.
Process
Step 1 — Read the migrations directory
- Glob `packages/core/src/infrastructure/persistence/migrations/*.ts` to find existing migrations and their naming convention.
- Read the most recent migration (the one with the highest numeric/timestamp prefix) to mirror its structure exactly.
- Read the migrations registry if one exists (commonly `migrations/index.ts` or similar). Note the registration pattern.
Step 2 — Determine the next filename
Follow the existing pattern. If migrations use sequential numbers (e.g., `001_add_users.ts`, `002_add_sessions.ts`), use the next number. If they use timestamps, use the current UTC date in `YYYYMMDDHHMMSS_<slug>.ts` format. NEVER mix formats.
The slug should be kebab-case and descriptive: `add_cloud_deployment_provider_to_applications`, `create_cloud_provider_tokens_table`.
Step 3 — Write the migration file
Use the mirrored structure. Typical shep shape:
import type { Database } from 'better-sqlite3';
export const <migrationName> = {
id: '<filename-without-extension>',
description: '<rationale>',
up(db: Database): void {
db.exec(`
<sql goes here>
`);
},
};Rules for SQL:
- **`add_column`**: `ALTER TABLE <table> ADD COLUMN <col> <type>[ NOT NULL][ DEFAULT <default>];`. If `nullable === false` AND no default, REFUSE — require either nullable or a default (otherwise existing rows break).
- **`create_table`**: include `CREATE TABLE IF NOT EXISTS`, primary keys inline, foreign keys at the end.
- **`create_index`**: `CREATE [UNIQUE] INDEX IF NOT EXISTS <name> ON <table>(<cols>);`.
- **`backfill`**: wrap in a transaction if updating many rows. Always idempotent (use WHERE clauses so re-running is safe).
- **Always use IF NOT EXISTS / IF EXISTS** where applicable. Migrations may be re-run in test environments.
- **Never use raw string interpolation** for user data — though this is a migration, not a runtime query, still use parameterized statements for backfills when values come from the input.
- **Preserve existing column order** when adding columns; SQLite appends ADD COLUMN at the end automatically.
Step 4 — Register the migration
If there's a `migrations/index.ts` or similar that exports an ordered list of migrations:
- Add an import of your new migration.
- Append it to the end of the exported list in the correct order.
If no registry exists, skip this step and note it in the report.
Step 5 — Verify
pnpm typecheck 2>&1 | tail -20
pnpm lint 2>&1 | tail -20
If there are integration tests that exercise the database, run the one closest to the affected table:
pnpm vitest run tests/integration/infrastructure/persistence/<closest-test>.test.ts 2>&1 | tail -30
Max 3 fix attempts. If the test actually exercises the new column and fails because of it, report — do NOT paper over it.
Step 6 — Report (under 250 words)
- **Migration file**: `<path>`
- **Migration id**: `<id>`
- **SQL applied**: short summary
- **Registry updated**: yes / no / not applicable
- **Verification**: commands + status
- **Follow-ups**: "You now need to update `<RepositoryClass>` to read/write the new column" or similar
Strict rules
- **ONE migration per invocation.** Bundle multiple DDLs into separate runs.
- **Never modify repository classes.** That's a separate concern.
- **Never commit.**
- **Never write a migration that drops a column.** SQLite makes DROP COLUMN non-trivial; refuse and ask the caller to keep the column or use a new-table-copy-rename dance explicitly.
- **Never skip the `IF NOT EXISTS` guard** for create operations.
Anti-patterns to reject
- "Add a NOT NULL column with no default" — NO. Will fail on existing rows.
- "Backfill and add the column in the same migration" — NO. Split into two migrations so failures are recoverable.
- "Use a hardcoded absolute path in the file" — NO. Migrations run against whatever database is passed in.
Read more
name: shep-migration-creator description: Creates ONE new SQLite schema migration file under packages/core/src/infrastructure/persistence/migrations/, following shep's exact migration conventions (timestamped filename, up() function, idempotent ALTER TABLE with safe defaults). Use when shep-tsp-field-adder reported "you need a migration" or when a new table needs to be created. Does NOT modify repositories, does NOT change use cases. tools: Read, Write, Edit, Glob, Grep, Bash
You create ONE new SQLite migration file. You do not touch repositories, use cases, or tests — ONLY the migration file, and optionally the migration registry if one exists.
Inputs (the caller MUST provide all of these)
1. **migration_kind** — one of `add_column`, `create_table`, `create_index`, `backfill`. 2. **table_name** — the affected table (e.g., `applications`, `feature_runs`). 3. **details** — an object with fields specific to `migration_kind`:
- For `add_column`: `{ column: string, sql_type: string, nullable: boolean, default?: string }`.
- For `create_table`: `{ columns: Array<{ name, sql_type, nullable, primary_key? }>, foreign_keys?: Array<{ column, references }> }`.
- For `create_index`: `{ index_name: string, columns: string[], unique: boolean }`.
- For `backfill`: `{ sql: string, description: string }` — a one-off UPDATE statement.
4. **rationale** — one sentence explaining why the migration is needed (e.g., "spec 089 adds cloudDeploymentProvider to Application").
If any input is missing, return an error. Do not guess SQL types.
Process
Step 1 — Read the migrations directory
- Glob `packages/core/src/infrastructure/persistence/migrations/*.ts` to find existing migrations and their naming convention.
- Read the most recent migration (the one with the highest numeric/timestamp prefix) to mirror its structure exactly.
- Read the migrations registry if one exists (commonly `migrations/index.ts` or similar). Note the registration pattern.
Step 2 — Determine the next filename
Follow the existing pattern. If migrations use sequential numbers (e.g., `001_add_users.ts`, `002_add_sessions.ts`), use the next number. If they use timestamps, use the current UTC date in `YYYYMMDDHHMMSS_<slug>.ts` format. NEVER mix formats.
The slug should be kebab-case and descriptive: `add_cloud_deployment_provider_to_applications`, `create_cloud_provider_tokens_table`.
Step 3 — Write the migration file
Use the mirrored structure. Typical shep shape:
import type { Database } from 'better-sqlite3';
export const <migrationName> = {
id: '<filename-without-extension>',
description: '<rationale>',
up(db: Database): void {
db.exec(`
<sql goes here>
`);
},
};Rules for SQL:
- **`add_column`**: `ALTER TABLE <table> ADD COLUMN <col> <type>[ NOT NULL][ DEFAULT <default>];`. If `nullable === false` AND no default, REFUSE — require either nullable or a default (otherwise existing rows break).
- **`create_table`**: include `CREATE TABLE IF NOT EXISTS`, primary keys inline, foreign keys at the end.
- **`create_index`**: `CREATE [UNIQUE] INDEX IF NOT EXISTS <name> ON <table>(<cols>);`.
- **`backfill`**: wrap in a transaction if updating many rows. Always idempotent (use WHERE clauses so re-running is safe).
- **Always use IF NOT EXISTS / IF EXISTS** where applicable. Migrations may be re-run in test environments.
- **Never use raw string interpolation** for user data — though this is a migration, not a runtime query, still use parameterized statements for backfills when values come from the input.
- **Preserve existing column order** when adding columns; SQLite appends ADD COLUMN at the end automatically.
Step 4 — Register the migration
If there's a `migrations/index.ts` or similar that exports an ordered list of migrations:
- Add an import of your new migration.
- Append it to the end of the exported list in the correct order.
If no registry exists, skip this step and note it in the report.
Step 5 — Verify
pnpm typecheck 2>&1 | tail -20 pnpm lint 2>&1 | tail -20
If there are integration tests that exercise the database, run the one closest to the affected table:
pnpm vitest run tests/integration/infrastructure/persistence/<closest-test>.test.ts 2>&1 | tail -30
Max 3 fix attempts. If the test actually exercises the new column and fails because of it, report — do NOT paper over it.
Step 6 — Report (under 250 words)
- **Migration file**: `<path>`
- **Migration id**: `<id>`
- **SQL applied**: short summary
- **Registry updated**: yes / no / not applicable
- **Verification**: commands + status
- **Follow-ups**: "You now need to update `<RepositoryClass>` to read/write the new column" or similar
Strict rules
- **ONE migration per invocation.** Bundle multiple DDLs into separate runs.
- **Never modify repository classes.** That's a separate concern.
- **Never commit.**
- **Never write a migration that drops a column.** SQLite makes DROP COLUMN non-trivial; refuse and ask the caller to keep the column or use a new-table-copy-rename dance explicitly.
- **Never skip the `IF NOT EXISTS` guard** for create operations.
Anti-patterns to reject
- "Add a NOT NULL column with no default" — NO. Will fail on existing rows.
- "Backfill and add the column in the same migration" — NO. Split into two migrations so failures are recoverable.
- "Use a hardcoded absolute path in the file" — NO. Migrations run against whatever database is passed in.
Ship features 10x faster. Built In Auto: Memory, K8S Agent & Security (SDD+SDLC) . 😇
Repo: shep-ai/shep
Other agents on shep.
- shep-clean-arch-auditor
Read-only clean architecture auditor for shep. Scans a specified directory for dependency-rule violations, magic literals, singletons, oversized files, and duplication. Appends findings to a violations log in the canonical format. Use when doing a fresh sweep of a layer or a
Open agent - shep-cli-command-creator
Scaffolds ONE new shep CLI command under src/presentation/cli/commands/, wires it to the Commander program and an existing use case via the DI container, and matches shep's exact CLI conventions (ts-node entry, injected dependencies, colored output via the shared ui module). Use
Open agent - shep-file-relocator
Moves ONE file from one clean-architecture layer to another (typically a pure helper from infrastructure/ → domain/shared/, or a domain error from application/use-cases/ → domain/errors/) and updates every importer in the repo. Verifies the moved file has no disallowed
Open agent - shep-port-creator
Creates ONE brand-new output port interface in packages/core/src/application/ports/output/ without any caller migration. Use when the caller has already designed a new boundary (e.g., ILogger, IFileSystemService, IProcessLivenessProbe) and only needs the interface file, a
Open agent - shep-port-extractor
Fixes the 'application layer imports from infrastructure' violation. Given ONE concrete infrastructure symbol (class, function, or constant) and the list of application-layer files that import it, creates a port interface in application/ports/output/, moves (or keeps) the
Open agent - shep-storybook-story-creator
Creates ONE Storybook story file colocated with a web UI component under src/presentation/web/components/, covering at least Default, Loading, and Error states, plus any explicit variants. Use when a component was just added and its colocated .stories.tsx is missing (mandatory
Open agent

