migrate-to-nextjs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Site Migration Skill

网站迁移Skill

STOP. READ THIS ENTIRE DOCUMENT BEFORE DOING ANYTHING.
This skill migrates legacy websites to modern Next.js. You MUST follow every step exactly. Skipping steps will result in failed migrations.

注意:在进行任何操作前,请完整阅读本文档。
本Skill可将旧版网站迁移至现代化的Next.js框架。你必须严格遵循每一个步骤,跳过步骤会导致迁移失败。

PHASE 1: SETUP

第一阶段:环境搭建

Install all dependencies, ask the user questions, create the Next.js project, and configure MCP. Do not proceed to Phase 2 until everything is verified.
安装所有依赖项,询问用户相关问题,创建Next.js项目,并配置MCP。在所有内容验证通过前,请勿进入第二阶段。

1.1 Install Skills

1.1 安装Skills

Run each command and verify it succeeds:
bash
npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices --yes
npx skills add https://github.com/vercel-labs/next-skills --skill next-best-practices --yes
npx skills add https://github.com/vercel-labs/next-skills --skill next-cache-components --yes
npx skills add https://github.com/vercel-labs/agent-skills --skill web-design-guidelines --yes
运行以下每个命令并验证执行成功:
bash
npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices --yes
npx skills add https://github.com/vercel-labs/next-skills --skill next-best-practices --yes
npx skills add https://github.com/vercel-labs/next-skills --skill next-cache-components --yes
npx skills add https://github.com/vercel-labs/agent-skills --skill web-design-guidelines --yes

1.2 Install Migent

1.2 安装Migent

bash
npm install -g migent
VERIFY:
migent --version
returns a version number.
bash
npm install -g migent
验证:执行
migent --version
应返回版本号。

1.3 Ask User Questions

1.3 询问用户问题

  1. "Which directory contains your legacy site?"
    • Scan workspace for
      package.json
      ,
      composer.json
      ,
      Gemfile
      ,
      index.html
      ,
      index.php
    • Offer detected options as choices
  2. "What port is your legacy site running on?" (or "How do I start it?")
    • Common ports: 3000, 4000, 8000, 8080
  3. "What should I name the Next.js project?"
    • Suggest:
      <legacy-name>-next
  1. “你的旧版网站所在目录是哪个?”
    • 扫描工作区寻找
      package.json
      composer.json
      Gemfile
      index.html
      index.php
      文件
    • 将检测到的选项提供给用户选择
  2. “你的旧版网站运行在哪个端口?”(或“我该如何启动它?”)
    • 常见端口:3000、4000、8000、8080
  3. “Next.js项目的名称应该是什么?”
    • 建议命名格式:
      <旧网站名称>-next

1.4 Validate Legacy Site

1.4 验证旧版网站

bash
ls -la <legacy-directory>
curl -s -o /dev/null -w "%{http_code}" http://localhost:<port>/
MUST PASS: Directory exists AND curl returns
200
.
CAPTURE any observed request patterns. Example: Redirect to /en means the site has localisation — include it in the migration plan.
IF VALIDATION FAILS: Return to user with specific error. Do not guess or proceed.
bash
ls -la <legacy-directory>
curl -s -o /dev/null -w "%{http_code}" http://localhost:<port>/
必须通过:目录存在且curl返回
200
状态码。
记录:任何观察到的请求模式。例如:重定向至/en意味着网站支持多语言本地化——需将此纳入迁移计划。
若验证失败:向用户返回具体错误信息,请勿猜测或继续执行后续步骤。

1.5 Load All Skills

1.5 加载所有Skills

Load all skill contexts now. They will be used throughout the migration.
/next-best-practices
/vercel-react-best-practices
/web-design-guidelines
IMPORTANT: Always use the latest Next.js and tailwindcss (check versions).
立即加载所有Skill上下文,它们将在整个迁移过程中被使用。
/next-best-practices
/vercel-react-best-practices
/web-design-guidelines
重要提示:始终使用最新版本的Next.js和tailwindcss(请检查版本)。

1.6 Create Next.js Project

1.6 创建Next.js项目

bash
bunx create-next-app@latest <project-name> \
  --typescript \
  --tailwind \
  --app \
  --src-dir \
  --import-alias "@/*" \
  --use-bun \
  --yes
NOTE:
  • Use
    bun
    , not
    npm
  • Use
    bunx
    , not
    npx
  • ESLint is NOT included — we use Biome
bash
bunx create-next-app@latest <project-name> \
  --typescript \
  --tailwind \
  --app \
  --src-dir \
  --import-alias "@/*" \
  --use-bun \
  --yes
注意
  • 使用
    bun
    而非
    npm
  • 使用
    bunx
    而非
    npx
  • 不包含ESLint——我们使用Biome

1.7 Install Biome

1.7 安装Biome

bash
cd <project-name>
bun add -D @biomejs/biome
Create
biome.json
:
json
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "organizeImports": { "enabled": true },
  "linter": {
    "enabled": true,
    "rules": { "recommended": true }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  }
}
bash
cd <project-name>
bun add -D @biomejs/biome
创建
biome.json
文件:
json
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "organizeImports": { "enabled": true },
  "linter": {
    "enabled": true,
    "rules": { "recommended": true }
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  }
}

1.8 Configure MCP

1.8 配置MCP

Configure MCP servers in the workspace root (NOT inside the Next.js project):
workspace/              ← config goes HERE
├── legacy-site/
├── my-next-app/
└── .mcp.json
Create
.mcp.json
:
json
{
  "mcpServers": {
    "migent": {
      "command": "npx",
      "args": ["-y", "migent", "mcp"]
    }
  }
}
工作区根目录(而非Next.js项目内部)配置MCP服务器:
workspace/              ← 配置文件需放在此处
├── legacy-site/
├── my-next-app/
└── .mcp.json
创建
.mcp.json
文件:
json
{
  "mcpServers": {
    "migent": {
      "command": "npx",
      "args": ["-y", "migent", "mcp"]
    }
  }
}

1.9 Verify MCP

1.9 验证MCP

