Skip to content

Migration

Amir Olyaei edited this page Oct 5, 2026 · 6 revisions

Migration

ai-config migrate moves existing provider configurations into the unified layout. Original files are backed up with a .backup extension before any change.

Detected providers

Provider User config Project config
Devin CLI ~/.config/devin/config.json .devin/config.json
Windsurf ~/.windsurf/config.json .windsurf/config.json
Claude ~/.claude/settings.json, ~/.claude.json .claude/settings.json, .mcp.json
Cursor ~/.cursor/settings.json, ~/.cursor/mcp.json .cursor/mcp.json, .cursor/rules/
Codex CLI ~/.codex/config.toml (TOML) AGENTS.md
Gemini CLI ~/.gemini/settings.json .gemini/settings.json
VS Code <user-dir>/mcp.json .vscode/mcp.json, .github/copilot-instructions.md
Copilot CLI ~/.copilot/settings.json, ~/.copilot/mcp-config.json .mcp.json, .github/mcp.json, .github/copilot-instructions.md
Zed ~/.config/zed/settings.json (context_servers) .zed/settings.json, .rules
Continue ~/.continue/config.yaml (YAML) .continue/config.yaml

JSON, TOML, and YAML configs are all read. Provider-native MCP shapes (Zed context_servers, VS Code servers, Continue's list, Codex TOML tables) are normalized into the unified context_servers format on import, and translated back on ai-config sync.

Legacy lineage is covered: Windsurf migration reads Codeium-era paths (~/.codeium/mcp_config.json, ~/.codeium/config.json) alongside current ~/.windsurf/ locations, and Devin detection covers both ~/.config/devin/ (active config) and ~/.devin/ (application data). Detection only requires the directory to exist: real dirs, symlinks, and stale compat links all count, and contents decide what gets imported.

Duplicates and conflicts

When multiple providers define the same MCP server name:

  • Identical configs merge into one entry (the second import is skipped).
  • Name schemes are normalized: registry names like io.github.github/github-mcp-server or mcp-foo match a unified foo entry, and empty fields injected by native formats (env: {}) do not count as differences.
  • Exact-name conflicts keep both: the provider's version lands under a <name>.<provider> alias like github.cursor.
  • Similar-name conflicts (normalized match, different config, unique name) are imported as-is with a "looks similar to" note.

Nothing is silently overwritten. Format limitations matter too: if a provider cannot express a field (e.g. zed drops disabled), a sync-out/migrate-in round trip produces a genuinely different config and will alias.

Migrate everything

ai-config migrate

Migrates all detected user-level provider configs. Prints what was found and migrated; if nothing is detected it reports that and exits.

Migrate one provider

ai-config migrate devin

Useful when a bulk migration partially failed or you only use one provider. Unknown provider names exit with an error.

Migrate a project

cd your-project
ai-config migrate --project

Finds the project root via .git or .ai and migrates project-level provider configs (.devin/, .windsurf/, .claude/ style directories) into .ai/.

Migrate skills from all projects

ai-config migrate --all-projects

Scans all projects and moves their skills into the global ~/.agents/skills directory so every provider and project can use them.

After migrating

ai-config validate   # verify structure and required keys
ai-config status     # see providers, MCP servers, and skills now configured

Rollback

Originals are preserved as *.backup files next to the legacy configs. To roll back, restore the backup and remove the migrated entries from ~/.agents/config/config.json (or run ai-config init --fresh to start over entirely).

For provider authors

Providers are registered in universal_ai_config/providers.py via a ProviderSpec entry covering detect paths, migration read paths, sync targets, and MCP shape translation. Migration iterates the registry, so a new entry is picked up automatically.

See Provider Integration.

universal-ai-config

Getting started

Using it

For integrators

For maintainers


Repository | Issues | PyPI

Clone this wiki locally