Connor Fortin
Jun 24, 2026
visibility 69
star star star star star
(0 votes)

Automated Search & Navigation to Graph Migration with Claude Code

A Claude Code plugin that scans your S&N codebase, applies Graph SDK transformations, and validates the result. Install once, run one command.

CMS 13 Optimizely Graph Migration Claude Code Automation Search & Navigation

TL;DR

  • The cg-migrate Claude Code plugin automates the Search & Navigation to Graph SDK migration. It scans your .NET solution for every Find API usage, rewrites the code to use Optimizely.Graph.Cms.Query, and validates the result by building the project and attributing compiler errors back to the specific transformation that caused them.
  • Five skills cover the full workflow: /migrate-all runs the entire pipeline end-to-end, or use the individual skills for more control: /migrate-scan discovers all S&N usage and classifies complexity, /migrate-transform rewrites the code, /migrate-validate checks the build, and /migrate-map handles one-off translations.
  • It understands your codebase structure. The scan traces dependency graphs through wrapper classes and DI registrations, so indirect consumers of Find are identified too.
  • You keep control over the hard decisions. When a pattern has no direct Graph equivalent (like UnifiedSearch or custom FilterForVisitor implementations), the plugin generates a decisions file and pauses for your input before continuing.
  • Install with two commands and the plugin loads on every Claude Code session automatically.

1. Why This Plugin Exists

The Graph SDK blog post on Optimizely World covers how to write queries with Optimizely.Graph.Cms.Query. If you have not read it yet, start there. It walks through Where(), SearchFor(), Facet(), and the rest of the API that replaces Search & Navigation.

What the blog post does not cover is the mechanical work of actually migrating a real codebase. A typical Optimizely solution has Search & Navigation calls spread across dozens of files. Some are straightforward Search<T>().Filter().GetResult() chains. Others are wrapped behind repository classes, injected through DI containers, or built up conditionally with .OrFilter() composition. Finding all of that, understanding the dependency graph, and rewriting it file by file takes time that could be spent on the parts of a CMS 13 migration that genuinely require human judgment.

The cg-migrate plugin handles the mechanical part. It is a set of Claude Code skills that scan your solution, apply the known API mappings from the Graph SDK blog post, flag the patterns that need human decisions, and validate the result against the compiler. You stay in control of architecture questions. The plugin handles the find-and-replace at scale.


2. Getting Started

Prerequisites

You need Claude Code installed and a .NET solution that currently uses the Search & Navigation (EPiServer Find) SDK.

Before running the migration, check out a dedicated branch with no pending changes. The solution should build cleanly and all tests should pass. This gives the plugin a clean baseline so it can distinguish migration-introduced errors from pre-existing issues.

Installation

Run these two commands in Claude Code:

/plugin marketplace add episerver/content-graph-sdk-migrations
/plugin install cg-migrate@content-graph-sdk-migrations

The plugin loads automatically on every subsequent Claude Code session. No configuration files to edit.

What Gets Installed

The plugin adds five slash commands to your Claude Code session:

Command Purpose
/migrate-all Run the full migration pipeline end-to-end: scan, transform, validate, and fix
/migrate-scan Discover all S&N usage across the solution and produce a migration manifest
/migrate-map Translate a single file or code snippet from S&N to Graph SDK
/migrate-transform Apply transformations to all files identified by the scan
/migrate-validate Build the project and map each compiler error back to the transformation that caused it

Three Ways to Use the Plugin

You do not have to use all five commands. Pick the approach that fits where you are in your migration:

Approach Commands When to use
Full pipeline /migrate-all You want to migrate the entire solution end-to-end in one run. The command handles scan, transform, validate, and build-fix phases automatically, pausing only for manual decisions.
Step by step /migrate-scan/migrate-transform/migrate-validate You want to review the scan report before transforming, re-run a single phase after making manual edits, or iterate on specific files without restarting the full pipeline.
One-off translation /migrate-map You want to translate a single file or code snippet, understand how a specific S&N pattern maps to the Graph SDK, or handle a file individually before or after a larger migration run.