Start the Next.js dev server, then test MCP:
bash
bun run dev
ir_capture(port: 3000, route: "/")
VERIFY: Returns JSON with
elementCount > 0
.
启动Next.js开发服务器,然后测试MCP:
bash
bun run dev
ir_capture(port: 3000, route: "/")
验证:返回的JSON中
elementCount > 0

1.10 Copy Assets

1.10 复制资源文件

bash
mkdir -p <next-project>/public
cp -r <legacy-directory>/images/* <next-project>/public/images/ 2>/dev/null || true
cp -r <legacy-directory>/fonts/* <next-project>/public/fonts/ 2>/dev/null || true
cp -r <legacy-directory>/assets/* <next-project>/public/assets/ 2>/dev/null || true
bash
mkdir -p <next-project>/public
cp -r <legacy-directory>/images/* <next-project>/public/images/ 2>/dev/null || true
cp -r <legacy-directory>/fonts/* <next-project>/public/fonts/ 2>/dev/null || true
cp -r <legacy-directory>/assets/* <next-project>/public/assets/ 2>/dev/null || true

Setup Checkpoint

环境搭建检查点

Before proceeding, confirm ALL of the following:
  • All 4 skills installed
  • Legacy site running and accessible
  • Next.js project created with Biome configured
  • MCP returning captures for both sites
  • Assets copied
IF ANY FAILS: Stop and report the error to the user.

在继续之前,请确认以下所有项均已完成:
  • 已安装全部4个Skills
  • 旧版网站已启动且可访问
  • 已创建Next.js项目并配置好Biome
  • MCP可返回两个网站的捕获数据
  • 资源文件已复制完成
若有任何项未完成:停止操作并向用户报告错误。

PHASE 2: DISCOVERY

第二阶段:发现与分析

Use MCP tools to capture the legacy site and analyze patterns. DO NOT USE CURL. DO NOT FETCH HTML MANUALLY.
使用MCP工具捕获旧版网站数据并分析模式。禁止使用CURL,禁止手动获取HTML内容。

2.1 Discover Routes

2.1 发现路由

Analyze legacy codebase to find all routes:
  • Check
    sitemap.xml
    if exists
  • Check router files (Express routes, Next.js pages, PHP files)
  • Check navigation links in captured IR
分析旧版代码库以找出所有路由:
  • 若存在
    sitemap.xml
    则检查该文件
  • 检查路由文件(Express路由、Next.js页面、PHP文件)
  • 检查捕获的IR数据中的导航链接

2.2 Capture All Routes (Parallel)

2.2 批量捕获所有路由(并行执行)

Call
ir_capture
for all discovered routes in parallel (batch all calls in a single message). Each
ir_capture
creates a fresh Playwright page — no shared state conflicts.
undefined
为所有发现的路由调用
ir_capture
并行执行(在一条消息中批量调用所有命令)。每个
ir_capture
会创建一个全新的Playwright页面——无共享状态冲突。
undefined

Call all in one message — they run concurrently:

在一条消息中调用所有命令——它们会并发执行:

ir_capture(port: <legacy-port>, route: "/") ir_capture(port: <legacy-port>, route: "/about") ir_capture(port: <legacy-port>, route: "/contact")
ir_capture(port: <legacy-port>, route: "/") ir_capture(port: <legacy-port>, route: "/about") ir_capture(port: <legacy-port>, route: "/contact")

... all discovered routes

... 所有发现的路由


Collect all results. Write captures to `migration.json`:
```json
{
  "legacy": {
    "directory": "./legacy-site",
    "port": 8000,
    "framework": "php",
    "routes": ["/", "/about", "/contact"]
  },
  "captures": {
    "/": { "elementCount": 150, "animationCount": 12 },
    "/about": { "elementCount": 89, "animationCount": 3 }
  },
  "next": {
    "directory": "./my-next-app",
    "port": 3000
  },
  "routeStatus": {
    "/": "pending",
    "/about": "pending",
    "/contact": "pending"
  },
  "progress": {},
  "skippedIssues": []
}

收集所有结果并将捕获数据写入`migration.json`:
```json
{
  "legacy": {
    "directory": "./legacy-site",
    "port": 8000,
    "framework": "php",
    "routes": ["/", "/about", "/contact"]
  },
  "captures": {
    "/": { "elementCount": 150, "animationCount": 12 },
    "/about": { "elementCount": 89, "animationCount": 3 }
  },
  "next": {
    "directory": "./my-next-app",
    "port": 3000
  },
  "routeStatus": {
    "/": "pending",
    "/about": "pending",
    "/contact": "pending"
  },
  "progress": {},
  "skippedIssues": []
}

2.3 Analyze JavaScript Patterns

2.3 分析JavaScript模式

IMPORTANT: Legacy JavaScript is ANYTHING that is NOT React or Next.js.
Search legacy codebase for patterns that need conversion:
bash
undefined
重要提示:旧版JavaScript指所有非React或Next.js的JavaScript代码。
在旧版代码库中搜索需要转换的模式:
bash
undefined

Find jQuery

查找jQuery

grep -r "jquery|jQuery|\$(" <legacy-directory> --include=".js" --include=".html" --include="*.php"
grep -r "jquery|jQuery|\$(" <legacy-directory> --include=".js" --include=".html" --include="*.php"

Find inline handlers

查找内联事件处理器

grep -r "onclick=|onsubmit=|onchange=" <legacy-directory> --include=".html" --include=".php"

Document findings in `migration.json` under `legacy.javascript`.
grep -r "onclick=|onsubmit=|onchange=" <legacy-directory> --include=".html" --include=".php"

将发现的结果记录在`migration.json`的`legacy.javascript`字段下。

2.4 Detect Locales & Validate Links

2.4 检测多语言本地化并验证链接

Check
ir_capture
results for locale patterns:
  1. Redirect-based detection: If
    ir_capture
    returns
    redirects
    (e.g.,
    /
    /en/
    ), the site uses locale prefixes.
  2. Route-based detection: If discovered routes have locale prefixes (e.g.,
    /en/about
    ,
    /fr/about
    ), locales are in use.
If locales are detected:
  • Set up Next.js i18n middleware for locale routing
  • Create
    src/middleware.ts
    with locale detection and redirect logic
  • Use
    next-intl
    or Next.js built-in i18n for locale-aware Link components
  • Validate all internal links include the correct locale prefix
  • Map each locale route to its Next.js equivalent
Internal link validation: Check
ir_capture
internalLinks
against detected locales. Links missing locale prefixes will break in the migrated site.
检查
ir_capture
结果中的本地化模式:
  1. 基于重定向的检测:若
    ir_capture
    返回
    redirects
    数据(例如:
    /
    /en/
    ),则网站使用本地化前缀。
  2. 基于路由的检测:若发现的路由包含本地化前缀(例如:
    /en/about
    /fr/about
    ),则网站已启用本地化。
若检测到本地化
  • 为Next.js配置i18n中间件以支持本地化路由
  • 创建
    src/middleware.ts
    文件,包含本地化检测和重定向逻辑
  • 使用
    next-intl
    或Next.js内置的i18n功能实现支持本地化的Link组件
  • 验证所有内部链接均包含正确的本地化前缀
  • 将每个本地化路由映射至对应的Next.js路由
内部链接验证:检查
ir_capture
返回的
internalLinks
数据是否符合检测到的本地化规则。缺少本地化前缀的链接在迁移后的网站中会失效。

2.5 Install Conditional Dependencies

2.5 安装条件依赖

Based on discovery results:
If animations were detected in any
ir_capture
:
bash
cd <next-project>
bun add framer-motion

根据发现阶段的结果安装依赖:
若在任意
ir_capture
结果中检测到动画:
bash
cd <next-project>
bun add framer-motion

PHASE 3: MIGRATE (PER ROUTE)

第三阶段:按路由迁移

For EACH route discovered in Phase 2, repeat steps 3.1 through 3.4. Start with
/
to build the shared shell first.
针对第二阶段发现的每个路由,重复执行3.1至3.4步骤。先从
/
路由开始,构建共享的页面框架。

CRITICAL RULES — VIOLATIONS ARE FAILURES

关键规则——违反即视为迁移失败

FORBIDDEN:
  • dangerouslySetInnerHTML
    — NEVER use to copy legacy HTML
  • onclick="..."
    or any inline event handlers
  • jQuery or any jQuery patterns
  • <script>
    tags with inline JavaScript
  • class=
    instead of
    className=
REQUIRED:
  • Proper JSX with
    className
  • React event handlers (
    onClick={handler}
    )
  • Match computed styles visually (Tailwind utilities, CSS modules, or global CSS — see Appendix A)
  • next/image
    for images
  • Server Components by default
  • 'use client'
    only when needed
RECOMMENDED (enforce in Phase 5):
  • next/font
    for fonts (see Appendix C)
  • shadcn/ui components for form elements, dialogs, tables (see Appendix D)
  • Tailwind utilities over CSS modules/global CSS
禁止操作
  • 使用
    dangerouslySetInnerHTML
    ——绝不能用此方法复制旧版HTML
  • 使用
    onclick="..."
    或任何内联事件处理器
  • 使用jQuery或任何jQuery模式
  • 使用包含内联JavaScript的
    <script>
    标签
  • 使用
    class=
    而非
    className=
强制要求
  • 使用标准JSX语法并搭配
    className
  • 使用React事件处理器(
    onClick={handler}
  • 视觉上匹配计算样式(使用Tailwind工具类、CSS模块或全局CSS——见附录A)
  • 图片使用
    next/image
    组件
  • 默认使用Server Components
  • 仅在必要时添加
    'use client'
    指令
推荐做法(在第五阶段强制执行):
  • 字体使用
    next/font
    (见附录C)
  • 表单元素、对话框、表格等使用shadcn/ui组件(见附录D)
  • 优先使用Tailwind工具类而非CSS模块/全局CSS

3.1 Build Page

3.1 构建页面

For
/
(first route):
also build the shared shell:
  1. Create
    src/app/layout.tsx
    (RootLayout) with HTML structure, metadata
  2. Extract shared components: Header, Footer, Nav →
    src/components/
  3. Register components in
    migration.json
    under
    components
For all routes (including
/
):
Read captured IR from
migration.json
for this route.
Use
ir_inspect(selector: "...", site: "legacy")
to get full computed styles for specific elements.
Based on captured IR:
  1. Create
    src/app/<route>/page.tsx
  2. Convert layout structure to JSX
  3. Convert captured computed styles to Tailwind (see Appendix A)
  4. Convert event handlers to React patterns
  5. Import shared components from
    src/components/
    — do NOT recreate them
  6. Recreate animations using captured animation data (see Appendix B)
针对
/
路由(第一个路由)
:同时构建共享页面框架:
  1. 创建
    src/app/layout.tsx
    (RootLayout),包含HTML结构和元数据
  2. 提取共享组件:Header、Footer、Nav → 存入
    src/components/
    目录
  3. migration.json
    components
    字段下注册这些组件
针对所有路由(包括
/
migration.json
中读取当前路由的捕获IR数据。
使用
ir_inspect(selector: "...", site: "legacy")
获取特定元素的完整计算样式。
基于捕获的IR数据:
  1. 创建
    src/app/<route>/page.tsx
    文件
  2. 将布局结构转换为JSX语法
  3. 将捕获的计算样式转换为Tailwind工具类(见附录A)
  4. 将事件处理器转换为React模式
  5. src/components/
    导入共享组件——请勿重复创建
  6. 使用捕获的动画数据重新实现动画效果(见附录B)

3.2 Code Quality Gate

3.2 代码质量检查

BEFORE visual validation, verify no anti-patterns:
bash
undefined
在视觉验证之前,验证代码中不存在反模式:
bash
undefined

Anti-patterns (ALL MUST RETURN no results)

反模式检查(所有命令必须返回空结果)

grep -r "dangerouslySetInnerHTML" <next-project>/src/ grep -r 'onclick="' <next-project>/src/ grep -r 'class="' <next-project>/src/ --include=".tsx" --include=".jsx" grep -r 'style={{' <next-project>/src/ --include=".tsx" --include=".jsx" grep -r "from ['"]jquery['"]" <next-project>/src/

**ALL MUST RETURN**: No results. Fix any violations before proceeding.
grep -r "dangerouslySetInnerHTML" <next-project>/src/ grep -r 'onclick="' <next-project>/src/ grep -r 'class="' <next-project>/src/ --include=".tsx" --include=".jsx" grep -r 'style={{' <next-project>/src/ --include=".tsx" --include=".jsx" grep -r "from ['"]jquery['"]" <next-project>/src/

**所有命令必须返回**:无结果。若有违反项,请在继续之前修复。

3.3 Visual Validation Loop

3.3 视觉验证循环

Start watch mode:
ir_start(legacyPort: <legacy-port>, nextPort: 3000, legacyRoute: "<route>", nextRoute: "<route>")
→ { status: "watching", match: {...}, totalIssues: N, firstIssue: {...} }
Loop until match >= 95%:
result = ir_next()

IF result.clsBlocked:
  - CLS score is above 0.1 — ir_next REFUSES to serve other issues
  - Read result.cls.topShifters to identify which elements shifted
  - Fix using result.suggestedFixes:
    1. Font shift → next/font with display: "swap", adjustFontFallback: true
    2. Image shift → next/image with explicit width + height
    3. Dynamic content → min-height or skeleton placeholders
    4. Embeds → fixed aspect-ratio container
  - Save file → watch recaptures → call ir_next again
  - Repeat until clsBlocked is gone

IF result.regressionBlocked:
  - New issues were introduced — fix the regression first
  - Save file → watch recaptures → call ir_next again

IF result.issue exists:
  - Read issue details (selector, styles, position)
  - Fix the specific issue
  - Save file → wait for rebuild → call ir_next again
  - After 3 failed attempts on the same issue: ir_next(skip: true)
  - Document skipped issue in migration.json under skippedIssues

IF result.complete or match >= 95%:
  - Proceed to 3.4
CLS is a hard gate.
ir_next
will not serve style/content/missing issues until CLS score is "good" (<= 0.1). This is enforced by the tool, not by convention. You cannot skip it.
启动监听模式:
ir_start(legacyPort: <legacy-port>, nextPort: 3000, legacyRoute: "<route>", nextRoute: "<route>")
→ { status: "watching", match: {...}, totalIssues: N, firstIssue: {...} }
循环执行直到匹配度≥95%:
result = ir_next()

如果 result.clsBlocked:
  - CLS分数超过0.1 —— ir_next会拒绝返回其他问题
  - 查看result.cls.topShifters以确定哪些元素导致了偏移
  - 使用result.suggestedFixes修复:
    1. 字体偏移 → 使用next/font并设置display: "swap", adjustFontFallback: true
    2. 图片偏移 → 使用next/image并指定明确的width和height
    3. 动态内容 → 设置min-height或骨架屏占位符
    4. 嵌入内容 → 使用固定宽高比的容器
  - 保存文件 → 等待监听模式重新捕获数据 → 再次调用ir_next
  - 重复执行直到clsBlocked状态解除

如果 result.regressionBlocked:
  - 引入了新问题 —— 先修复回归问题
  - 保存文件 → 等待监听模式重新捕获数据 → 再次调用ir_next

如果 result.issue 存在:
  - 查看问题详情(选择器、样式、位置)
  - 修复具体问题
  - 保存文件 → 等待重建完成 → 再次调用ir_next
  - 若同一问题修复3次仍失败:执行ir_next(skip: true)
  - 将跳过的问题记录在migration.json的skippedIssues字段下

如果 result.complete 或 匹配度≥95%:
  - 进入3.4步骤
CLS是硬性检查项
ir_next
在CLS分数降至“良好”(≤0.1)之前,不会返回样式/内容/缺失类问题。此规则由工具强制执行,而非约定俗成,无法跳过。

3.4 Mark Route Complete

3.4 标记路由迁移完成

ir_stop()
Update
routeStatus
to
"validated"
in
migration.json
. Move to next route.
If only skipped issues remain and match is below 95%: update
routeStatus
to
"failed"
, mark route for human review, and continue.

ir_stop()
migration.json
中将
routeStatus
更新为
"validated"
。继续处理下一个路由。
若仅剩余跳过的问题且匹配度低于95%:将
routeStatus
更新为
"failed"
,标记该路由需人工审核,然后继续处理其他路由。

PHASE 4: COMPLETION

第四阶段:迁移完成

4.1 Generate Report

4.1 生成迁移报告

Create
MIGRATION_REPORT.md
with:
  • Summary (routes migrated, match percentages)
  • Per-route breakdown
  • Skipped issues requiring human review
  • Components created
  • Recommendations
创建
MIGRATION_REPORT.md
文件,包含以下内容:
  • 摘要(已迁移路由数量、匹配度百分比)
  • 各路由详细情况
  • 需人工审核的跳过问题
  • 创建的组件列表
  • 后续建议

4.2 Cleanup

4.2 清理操作

ir_stop()

ir_stop()

PHASE 5: MODERNIZE (OPTIONAL)

第五阶段:现代化改造(可选)

Post-migration pass to adopt modern component patterns and tooling. Run this after all routes pass visual parity in Phase 4. Each step is independent — skip any that don't apply.
迁移完成后的优化步骤,采用现代组件模式和工具。需在第三阶段所有路由通过视觉一致性检查后执行此阶段。每个步骤相互独立——可跳过不适用的步骤。

5.1 Install shadcn/ui

5.1 安装shadcn/ui

bash
cd <next-project>
bunx shadcn@latest init -y
bash
cd <next-project>
bunx shadcn@latest init -y

5.2 Configure shadcn MCP

5.2 配置shadcn MCP

Add to
.mcp.json
:
json
{
  "mcpServers": {
    "migent": {
      "command": "npx",
      "args": ["-y", "migent", "mcp"]
    },
    "shadcn": {
      "command": "npx",
      "args": ["shadcn@latest", "mcp"]
    }
  }
}
.mcp.json
中添加以下内容:
json
{
  "mcpServers": {
    "migent": {
      "command": "npx",
      "args": ["-y", "migent", "mcp"]
    },
    "shadcn": {
      "command": "npx",
      "args": ["shadcn@latest", "mcp"]
    }
  }
}

5.3 Install Components

5.3 安装组件

Use
ir_capture
uiPatterns.shadcnComponentsNeeded
data to install the right components:
bash
undefined
使用
ir_capture
返回的
uiPatterns.shadcnComponentsNeeded
数据安装合适的组件:
bash
undefined

Example: if uiPatterns shows Button, Dialog, Table, NavigationMenu

示例:若uiPatterns显示需要Button、Dialog、Table、NavigationMenu

bunx shadcn@latest add button dialog table navigation-menu -y

Also search for **blocks** that match legacy page patterns (e.g., login forms, dashboards, pricing pages).
bunx shadcn@latest add button dialog table navigation-menu -y

同时搜索与旧版页面模式匹配的**区块**(例如:登录表单、仪表盘、定价页面)。

5.4 Convert to shadcn Components

5.4 转换为shadcn组件

Replace raw HTML elements with shadcn equivalents (see Appendix D):
  • <button>
    <Button>
  • <input>
    <Input>
  • <table>
    <Table>
  • <dialog>
    /
    .modal
    <Dialog>
将原生HTML元素替换为对应的shadcn组件(见附录D):
  • <button>
    <Button>
  • <input>
    <Input>
  • <table>
    <Table>
  • <dialog>
    /
    .modal
    <Dialog>

5.5 Convert to Tailwind Utilities

5.5 转换为Tailwind工具类

Replace CSS modules and global CSS with Tailwind utilities where possible. Use Appendix A as a mapping reference.
尽可能将CSS模块和全局CSS替换为Tailwind工具类。可参考附录A的映射表。

5.6 Convert to next/font

5.6 转换为next/font

Replace
@font-face
declarations with
next/font
(see Appendix C).
@font-face
声明替换为
next/font
(见附录C)。

5.7 Verify No Visual Regressions

5.7 验证无视觉回归

Run
ir_start
again after modernization to confirm no regressions:
ir_start(legacyPort: <legacy-port>, nextPort: 3000, legacyRoute: "<route>", nextRoute: "<route>")
Check that match percentages are unchanged or improved.
现代化改造完成后,再次运行
ir_start
以确认无回归问题:
ir_start(legacyPort: <legacy-port>, nextPort: 3000, legacyRoute: "<route>", nextRoute: "<route>")
检查匹配度百分比是否保持不变或有所提升。

5.8 Code Quality Checks

5.8 代码质量检查

bash
undefined
bash
undefined

shadcn enforcement — raw HTML elements should be replaced

验证shadcn组件的使用——原生HTML元素应已被替换

grep -rn '<button' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<input' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<textarea' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<select' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<table' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<dialog' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'
grep -rn '<button' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<input' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<textarea' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<select' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<table' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/' grep -rn '<dialog' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'

Font enforcement — no raw @font-face

验证字体使用——禁止使用原生@font-face

grep -r "@font-face" <next-project>/src/
grep -r "@font-face" <next-project>/src/

Verify shadcn IS being used

验证shadcn组件已被正确使用

grep -rn "from ['"]@/components/ui/" <next-project>/src/ --include="*.tsx"
grep -rn "from ['"]@/components/ui/" <next-project>/src/ --include="*.tsx"

**所有命令必须返回**:无结果(除shadcn组件验证命令应返回匹配结果外)。这些检查在第五阶段强制执行,第三阶段迁移过程中不做要求。

---

Legacy CSS class names should be converted to Tailwind

附录A:捕获样式 → Tailwind映射表

grep -rE 'className="[^"][a-z]+_[a-z]+' <next-project>/src/ --include=".tsx"

---
使用
ir_inspect(selector: "...", site: "legacy")
获取计算样式,然后按照以下规则转换:
颜色(backgroundColor、color、borderColor):
rgb(196, 30, 58) → bg-[#c41e3a] 或 bg-red-600(若颜色匹配度较高)
rgb(255, 255, 255) → bg-white
rgb(0, 0, 0) → bg-black
rgba(0,0,0,0.5) → bg-black/50
间距(padding、margin):
padding: "16px" → p-4
padding: "15px 20px" → py-[15px] px-5
margin: "0 auto" → mx-auto
margin: "24px 0 0 0" → mt-6
排版
fontSize: "14px" → text-sm
fontSize: "18px" → text-lg
fontSize: "32px" → text-3xl
fontWeight: "700" → font-bold
fontWeight: "600" → font-semibold
lineHeight: "1.5" → leading-normal
textAlign: "center" → text-center
布局
display: "flex" → flex
display: "grid" → grid
flexDirection: "column" → flex-col
justifyContent: "center" → justify-center
alignItems: "center" → items-center
gap: "16px" → gap-4
尺寸
width: "100%" → w-full
maxWidth: "1280px" → max-w-7xl
height: "auto" → h-auto
minHeight: "100vh" → min-h-screen
定位
position: "absolute" → absolute
position: "relative" → relative
position: "fixed" → fixed
top: "0px" → top-0
left: "50%" → left-1/2
边框
borderRadius: "8px" → rounded-lg
borderRadius: "9999px" → rounded-full
borderWidth: "1px" → border
borderColor: "rgb(229,231,235)" → border-gray-200
字体样式
fontStyle: "italic" → italic
fontStyle: "normal" → not-italic
文本转换
textTransform: "uppercase" → uppercase
textTransform: "lowercase" → lowercase
textTransform: "capitalize" → capitalize
textTransform: "none" → normal-case
文本装饰
textDecoration: "underline" → underline
textDecoration: "line-through" → line-through
textDecoration: "none" → no-underline
溢出
overflow: "hidden" → overflow-hidden
overflow: "auto" → overflow-auto
overflow: "scroll" → overflow-scroll
overflowX: "auto" → overflow-x-auto
overflowY: "hidden" → overflow-y-hidden
网格布局
gridTemplateColumns: "repeat(3, 1fr)" → grid-cols-3
gridTemplateColumns: "repeat(4, minmax(0, 1fr))" → grid-cols-4
gridTemplateColumns: "200px 1fr" → grid-cols-[200px_1fr]
变换
transform: "translateX(-50%)" → -translate-x-1/2
transform: "rotate(45deg)" → rotate-45
transform: "scale(1.1)" → scale-110
效果
opacity: "0.5" → opacity-50
boxShadow: "0 1px 3px rgba(0,0,0,0.1)" → shadow-sm
boxShadow: "0 10px 15px rgba(0,0,0,0.1)" → shadow-lg
自定义值(无对应Tailwind工具类时使用):
padding: "13px" → p-[13px]
backgroundColor: "#c41e3a" → bg-[#c41e3a]
fontSize: "17px" → text-[17px]
maxWidth: "1140px" → max-w-[1140px]

ERROR HANDLING

附录B:重实现动画效果

MCP tool fails

Stop and report to user. Do not attempt workarounds.
ir_capture
返回的
animations
数据中提取信息:
CSS @keyframes → Framer Motion
tsx
// 捕获的数据:{ name: "fadeInUp", duration: "0.6s", timingFunction: "ease-out" }

import { motion } from 'framer-motion';

<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.6, ease: "easeOut" }}
>
CSS @keyframes → Tailwind动画
css
// 添加至globals.css文件——复制捕获的keyframes规则
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}
tsx
<div className="animate-[fadeInUp_0.6s_ease-out]">
过渡效果
tsx
// 捕获的数据:{ property: "background-color", duration: "0.2s", timingFunction: "ease" }

<button className="transition-colors duration-200 ease-in-out hover:bg-red-700">
jQuery动画 → Framer Motion
tsx
// 捕获的数据:jQueryAnimations: [".fadeIn(300)"]

import { AnimatePresence, motion } from 'framer-motion';

<AnimatePresence>
  {isVisible && (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      transition={{ duration: 0.3 }}
    >
  )}
</AnimatePresence>

Site unreachable

附录C:字体迁移

Stop and ask user to restart the server.

ir_capture
返回的
fonts
字段中读取字体数据。针对每个检测到的字体族:
Google Fonts →
next/font/google
tsx
import { Inter, Roboto } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  weight: ['400', '700'],  // 来自fonts[].weight
  style: ['normal', 'italic'],  // 来自fonts[].style
  display: 'swap',  // 来自fonts[].display或默认使用'swap'
  variable: '--font-inter',
});
自定义字体 →
next/font/local
tsx
import localFont from 'next/font/local';

const customFont = localFont({
  src: [
    { path: './fonts/custom-regular.woff2', weight: '400', style: 'normal' },
    { path: './fonts/custom-bold.woff2', weight: '700', style: 'normal' },
  ],
  variable: '--font-custom',
  display: 'swap',
});
layout.tsx
中应用
tsx
export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${customFont.variable}`}>
      <body>{children}</body>
    </html>
  );
}
tailwind.config.ts
中配置
ts
fontFamily: {
  sans: ['var(--font-inter)', ...defaultTheme.fontFamily.sans],
  custom: ['var(--font-custom)'],
},
下载字体文件:若
ir_capture
返回的
fonts[].src
包含URL,将.woff2文件下载至
public/fonts/
目录,供
next/font/local
使用。

RESUMABILITY

附录D:shadcn组件映射表(第五阶段)

migration.json
tracks per-route status in
routeStatus
:
StatusMeaning
pending
Not yet migrated
validated
Passed visual validation (match >= 95%)
failed
Below 95% after exhausting fixes, needs human review
If
migration.json
exists when
/migration
is invoked:
  1. Read existing state
  2. Skip
    validated
    routes entirely
  3. Resume from first
    pending
    route
  4. Retry
    failed
    routes if user requests it

使用shadcn组件替代原生HTML元素。第五阶段(现代化改造)期间可参考此表。将旧版元素映射为对应的shadcn组件:
旧版HTML元素shadcn组件导入路径
<button>
<input type="submit">
<Button>
@/components/ui/button
<input type="text|email|password">
<Input>
@/components/ui/input
<textarea>
<Textarea>
@/components/ui/textarea
<select>
<Select>
@/components/ui/select
<table>
<Table>
@/components/ui/table
<dialog>
.modal
<Dialog>
@/components/ui/dialog
<nav>
<NavigationMenu>
@/components/ui/navigation-menu
.card
<article>
<Card>
@/components/ui/card
<input type="checkbox">
<Checkbox>
@/components/ui/checkbox
<input type="radio">
<RadioGroup>
@/components/ui/radio-group
.tabs
[role="tablist"]
<Tabs>
@/components/ui/tabs
.accordion
<details>
<Accordion>
@/components/ui/accordion
.breadcrumb
<Breadcrumb>
@/components/ui/breadcrumb
.pagination
<Pagination>
@/components/ui/pagination
转换示例
tsx
// 错误示例 - 使用原生HTML
<button className="bg-red-600 text-white px-4 py-2 rounded">Submit</button>

// 正确示例 - 使用shadcn Button组件
import { Button } from "@/components/ui/button";
<Button className="bg-red-600 text-white">Submit</Button>
tsx
// 错误示例 - 使用原生HTML表格
<table><tr><td>Name</td></tr></table>

// 正确示例 - 使用shadcn Table组件
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
<Table>
  <TableBody>
    <TableRow>
      <TableCell>Name</TableCell>
    </TableRow>
  </TableBody>
</Table>
第五阶段需替换原生HTML元素
<button>
<input>
<table>
<dialog>
等)。
第五阶段代码质量检查
bash
undefined

APPENDIX A: Captured Styles → Tailwind Mapping

CSS中禁止使用原生@font-face(必须使用next/font)

Use
ir_inspect(selector: "...", site: "legacy")
to get computed styles, then convert:
Colors (backgroundColor, color, borderColor):
rgb(196, 30, 58) → bg-[#c41e3a] or bg-red-600 (if close match)
rgb(255, 255, 255) → bg-white
rgb(0, 0, 0) → bg-black
rgba(0,0,0,0.5) → bg-black/50
Spacing (padding, margin):
padding: "16px" → p-4
padding: "15px 20px" → py-[15px] px-5
margin: "0 auto" → mx-auto
margin: "24px 0 0 0" → mt-6
Typography:
fontSize: "14px" → text-sm
fontSize: "18px" → text-lg
fontSize: "32px" → text-3xl
fontWeight: "700" → font-bold
fontWeight: "600" → font-semibold
lineHeight: "1.5" → leading-normal
textAlign: "center" → text-center
Layout:
display: "flex" → flex
display: "grid" → grid
flexDirection: "column" → flex-col
justifyContent: "center" → justify-center
alignItems: "center" → items-center
gap: "16px" → gap-4
Sizing:
width: "100%" → w-full
maxWidth: "1280px" → max-w-7xl
height: "auto" → h-auto
minHeight: "100vh" → min-h-screen
Position:
position: "absolute" → absolute
position: "relative" → relative
position: "fixed" → fixed
top: "0px" → top-0
left: "50%" → left-1/2
Borders:
borderRadius: "8px" → rounded-lg
borderRadius: "9999px" → rounded-full
borderWidth: "1px" → border
borderColor: "rgb(229,231,235)" → border-gray-200
Font Style:
fontStyle: "italic" → italic
fontStyle: "normal" → not-italic
Text Transform:
textTransform: "uppercase" → uppercase
textTransform: "lowercase" → lowercase
textTransform: "capitalize" → capitalize
textTransform: "none" → normal-case
Text Decoration:
textDecoration: "underline" → underline
textDecoration: "line-through" → line-through
textDecoration: "none" → no-underline
Overflow:
overflow: "hidden" → overflow-hidden
overflow: "auto" → overflow-auto
overflow: "scroll" → overflow-scroll
overflowX: "auto" → overflow-x-auto
overflowY: "hidden" → overflow-y-hidden
Grid:
gridTemplateColumns: "repeat(3, 1fr)" → grid-cols-3
gridTemplateColumns: "repeat(4, minmax(0, 1fr))" → grid-cols-4
gridTemplateColumns: "200px 1fr" → grid-cols-[200px_1fr]
Transform:
transform: "translateX(-50%)" → -translate-x-1/2
transform: "rotate(45deg)" → rotate-45
transform: "scale(1.1)" → scale-110
Effects:
opacity: "0.5" → opacity-50
boxShadow: "0 1px 3px rgba(0,0,0,0.1)" → shadow-sm
boxShadow: "0 10px 15px rgba(0,0,0,0.1)" → shadow-lg
Arbitrary values (when no Tailwind match):
padding: "13px" → p-[13px]
backgroundColor: "#c41e3a" → bg-[#c41e3a]
fontSize: "17px" → text-[17px]
maxWidth: "1140px" → max-w-[1140px]

grep -r "@font-face" <next-project>/src/

APPENDIX B: Recreating Animations

禁止在components/ui/目录外使用原生<button>(必须使用shadcn Button组件)

From captured
animations
data in
ir_capture
:
CSS @keyframes → Framer Motion:
tsx
// Captured: { name: "fadeInUp", duration: "0.6s", timingFunction: "ease-out" }

import { motion } from 'framer-motion';

<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.6, ease: "easeOut" }}
>
CSS @keyframes → Tailwind animation:
css
/* Add to globals.css — copy the captured keyframes rule */
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}
tsx
<div className="animate-[fadeInUp_0.6s_ease-out]">
Transitions:
tsx
// Captured: { property: "background-color", duration: "0.2s", timingFunction: "ease" }

