{"id":26656933,"url":"https://github.com/jamsilver/migraine","last_synced_at":"2026-05-13T12:33:43.482Z","repository":{"id":281446677,"uuid":"945291633","full_name":"jamsilver/migraine","owner":"jamsilver","description":"AI-powered Drupal-to-Drupal migrations","archived":false,"fork":false,"pushed_at":"2025-03-24T00:30:26.000Z","size":747,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-04T11:50:00.094Z","etag":null,"topics":["aider","drupal","llm","migrations"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamsilver.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-09T04:35:22.000Z","updated_at":"2025-03-24T00:30:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"9004f5d6-49ef-4fa8-baf0-a501f907e934","html_url":"https://github.com/jamsilver/migraine","commit_stats":null,"previous_names":["jamsilver/migraine"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jamsilver/migraine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsilver%2Fmigraine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsilver%2Fmigraine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsilver%2Fmigraine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsilver%2Fmigraine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamsilver","download_url":"https://codeload.github.com/jamsilver/migraine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsilver%2Fmigraine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32982786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T11:31:52.688Z","status":"ssl_error","status_checked_at":"2026-05-13T11:31:52.072Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aider","drupal","llm","migrations"],"created_at":"2025-03-25T08:16:00.391Z","updated_at":"2026-05-13T12:33:43.449Z","avatar_url":"https://github.com/jamsilver.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Drupal migrAIne\n\n![migrAIne logo](migraine.jpeg \"migrAIne logo\")\n\n**Use AI to support the process of developing Drupal → Drupal migrations.**\n\nA small suite of commands that reduce the developer burden involved in information gathering and migration yml\ncreation. LLMs are used where they make sense and good old-fashioned helper scripts are used where they don\u0026apos;t.\n\nThese are rough-and-ready scripts that I made to test if AI could add value for writing migrations. There's lots that needs to be improved!\n\n\n## TL;DR\n\nInstall in your destination Drupal project folder and then:\n\n    # Gather entity type/field structure data about your source and destination sites.\n    mise run mig:register source /path/to/d7\n    mise run mig:register dest .\n\n    # Make JSON list of all needed migrations.\n    mise run mig:llm:guess-migrations\n\n      # Hand-fix/tweak .migraine/migrations.json.\n\n    # Generate an AI prompt file for each migration.\n    mise run mig:prompt \\*\n\n    # Use aider to generate a first-draft migration yml file.\n    mise run mig:aider:migrate node_article\n\n      # Hand-fix/tweak config/sync/migrate_plus.migration.\u003cMIGRATION_ID\u003e.yml.\n\n    # Pass an existing yml to aider for improvement.\n    mise run mig:aider:migrate node_article config/sync/migrate_plus.migration.node_article.yml\n\n    # Generate mysql queries that list all entity field values in source and destination.\n    mise run mig:sql source node article | pbcopy\n    mise run mig:sql dest node article | pbcopy\n\n    # Update the destination site inventory following some field schema changes.\n    mise run mig:inventory dest\n\n    # Dump markdown files for every entity type in source into ~/.migraine/document\n    mise run mig:document source\n\n\n### Drupal 6/7 upgrades\n\nYou can use Drupal core + [Migrate Upgrade](https://www.drupal.org/project/migrate_upgrade) to generate the first\nversion of all migrations and then pass it to aider for improvement:\n\n    # Get Drupal to generate the first version of your migrations.\n    ddev drush migrate:upgrade --configure-only --legacy-db-key=migrate\n\n    # Get migraine to guess its migrations.json from the generated migrations.\n    mise run mig:set-migrations --group=\"drupal_7\" --tag=\"Content\"\n\n    # Migraine's attempt to guess migrations.json values from the config sucks, improve with AI.\n    mise run mig:llm:guess-migrations --improve\n\n      # Hand-fix/tweak .migraine/migrations.json.\n\n    # Re-generate prompts from the new migrations.json.\n    mise run mig:prompt \\* --force\n\n    # Export the generated migrations and use aider to improve one.\n    ddev drush config:export -y\n    mise run mig:aider:migrate upgrade_d7_node_article config/sync/migrate_plus.migration.upgrade_d7_node_article.yml\n\n\n## More task tips\n\nFor more details about any task, run it with the `--help` flag, e.g.:\n\n    mise run mig:inventory --help\n\nMost commands can be accessed via their initialism, and the same for \"source\" and \"dest\", e.g.:\n\n    # Take inventory of the source\n    mise run mig:i s\n\n    # Set migration from config\n    mise run mig:sm\n\n\n## Installation/requirements\n\n - [Install mise](https://mise.jdx.dev/getting-started.html),\n - Install PHP version \u003e= 8.2,\n - Install and configure [llm](https://github.com/simonw/llm) to unlock `migraine:llm:*` tasks,\n - Install and configure [aider](https://github.com/Aider-AI/aider) to unlock `migraine:aider:*` tasks. Recommend `--architect` mode,\n - Download the contents of this repo's `mise-tasks` folder a Drupal project folder at `.mise/tasks`,\n   - To make this globally available, copy it to `$HOME/.config/mise/tasks` (see [mise task docs](https://mise.jdx.dev/tasks/) for more information),\n - Each command-file must be made executable. Execute the `install` command to do this automatically:\n\n       chmod u+x .mise/tasks/migraine/install \u0026\u0026 mise run migraine:install\n\n(If you installed it somewhere else, modify the above command to use the right path).\n\nThis tool _invokes_ `llm` and `aider` but does not configure them. It is up to you to register with your preferred AI \nprovider, generate API keys, and configure `llm` and `aider`the way you like it. If you like, you can look at \n[example aider configuration below](#example-aider-configuration). This closely resembles my own set-up, so should work\nwell with migraine.\n\n\n## The Set Up\n\nIt is assumed you have a working installation of both Drupal sites locally on your machine. The sites should be \"upped\"\nand functional. You must be able to run Drush commands that require a full bootstrap and database connection.\n\nThese tasks create a `.migraine/` folder in your working directory to store/retrieve (e.g.) site inventory and \nprompt information. As such, you must remain in the same working directory when running migraine tasks. Most likely this \nwill be your Drupal 10 project root.\n\nYou may choose to exclude the `.migraine/` folder via a line in your `.gitignore` file. Or, you may choose to commit it \nto your repo to collaborate with others on migration planning and AI prompt documents.\n\n\n## The Tasks\n\nThe examples here assume your working directory is a [ddev](https://github.com/ddev/ddev) project root of your Drupal 10 site.\n\n\n### 1. Register/unregister sites.\n\nTells migraine how to find your source and/or destination sites:\n\n    mise run migraine:register source /path/to/site\n    mise run migraine:register dest ../path/to/another/site\n\nKey facts about your sites are stored in `.migraine/migraine.yml`. Migraine does\nsome basic validation of the Drupal root and detects the version of Drupal.\n\nUn-register a site by passing a `--delete` or `-D` flag:\n\n    mise run migraine:register source /path/to/site --delete\n\nBy default an immediate inventory is taken on a newly registered site. To skip\nthis for some reason, pass the `--no-inventory` options:\n\n    mise run migraine:register s /path/to/site --no-inventory\n\n\n### 2. List all registered sites.\n\nPrints a table of all registered sites, their Drupal version, and status:\n\n    mise run migraine:status\n\nThis command also checks drush can be executed against the site, and verifies\ndrush can connect to the database.\n\n\n### 3. Take an inventory of your site\n\nDiscovers entity type and field information about your source site:\n\n    mise run migraine:inventory source\n\nDiscovers entity type and field information about your destination site:\n\n    mise run migraine:inventory dest\n\nThis information is stored in JSON files inside `.migraine/inventory`. It is\nused by other commands to use to, e.g., generate prompts.\n\nAn inventory is taken automatically when a site is first registered.\n\n#### Non-ddev drush support\n\nThis task needs to invoke drush in the context of the given site. It does this\nby `cd`-ing to the Drupal webroot and exec-ing `ddev drush`. If you don't use\nddev, this will not work. You must override the drush command-string with one\nthat works via the `--drush` command-line option. For example:\n\n    mise run migraine:inventory source --drush \"php vendor/bin/drush\"\n\n\n### 4. Work out what migrations you need\n\nThe idea is to have a file `.migraine/migrations.json` that lists all needed migrations.\n\nRun this task to ask AI to look at your site inventories and generate this file for you:\n\n    mise run migraine:llm:guess-migrations\n\nYou can change what model is used by passing an `-m`/`--model` option. Run `llm models` to see possible values.\n\nIt's possible invalid JSON will be generated, and of course only you know what the right answer is, so check/tweak/fix\nthis file by hand before moving on to the next step.\n\n\n### 5. Generate an AI prompt file for each migration\n\nThis task iterates over each migration in `.migraine/migrations.json` and generates a suitable markdown file:\n\n    mise run migraine:prompt \\*\n\nYou can also generate one at a time:\n\n    mise run migraine:prompt node_article\n\nThe first time this runs, the template used to generate them is placed in `.migraine/templates/prompts/migration-prompt.md`.\nYou are free to update this and re-run.\n\nThese prompt files are only a starting point. \n\nComplete the ## Field mapping section to describe how source files are mapped to destination files in whatever format\nmakes sense to you and is clear and non-ambiguous for LLMs to understand.\n\n\n### 6. Improve the prompt document\n\nThis task passes an existing prompt document to an LLM to flesh-out the ## Field mapping section:\n\n    mise run migraine:llm:improve-mapping node_article\n\nIt doesn't update the file, it just outputs an updated Field Mapping table to stdout. You can use it to\nimprove the document, or not. If there are no mappings specified, the AI will try to guess what the\nbasic mappings should be.\n\n\n### 7. Generate the yml file for a migration\n\nThis task passes your carefully-crafted prompt file to `aider` to generate a migration yml. For example:\n\n    mise run migraine:aider:migrate node_article\n\nFor good results it's vital you put high-quality example migrations that exhibit the patterns you wish the AI to use in \n`.migraine/templates/migrations`.\n\n\n### 8. Generate markdown files that document all entity types\n\nThis task generates a markdown file in ~/.migraine/document for each entity type. This can be very useful to\nbrief yourself quickly on a website:\n\n    msie run migraine:document source\n\n\n## Example aider configuration\n\nAt the time of writing I've experienced good results with openai's `o3*` or `o1` models for planning/reasoning, and anthropic's\nclaude `sonnet` for code generation/editing.\n\nMy personal `~/.aider.conf.yml` file looks something like this:\n\n    model: openai/o1\n    openai-api-key: ...\n    anthropic-api-key: ...\n    architect: true\n    editor-model: sonnet\n    map-tokens: 0          # Never want to accidentally send codebase details up to the cloud,\n    dark-mode: true\n    auto-commits: false    # I want to control my own commits thank you,\n    analytics-disable: true\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamsilver%2Fmigraine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamsilver%2Fmigraine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamsilver%2Fmigraine/lists"}