Community编程与开发github.com

ethanplusai/astro-conversion-kit

将站点捕获结果冻结为基线(baseline/),并验证Astro构建是否与基线完全一致——涵盖每条路由、重定向(单跳,通过主机模拟)、SEO头部字段、可见文本、内部链接、媒体文件、站点地图条目、Feed、表单、第三方脚本和安全标头,并通过 exceptions.json 记录已批准的差异、通过 proposals.json 记录改进建议。适用于:捕获后构建基线、为 Vercel 或 Cloudflare 编译 redirects.csv、在调用任何页面转换完成之前、当某个门禁失败需要排查时,或在暂存部署与基线对比验证时使用。

astro-conversion-kit 是什么?

ethanplusai/astro-conversion-kit 是一个 AI 智能体技能,专为 Astro 站点迁移和持续验证而设计。其核心工作流程是:首先从捕获的站点快照(capture/)构建一个只读的基线(baseline/),然后在每次构建时运行一组严格的“门禁”(gates),逐项验证构建输出是否与基线完全一致。该技能支持 Vercel 和 Cloudflare 部署平台,能够模拟主机行为来测试重定向规则,并合理处理多跳重定向。门禁覆盖了路由、SEO 元数据、可见文本、内部链接、媒体文件(字节级对比)、站点地图、Feed、表单(含 POST/GET 表单的端点验证)、第三方脚本、安全标头等十类项目。所有不可避免的差异必须记录在 exceptions.json 中,改进应在 proposals.json 中提出——技能严格遵循“永不编辑基线、永不软化门禁”的原则。此外,它还提供 `verify-live` 命令,用于对已经部署的暂存或生产环境进行 HTTP 级别的回归检查,确保线上行为与基线一致。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ethanplusai/astro-conversion-kit/tree/HEAD/skills/parity-gates

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

一致性门禁 (parity-gates)

从 capture/ 中构建基线一次,然后每次构建都必须通过所有门禁,直到全部通过。 门禁只读取 baseline/,绝不访问线上站点,因此即便旧主机已经下线,构建仍然可以被验证。

黄金法则

默认构建等于线上站点,且门禁永远不会被软化。 没有任何标志可以将失败变为警告。由此衍生出四条规则:

  1. 绝不要手动编辑 baseline/。 唯一的例外是 baseline/scripts.json 中的 keep 字段,用来标记某个供应商被移除(将其设为 false,并记录决策)。
  2. 差异是条目,而不是修改。 不可避免的差异记录在 exceptions.json 中;改进记录在 proposals.json 中(仅在 PROPOSALS=on 时生效)。两者均在 references/exceptions.md 中有说明。
  3. 跳过不等于通过。 某个门禁无法检查某项目时,会在其摘要行中显示 N 个跳过 (原因)。每个跳过都必须报告。
  4. 报告错误门禁为工具包缺陷;绝不要在站点中修改门禁,也不要修改基线。

运行脚本

由 convert-site 创建的站点,脚本位于 scripts/ 中;在站点根目录下,以以下 npm 脚本方式运行,选项放在 -- 之后。 在非脚手架站点中,请在站点根目录下以以下 node 命令形式运行,其中 <skill-dir> 是此技能文件夹的绝对路径(位于已执行 npm install 的工具包副本中)。 每个脚本都支持 --help;请运行它而不是阅读源码。 退出码 0 表示通过,1 表示门禁失败或基线漂移,2 表示用法错误或缺少输入。

1. 基线(一次)

npm run baseline
node <skill-dir>/scripts/build-baseline.mjs --capture capture --out baseline    # 备用命令

它需要 capture/inventory.json、media.json(来自捕获的 media.mjs)和 templates.json (来自 templates.mjs),如果缺少其中之一,则退出码 2 并指明需要运行的步骤。 它会写入 baseline/{routes,seo,redirects,media,forms,scripts,meta,counts}.json 和 baseline/text/, 以及站点根目录下的 redirects.csv。没有目标值的短链接会被排除并在 stderr 中列出。 捕获链中的每个中间跳转都会获得单独的一行,直指最终目标 (例如 /old-services → /old-services/ → /services/ 也会生成 /old-services/,/services/, 注意标记为 hop of /old-services),除非该跳转本身就是一个构建路由, 使得指向该链中任何 URL 的链接都能一次跳转到位。 baseline/ 和 redirects.csv 一起提交。--check 会重新统计 baseline/ 并与 counts.json 对比(漂移则退出码 1)。 每个文件的格式定义见 references/contracts.md。

2. 重定向

redirects.csv(from,to,status,source,note)是站点重定向的唯一数据源。 source 为来自捕获的 crawl、sitemap、shortlink 或 seed; 你添加的行使用 decision,其 note 字段标明决策 ID。 切勿手动编辑编译后的输出;请使用以下命令进行编译:

