create-migration

star 1

Scaffold a new Clack migration file. Use when you need to create a migration for config changes, data format updates, or other upgrade tasks.

francoislg By francoislg schedule Updated 3/27/2026

name: create-migration description: Scaffold a new Clack migration file. Use when you need to create a migration for config changes, data format updates, or other upgrade tasks.

Create a new migration for Clack's boot migration system.

Input: The argument after /create-migration describes what the migration should do (e.g., "rename config field X to Y", "add new required config field").

Steps

  1. If no input provided, ask what the migration should do

    Use the AskUserQuestion tool to ask:

    "What should this migration do? Describe the change needed."

  2. Read existing migrations to determine next version

    Read src/migrations/index.ts to see which migrations are registered. If migrations exist, read them to find the highest version number. The new migration version = highest existing version + 1, or 1 if none exist.

  3. Determine priority

    Use the AskUserQuestion tool to ask:

    "Is this migration blocking or enhancement?"

    Options:

    • Blocking: Must complete before Clack starts (e.g., breaking config changes)
    • Enhancement: Can run in background after boot (e.g., optional improvements)
  4. Determine file scope

    Based on what the migration does, identify which files it needs read/write access to. Common files:

    • data/config.json — for config schema changes
    • data/state/roles.json — for role data changes
    • data/state/user-preferences.json — for user preference changes
  5. Determine migration type: static vs Claude

    Decide based on the nature of the change:

    • Static (use static function): For deterministic JSON transforms — adding fields, removing fields, renaming keys, mapping values. These run as TypeScript functions without invoking Claude. Use static when the transformation has no ambiguity and doesn't touch markdown/text files that may have user customizations.
    • Claude (use prompt): For markdown/text file changes that require merging content into user-customized overrides, or complex transformations where AI judgment helps.
    • Mixed (both static and prompt): When you need deterministic JSON changes AND Claude-powered text changes. The static transform runs first, then Claude sees the updated files.

    Default to static for JSON-only config changes. These are faster and more reliable.

  6. Create the migration file

    Create src/migrations/NNN-<kebab-name>.ts where NNN is the zero-padded version.

    For static migrations (JSON-only changes):

    import type { Migration } from "./types.js";
    
    export const migration: Migration = {
      version: <next-version>,
      name: "<human-readable name>",
      priority: "<blocking|enhancement>",
      files: [<list of files>],
      static: (files) => {
        const result: Record<string, string> = {};
        for (const [path, content] of Object.entries(files)) {
          if (!path.endsWith("<target-file>") || !content) continue;
          const data = JSON.parse(content);
          // ... transform data ...
          result[path] = JSON.stringify(data, null, 2) + "\n";
        }
        return result;
      },
    };
    

    Static function rules:

    • Identify files by path suffix (e.g., path.endsWith("config.json")) — not by exact path
    • Return only files that were actually modified
    • Handle "already migrated" by checking if the change is needed and returning early
    • Use { delete: true } as the value to delete a file

    For Claude migrations (markdown/text changes):

    import type { Migration } from "./types.js";
    
    export const migration: Migration = {
      version: <next-version>,
      name: "<human-readable name>",
      priority: "<blocking|enhancement>",
      prompt: `<detailed prompt for Claude to execute the migration>`,
      files: [<list of files>],
    };
    

    The prompt should be specific and actionable:

    • Describe exactly what to change
    • Include the expected before/after state
    • Handle edge cases (field doesn't exist, already migrated, etc.)
  7. Register in barrel export

    Update src/migrations/index.ts to import and include the new migration:

    import { migration as mN } from "./NNN-<name>.js";
    // Add to the migrations array
    
  8. Create test file

    Create scripts/migration-tests/NNN.ts with test cases for the new migration:

    import type { MigrationTest } from "./types.js";
    
    export const test: MigrationTest = {
      version: <version>,
      cases: [
        {
          name: "<describe what this case tests>",
          input: { /* config in the OLD format (before migration) */ },
          validate: (output) => {
            // Return null if passed, error string if failed
            // Check that the migration transformed the config correctly
            return null;
          },
        },
        // Always include an "already migrated / no-op" case
      ],
    };
    

    Test case guidelines:

    • Cover each transformation the migration performs (one case per variant)
    • Include an "already migrated" case that verifies no-op behavior
    • Include a mixed case if the migration handles multiple items (e.g., repos array)
    • Validation should check both positive (new fields exist) and negative (old fields removed)
  9. Register test in runner

    Update scripts/migration-tests/run.ts:

    • Add import: import { test as testNNN } from "./NNN.js";
    • Add to allTests array: const allTests: MigrationTest[] = [..., testNNN];
  10. Update the full-path test

In scripts/migration-tests/run.ts:

  • Update VERSION_0_CONFIG if needed (it should represent the oldest supported format)
  • Update validateFinalState() to verify the output after ALL migrations including the new one
  1. Run the tests

    npx tsx scripts/migration-tests/run.ts
    

    Verify all individual tests and the full-path test pass.

  2. Show summary

    Display:

    • Migration file path
    • Test file path
    • Version number
    • Priority
    • Type (static, Claude, or mixed)
    • File scope
    • Number of test cases

Guardrails

  • Migrations MUST be idempotent — running twice should be safe
  • The prompt should handle "already migrated" cases gracefully
  • Version numbers must be sequential with no gaps
  • File scope should be minimal — only list files the migration actually needs
Install via CLI
npx skills add https://github.com/francoislg/clack --skill create-migration
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator