Skip to content
Blume
Esc
↑↓navigate↵open⌘Jpreview
Guides

Migrate

Migrate your docs from a GitHub wiki

Hand your GitHub wiki to a coding agent, convert its wiki links, alerts, and images with a codemod, rebuild _Sidebar.md as folders, and prepare a link stub for every old wiki page, ready for you to push.

By 10 min read

By the end of this guide, your GitHub wiki is a Blume site: every page at a clean URL, your _Sidebar.md rebuilt as the sidebar, wiki links and GitHub alerts converted, the wiki's images moved beside the pages, and a link stub ready for every old wiki page. A coding agent does the conversion with a codemod that ships with Blume, and you review it.

It's written for wikis in Markdown. Pages in the other formats GitHub renders, like MediaWiki and AsciiDoc, are converted to Markdown with pandoc first.

What carries over

The run behind this guide migrated a real wiki: 77 pages, a sidebar with six groups, pages in subfolders, images stored in the wiki, and a sync with a mirror repository. Every content page carried over, and every old URL is accounted for: a new page, the page a moved notice pointed to, or a page left out on purpose and reported. Here's how the pieces map:

In the wikiIn Blume
Home.mddocs/index.md
Every other page, in any folderA file in docs/ at a flat, lowercase URL: /wiki/Getting-Started becomes /getting-started
The page name above each pageThe page's title
_Sidebar.md groups(group)/ folders with a meta.ts, which add a sidebar group but no URL segment
Pages the sidebar doesn't listA collapsed "More pages" group
External links in the sidebarFeatured links above the sidebar
[[Page Name]] and [[text|Page Name]][text](/page-name)
Links to github.com/<owner>/<repo>/wiki/…Links to the new page
> [!NOTE] and the other alerts:::note callouts, in .mdx pages
Images stored in the wikiFiles beside the pages, optimized by Blume
_Footer.mdFooter links, or dropped
Old wiki URLsA route table, and a link stub for each page to push to the wiki

Here's one page before and after:

# Getting started

Install the CLI, then read [[Configuration]] or [[the FAQ|FAQ#install-errors]].

> [!WARNING]
> Widget 1.x configs don't load in 2.0.

![Dashboard](https://raw.githubusercontent.com/wiki/acme/widget/images/dashboard.png)

# Next steps

See [Plugins](https://github.com/acme/widget/wiki/Plugins).
---
title: Getting Started
---

Install the CLI, then read [Configuration](/configuration) or [the FAQ](/faq#install-errors).

:::warning
Widget 1.x configs don't load in 2.0.
:::

![Dashboard](../images/dashboard.png)

## Next steps

See [Plugins](/plugins).

GitHub showed the page name above every page and each # heading below it, so a body H1 is a section. The H1 that repeated the page name is gone, and the others moved down a level. Most heading anchors keep their ids, since GitHub and Blume build them the same way.

The sidebar becomes folders:

**Start**
* [[Getting Started]]
* [[Configuration]]

**Reference**
* [[FAQ]]
* [[Plugins]]
* [Changelog](https://github.com/acme/widget/releases)
docs/
├── index.md                 ← Home.md
├── meta.ts                  start, reference, more
├── (start)/
│   ├── meta.ts
│   ├── getting-started.mdx
│   └── configuration.md
├── (reference)/
│   ├── meta.ts
│   ├── faq.md
│   └── plugins.md
├── (more)/
│   ├── meta.ts              "More pages", collapsed
│   └── release-process.md
└── images/
    └── dashboard.png
import { defineMeta } from "blume";

export default defineMeta({
  title: "Start",
  pages: [
    "getting-started",
    "configuration",
  ],
});

Before you start

A wiki repository can't host the new site: GitHub runs no Actions in it and serves no Pages from it. Decide where the docs will live, usually a folder in your main repository or a repository of their own. The agent converts a copy of the wiki clone in place, and you move it there afterwards. Clone the wiki, copy it, and remove the copy's remote, so nothing in it can be pushed to the wiki by mistake:

git clone https://github.com/acme/widget.wiki.git
cp -R widget.wiki widget-docs
cd widget-docs
git remote remove origin

GitHub publishes only the wiki's default branch, which is what the clone checks out. Then save the list of pages GitHub serves, from the wiki's own page index:

curl -s https://github.com/acme/widget/wiki/_pages \
  | grep -oE 'href="/acme/widget/wiki(/[^"]*)?"' \
  | sed -e 's/^href="//' -e 's/"$//' -e "s/&#39;/'/g" \
  | grep -vE '/wiki/_(new|pages|history|compare)$' | sort -u > ../old-pages.txt

Use it at the end to confirm that every page is accounted for. While you're in the repository settings, turn on Restrict editing to collaborators only under Features, so the wiki stops changing while you migrate it. You also need Node.js 22.19 or later, and Claude Code or Codex installed and signed in.

Run the migration

From the copy, run:

npx blume migrate github-wiki --claude

To use Codex, swap --claude for --codex. Name the source: a wiki has no config file, so Blume can't detect one.

blume migrate converts nothing itself. It opens the agent on the blume-migrate skill inside the package, pointed at its GitHub wiki reference. Following it, the agent:

  1. Asks where the docs will live, and converts the copy in place if you don't say.
  2. Runs the codemod's routes step, which lists the pages the way GitHub serves them and writes the route table, wiki-routes.json. It also flags the pages that only point elsewhere and the ones that aren't content.
  3. Classifies every page in the table: a page to keep, a moved notice mapped to its target, or a page to leave out (a mirror repository's README, a test page) set to null.
  4. Runs the codemod's convert step, which moves each page into place, writes its title, converts wiki links, alerts, headings, and images, and writes every meta.ts from _Sidebar.md.
  5. Works through what the codemod reports: links that were already broken on GitHub, uploaded images to download, MDX errors, image sizes, and the files the conversion left behind.
  6. Writes blume.config.ts, scaffolds package.json, and runs blume build, blume validate --strict, and the codemod's check until they pass.
  7. Prepares the link stubs for the old wiki and lists the links to it in your main repository.

Here's the routes step on the Acme wiki, and the table after classifying its one moved notice:

7 page(s)

Looks like a moved notice: set its entry to the target
  "Old-Setup": "/getting-started"
{
  "Configuration": "/configuration",
  "FAQ": "/faq",
  "Getting-Started": "/getting-started",
  "Home": "/",
  "Old-Setup": "/getting-started",
  "Plugins": "/plugins",
  "Release-Process": "/release-process"
}

The agent ends with a summary of what it migrated, dropped, and approximated. Keep it for the review.

What happens to the old wiki

Your old wiki URLs stay on github.com, and nothing can redirect them. GitHub has no redirect setting for wiki pages, and it strips scripts and meta refreshes from them. Blume's redirects can't help either, since they only match paths on your new site. Links in issues, pull requests, bookmarks, and search results keep pointing at the wiki. Here's what you can do instead, in this order.

  1. Restrict editing to collaborators, if you haven't, so nobody edits pages that are about to move.
  2. Stop anything that syncs the wiki. Look for a workflow in your main repository that runs on: gollum (wiki edits), a mirror repository whose workflow pushes to the wiki, and a copy of that workflow inside the wiki itself. Left running, they revert your stubs or copy them into the mirror. The agent lists every one it finds.
  3. Replace each page with a link stub once the new site is live. Each page keeps its name, so every old link still opens, now on a one-line page that links to the new one. The agent only prepares the stubs: it never commits or pushes to the wiki, so whether and when they go live is your decision. When you're ready, in a fresh clone of the wiki:
git clone https://github.com/acme/widget.wiki.git widget-stubs
cd widget-stubs
cp ../widget-docs/wiki-routes.json ../widget-docs/wiki-files.json .
node ../widget-docs/node_modules/blume/skills/blume-migrate/scripts/github-wiki-codemod.mjs \
  stubs --site https://docs.acme.dev --write
rm wiki-routes.json wiki-files.json
git add -A
git commit -m "Point every page at the new docs"
git push
**This page has moved to <https://docs.acme.dev/getting-started>.**

The sidebar becomes a notice that the docs have moved, the footer goes, and images stay for sites that link them. Pages you left out keep their content, and the command lists them so you can decide. If you'd rather keep the old pages readable, add --notice-only: it only puts the notice at the top of Home and the sidebar.

  1. Update the links in your repository. Search your main repository for the wiki's URL: git grep -n -i 'github.com/acme/widget/wiki'. Issue templates, the README, CONTRIBUTING, and code comments often link it. The codemod's check prints each URL's new address and checks its anchor against your build, including anchors that were already broken on the wiki.
node node_modules/blume/skills/blume-migrate/scripts/github-wiki-codemod.mjs check \
  'https://github.com/acme/widget/wiki/FAQ#install-errors' \
  'https://github.com/acme/widget/wiki/Getting-Started#setup' \
  'https://github.com/acme/widget/wiki/Old-Setup'
ok  https://github.com/acme/widget/wiki/FAQ#install-errors → /faq#install-errors
MISSING ANCHOR  https://github.com/acme/widget/wiki/Getting-Started#setup → /getting-started#setup
ok  https://github.com/acme/widget/wiki/Old-Setup → /getting-started

1 problem(s).

For a missing anchor it suggests the ids on that page with the same words, when there are some. Point the link at a real heading, or drop the anchor. Also set the repository's About website to the new site if it pointed at the wiki.

  1. Disable the wiki under Features in the repository settings, if you want, once your own links point at the new site. GitHub hides every page, stubs included, without deleting them, and turning the wiki back on restores them.

To see whether search engines show your wiki, run curl -sI https://github.com/acme/widget/wiki | grep -i x-robots-tag. If it prints x-robots-tag: none, they don't. If it prints nothing, search results keep sending readers to the old pages for a while, and the stubs are what they find.

Review the changes

In the copy, run git add -A, then start with git diff --cached --stat -M, which pairs each page's old file with its new one. Run npm run dev, and work through these, which are where a wiki migration most often needs a second look.

  • The route table. Check each moved notice's target and each page set to null. A page that only links somewhere else without saying it moved is content, so the agent keeps it.
  • Navigation. Click through the sidebar against the old one. A page listed twice keeps its first place. A page with pages nested under it in the sidebar is now a collapsible group, listed as its own first row. The "More pages" group holds everything the sidebar didn't list, sorted by title, so move pages out of it where they belong.
  • Links that were already broken. The codemod leaves a link to a page the wiki didn't have as it was and reports it. These often point at pages that moved to another repository, so retarget them or remove the link.
  • Images. Images uploaded through GitHub's editor are hosted outside the wiki. The agent downloads them next to the pages unless you object; for a private repository it has to, since they need a GitHub login. Widths and heights from raw <img> tags are gone, and an image that pointed at nothing on GitHub is dropped.
  • Leftover files. Converting in place leaves the moved notices, the pages you left out, and images nothing uses where they were. They don't publish, but delete them so they don't confuse later edits: the wiki keeps them. Delete wiki tooling that no page needs any more too, like a sync script.
  • Leftover wiki links. Blume shows [[Page]] as text. It warns about one that names a page on the site, or has a |, as BLUME_WIKILINK_UNSUPPORTED, but not about a link to a page that doesn't exist. grep -rn '\[\[' docs should print only code samples.
  • The lockfile. A mirror repository's .gitignore often ignores package-lock.json or yarn.lock. Remove those lines so the lockfile is committed.

Check the route table

Build the site, then check that every page in the route table exists in the build, with its anchor where it has one. From the folder holding blume.config.ts:

npm run build
node node_modules/blume/skills/blume-migrate/scripts/github-wiki-codemod.mjs check
ok  Configuration → /configuration
ok  FAQ → /faq
ok  Getting-Started → /getting-started
ok  Home → /
ok  Old-Setup → /getting-started
ok  Plugins → /plugins
ok  Release-Process → /release-process

0 problem(s).

Then check every URL in the page list you saved, the same way:

node node_modules/blume/skills/blume-migrate/scripts/github-wiki-codemod.mjs check $(sed 's#^#https://github.com#' ../old-pages.txt)

Each old URL should come back ok with its new address, moved with a moved notice's outside target, or DROPPED for a page you left out on purpose, which counts as a problem so it stays visible. A MISSING line is a page the table doesn't have. Run npx blume validate --strict too, which checks every link and anchor on the new site.

Deploy

Many projects publish their docs on GitHub Pages, and a Blume site can go there. Deploy Markdown docs to GitHub Pages has a complete workflow. Blume can't detect your URL there, so set it, with the repository name as the base for a project site:

deployment: { site: "https://acme.github.io", base: "/widget" },

On Vercel or Netlify, Blume detects the URL. For other hosts, see Deployment. With a base, Blume prefixes it to root paths in Markdown links and raw HTML alike, such as the <a href> the codemod writes for a wiki link inside an HTML block. Once the site is live, run the stubs command with its real address, push the stubs if you've decided to, and rewrite the links in your repository.

What doesn't carry over

  • Editing in the browser. Anyone with access could edit a wiki page on GitHub. Changes now go through the repository. Set github in blume.config.ts once the docs live in one, and every page gets an Edit on GitHub link.
  • Page history. The revisions list, history, and compare views stay on the wiki. If you bring the wiki's history into your repository with git subtree add, git log --follow still finds it. If you turn on lastModified: "git", though, every page's date starts at the migration: Blume's dates follow a rename only when the file didn't change, and every page was edited too.
  • Redirects. Old wiki URLs never redirect, as above. The stubs are the closest you get.
  • Wiki chrome. The Pages list, the clone box, footer text, sidebar prose, and links to headings in the sidebar.
  • Some rendering. Image sizes, GeoJSON maps and 3D models, and light and dark image pairs. Raw HTML that GitHub stripped, like inline styles and scripts, would now work, so the agent removes it or asks you about it.

Troubleshooting

The build fails with an MDX error on a .mdx page

The page became .mdx for an alert, and it has raw HTML with Markdown inside, like a <details> block in a list. MDX reads Markdown inside HTML only with blank lines around it. Add them, or turn the block into Markdown.

validate reports an anchor that exists

Anchors are case-sensitive in Blume and weren't on GitHub. Match the case of the heading's id, like #TsDeclFiles for a link written #TSDeclFiles.

Next step

Migrate your wiki

Run it in a copy of your wiki clone with its remote removed, after saving the old page list. Then work through the review above.

npx blume migrate github-wiki --claude
Read the migration reference

A step here not working for you? Report a broken step.

Keep going.More guides.

  • Migrate your docs from Mintlify

    Hand your Mintlify repository to a coding agent, review what it changed, keep your URLs working, and deploy a docs site you host yourself.

  • Migrate your docs from Docusaurus

    Move a Docusaurus site's docs to Blume with a coding agent, and check the sidebars, admonitions, and React components it carries over.

  • Migrate your docs from Fumadocs

    Hand your Fumadocs repository to a coding agent, check its meta.ts and component rewrites, keep your docs at /docs, and deploy with every old URL working.

Upgrade your docs with Blume.

Install today and ship a production-grade docs site in minutes. Free and open source, forever.

npx blume init