npm run redirects:build
node <skill-dir>/scripts/build-redirects.mjs --csv redirects.csv --host vercel --out vercel.json --trailing-slash always    # 备用命令

--host cloudflare --out public/_redirects 用于 Pages(Astro 会将其复制到 dist/ 中)。 Vercel:redirects 键被替换,其他所有键保持不变;trailingSlash 会被移除, 并以编译器自身的 308 重定向规则形式写入,这些规则放在站点规则之后, 确保无结尾斜杠的源路径仍然只需要一次跳转。 只有以静态文件扩展名结尾的路径才会被这些规则视为文件, 不区分大小写:包括 Web 类型、文档、媒体和归档类型(上传文件夹中的 docx、xlsx、csv、mov、wav、heic、gz 等), 以及 dist/ 中(否则是 public/)的所有扩展名,因此请在构建之后重新运行; /upgrade-guide/3.5/ 是一个页面,会保持其结尾斜杠。 如果存在会被这些规则重定向的已构建文件,则重定向门禁会报告“已构建文件被重定向”并失败。 Cloudflare:查询行(/?p=7)无法被 _redirects 匹配,会输出到 cloudflare-query-redirects.csv,需要您手动创建为 Single Redirect(属于“需要您操作”的项目)。 目标路径与自身相同(例如 /?p=2 到 /)的查询行是无效的,在两个主机平台上都会被排除。