The rest of this post covers each approach in detail. If you just want to get started, skip to section 3 for the full pipeline or section 5 for one-off translations.


3. Running the Full Migration

The fastest way to migrate is /migrate-all. It orchestrates the entire pipeline in a single invocation:

/migrate-all --project path/to/YourSolution.sln

If you omit --project, the plugin searches for .sln files in the current directory.

What Happens

The command runs six phases sequentially:

Phase 1: Scan. Invokes /migrate-scan to discover all Find SDK usage, trace dependency graphs, and classify each pattern by complexity. If no migration targets are found, the command stops early.

Phase 2: Confirm. Presents a summary of what will be transformed automatically and what will require manual decisions, then pauses for your confirmation before making any changes.

Phase 3: Transform. Invokes /migrate-transform to apply all known API mappings, swap NuGet packages, and flag items for manual review.

Phase 4: Validate. Invokes /migrate-validate to build the project and attribute compiler errors to specific transformation rules. If errors are found, it automatically retries the transformation on the failed files for a second pass.

Phase 5: Build & Fix. Rebuilds the project and iteratively fixes remaining compiler errors that have clear automated solutions, such as CS0266 type mismatches, CS1061 missing members, CS0246 unknown types, and NuGet version conflicts. Errors that require business logic decisions are deferred for you to handle manually.

Phase 6: Report. Generates a comprehensive migration report at .cg-migrations/migrate-all/ that ties together the results from every phase: targets scanned, files transformed, errors fixed, and remaining manual items with prioritized next steps.

The final report classifies the migration status as Complete (all errors resolved), Partial (some errors remain), or Nothing to migrate (no targets found).

Tip

Use /migrate-all for the typical workflow. The individual skills (/migrate-scan, /migrate-transform, /migrate-validate) are still available when you need to re-run a specific phase or want more control over the process.


4. Scanning Your Codebase

If you want to inspect the migration scope before committing to a full run, or if you need to re-run just the scan phase, use /migrate-scan directly:

/migrate-scan --project path/to/YourSolution.sln

If you omit --project, the plugin searches for .sln files in the current directory.

The scan runs in four phases:

Phase 1: NuGet Detection. The scan parses .csproj files to find which projects reference EPiServer.Find packages and what versions they use.

Phase 2: Direct Reference Scan. Using regex patterns from a built-in registry of 50+ S&N API patterns, the scan locates every file that calls into the Find SDK. Each match is categorized by type (filtering, facets, sorting, search, DI registration, etc.).

Phase 3: Dependency Graph Tracing. This is where the scan goes beyond simple text search. It follows constructor injection, DI registrations, and wrapper class hierarchies up to 5 hops deep. If you have a SearchService that wraps IClient and a SearchController that injects SearchService, both get flagged as migration targets even though only SearchService contains direct Find API calls.

Phase 4: Complexity Classification. Every identified pattern gets one of three labels:

Classification Meaning Example
direct-swap 1:1 Graph SDK equivalent exists .Filter() to .Where(), .Take() to .Limit()
pattern-rewrite Needs restructuring but the approach is known .OrFilter() composition to BuildFilter<T>().Or()
manual No automated equivalent, needs human decision UnifiedSearch, custom FilterForVisitor

Scan Output

The scan produces a console summary with statistics and a detailed markdown report at .cg-migrations/migrate-scan/scan-report.md. The report includes every target file, the specific patterns found, the dependency tree, and the complexity breakdown.

Tip

Add .cg-migrations/ to your .gitignore. This directory is for migration artifacts and should not be committed.


5. Translating Individual Queries

For one-off translations or to understand how a specific pattern maps to the Graph SDK, use /migrate-map:

/migrate-map

Point it at a file or paste a code snippet. The skill applies 17 translation rules covering the full S&N API surface.

Before and After

Before - Search & Navigation
var result = searchClient.Search<ArticlePage>()
    .For("chocolate cake")
    .InField(x => x.Title, 3.0)
    .InField(x => x.Summary, 2.0)
    .Filter(x => x.Category.Match("Dessert"))
    .Filter(x => x.IsPublished.Match(true))
    .TermsFacetFor(x => x.Category)
    .OrderByDescending(x => x.PublishDate)
    .Take(10)
    .Skip(20)
    .GetResult();
After - Graph SDK
var result = await _graphClient
    .QueryContent<ArticlePage>()
    .SearchFor("chocolate cake")
    .UsingField(x => x.Title, boost: 3)
    .UsingField(x => x.Summary, boost: 2)
    .Where(x => x.Category == "Dessert" && x.IsPublished)
    .Facet(x => x.Category)
    .OrderBy(x => x.PublishDate, OrderDirection.Descending)
    .Limit(10)
    .Skip(20)
    .IncludeTotal()
    .GetAsContentAsync();

The /migrate-map skill is useful for building intuition about the mapping rules before running a full transformation, and for handling files you want to review individually.


6. Transforming the Full Codebase

Once you have reviewed the scan report and understand the scope, run the full transformation:

/migrate-transform --project path/to/YourSolution.sln

You can optionally pass the scan report path if you have already run /migrate-scan:

/migrate-transform --project path/to/YourSolution.sln --scan-report .cg-migrations/migrate-scan/scan-report.md

How the Transform Works

The transform runs in six phases:

Phase 1: Decision Resolution. For every pattern classified as manual in the scan, the transform generates a decisions file at .cg-migrations/migrate-decisions.md. This file groups patterns and presents strategy options. The plugin pauses here and asks you to choose a strategy for each group before continuing.

Example decision groups and strategies:

Pattern Group Suggested Strategy
UnifiedSearch (searches across multiple content types) type-specific-queries (create separate QueryContent<T>() per type)
ContentIndexer / .Track() calls remove (comment out, no Graph equivalent for direct indexing)
Custom site/visitor filters custom-implementation (you provide the replacement logic)
.UsingSynonyms() per-filter-synonym (move to .Match("value", SynonymSlot.One))

Phase 2: NuGet Package Setup. Adds Optimizely.Graph.Cms.Query to the relevant projects and tracks the S&N packages for removal.

Phase 3: Code Transformation. Each file gets 13 transformation steps applied in sequence: namespace updates, DI registration changes, query entry points, full-text search, filtering, sorting, pagination, facets, projections, execution (all made async), result access patterns, manual pattern strategies, and cleanup of orphaned using statements.

Phase 4: Cleanup. Removes using EPiServer.Find statements that are no longer needed.

Phase 5: Build Validation. Runs dotnet build and auto-fixes common issues like NuGet conflicts and generic constraint mismatches (the Graph SDK requires where T : class, IContentData rather than just IContentData). Iterates up to 2 cycles.

Phase 6: Reporting. Generates a transform report at .cg-migrations/migrate-transform/transform-report.md with per-file results.

The decisions file persists across runs

If you run /migrate-transform again, it reads your previous decisions. You can edit the file directly to change strategies for specific patterns or files.


7. Validating the Result

After the transform completes, run validation:

/migrate-validate --project path/to/YourSolution.sln

The validation phase does more than just run dotnet build. It parses every compiler error, matches it against a built-in lookup table of known migration-related error signatures, and attributes each failure to the specific transformation rule that likely caused it.

Validation Output

The report separates errors into three buckets:

Bucket Meaning
Migration-caused (high confidence) File was transformed AND error matches a known migration pattern
Possibly migration-related (medium confidence) Error matches a known cause but the file was not directly transformed
Pre-existing / out-of-scope Error does not match any migration pattern

Each error includes the file path, line number, error code, message, and the likely cause with a suggested fix.

Example Error Attribution

CS1061 in SearchService.cs:42
  'IContentQuery<ArticlePage>' does not contain a definition for 'TotalMatching'
  Likely cause: Rule 11 (Result Access)
  Suggested fix: Add .IncludeTotal() to the query chain and access result.Total