<button className="transition-colors duration-200 ease-in-out hover:bg-red-700">
jQuery animations → Framer Motion:
tsx
// Captured: jQueryAnimations: [".fadeIn(300)"]

import { AnimatePresence, motion } from 'framer-motion';

<AnimatePresence>
  {isVisible && (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      transition={{ duration: 0.3 }}
    >
  )}
</AnimatePresence>

grep -rn '<button' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'

APPENDIX C: Font Migration

禁止在components/ui/目录外使用原生<input>(必须使用shadcn Input组件)

Read font data from
ir_capture
response (
fonts
section). For each detected font family:
Google Fonts →
next/font/google
:
tsx
import { Inter, Roboto } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  weight: ['400', '700'],  // from fonts[].weight
  style: ['normal', 'italic'],  // from fonts[].style
  display: 'swap',  // from fonts[].display or default to 'swap'
  variable: '--font-inter',
});
Custom Fonts →
next/font/local
:
tsx
import localFont from 'next/font/local';

const customFont = localFont({
  src: [
    { path: './fonts/custom-regular.woff2', weight: '400', style: 'normal' },
    { path: './fonts/custom-bold.woff2', weight: '700', style: 'normal' },
  ],
  variable: '--font-custom',
  display: 'swap',
});
Apply in
layout.tsx
:
tsx
export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${customFont.variable}`}>
      <body>{children}</body>
    </html>
  );
}
Configure in
tailwind.config.ts
:
ts
fontFamily: {
  sans: ['var(--font-inter)', ...defaultTheme.fontFamily.sans],
  custom: ['var(--font-custom)'],
},
Download font files: If
ir_capture
fonts[].src
contains URLs, download .woff2 files to
public/fonts/
for
next/font/local
.

grep -rn '<input' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'

APPENDIX D: shadcn Component Mapping (Phase 5)

禁止在components/ui/目录外使用原生<table>(必须使用shadcn Table组件)

Use shadcn components instead of raw HTML. Reference this during Phase 5 (Modernize). Map legacy elements:
Legacy HTMLshadcn ComponentImport
<button>
,
<input type="submit">
<Button>
@/components/ui/button
<input type="text|email|password">
<Input>
@/components/ui/input
<textarea>
<Textarea>
@/components/ui/textarea
<select>
<Select>
@/components/ui/select
<table>
<Table>
@/components/ui/table
<dialog>
,
.modal
<Dialog>
@/components/ui/dialog
<nav>
<NavigationMenu>
@/components/ui/navigation-menu
.card
,
<article>
<Card>
@/components/ui/card
<input type="checkbox">
<Checkbox>
@/components/ui/checkbox
<input type="radio">
<RadioGroup>
@/components/ui/radio-group
.tabs
,
[role="tablist"]
<Tabs>
@/components/ui/tabs
.accordion
,
<details>
<Accordion>
@/components/ui/accordion
.breadcrumb
<Breadcrumb>
@/components/ui/breadcrumb
.pagination
<Pagination>
@/components/ui/pagination
Example conversion:
tsx
// WRONG - raw HTML
<button className="bg-red-600 text-white px-4 py-2 rounded">Submit</button>

// CORRECT - shadcn Button
import { Button } from "@/components/ui/button";
<Button className="bg-red-600 text-white">Submit</Button>
tsx
// WRONG - raw HTML table
<table><tr><td>Name</td></tr></table>

// CORRECT - shadcn Table
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
<Table>
  <TableBody>
    <TableRow>
      <TableCell>Name</TableCell>
    </TableRow>
  </TableBody>
</Table>
Raw HTML elements (
<button>
,
<input>
,
<table>
,
<dialog>
) should be replaced in Phase 5.
Code quality checks (run during Phase 5):
bash
undefined
grep -rn '<table' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'

No raw @font-face in CSS (must use next/font)

验证shadcn组件已被正确使用

grep -r "@font-face" <next-project>/src/
grep -rn "from ['"]@/components/ui/" <next-project>/src/ --include="*.tsx"

**所有命令必须返回**:无结果(除shadcn组件验证命令应返回匹配结果外)。这些检查在第五阶段强制执行,第三阶段迁移过程中不做要求。

---

No raw <button> outside components/ui/ (must use shadcn Button)

附录E:MCP工具参考

ir_capture

grep -rn '<button' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'
捕获页面的DOM树、计算样式、动画元数据和CLS分数。
ir_capture(port: number, route?: string, width?: number, height?: number)
确定性捕获流程:
  1. 等待网络空闲
  2. 强制加载所有懒加载图片
  3. 等待所有图片和字体加载完成
  4. 提取动画元数据(在动画完成前)
  5. 强制所有动画进入结束状态
  6. 等待DOM稳定
返回值
  • 布局模式(header、nav、footer、sidebar、main)
  • 组件层级结构
  • 包含计算样式的顶层元素
  • 动画数据:keyframes、animatedElements、transitionElements、jQueryAnimations
  • CLS数据:score、rating、top shifters
  • 字体数据
    fonts
    ):totalFontFaces、fontFaces、uniqueFamilies
  • UI模式
    uiPatterns
    ):包含shadcnComponentsNeeded的模式
  • 重定向数据
    redirects
    ):{ from, to, statusCode }数组——用于检测本地化
  • 内部链接
    internalLinks
    ):{ total, links[] }——用于路由验证

No raw <input> outside components/ui/ (must use shadcn Input)

ir_start

grep -rn '<input' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'
启动迁移监听模式。捕获两个网站的数据、对比差异、启动文件监听器,返回第一个问题。
ir_start(legacyPort, nextPort, legacyRoute?, nextRoute?, watchPaths?)
返回值:
{ status: "watching", match: {...}, totalIssues: N, firstIssue: {...} }

No raw <table> outside components/ui/ (must use shadcn Table)

ir_next

grep -rn '<table' <next-project>/src/ --include=".tsx" --include=".jsx" | grep -v 'components/ui/'
获取下一个需要修复的问题。会被CLS检查和回归问题阻塞。
ir_next(skip?: boolean)
  • skip: true
    —— 修复失败后跳过当前问题,进入下一个问题
  • 返回值:包含选择器、位置、样式和修复建议的问题详情。

Verify shadcn components ARE being used

ir_status

grep -rn "from ['"]@/components/ui/" <next-project>/src/ --include="*.tsx"
**ALL MUST RETURN**: No results (except shadcn verification which SHOULD return matches). These checks are enforced in Phase 5, not during Phase 3 migration.

---
迁移进度:匹配度百分比、按严重程度分类的问题数量、CLS分数、回归状态。

APPENDIX E: MCP Tools Reference

ir_inspect

ir_capture

Capture a page's DOM tree, computed styles, animation metadata, and CLS score.
ir_capture(port: number, route?: string, width?: number, height?: number)
Deterministic capture sequence:
  1. Waits for network idle
  2. Forces all lazy images to load
  3. Waits for all images and fonts
  4. Extracts animation metadata (BEFORE finishing animations)
  5. Forces all animations to END STATE
  6. Waits for DOM stability
Returns:
  • Layout patterns (header, nav, footer, sidebar, main)
  • Component hierarchy
  • Top-level elements with computed styles
  • Animation data: keyframes, animatedElements, transitionElements, jQueryAnimations
  • CLS data: score, rating, top shifters
  • Font data (
    fonts
    ): totalFontFaces, fontFaces, uniqueFamilies
  • UI patterns (
    uiPatterns
    ): patterns with shadcnComponentsNeeded
  • Redirects (
    redirects
    ): Array of { from, to, statusCode } — useful for locale detection
  • Internal links (
    internalLinks
    ): { total, links[] } — for route validation
通过选择器或文本检查元素。
ir_inspect(selector: string, site?: "legacy" | "next" | "both")
  • site="legacy"
    "next"
    :返回单一侧元素的完整样式、矩形区域和代码片段
  • site="both"
    (默认值):返回两侧元素的对比数据及样式差异

ir_start

ir_stop

Start migration watch mode. Captures both sites, diffs, starts file watcher, returns first issue.
ir_start(legacyPort, nextPort, legacyRoute?, nextRoute?, watchPaths?)
Returns:
{ status: "watching", match: {...}, totalIssues: N, firstIssue: {...} }
.
停止监听模式并关闭浏览器。

ir_next

Get next issue to fix. Blocks on CLS gate and regressions.
ir_next(skip?: boolean)
  • skip: true
    — skip current issue after failed attempts, advance to next
  • Returns: Issue with selector, position, styles, and fix suggestion.

ir_status

Migration progress: match percentages, issue counts by severity, CLS score, regression state.

ir_inspect

Inspect element by selector or text.
ir_inspect(selector: string, site?: "legacy" | "next" | "both")
  • site="legacy"
    or
    "next"
    : full styles, rect, snippet for one side
  • site="both"
    (default): side-by-side comparison with style diffs

ir_stop

Stop watch mode and close browser.