捕获到的行(除 decision 外的任何 source)代表一个线上 URL,不包含参数: 对于 Vercel,其中的每个 : 和 * 都会被转义 (例如 /foo:bar/ 写成 /foo\:bar/,/trackshttps://www.example.com/x/ 也是如此, 这是一个破损的线上链接,被捕获时仍被跟踪);// 保持不变。 只有 decision 行可以使用 :name 参数和 * 通配符, 但在这种情况下,数字之前的 :(如端口号)也会被转义。Vercel 拒绝裸的 :, 门禁对其的模拟也同样如此。 没有有效格式的源(没有前导 /、包含空白字符、控制字符; 在 Cloudflare 上还包括捕获到的 :<name> 或 *,因为 _redirects 无法对其进行转义) 会被丢弃,在 stderr 中列出,并作为 { source, reason } 写入 redirects-skipped.json。 该文件与 redirects.csv 并列(站点根目录),绝不会放在 public/ 下,因此不会被部署。 重定向门禁和 verify-live 会将此类行计为跳过,而不是通过。 当某路径应该完全没有重定向时,请改用“无重定向”异常(第 4 节): { "kind": "redirect", "path": "<source>", "reason": "...", "action": "none", "approvedBy": "...", "date": "..." }。

3. 验证

npm run build
npm run verify -- --endpoint https://forms.example.com/f/abc --thank-you /thank-you/
node <skill-dir>/scripts/verify/run-all.mjs --endpoint https://forms.example.com/f/abc --thank-you /thank-you/    # 备用命令

run-all 按字母顺序运行所有十个门禁,为每个门禁传递其接受的选项, 并在第一个失败时停止。共享选项:--paths(逗号分隔列表,* 匹配任意内容)、 --json、--origin(链接、SEO、站点地图:构建的目标主机)、--allow-extra(路由)、 --endpoint、--honeypot、--thank-you(表单;当基线中存在 POST 表单时,--endpoint 是必需的)、 --host、--config(重定向、链接)、--csv(重定向)、--media-plan(媒体)。 修复第一个失败,重新构建,重新运行。单独运行一个门禁:node <skill-dir>/scripts/verify/<gate>.mjs。

门禁通过条件跳过含义
forms每个线上表单都出现在其页面上,字段名称、顺序和方法相同;POST 表单的 action 是 --endpoint 并携带 --honeypot;GET 表单(如搜索框)保持线上的 action 路径,不需要这两个参数(“N 个搜索/GET 表单已比较”);如果 POST 表单的线上 action 位于其他可注册域名上(例如新闻通讯列表主机;www、http、子域名以及 WordPress 处理器如 admin-ajax.php 或 ?wpcf7 被视为同一站点),则保持原样,按其完整 action URL 进行像 GET 表单一样的比较,且不需要 --endpoint(“N 个表单保持原样(提交到 )”)没有 --thank-you:POST 表单的感谢页面未被检查
headers主机配置在所有路径上发送 nosniff、Referrer-Policy、X-Frame-Options 或 CSP frame-ancestors,并且当站点不可索引时,在每个路径上发送 X-Robots-Tag: noindex(或者对于 Vercel,在每个构建页面上设置 meta robots noindex);预览主机规则(https://*.pages.dev/*)仅针对预览主机生效;当 PUBLIC_INDEXABLE=true 时,禁止在所有路径上设置 noindex
links每个内部 href 和 src 解析到一个已构建的页面或文件、一个重定向源或基线媒体文件,或者通过编译后的规则(vercel.json 或 dist/_redirects:包括斜杠规则和所有 redirects.csv 行)在一个跳转内到达目标构建页面,如同线上被重定向一样(“N 个链接通过重定向解析”);两次跳转或缺少目标则失败;如果没有主机配置,/about 在构建服务于 /about/ 的情况下也会失败
media每个基线媒体文件都由构建以相同的字节数提供(--paths 通过路径或引用它的页面来选择文件):在 dist/ 中(计划模式为 repo);在计划的 prefix 下,在存储清单(store)中或仍在线上(live-temporary);在前缀之外,在所有模式下都在 dist/ 中。当 PUBLIC_INDEXABLE=true 时,前缀下的每个文件使用 live-temporary 都会失败(发布构建可能无法从线上站点提供媒体;没有前缀的计划覆盖所有文件),并且仍然检查前缀外的文件是否在 dist/ 中暂存构建的计划模式 live-temporary:前缀下的文件从线上获取,不被检查
redirects每个基线重定向和 CSV 行在一个跳转内以相同状态码响应,并且目标在 dist/ 中返回 200(或者是外部链接、或者位于基线 meta.json 中 excluded 前缀下:此时仅检查第一个跳转);没有源同时也是一个已构建的文件;没有已构建的页面或文件被从其自身的 URL 重定向(“built page redirected by rule ”;没有异常覆盖它,请修复规则);具有“无重定向”异常(kind: "redirect",action: "none")的路径根本不会被重定向无效的查询行、Cloudflare 上的任何查询行,或 redirects-skipped.json 中列出了原因(not compiled)且没有有效格式的源
routes每个页面、文章、归档和 Feed 都以 200 状态返回并且已构建,noindex 路由仍然标记为 noindex,没有额外的构建内容(捕获自身的脚本位于 baseline/js.json 中、样式表位于 baseline/css.json 中的线上路径、线上站点地图名称位于 baseline/sitemaps.json 中以及启动图标集不被视为额外内容);捕获时状态码不是 200、3xx、404 或 410 的路由会失败,报告为 not captured: <status>,除非 routes 异常跳过它或为其指定了构建替代项routes 异常中带有 to: "skip":该路由从未被捕获
scripts每个保留的供应商脚本恰好出现在其线上页面中,并具有相同的 ID;已移除的供应商脚本不出现在任何页面上;没有新增的供应商脚本
seo每页的 Title、description、canonical、robots、lang、h1、h2、og 和 JSON-LD @types 相等;站点 URL 位于站点的源(origin)上。除非 PUBLIC_INDEXABLE=true,否则 robots 必须携带暂存的 noindex, nofollow,其余内容与线上相比除这两项外保持一致;发布构建时,robots 会进行完全比较
sitemapsdist/(Astro 的 sitemap-index.xml,或 baseline/sitemaps.json 中的线上名称,例如 wp-sitemap.xml 或 sitemap_index.xml)中的每个顶级站点地图都恰好列出源(origin)上可索引的 200 页面(百分比转义比较时不区分大小写:%c3 等于 %C3);robots.txt 中的每行 Sitemap: 指向上线源(origin)并命中了 dist/ 中的一个文件,或者是线上 robots.txt(capture/robots.txt)逐字携带的行;每个 Feed 都已构建并包含正确数量的条目
text每个页面的可见文本与 baseline/text/ 中的内容相等(单词差异对比,显示前后各 5 个单词)

使用 --exclude 进行的捕获,其路径前缀记录在 baseline/meta.json 的 excluded 字段中: links 门禁不检查位于此类前缀下的链接(就像链接到其他主机的链接一样,报告为 "N links under excluded paths ... not checked"), site-maps 门禁接受位于此类前缀下的 robots.txt Sitemap: 行——前提是线上的 robots.txt 逐字携带了该行(同一主机上多站点子站点的站点地图)。

4. 差异与决策

当构建无法或不应与线上匹配时,请阅读 references/exceptions.md。 简要说来:一个异常条目格式为 { path, field, from, to, reason, approvedBy, date }; from: "" 表示构建新增的值,to: "" 表示构建移除的值; 不再匹配的异常,或命名了门禁未检查的路径的异常,会使门禁失败。 线上站点的每个异常(如缺少 H1、无效标签、断开的链接)默认都会被复现, 并且会有一个决策行(id、topic、choice、default、reason、cost if wrong)来说明这一点以及修复方案。

有一种记录使用 kind 而不是 field: { "kind": "redirect", "path": "<source>", "reason": "...", "action": "none", "approvedBy": "...", "date": "..." } 表示“此路径有意无重定向”(针对一个不应产生重定向的破损链接所记录的线上重定向)。 每个键都是必需的:它移除了一个线上行为,因此像任何异常一样需要签核。 重定向门禁会检查该路径,只有在规则不重定向它时才通过; 如果门禁不检查该路径,则失败并显示 path not checked; verify-live 会跳过它并给出原因。其他所有门禁都忽略此记录。

5. 暂存、提案与发布构建

PUBLIC_INDEXABLE 和 PROPOSALS 会改变 Astro 的构建内容, 因此请务必在构建时设置它们,而不仅仅是在门禁运行时设置; 在未设置这些变量的构建上运行门禁,检查的是错误的站点。 仅当构建本身是为特定主机创建时,才传递 --origin; 脚手架构建的 site 为线上源(origin),因此其 canonical 和站点地图保持不变。

npm run build && npm run verify -- --endpoint ... --thank-you ...                                       # 暂存
PROPOSALS=on npm run build && npm run verify:proposals -- --endpoint ... --thank-you ...                 # 提案
PUBLIC_INDEXABLE=true npm run build && npm run verify:release -- --endpoint ... --thank-you ...          # 发布

在暂存部署之后,通过 HTTP 对照基线检查它。发布部署以相同方式检查,但不使用 --expect-noindex:

npm run verify:live -- --base https://my-site.vercel.app --expect-noindex
node <skill-dir>/scripts/verify-live.mjs --base https://my-site.vercel.app --expect-noindex    # 备用命令

--bypass "$VERCEL_AUTOMATION_BYPASS_SECRET" 仅在暂存被有意保护时使用(绕过值来自环境变量,绝不要粘贴到聊天中); 公开的暂存别名则不需要。

它会检查每条基线路由是否返回 200(或使用 --sample 抽样)、每个重定向是否在一个跳转内返回相同类型的状态码、 页面上的 noindex 设置是否与 --expect-noindex 一致、检查站点地图,并检查 20 个媒体文件的字节数 (或使用 --media-manifest,其 url 可以是存储的绝对 URL,也可以是在 <base><path> 处检查的站点路径)。 列在 redirects-skipped.json 中的重定向或被“无重定向”异常覆盖的重定向 (--skipped、--exceptions;默认都从站点根目录读取)会被跳过,并附上原因。 每个请求在 30 秒后超时,这是检查失败而不是封锁。退出码 3 表示被封锁:请等待并重新运行。 退出码 3 并附带 deployment protection: 表示 Vercel 以登录重定向(302 到 vercel.com/sso-api)应答: 请使用项目的生产别名(默认公开)而不是原始部署 URL;仅当暂存被有意保护时,

…

[内容因长度截断]

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

ethanplusai/astro-conversion-kit

Crawl a live website, read-only and paced, into a capture/ folder that a rebuild is checked against — every page's HTML, text and head tags, redirects with every hop, links, third-party scripts, the media set, stylesheets, fonts, logo and icons, feeds, templates, and live screenshots at three widths. Use before converting or rebuilding a site, when asked to crawl, capture, snapshot or inventory a site, or when a capture stopped on blocks and needs resuming.

ethanplusai/astro-conversion-kit

Convert a live website (WordPress first; Drupal, Webflow, Squarespace, Wix and static sites too) into a static Astro site with the same URLs, content, head tags, media, redirects and look, proven by parity gates and a pixel-level visual review, ending in one pull request, a noindexed staging deploy and a launch runbook. Use when asked to convert, rebuild, migrate or clone a site to Astro, when handed a site URL with this kit's link, to resume a conversion from .conversion/state.json, or with --apply <id> to apply one suggestion as a proposal.

ethanplusai/astro-conversion-kit

Audit a live or staging website and report what would go wrong at launch — crawl and inventory, indexability, redirects for a domain cutover, links, images, forms, accessibility, performance, analytics, content sanity, and full-page screenshots for a visual pass. Use before or right after a site launch, when asked to QA, audit, or review a website, when a site is moving to a new domain, or when somebody asks whether a site is ready to go live. Read-only: it never submits a form, clicks a control, or edits anything.

ethanplusai/astro-conversion-kit

The moment DNS moves and for the thirty days after — verifies the cutover (DNS propagation, certificate, http and www redirects, the noindex, robots and sitemap on the live host, every redirect the audit mapped, forms, analytics, the staging copy, the old host, security headers), diffs production against the pre-launch baseline, and schedules the watch. Use when asked did the launch work, is the site live, what should I check after launch, the domain just switched, or when a site went live in the last month and something looks wrong. It is read-only and exits 0 whatever it finds.

相关技能