After reporting, the validation skill offers to re-run /migrate-transform on just the failed files for a second pass (max 2 rounds). This catches cases where the first pass produced valid but incomplete transformations.


8. Full Workflow at a Glance

# 1. Check out a clean branch
git checkout -b feature/graph-migration

# 2. Run the full migration
/migrate-all --project src/MySolution.sln

# 3. Make decisions for manual patterns when prompted

# 4. Review the migration report
# (open .cg-migrations/migrate-all/*-migration-report.md)

# 5. Fix any remaining manual items and commit

That is the simplest path. If you need more control, the individual skills (/migrate-scan, /migrate-transform, /migrate-validate) can be run separately and repeated as needed. The decisions file and scan report persist between runs, so you can iterate without losing your choices.


9. What the Plugin Handles vs. What You Handle

The plugin handles You handle
Finding every S&N API call across the solution Reviewing the scan report for completeness
Tracing wrapper classes and DI dependency chains Deciding strategies for manual patterns
Applying the 17 known API translation rules Custom filter/visitor logic that has no Graph equivalent
Making methods async when execution changes to GetAsync() / GetAsContentAsync() Business logic changes (e.g., if UnifiedSearch was used to merge results, you decide how to restructure)
Adding generic constraints (class, IContentData) Integration test updates
Attributing compiler errors to specific transformation rules Verifying that query results are functionally equivalent
NuGet package additions NuGet package removals (tracked but left for you to confirm)

Looking Ahead

The plugin's pattern registry will continue to grow as partners encounter additional S&N patterns during real-world migrations.

If you run into a Search & Navigation pattern that the plugin does not handle, the validation report will call it out. File an issue on the GitHub repository at episerver/content-graph-sdk-migrations so the pattern can be added to the registry.

Next Steps

  1. Install the plugin with /plugin marketplace add episerver/content-graph-sdk-migrations
  2. Run /migrate-all on your solution to migrate end-to-end, or start with /migrate-scan to preview the scope first
  3. Review the migration report and fix any remaining manual items
  4. Read the Graph SDK blog post to understand the API you are migrating to

Resources:

Start with the scan

Running /migrate-scan is read-only. It does not modify any files. You can run it against any branch at any time to understand the scope of the migration before committing to the transformation.

This guide is based on the cg-migrate Claude Code plugin for migrating Search & Navigation to the Optimizely Graph SDK. The plugin is actively maintained and the pattern registry grows with each release.

Jun 24, 2026

Comments

error Please login to comment.
Latest blogs
Migrating from Find to Graph: Lessons Learned from a Real CMS 13 Project

While migrating a search solution from Optimizely Search & Navigation (Find) to Optimizely Graph in CMS 13, I encountered several issues that were...

Binh Nguyen Thi | Jun 24, 2026

Optimizely: Upgrade Opti-ID and .NET 10 in CMS 12

Many Optimizely customers are planning their roadmap around a future migration to Optimizely CMS 13. As a result, upgrades such as Opti ID adoption...

Madhu | Jun 23, 2026 |

Understanding Optimizely Graph: Caching, Webhooks & Avoiding Stale Content (Optimizely SaaS CMS)

📌 Scope: This post covers Optimizely CMS (SaaS) only — using the official @optimizely/cms-sdk and @optimizely/cms-cli packages with Next.js 15. If...

Kiran Patil | Jun 23, 2026 |

Optimizely Content APIs: the Setup the Docs Don't Walk You Through

CMS 13 is pushing things firmly in the direction of Optimizely Graph, but plenty of teams are still running on older CMS versions, or have good...

Andre | Jun 22, 2026

Translating content in Optimizely CMS with Anthropic Claude

An add-on with an Anthropic translator provider that lets you translate content in Optimizely CMS using Anthropic Claude.

Tomas Hensrud Gulla | Jun 20, 2026 |