关于 create-site
create-site 是指导创建 Power Pages 站点的技能,涵盖需求梳理、模板选择和实现。它支持 SPA 框架及 Enhanced 模型的非 SPA 站点,并为两种路径提供不同工作流。
主要功能
- 01梳理站点用途与受众
- 02从模板目录选择模板
- 03SPA 与非 SPA 创建路径
- 04脚手架与实时预览反馈
- 05设计和本地化指导
适用场景
- 01为明确受众创建门户
- 02选择模板或从头开始
- 03构建 Power Pages SPA 站点
- 04创建 Enhanced 模型非 SPA 站点
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill create-site安装后在 Claude Code 中输入/create-site确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/create-site-plan.htmlnon-spa-creation-status-template.htmlassets/angular/angular.jsongitignorepackage.jsonpowerpages.config.jsontsconfig.app.jsontsconfig.jsonassets/angular/public/.gitkeepassets/angular/src/index.htmlmain.tsstyles.cssassets/angular/src/app/app.component.tsapp.config.tsapp.routes.tsassets/angular/src/app/components/layout.component.tsassets/angular/src/app/pages/about.component.tshome.component.tsassets/astro/astro.config.mjsgitignorepackage.jsonpowerpages.config.jsontsconfig.jsonassets/astro/public/.gitkeepassets/astro/src/components/Footer.astroHeader.astroassets/astro/src/layouts/Layout.astroassets/astro/src/pages/about.astroindex.astroassets/astro/src/styles/theme.css详细介绍
原文在翻译后有更新,译文可能不是最新。
插件检查:运行
node "${PLUGIN_ROOT}/scripts/check-version.js"—— 如果输出消息,请在继续前展示给用户。
创建 Power Pages 代码站点
引导用户从最初的构思到站点部署,创建完整且达到生产质量的 Power Pages 代码站点。采用系统化流程:了解需求、搭建脚手架并立即启动、规划组件和设计、应用设计并进行实现、验证、评审和部署。
核心原则
- 打造令人印象深刻的第一印象:当首页替换脚手架后,站点必须立刻让用户惊叹,并在每个页面持续赢得赞赏。用户回答审美、氛围和品牌相关问题后,自信地自行决定字体、颜色、布局、图像和动效——设计参考(
${PLUGIN_ROOT}/references/下的design-aesthetics.md、page-blueprints.md、design-critique.md)定义了标准,其余由你发挥品味。 - 使用 TaskCreate/TaskUpdate:在所有阶段持续跟踪进度——先创建与路径无关的前置任务,再在选定创建路径后追加分支专属任务。
- 尽早搭建脚手架,有意图地进行设计:需求了解完毕后立即启动开发服务器,让用户有所见。然后在脚手架运行期间规划设计和功能——实现时应用选定的美学风格。
- 实时预览反馈循环:开始任何自定义工作前,开发服务器必须已启动。通过 Playwright(
browser_navigate+browser_snapshot)浏览站点,验证每项重要改动的结构。视觉设计评审通过一次脚本调用捕获每个页面,并在同一轮中打开截图(参见 5.7);仅在第 3 阶段提取品牌信息时使用 Playwright MCP 截图。 - 让脚手架加载器与实际进度保持同步:脚手架加载器会轮询
public/scaffold-status.json。每次AskUserQuestion前更新此文件(显示“等待你输入”的横幅,避免用户错过终端提示),并在第 5 阶段每个实现步骤开始前更新此文件(让进度条标签与当前操作相符,同时装饰性加载动画继续默认循环)。参见 实时预览状态协议。 - 使用有目的的视觉素材:每张图片都应解释、引导、演示或强化身份认同。优先使用由站点自身 UI 构成的产品展示画面,其次使用专门制作的内联 SVG,再次使用体现统一艺术指导的特定 Unsplash 摄影作品(参见 5.3)。绝不留下图片占位符或指向不存在文件的失效
<img>标签。 - Git 检查点:每完成一个页面和组件后都提交——每个页面和组件各自单独提交,以便回退造成问题的改动。
- 站点内容语言独立于 Dataverse 语言:所有面向用户显示的 SPA 字符串都使用已批准的内容语言生成。在此工作流中,Dataverse 和 Power Pages 平台管理的消息保持为英语(
en-US,LCID1033)。 - 默认支持双向文本:阅读
${PLUGIN_ROOT}/references/bidirectional-design.md。每个站点都必须采用方向中性的布局、混合内容边界以及支持脚本的排版,即使初始语言只有一种文本方向。后续添加 LTR/RTL 区域设置时,不应需要全面重新设计。 - 输出须为生命周期做好准备:遵循
${PLUGIN_ROOT}/references/site-modification-integrity.md,确保后续页面和组件能通过共享的修改后验证器,保留本地化、文本方向和内容扩展行为。
约束:仅支持静态 SPA 框架(React、Vue、Angular、Astro)。不支持:Next.js、Nuxt.js、Remix、SvelteKit、Liquid。
初始请求: $ARGUMENTS
实时预览状态协议
在脚手架加载屏幕可见期间(从阶段 2.6 到阶段 5 替换 Home 页面为止),加载器每 1.5 秒轮询一次 GET /scaffold-status.json。你写入 <PROJECT_ROOT>/public/scaffold-status.json 的 message 会显示为进度条下方的标签,而 awaitingInput 则控制“等待你输入”横幅。进度条上方的装饰性旋转图标会继续按内置短语循环显示;请及时更新进度条标签,让加载器反映当前实际进度。
重要原因:当显示加载器的浏览器接管用户屏幕后,终端中的提示可能会长时间得不到回应,因为用户没有意识到有内容正在等待处理。横幅能让用户一目了然。
文件格式(所有字段均为可选 — 不想更改的字段可以省略):
{
"message": "Creating Contact page",
"awaitingInput": false,
"inputPrompt": "Please check your terminal to respond."
}
message— 一条简短的现在分词短语,显示在加载器进度条下方的状态行中(替代默认的“开始使用…” / “正在设置基础设施…”循环文本)。如有帮助,可在短语中包含分组上下文(例如,"Creating Footer component (shared components)")。awaitingInput— 当其值为true时,加载器顶部会显示醒目的脉动横幅,并一直显示,直到此字段被清除。在每次调用AskUserQuestion之前设置此字段,并在用户回答后立即清除(设为false)。inputPrompt— 横幅的简短上下文(例如,"Choose a framework")。可选。
何时更新文件:
- 脚手架启动后(阶段 2 结束时):写入初始状态,例如
{ "message": "Planning your site", "awaitingInput": false }。 - 在脚手架可见期间运行的任何
AskUserQuestion调用之前(阶段 3、4,以及阶段 5 中任何脚手架内提示):将awaitingInput设为true,并填写简短的inputPrompt。用户回答后,再次写入并将awaitingInput设为false。 - 阶段 5 中的每个实施步骤之前 — 应用设计令牌、创建每个共享组件、创建每个页面、更新路由器、更新导航 — 将
message更新为对应的具体操作。例如:"Applying design tokens"、"Creating Navbar component"、"Creating Contact page"。 - 阶段 5 结束时,即 Home 页面已替换后:删除
public/scaffold-status.json,以免将其部署到网站中。
使用 Write 工具写入文件(原子覆盖)。无需先读取文件。
阶段 1:发现
目标:了解需要构建什么网站,以及它要解决什么问题
操作:
🚦 **关卡(计划 · create-site:1.purpose):**与路径无关的发现提示词,用于收集网站名称、用途和受众。在技能决定采用基于模板的路径还是从零搭建脚手架路径之前,先确定用户需要哪种网站。仅在“网站用途不明确”的分支中触发(见下方步骤 3)。
**触发条件:**阶段 1 中,
$ARGUMENTS未提供网站名称、用途或受众。 **询问原因:**用途或受众背景有误 → 分支决策和生成的网站计划也会有误;后续清理会很麻烦。 **取消后状态:**无 — 尚未开始搭建脚手架。
-
创建最精简的初始待办事项列表(参见进度跟踪):
- 了解网站需求
- 选择模板或决定从零开始
-
如果从参数中可以明确得知网站名称、用途和受众:
- 总结理解
- 确定网站类型(门户网站、仪表盘、落地页、博客等)
-
如果网站名称、用途或受众不明确,请使用
AskUserQuestion:问题 标题 选项 网站应该叫什么?(例如,“Contoso 门户网站”、“HR 仪表盘”) 网站名称 (自由文本 — 使用单个通用选项,让用户通过“其他”输入自定义名称) 网站的用途是什么? 用途 公司门户网站、博客/内容、仪表盘、落地页 目标受众是谁? 受众 内部(员工、合作伙伴)、外部(面向公众的客户) -
根据用户的回答,得出:
__SITE_NAME__(标题式大小写,例如Contoso Portal)- 初步的
__SITE_SLUG__(尽可能根据网站名称生成的小写 ASCII 短横线命名格式)
-
总结与路径无关的理解,并在继续之前向用户确认:
- 网站名称
- 网站用途/类型
- 目标受众
不要在阶段 1 询问框架或项目位置。每种创建路径都会在阶段 1.5 选定路径后询问项目位置。
受众对网站生成的影响:
- 内部:优先考虑任务完成 — 状态、下一步操作、数据表、仪表盘、身份验证、导航深度。出彩之处在于清晰度、恰到好处的信息密度,以及细节上的精致,而不是营销型主视觉。
- 外部:优先打造令人印象深刻的第一印象、在疑虑出现时以证据支撑的说服性页面叙事、适合 SEO 的结构以及联系表单。
输出:清晰说明网站用途、受众、网站名称和初步技术 slug。
阶段 1.5:模板分支决策
目标:在与路径无关的发现阶段之后,引导用户进入合适的创建路径。
当前实现状态:模板发现、选择、辅助解决方案导入、打包的 SPA 克隆/上传、可选种子数据、激活、实时网站预览和终端遥测功能,均已针对
kind: "spa"实现。传统目录条目已被接受,但在仅解决方案的配置流程实现之前不会显示。用户始终可以选择从零开始,继续使用现有的脚手架流程。
操作:
-
将选择模板或决定从零开始标记为
in_progress。 -
获取模板目录:
node "${PLUGIN_ROOT}/scripts/fetch-template-catalog.js"在本次运行中,对每个模板资源请求都使用返回的不可变提交 SHA。仅在有意进行测试或回滚时传入
--ref <tag-or-branch>。 评估 JSON 结果:- 如果
ok: false:告知用户模板暂时不可用,并继续采用从头开始的路径。这是附加处理;目录加载失败绝不能阻止create-site。 - 如果
ok: true,但selectableCatalog.templates为空:告知用户目前没有可用的受支持 SPA 模板,并继续采用从头开始的路径。不要提供catalog.templates中kind为traditional的条目。 - 如果有可用的受支持 SPA 模板:在以下所有匹配、预览、浏览和选择步骤中,使用
selectableCatalog.templates。仅将catalog.templates用作完整的已下载清单。
- 如果
-
根据 Phase 1 上下文(
$ARGUMENTS、网站名称、用途、受众,以及用户提到的任何框架)对模板系列进行语义匹配:- 使用每个系列模板的
displayName、description、keywords、audience、可用的变体框架以及任何特定于变体的预览信息。 - 不要计算数值分数,也不要编写排名脚本。关键词用于指导 Agent 判断,不进行计数。
- 将模板系列视为面向用户的模板,将框架变体视为可安装的软件包。一个系列可以有多个变体(
react、vue、angular、astro)。
- 使用每个系列模板的
-
语义匹配后,询问用户如何继续:
匹配情况 问题 标题 选项 有一个或多个明确匹配项 我找到了适合你网站的模板。你想如何继续? 创建路径 显示匹配的模板(推荐)、浏览所有模板、从头开始创建 没有明确匹配项 我没有找到适合你网站的模板。你想如何继续? 创建路径 浏览所有模板、从头开始创建(推荐) 根据回答分支:
- 显示匹配的模板:将匹配的一个或多个系列设置为
TEMPLATE_PREVIEW_FAMILIES,然后继续进行浏览器预览。 - 浏览所有模板:将
selectableCatalog.templates中的所有条目设置为TEMPLATE_PREVIEW_FAMILIES,然后继续进行浏览器预览。 - 从头开始创建:设置
CREATION_PATH = "from-scratch",然后继续询问后续的框架和位置问题。
- 显示匹配的模板:将匹配的一个或多个系列设置为
-
渲染
TEMPLATE_PREVIEW_FAMILIES,以供浏览器预览:- 在渲染之前,将每个
previewImages资源下载到以 SHA 为键的缓存中:
将每个预览图片路径替换为返回的node "${PLUGIN_ROOT}/scripts/fetch-template-artifact.js" --sha "<catalog-sha>" --artifactPath "<preview-image-path>"localUrl。如果预览下载返回ok: false,则从图库中省略该图片并继续;缺少预览图片不应妨碍使用其他方面有效的模板。 - 编写一个临时 JSON 文件,其中包含
TEMPLATES_JSON数组,列出用户应预览的模板系列及缓存的预览图片 URL。包含每个系列可用的变体,以便浏览器显示框架徽标/选项卡,但保持浏览器只读。 - 运行:
告诉用户使用浏览器之前,先评估 JSON 结果:node "${PLUGIN_ROOT}/scripts/render-template-browser.js" --templatesJsonPath "<temp-json>" --outputPath "<temp-html>" --openstatus: "ok":继续;浏览器预览已生成并打开。status: "invalid":不要继续选择模板。向用户展示验证错误,根据匹配到的系列重新构建TEMPLATES_JSON文件,然后重新渲染。如果仍然无效,则回退到从头开始,而不是显示空白或不完整的模板浏览器。status: "ok"且opened: false:HTML 已通过验证,但无法自动打开浏览器。显示文件路径,并在用户有机会手动打开后,再继续使用终端进行选择。
- 浏览器视图为只读,仅用于浏览模板功能和可用的框架变体;终端
AskUserQuestion仍是作出选择的界面。
- 在渲染之前,将每个
-
提出以下
AskUserQuestion问题之一:匹配情况 问题选项 一个强匹配的系列和框架 使用 <displayName>-<framework>(推荐),选择其他框架,查看所有模板,从头开始一个强匹配的系列,但没有匹配的框架 每个可用框架各一个选项,查看所有模板,从头开始 有多个可能匹配的系列 为每个入围系列提供一个选项,查看所有模板,从头开始 浏览完整目录 为每个系列提供一个选项,从头开始 用户在匹配模板分支中选择查看所有模板时,将
TEMPLATE_PREVIEW_FAMILIES设置为selectableCatalog.templates中的所有条目,渲染以系列为先的 SPA 模板图库,并使用浏览完整目录选项再次提问。如果所选系列有多个变体,则再通过终端询问一次框架。 -
根据用户的选择分支:
- 已选择模板系列和框架变体:
-
将
SELECTED_TEMPLATE设置为系列条目,并将SELECTED_TEMPLATE_VARIANT设置为准确的框架变体。根据固定的目录 SHA 一次性解析所选模板资源。结果会合并该系列支持的解决方案与所选变体的网站代码:node "${PLUGIN_ROOT}/scripts/fetch-template-variant.js" \ --sha "<catalog-sha>" \ --catalogPath "<catalogPath from fetch-template-catalog.js>" \ --kind "<SELECTED_TEMPLATE.kind>" \ --templateId "<SELECTED_TEMPLATE.id>" \ --variant "<SELECTED_TEMPLATE_VARIANT.variantKey>" \ --solutionSettingsJson '<JSON.stringify(SELECTED_TEMPLATE.solutions || [])>'使用返回的
websiteCodePath和solutions。按返回顺序处理solutions;不要在此技能中重新发现、调整顺序或重新验证模板资源。 -
如果结果为
ok: false,告知用户所选框架变体不可用或无效。如果同一系列还有其他可用的框架变体,优先提供这些选项;否则提供从头开始或停止。如果该变体的包未通过验证,不要发送template_used。如果用户改为从头开始,推荐其之前选择的框架。 -
如果结果为
ok: true,将CREATION_PATH = "template"、SELECTED_TEMPLATE_SOLUTIONS = <result.solutions>和SELECTED_TEMPLATE_WEBSITE_CODE = <result.websiteCodePath>。静默运行template_used遥测命令(失败时关闭),然后立即追加模板预安装任务(见进度跟踪);在确认重新安装策略后再追加执行任务。不要继续进入阶段 2。 不要向用户提及此遥测命令,也不要输出其结果。node "${PLUGIN_ROOT}/scripts/emit-create-site-template-outcome.js" \ --eventName template_used \ --templateId "<SELECTED_TEMPLATE.id>" \ --templateKind "<SELECTED_TEMPLATE.kind>" \ --framework "<SELECTED_TEMPLATE_VARIANT.framework>" \ --audience "<internal|external from Phase 1 discovery>"--audience是在阶段 1 中记录的网站受众(internal或external),不是目录清单中的模板audience人设数组。不要包含网站名称、URL、子域名、自由文本形式的用途说明或任何其他可识别用户身份的信息。 -
将选择本地模板目录标记为
in_progress。询问要在哪里创建本地模板项目:
问题 标题 选项 我应该在哪里创建模板网站的本地文件? 项目位置 当前目录、在当前目录中新建文件夹(推荐)、其他任意目录 确定克隆目标:
- 当前目录:使用
<cwd>。 - 在当前目录中新建文件夹:使用
<cwd>/<__SITE_SLUG__>/。 - 其他任意目录:询问完整路径,并将其解析为绝对路径。
目标目录必须不存在或为空。检查目录时不要创建它。如果目录非空,询问用户选择其他目录。将解析后的路径存储为
TEMPLATE_CLONE_OUTPUT_DIRECTORY,确认“模板项目将在<resolved path>下创建。”,并将选择本地模板目录标记为completed。 - 当前目录:使用
-
- 从头开始或目录不可用:将
CREATION_PATH = "from-scratch",然后继续下文。
- 已选择模板系列和框架变体:
-
仅对模板路径:
- 将 Resolve target environment 标记为
in_progress。 - 通过共享身份验证辅助程序解析目标环境:
将返回的node "${PLUGIN_ROOT}/scripts/resolve-template-import-context.js"environmentUrl用于后续模板工作流。PAC 导入包装程序使用当前 PAC 身份验证和此显式目标 URL;Dataverse 预检和种子辅助程序会在内部获取 Azure 令牌,因此凭据不会在 JSON 中返回,也不会在任务之间传递。如果ok: false,请显示错误并停止,不要继续导入。由于尚未尝试导入,因此不要发出template_import_failure;选择模板路径时已经发出过template_used。
- 将 Resolve target environment 标记为
🚦 关卡(同意 · create-site:1.5.confirm-environment): 在运行模板安装预检前,确认已解析的目标环境。
触发条件: 第 1.5 阶段,在
resolve-template-import-context.js返回environmentUrl后,以及任何 CLI 租户、解除.js阻止、语言、解决方案导入、种子或激活步骤之前。 询问原因: PAC 身份验证可能指向与用户预期不同的 Dataverse 环境。即使是预检也可能检查或修改环境级设置,因此该技能不得在未告知用户的情况下继续。 取消后的保留内容:template-cache— 模板目录资源、发现的解决方案源文件以及网站代码可能已缓存在本地;如果在此处取消,则尚未对组织进行任何更改。
-
将 Resolve target environment 标记为
completed,并将 Confirm target environment 标记为in_progress,然后在任何环境预检前,请用户确认已解析的目标环境:问题 标题 选项 将 <environmentUrl>用作此模板安装的目标环境?确认环境 是,使用此环境(推荐)、否,从头开始、取消 - 是:继续执行下方的预检。
- 否,从头开始:设置
CREATION_PATH = "from-scratch",并继续处理延后的框架/位置问题。不要运行环境预检或导入模板。 - 取消:在环境预检前停止。由于尚未尝试导入,因此不要发出
template_import_failure;选择模板路径时已经发出过template_used。
-
将 Confirm target environment 标记为
completed,并将 Validate CLI tenant alignment 标记为in_progress,然后确认 PAC CLI 和 Azure CLI 已通过同一租户的身份验证,再执行任何依赖这两个 CLI 的导入前检查:node "${PLUGIN_ROOT}/scripts/validate-cli-tenant-alignment.js" --envUrl "<environmentUrl>"ok: true:将 Validate CLI tenant alignment 标记为completed。ok: false:显示错误,以及存在时的pacTenantId、azTenantId和tokenTenantId字段。在导入前停止,并告知用户将 PAC 身份验证或 Azure CLI 切换到同一租户,然后重新运行此技能。由于尚未尝试导入,不要发出template_import_failure。
-
将 Validate JavaScript unblock requirement 和 Validate Dataverse language requirements 都标记为
in_progress。并行运行.js附件解除阻止试运行和 Dataverse 语言检查,因为这两项都是针对已确认目标环境的只读预检查:node "${PLUGIN_ROOT}/scripts/lib/fix-blocked-attachments.js" --envUrl "<environmentUrl>" --extensions js --dry-run --quiet node "${PLUGIN_ROOT}/scripts/check-available-languages.js" --envUrl "<environmentUrl>" --requiredLocaleIds "<SELECTED_TEMPLATE_VARIANT.requiredDataverseLanguages or SELECTED_TEMPLATE.requiredDataverseLanguages comma-separated>"在决定下一步之前,先获取两项检查的 JSON 结果。语言检查完成前,不要修改
blockedattachments;如果语言检查阻止导入,则转到语言要求问题,不要更改附件设置。 评估.js结果:wasBlocked不包含js:将 Validate JavaScript unblock requirement 标记为completed;允许 JavaScript 附件。wasBlocked包含js:将 Validate JavaScript unblock requirement 保持为in_progress;这可能需要通过下方的.js解除阻止同意门槛,但要先处理语言检查失败,因为语言要求会阻止导入,且无需修改环境。 评估语言检查结果:ok: true且hasRequiredLanguages: true:将 Validate Dataverse language requirements 标记为completed;目标环境包含所选变体要求的所有 LCID;如果变体未覆盖这些要求,则回退使用模板系列的要求。ok: false:告知用户此技能无法验证可用的 Dataverse 语言,显示脚本错误,然后询问用户是切换为从头开始,还是停止。不要导入模板,也不要修改blockedattachments。ok: true且hasRequiredLanguages: false:说明所选模板要求的 Dataverse 语言 LCID 中,有missingLocaleIds列出的语言缺失。在执行任何解决方案导入变更前阻止流程。不要提供“仍然继续”选项,也不要修改blockedattachments。
🚦 门槛(同意 · create-site:1.5.unblock-js): 在上传网站代码前,预检查目标环境
blockedattachments设置中是否允许解除对.js的阻止。触发条件: 阶段 1.5 中,
fix-blocked-attachments.js --dry-run --extensions js报告目标环境阻止了.js。 询问原因: 支持解决方案就绪后,打包的 SPA 会通过pac pages upload-code-site上传。上传内容包含 JavaScript 文件;如果阻止了.js,上传就会失败。在导入解决方案前检查,可避免网站无法创建时遗留支持性成品。 取消后的状态: 只有在用户同意且更新部分完成时,才可能出现attachment-block-modified。在此处直接取消会保留原始blockedattachments值,且不会开始安装模板。
使用 `AskUserQuestion`:
| 问题 | 标题 | 选项 |
|----------|--------|---------|
| 此环境目前会阻止 `.js` 附件。除非取消阻止 `.js`,否则模板站点的代码上传将失败。现在是否仅从 `blockedattachments` 中移除 `js`? | 取消阻止 JavaScript | 是,取消阻止 `.js` 并继续(推荐),否,从头开始,取消 |
- **是**:运行辅助程序,不带 `--dry-run`,并保留所有其他被阻止的扩展名:
```bash
node "${PLUGIN_ROOT}/scripts/lib/fix-blocked-attachments.js" --envUrl "<environmentUrl>" --extensions js --quiet
```
确认 JSON 结果中的 `removed` 包含 `js`,或 `changed: true`,然后重新运行试运行检查;仅当 `.js` 不再被阻止时才继续。
- **否,从头开始**:设置 `CREATION_PATH = "from-scratch"`,并继续回答延后处理的框架/位置问题。不要导入模板。
- **取消**:在安装模板前停止。由于尚未尝试导入,不要发出 `template_import_failure`;选择模板路径时已发出 `template_used`。
6. 仅当语言预检无法继续时,使用 `AskUserQuestion`:
| 问题 | 标题 | 选项 |
|----------|--------|---------|
| 此模板需要 Dataverse 语言 LCID `<requiredLocaleIds>`,但目标环境缺少 `<missingLocaleIds>`,或无法完成语言检查。你希望如何处理? | 模板语言要求 | 从头开始(推荐),取消 |
- **从头开始**:设置 `CREATION_PATH = "from-scratch"`,并继续回答延后处理的框架/位置问题。不要导入模板。
- **取消**:在安装模板前停止。由于尚未尝试导入,不要发出 `template_import_failure`;选择模板路径时已发出 `template_used`。
🚦 关卡(同意 · create-site:1.5.template-import): 确认在当前 Power Platform 环境中安装所选模板。
触发条件: 第 1.5 阶段,模板变体及其解包后的解决方案已下载,且目标环境已确定。 询问原因: 安装可能会导入非托管的支持解决方案并创建新的代码站点。选错环境或模板会造成干扰,且无法干净地撤销。 取消后的状态:
template-cache— 已发现的解决方案来源和预览图片可能仍保留在由 SHA 标识的私有模板缓存中;尚未对组织进行任何更改。
-
如果语言预检通过,且
.js被阻止但用户已批准/验证通过,则将 验证 JavaScript 取消阻止要求 标记为completed。然后将 确认模板安装 标记为in_progress,展示模板和环境,并询问:列出清单元数据中
publishChanges: true的所有支持解决方案。 说明这些解决方案会使用pac solution import --publish-changes,使导入的表单、视图和其他自定义项在导入后立即可见。 说明 PAC 的发布步骤也可能发布目标环境中的其他待处理自定义项。问题 标题 选项 要将 <SELECTED_TEMPLATE.displayName>安装到<environmentUrl>吗?此操作会导入所有必需的非托管支持解决方案,对模板清单中声明了此操作的解决方案执行 PAC 发布更改步骤,克隆并构建网站代码,然后上传新的代码站点。发布步骤可能包含此环境中的其他待处理自定义项。如果有种子数据,则会在激活前应用。安装模板 是,安装此模板(推荐),否,从头开始,取消 - 不,从头开始:设置
CREATION_PATH = "from-scratch",然后继续回答延后的框架和位置问题。 - 取消:停止;尚未对组织进行任何更改。由于未尝试导入,不要发出
template_import_failure;选择模板路径时已发出template_used。
- 不,从头开始:设置
-
将 确认安装模板 标记为
completed。初始化TEMPLATE_SOLUTIONS_TO_IMPORT = []和TEMPLATE_SOLUTIONS_TO_SKIP = [],然后按返回顺序处理SELECTED_TEMPLATE_SOLUTIONS中的每个条目:node "${PLUGIN_ROOT}/scripts/inspect-template-solution.js" --solutionPath "<solution.solutionPath>" node "${PLUGIN_ROOT}/scripts/check-solution-installed.js" --solutionName "<solution.uniqueName>" --envUrl "<environmentUrl>" node "${PLUGIN_ROOT}/scripts/inspect-template-solution.js" --solutionPath "<solution.solutionPath>" --installed "<true|false>" --installedVersion "<version-or-empty>"不要从解决方案推导网站名称,也不要认为导入支持性解决方案会在
pac pages list -v中添加一行。如果变体验证后元数据检查返回ok: false,或check-solution-installed.js退出码为 1,则将该解决方案视为decision: "ask",而不是假定它不存在。-
decision: "import":将该解决方案添加到TEMPLATE_SOLUTIONS_TO_IMPORT。 -
decision: "confirm-update":告知用户此特定解决方案有较新版本可用,并在将其添加到TEMPLATE_SOLUTIONS_TO_IMPORT前征求确认。🚦 关卡(同意 · create-site:1.5.update-installed): 确认是否更新已安装的非托管模板解决方案。对每个需要更新的解决方案分别确认。
触发条件: 阶段 1.5 中,某个选定的模板解决方案已安装,且下载的源包含更新版本。 循环行为: 对
SELECTED_TEMPLATE_SOLUTIONS中每个符合条件的条目触发一次;若有三个解决方案需要更新,就需要确认三次。 询问原因: 更新非托管解决方案会将更改合并到环境中,且无法干净地回滚。 取消后保留:template-cache— 下载的模板制品仍保留在私有的、以 SHA 为键的模板缓存中;如果取消,则不会对组织进行任何更改。使用
AskUserQuestion:问题 标题 选项 解决方案 <solution.uniqueName>已安装,版本为<installedVersion>,模板中包含<availableVersion>。要在此环境中更新它吗?更新解决方案 是,更新此解决方案(推荐);否,取消 如果用户拒绝或取消,则在导入前停止;尚未对组织进行任何更改。由于未尝试导入,不要发出
template_import_failure;选择模板路径时已发出template_used。 如果用户确认,则将此解决方案添加到TEMPLATE_SOLUTIONS_TO_IMPORT。 -
decision: "offer-clone":将版本相同或更新的解决方案添加到TEMPLATE_SOLUTIONS_TO_SKIP。 -
decision: "ask"或检测失败:询问用户是仍要导入该解决方案、从头开始,还是停止。🚦 关卡(同意 · create-site:1.5.reinstall-unknown): 在已安装解决方案检测失败时,确认是否导入。
触发条件: 阶段 1.5 中,
check-solution-installed.js无法确定某个选定的模板解决方案是否已存在。 循环行为: 对SELECTED_TEMPLATE_SOLUTIONS中每个状态未知的条目触发一次;答案仅适用于指定的解决方案。 询问原因: 导入可能已存在的非托管解决方案会合并组件。 取消后保留:template-cache— 下载的模板制品仍保留在私有的、以 SHA 为键的模板缓存中;如果取消,则不会对组织进行任何更改。 使用AskUserQuestion:问题 标题 选项 无法确定解决方案 <solution.uniqueName>是否已安装。导入可能会合并非托管组件。你想如何继续?解决方案安装状态未知 仍然导入(高级选项)、从头开始(推荐)、停止 根据回答分支:
- 仍然导入:将此解决方案追加到
TEMPLATE_SOLUTIONS_TO_IMPORT。 - 从头开始:设置
CREATION_PATH = "from-scratch",并继续处理延后提出的框架和位置问题。 - 停止:在导入前停止;尚未对组织进行任何更改。不要发出
template_import_failure,因为尚未尝试导入;选择模板路径时已发出template_used。
- 仍然导入:将此解决方案追加到
追加完整的站点安装任务。 仅当
TEMPLATE_SOLUTIONS_TO_IMPORT非空时,才包含 导入模板支持解决方案。 如果所有解决方案都被跳过,在创建站点前询问一次:🚦 关卡(同意 · create-site:1.5.clone-existing): 确认在复用已安装的支持解决方案的同时创建新站点。
触发条件: 第 1.5 阶段中,所有选定的模板解决方案都已安装,且版本相同或更高。 询问原因: 技能会跳过所有解决方案导入,但仍会克隆并上传新的代码站点。 取消后的状态:
template-cache— 已下载的模板构件仍保留在私有的 SHA 键控模板缓存中;取消后不会克隆或上传站点。问题 标题 选项 所有支持解决方案都已安装,且版本相同或更高。要根据网站代码创建新的 <SELECTED_TEMPLATE_VARIANT.framework>站点吗?创建模板站点 是,创建站点(推荐)、否,取消 如果用户确认,设置
SKIP_TEMPLATE_SOLUTION_IMPORT = true。如果用户拒绝,则停止。不要发出导入结果事件,因为尚未尝试导入任何解决方案。 -
-
渲染并打开只读状态页面:
# 在操作系统的临时目录下创建 <temp-import-status-dir>/, # 然后将以下初始状态 JSON 写入 <temp-import-status-dir>/status.json: # 导入路径: # 当 TEMPLATE_SOLUTIONS_TO_IMPORT 中有一项时,使用 "Installing solution", # 否则使用 "Installing solutions": # { "state": "running", "phase": "solution", "message": "<solution install label>" } # SKIP_TEMPLATE_SOLUTION_IMPORT 路径: # { "state": "running", "phase": "site", "message": "Preparing template site" } node "${PLUGIN_ROOT}/scripts/render-template-import-status.js" \ --templateName "<SELECTED_TEMPLATE.displayName>" \ --statusPath "<temp-import-status-dir>/status.json" \ --previewImagesJson '<JSON array of SELECTED_TEMPLATE.previewImages localUrl values from the template browser step>' \ --solutionCount "<TEMPLATE_SOLUTIONS_TO_IMPORT.length, or SELECTED_TEMPLATE_SOLUTIONS.length when imports are skipped>" \ --outputPath "<temp-import-status-dir>/index.html" node "${PLUGIN_ROOT}/scripts/serve-static-dir.js" --root "<temp-import-status-dir>" --urlFile "<temp-import-status-dir>/url.txt" --cleanupRoot node "${PLUGIN_ROOT}/scripts/open-url.js" --url "<url from <temp-import-status-dir>/url.txt>"复用模板浏览步骤中已下载的本地预览图片 URL;不要为此页面再次获取预览图片。 服务器命令会在监听器就绪后才返回。成功重定向、浏览器停止轮询或达到最长存续时间后,它会移除临时状态目录。
-
除非
SKIP_TEMPLATE_SOLUTION_IMPORT = true,否则将 Import template supporting solutions 标记为in_progress。按现有的不区分大小写的字典序唯一名称顺序,依次处理TEMPLATE_SOLUTIONS_TO_IMPORT。对于每一项,设置CURRENT_TEMPLATE_SOLUTION,并准备要导入的非托管解决方案:# 更新状态 JSON: # { "state": "running", "phase": "solution", "message": "Preparing solution <CURRENT_TEMPLATE_SOLUTION.uniqueName>" } node "${PLUGIN_ROOT}/scripts/pack-template-solution.js" \ --solutionPath "<CURRENT_TEMPLATE_SOLUTION.solutionPath>"针对当前解决方案,设置
PACKED_TEMPLATE_SOLUTION_ZIP = <result.zipPath>、PACKED_TEMPLATE_SOLUTION_WORK_DIRECTORY = <result.workDirectory>、PACKED_TEMPLATE_SOLUTION_CLEANUP_MARKER = <result.cleanupMarker>和PACKED_TEMPLATE_SOLUTION_CLEANUP_TOKEN = <result.cleanupToken>。打包器仅会在由令牌管理的操作系统临时目录中创建 ZIP。绝不要将打包后的 ZIP 写入已下载的模板缓存或其他仓库路径。如果打包失败,不要调用 Dataverse,也不要发出
template_import_failure,因为尚未尝试导入。打包器会自动移除部分输出。🚦 Gate (progress · create-site:1.5.pack-failed): 选择在准备本地解决方案包失败后如何继续。
Trigger: 阶段 1.5 中,某个发现的解决方案未通过本地验证,或
pac solution pack失败。 Loop behavior: 对TEMPLATE_SOLUTIONS_TO_IMPORT中每个失败的迭代都会触发;回答仅适用于CURRENT_TEMPLATE_SOLUTION。 Why we ask: 尚未对环境进行任何更改,但没有有效的临时解决方案 ZIP 就无法继续安装模板。 Cancel leaves:template-cache— 已下载的模板源仍保留在私有 SHA 键控模板缓存中;部分打包输出已移除。使用
AskUserQuestion:问题 标题 选项 无法准备解决方案 <CURRENT_TEMPLATE_SOLUTION.uniqueName>以供导入。你希望如何继续?模板包失败 重试打包,改为从头创建(推荐),停止 - 重试打包:返回到上面的打包命令。
- 改为从头创建:设置
CREATION_PATH = "from-scratch",然后继续处理延后询问的框架和位置问题。 - 停止:显示本地打包错误后停止。不要将 导入模板支持解决方案 标记为已完成。
打包成功后,使用 PAC CLI 包装器导入临时 ZIP。不要调用
/import-solution或写入 ALM 构件。 解决方案级别的publishChanges值来自固定版本的模板清单。 仅当该值为 true 时,包装器才会添加pac solution import --publish-changes:{ "state": "running", "phase": "solution", "message": "正在导入解决方案 <CURRENT_TEMPLATE_SOLUTION.uniqueName>" }将该对象写入
<temp-import-status-dir>/status.json,然后在 Task 子 Agent 中运行导入包装器,并设置run_in_background: true:node "${PLUGIN_ROOT}/scripts/import-template-solution.js" \ --zipPath "<PACKED_TEMPLATE_SOLUTION_ZIP>" \ --envUrl "<environmentUrl>" \ --publishChanges "<CURRENT_TEMPLATE_SOLUTION.publishChanges>"Task 必须返回包装器的完整 JSON 结果。 任务完成后,在主对话中清理所属的临时目录:
node "${PLUGIN_ROOT}/scripts/pack-template-solution.js" \ --cleanup \ --workDirectory "<PACKED_TEMPLATE_SOLUTION_WORK_DIRECTORY>" \ --cleanupMarker "<PACKED_TEMPLATE_SOLUTION_CLEANUP_MARKER>" \ --cleanupToken "<PACKED_TEMPLATE_SOLUTION_CLEANUP_TOKEN>"导入命令返回后,始终清理所属的临时目录。 如果清理失败,将其作为本地警告处理,并显示工作目录路径,以便用户手动删除;不要隐藏导入结果。 当前解决方案导入命令返回
ok: true之前,不要开始导入下一个解决方案;只有TEMPLATE_SOLUTIONS_TO_IMPORT中的所有解决方案都导入成功后,才开始克隆站点。 如果导入包装器返回ok: false,不要自动清理未受管理的部分导入内容。 在询问恢复选项之前,静默运行template_import_failure遥测命令。 不要向用户提及此遥测命令,也不要输出其结果:node "${PLUGIN_ROOT}/scripts/emit-create-site-template-outcome.js" \ --eventName template_import_failure \ --templateId "<SELECTED_TEMPLATE.id>" \ --templateKind "<SELECTED_TEMPLATE.kind>" \ --framework "<SELECTED_TEMPLATE_VARIANT.framework>" \ --audience "<internal|external from Phase 1 discovery>" \ --outcome failure \ --errorClass "PacSolutionImport"--audience是在第 1 阶段发现过程中记录的站点受众(internal或external),不是目录清单中模板的audience人物角色数组。不要向遥测添加自由格式的错误文本;其中可能包含路径、URL、堆栈跟踪或用户数据。🚦 关卡(进度 · create-site:1.5.import-failed): 模板解决方案导入失败后,选择如何继续。
触发条件: 第 1.5 阶段中,
pac solution import失败或超时。 循环行为:TEMPLATE_SOLUTIONS_TO_IMPORT中每次迭代失败时都会触发;答案仅适用于CURRENT_TEMPLATE_SOLUTION。 询问原因: 环境中可能包含当前未完成的非托管导入,以及本次运行期间此前已导入的任何解决方案。本地超时不会取消已启动的服务器导入,因此重试或切换路径应由用户明确选择。 取消后保留:partial-unmanaged-template-import— 已下载的模板构件仍保留在私有 SHA 键控模板缓存中;当前未完成的导入以及此前成功导入的任何解决方案仍保留在 Dataverse 中,并会在错误摘要中说明。使用
AskUserQuestion:问题 标题 选项 支持解决方案 <CURRENT_TEMPLATE_SOLUTION.uniqueName>导入失败或未能完全完成。你希望如何继续?模板导入失败 重试导入,回退到从零开始(推荐),停止 根据答案采取分支:
- 重试导入:返回上文的打包并导入命令序列。
- 回退到从零开始:设置
CREATION_PATH = "from-scratch",并继续回答延后的框架和位置问题。告知用户,Dataverse 中可能仍保留未完成的非托管导入。最终的从零开始分支会发出唯一的终端遥测事件。 - 停止:显示错误摘要后停止。不要将 导入模板支持解决方案 标记为已完成,也不要克隆或上传站点。检测到导入失败时已发出
template_import_failure事件。
如果错误为
AttachmentBlocked,请指引用户参阅/import-solution第 5b 阶段的补救措施。 仅当导入包装器返回ok: true时,才继续下一步。 -
所有必需的解决方案导入成功后,继续进行种子数据规划和站点预配。 仅在完成以下检查后,才将 导入模板支持解决方案 标记为
completed,并设置EMIT_TEMPLATE_IMPORT_SUCCESS = true。 将该遥测事件延迟到种子数据工作流汇合时再发出,以便seedApplied反映本次运行的情况。 如果跳过了所有解决方案导入,则将任务标记为已跳过,并设置EMIT_TEMPLATE_IMPORT_SUCCESS = false。 -
在启动任一会修改数据的工作流之前,先在主对话中准备种子数据。 如果存在种子数据,则获取它:
node "${PLUGIN_ROOT}/scripts/fetch-template-seed-data.js" --sha "<catalog-sha>" --seedDataPath "<SELECTED_TEMPLATE_VARIANT.seedDataPath or SELECTED_TEMPLATE.seedDataPath>"将成功结果中的
localDir和seedFile保存为TEMPLATE_SEED_DIRECTORY和TEMPLATE_SEED_FILE。 检查源和目标,但不要插入记录:node "${PLUGIN_ROOT}/scripts/apply-seed-data.js" \ --seedDir "<TEMPLATE_SEED_DIRECTORY>" \ --seedFile "<TEMPLATE_SEED_FILE>" \ --envUrl "<environmentUrl>" \ --plan此命令会报告表、记录和文件数量、财务实体集以及目标中处于活动状态的货币,并显示
writes: 0。 如果requiresCurrencySelection为 true,请在主对话中询问应将现有的哪种启用状态货币用于财务示例行。 显示可用的货币代码/名称,说明不会换算数值金额,并提供 跳过示例数据 选项。 绝不创建货币、复制源环境的货币 GUID,或默认使用 USD。 将所选 ISO 代码保存为TEMPLATE_SEED_CURRENCY_CODE。 如果用户跳过示例数据,则不要启动种子数据写入。 如果没有财务行,则省略货币选择和--currencyCode标志。 获取/规划失败仍属于尽力而为的种子数据失败:明确显示失败,将其保留在种子数据摘要中,并在不创建种子数据任务的情况下创建站点,而不是假装记录已插入。只有在导入成功且已在主对话中完成种子数据决策后,才并发启动站点配置和种子数据应用:
- 将 克隆、构建并上传模板站点 标记为
in_progress。 - 如果种子数据已就绪且未被跳过,则将 应用模板种子数据 标记为
in_progress,并使用Task启动种子数据工作流,同时设置run_in_background: true。后台任务只能应用并验证已准备好的种子数据。不得克隆、构建、上传、激活、更新共享状态文件、询问用户问题或重试失败的写入。 - 在种子数据任务运行期间,在主对话中运行站点配置包装器。支持性解决方案导入创建所需的 Dataverse 表后,这些工作流彼此独立。 存在种子数据时,更新状态页面:
{ "state": "running", "phase": "siteAndSeed", "message": "正在创建模板站点并填充种子数据" }不存在种子数据时,使用以下内容更新状态页面:
{ "state": "running", "phase": "site", "message": "正在克隆、构建并上传模板站点" }将打包的 SPA 源代码克隆到之前选定的目录,然后上传克隆内容:
node "${PLUGIN_ROOT}/scripts/provision-template-site.js" \ --sourcePath "<SELECTED_TEMPLATE_WEBSITE_CODE>" \ --outputDirectory "<TEMPLATE_CLONE_OUTPUT_DIRECTORY>" \ --siteName "<__SITE_NAME__>"将此包装脚本视为模板站点配置的唯一入口。不要为了诊断而直接重新运行其底层
pac、npm或构建命令。切勿将pac pages clone或pac pages upload-code-site等会修改数据的命令通过head、tail或其他可能在命令完成前关闭输出流的程序进行管道传输。包装脚本会安全地截取有长度限制的诊断日志尾部,且不会中断命令。成功后,将返回的clonedPath同时保存为CLONED_TEMPLATE_SITE_PATH和PROJECT_ROOT,将siteName保存为IMPORTED_SITE_NAME,并将websiteRecordId保存为IMPORTED_WEBSITE_RECORD_ID。 - 将 克隆、构建并上传模板站点 标记为
-
在种子数据后台任务中,使用主对话准备好的目录、文件和货币决策:
node "${PLUGIN_ROOT}/scripts/apply-seed-data.js" \ --seedDir "<TEMPLATE_SEED_DIRECTORY>" \ --seedFile "<TEMPLATE_SEED_FILE>" \ --envUrl "<environmentUrl>" \ --currencyCode "<TEMPLATE_SEED_CURRENCY_CODE, only when financial rows were planned>"如果没有计划写入财务行,则省略整个货币参数。 在进行任何写入之前,种子数据导入器只会解析现有的有效目标货币及其准确的元数据导航属性。 它会从创建负载中移除声明的上传列值及其生成的文件名伴随字段;文件上传操作会填充这些字段。 将完整的 JSON 摘要(
ok、inserted、failed、skipped、errors)返回给主对话。 如果行、状态、验证或文件上传部分失败,必须报告为ok: false;切勿仅凭 CLI 成功退出就判断成功。 种子记录必须使用模板解决方案元数据中的准确表实体集名称、列逻辑名称,以及标量或集合型<NavigationProperty>@odata.bind查找名称。 切勿根据主键、实体集、显示名称或categoryId之类的应用样式别名推导查找导航属性;种子数据导入器会在首次向 Dataverse 写入之前拒绝有歧义的别名和格式错误的集合绑定。 验证时,查询计划中的记录 ID,而不只是查询$top=1,并比较预期引用和原生集合成员。 应通过目标文件的元数据和内容验证每个上传的附件,而不是只统计已创建的附件行。 普通的现有种子记录跳过操作不会协调原生 N:N 关系成员;请说明这一限制,且不要静默更新现有联系人、企业或分配记录。 -
等待两个工作流都完成后,再显示未激活站点摘要或开始激活。记录种子数据摘要,然后将 Apply template seed data 标记为
completed;如果没有种子数据,则标记为跳过。种子数据的获取和插入均为尽力而为:应展示其结果,但不得因此导致站点创建失败或阻止激活。 如果EMIT_TEMPLATE_IMPORT_SUCCESS = true,请立即发出导入结果:node "${PLUGIN_ROOT}/scripts/emit-create-site-template-outcome.js" \ --eventName template_import_success \ --templateId "<SELECTED_TEMPLATE.id>" \ --templateKind "<SELECTED_TEMPLATE.kind>" \ --framework "<SELECTED_TEMPLATE_VARIANT.framework>" \ --audience "<Phase 1 探索中获取的 internal|external>" \ --seedApplied "<仅当种子摘要中 inserted > 0 且 failed = 0 时为 true;否则为 false>"--audience是在 Phase 1 中记录的网站受众(internal或external),不是目录清单中模板的audiencepersona 数组。不要包含网站名称、URL、子域名、自由文本用途或任何其他可识别用户身份的信息。 -
如果克隆、克隆后的身份检查、依赖项安装、构建、构建输出验证或上传失败,请静默运行
template_clone_failure遥测。将返回的step映射到errorClass:clone/clone-output→PacPagesClone,install→NpmInstall,build→NpmBuild,build-output→CompiledOutput,upload→PacPagesUploadCodeSite:node "${PLUGIN_ROOT}/scripts/emit-create-site-template-outcome.js" \ --eventName template_clone_failure \ --templateId "<SELECTED_TEMPLATE.id>" \ --templateKind "<SELECTED_TEMPLATE.kind>" \ --framework "<SELECTED_TEMPLATE_VARIANT.framework>" \ --audience "<Phase 1 探索中获取的 internal|external>" \ --outcome failure \ --errorClass "<PacPagesClone|NpmInstall|NpmBuild|CompiledOutput|PacPagesUploadCodeSite>"在触发恢复选项门之前更新状态页:
{ "state": "error", "phase": "site", "message": "Template site creation failed" }仅根据包装器返回的
step、error和保留的本地路径诊断故障。在用户选择恢复选项之前,不要重新运行包装器或任何底层命令。 然后触发此选项门:
> 🚦 **选项门(进度 · create-site:1.5.clone-failed):** 打包模板网站克隆、构建或上传失败后,选择如何继续。
>
> **触发条件:** Phase 1.5 中,克隆、克隆后的身份检查、依赖项安装、项目构建、编译输出验证或 `pac pages upload-code-site` 失败。
> **询问原因:** 所选项目目录可能包含部分克隆或构建输出,环境中可能存在部分代码网站上传内容,并且可能已安装支持性解决方案。
> **取消后保留:** `partial-template-clone` — 缓存的模板构件和本地文件仍保留在所选项目目录中;Dataverse 中也可能保留支持性解决方案或部分网站上传内容。
| 问题 | 标题 | 选项 |
|----------|--------|---------|
| 无法克隆、构建或上传模板网站。你想如何继续? | 网站创建失败 | 重试网站创建(推荐)、退回到从头创建、停止 |
不要自动重试。如果包装脚本返回 `outputDirectoryRemoved: true`,**重试创建站点**可以复用同一个选定位置。否则,使用相同的 **项目位置**提示,请用户选择一个新的空目录,更新 `TEMPLATE_CLONE_OUTPUT_DIRECTORY`,然后重新运行包装脚本。如果用户改为从头创建,请说明本地文件、支持性解决方案或部分上传的站点可能仍然存在,并推荐 `<SELECTED_TEMPLATE_VARIANT.framework>`。
13. 克隆、构建和上传均成功后,将 克隆、构建并上传模板站点标记为 completed,并静默运行 template_clone_success 遥测:
bash node "${PLUGIN_ROOT}/scripts/emit-create-site-template-outcome.js" \ --eventName template_clone_success \ --templateId "<SELECTED_TEMPLATE.id>" \ --templateKind "<SELECTED_TEMPLATE.kind>" \ --framework "<SELECTED_TEMPLATE_VARIANT.framework>" \ --audience "<internal|external from Phase 1 discovery>"
不要在遥测中包含站点名称、克隆路径、URL 或 Website Record ID。
14. 将 显示未激活的模板站点标记为 in_progress。告知用户:“模板 <displayName> 已创建为 <IMPORTED_SITE_NAME>(<IMPORTED_WEBSITE_RECORD_ID>)。它尚未激活。种子数据处理已完成,接下来将进行激活。”如果模板没有种子数据,请如实说明,不要暗示已插入记录。将 显示未激活的模板站点标记为 completed。
15. 将 激活模板站点标记为 in_progress。调用 /activate-site 前,更新状态页:
json { "state": "running", "phase": "activation", "message": "Activating template site", "awaitingInput": false }
然后调用 /activate-site,并在请求中传入已解析的身份信息和状态路径,使其跳过本地项目发现,并在等待激活输入时显示 Toast:
text Activate cloned template site: - siteName: <IMPORTED_SITE_NAME> - websiteRecordId: <IMPORTED_WEBSITE_RECORD_ID> - environmentUrl: <environmentUrl> - statusPath: <temp-import-status-dir>/status.json - source: create-site template path
activate-site 技能负责子域选择、等待输入时显示 Toast、最终激活确认、轮询配置状态以及故障恢复。其前台激活脚本是此流程中唯一需要的轮询。
如果激活失败,告知用户克隆的站点已存在,但尚未上线;稍后可使用此身份信息重新运行 /activate-site 来激活。不要将激活失败视为支持性解决方案导入或站点上传失败。
16. /activate-site 返回 siteUrl 后,将 激活模板站点标记为 completed,并将 显示已上线的模板站点标记为 in_progress。
17. 将已打开的状态页重定向至已上线的站点:
json { "state": "succeeded", "message": "Template site is live. Opening it now...", "redirectUrl": "<siteUrl>", "shutdownServer": true }
不要为模板路径打开第二个浏览器页面。状态页会轮询此文件,并在 URL 为 http 或 https 时,将同一标签页重定向至 redirectUrl。shutdownServer: true 会指示本地服务器在短暂的重定向宽限期后移除临时状态目录。如果用户关闭了状态页,请显示 siteUrl,供其手动打开。
务必说明激活站点后的 DNS 传播注意事项:即使激活成功,网站也可能需要几分钟才能加载。不要启动单独的后台命令或 Task 来等待线上 URL 返回 HTTP 200。状态页重定向和 DNS 说明足以处理这段延迟,不会留下能在完成摘要后继续运行的工作。如果误启动了可访问性轮询,请在将 Show live template site 标记为 completed 之前停止它。
18. 将 Show live template site 标记为 completed,然后提供模板路径摘要:
- 模板名称和框架
- 克隆的网站名称和 Website Record ID
- 线上网站 URL
- DNS 传播说明:网站可能需要几分钟才能在各处加载
- 本地项目路径(PROJECT_ROOT)
- “您的网站已上线。要继续自定义本地项目吗?”
-
使用
AskUserQuestion:问题 标题 选项 您的模板网站已上线。现在要自定义本地项目吗? 自定义模板网站 是,现在自定义(推荐)、否,到此结束 - 否,到此结束:将 Select template or choose from-scratch 标记为
completed,然后停止。 - 是,现在自定义:添加模板自定义任务(参见进度跟踪),然后继续。
- 否,到此结束:将 Select template or choose from-scratch 标记为
-
将 Plan template customizations 标记为
in_progress,然后在询问用户想要更改哪些内容之前,先检查克隆的项目:node "${PLUGIN_ROOT}/scripts/lib/localization-config.js" inspect --projectRoot "<PROJECT_ROOT>"按以下顺序确定第 4 阶段计划所需的语言上下文:
- 当
localization.detected=true且localization.valid=true时,将localization.defaultLocale作为权威区域设置。使用resolve-locale解析它,根据返回的locale、direction和languageName设置SITE_LOCALE、SITE_DIRECTION和SITE_LANGUAGE;需要显示地区或文字系统细节时,保留localeName。不要扫描特定区域设置的文档根目录,并将其视为描述同一个单语言网站。 - 当
localization.detected=true且localization.valid=false时,显示本地化冲突,并在规划自定义之前停止。必须修复本地化配置;不要回退到静态文档属性,也不要将本地化根目录报告为单语言冲突。 - 当未检测到本地化,且
siteLanguage.detected=true、siteLanguage.valid=true时,使用resolve-locale解析siteLanguage.locale。根据返回的locale、direction和languageName设置SITE_LOCALE、SITE_DIRECTION和SITE_LANGUAGE。 - 当
siteLanguage.reason为document-not-found、html-root-not-found、document-ambiguous或document-configuration-invalid时,显示其冲突和expectedSources,然后在规划自定义之前停止。在询问内容语言之前,先定位或修复实际的应用根文档。 - 当文档存在,但其静态语言属性缺失或无效时,询问下方的 内容语言 问题,使用
resolve-locale解析答案,并将修复siteLanguage.source纳入获批的自定义计划。 在SITE_LANGUAGE、SITE_LOCALE和SITE_DIRECTION全部确定后,询问用户想在PROJECT_ROOT中进行哪些更改。针对克隆的项目,使用现有的 Phase 3/4/5/6/7 实施、验证和审查流程;不要运行 Phase 2 脚手架/复制模板流程。在 Phase 3 中,跳过 Brand 子提示词,并将BRAND_SOURCE设为template——除非用户要求重新设计,否则以模板现有的主题令牌为准。在此路径中,Phase 5 会直接修改克隆的项目:保留现有的主题文件、布局和页面,只更改已批准计划中列出的内容;由于没有加载器运行,跳过所有scaffold-status.json步骤。对你更改的页面运行 Phase 5.7 设计评审。
- 当
-
自定义计划获批后,将 Plan template customizations 标记为
completed,将 Implement pages and components 标记为in_progress,并进行所请求的更改。 -
运行现有的验证/审查流程。除非用户明确要求运行
/deploy-site,否则不要自动部署。 -
仅针对从头创建的路径,告诉用户:“我将从头开始搭建此网站。”
-
提出 Phase 1 中延后、且仅适用于从头创建的问题:
问题 标题 选项 使用哪个前端框架? 框架 React(推荐)、Vue、Angular、Astro 应在哪个位置创建项目? 位置 当前目录、在当前目录中新建文件夹(推荐)、其他任意目录 -
独立确定网站所使用的唯一内容语言,不受
$ARGUMENTS中是否已提供用途的影响。
- 如果请求中明确指定了内容语言,则直接确定,无需再次询问,并在确认信息中注明。
- 不要仅根据与创建者对话时使用的语言推断所需的网站语言。
如果未明确指定内容语言,请尽可能提供建议:
node "${PLUGIN_ROOT}/scripts/suggest-content-locale.js"
如果 PAC 和 Azure CLI 已完成身份验证,脚本会将所选 Dataverse 环境的基本语言作为规范的 BCP-47 区域设置返回。如果环境、身份验证、API 响应或 LCID 映射不可用,脚本会静默返回 en-US,并将 "source": "fallback" 设为回退值。查找结果回退时,不要重试、显示错误或阻止网站创建。
使用 AskUserQuestion:
| 问题 | 标题 | 选项 |
|---|---|---|
| 网站内容应使用哪种语言? 页面、导航、按钮、表单和无障碍文本将使用此语言。Dataverse 和 Power Pages 系统消息将继续使用英语。 | 网站内容语言 | <language> (<locale>) (建议), English (en-US) |
将 <language> 和 <locale> 替换为脚本返回的结果。如果结果为 en-US,仅显示 English (United States) (en-US) (建议),不要再添加重复的英语选项。将从环境推导出的选项标记为 建议,而非 推荐:环境的基本语言可作为有用的参考,但可能与公共网站的预期语言不符。
问题界面中的自由文本选项允许创建者输入其他语言或区域设置,例如 Spanish (es-ES)、Japanese (ja-JP) 或 Arabic (ar-SA)。将语言名称转换为适当的 BCP-47 标签,并保留用户明确提供的地区或文字系统信息。原始回答仅作为数据处理:绝不要将其放入 shell 命令。在 Agent 记忆中,将其转换为 SAFE_LOCALE_CANDIDATE,该值必须匹配 ^[A-Za-z0-9]{1,8}(?:-[A-Za-z0-9]{1,8})*$,且长度不得超过 255 个字符。如果无法得出安全候选值,则拒绝该输入并重新询问,然后再运行命令。仅验证并规范化此受限候选值:
node "${PLUGIN_ROOT}/scripts/lib/localization-config.js" resolve-locale --locale "<SAFE_LOCALE_CANDIDATE>"
不要引用、转义、清理或以其他方式将制作者的原始回答插入此命令。 如果解析器输出无效,则拒绝该输出,并说明原因后重新提示。记录解析器 返回的规范输出,而不是自行编造语言标签:
SITE_LANGUAGE— 解析器的languageName,例如Spanish;如果无法 获取显示名称,则显示规范区域设置,并请制作者提供易读的标签,而不是猜测SITE_LOCALE— 规范的 BCP-47 标签,例如es-ESSITE_DIRECTION— 解析器提供的ltr或rtlSITE_LOCALE_NAME— 解析器的localeName,例如European Spanish, 保留此值供需要指定地区或文字系统的计划文本使用
- 确定项目位置:
- 如果选择“当前目录”:项目根目录 =
<cwd>。 - 如果选择“在当前目录中新建文件夹”:在 cwd 中创建名为
__SITE_NAME__的文件夹。项目根目录 =<cwd>/__SITE_NAME__/。首先确认__SITE_NAME__是当前操作系统上有效的 目录名。不能仅因名称包含非拉丁字符就拒绝。如果站点名称包含无效的路径 字符、是操作系统保留名称、清理后为空,或因其他原因无法安全使用,则改用__SITE_SLUG__。创建备用文件夹前,告知制作者将使用的名称,以及原站点 名称不符合文件系统安全要求的原因。 - 如果选择“其他目录”:询问完整路径。确认该路径可用,必要时创建。项目根目录 = 提供的路径。
确定后,确认:“站点将在 <resolved path> 创建。”
将此值存储为 PROJECT_ROOT。
- 确定最终派生值:
__SITE_NAME__— 保留制作者输入的站点/品牌名称,例如Contoso Portal;名称不必与内容语言一致。__SITE_SLUG__— 小写 ASCII kebab-case 技术标识符,用于 npm、Angular 项目 配置、构建路径和默认文件夹。尽可能根据站点名称生成,例如Contoso Portal→contoso-portal。可以可靠转写时进行转写。如果非拉丁名称无法生成安全且有意义的 slug,则根据站点用途生成,或询问技术项目名称,而不要生成空标识符或无效标识符。__SITE_DESCRIPTION__— 根据名称和用途,以SITE_LANGUAGE撰写的一行描述。
- 总结对需求的理解,并在继续前请用户确认。内容包括站点名称、技术 slug、框架、
用途、受众、项目位置、
SITE_LANGUAGE(SITE_LOCALE、SITE_DIRECTION),以及 Dataverse 和 Power Pages 系统消息仍使用英语的明确说明。 - 确认后,输出经过批准且仅包含分类信息的站点配置:
- 将用途映射为
company-portal、blog-content、dashboard、landing-page或other。绝不传入制作者的自由文本用途。 - 仅当框架和内容区域设置均来自初始请求时,才将
choiceSource设为arguments;否则设为prompt。
node "${PLUGIN_ROOT}/scripts/emit-skill-configured-telemetry.js" --skillName "create-site" --projectRoot "<PROJECT_ROOT>" --framework "<react|vue|angular|astro>" --siteContentLocale "<SITE_LOCALE>" --purpose "<PURPOSE_TAXONOMY>" --audience "<internal|external>" --choiceSource "<arguments|prompt>"
遥测辅助函数采用失败即关闭策略,并会移除区域设置扩展和私用子标签。不要将站点名称、 描述、页面、路由、组件、功能标签或任何其他自由文本需求添加到此命令中。
- 将从零开始的任务清单(阶段 2-8)追加到待办事项列表(参见
进度跟踪),然后将 选择模板或选择从零开始
标记为
completed。
输出:已克隆模板的站点身份(IMPORTED_SITE_NAME、
IMPORTED_WEBSITE_RECORD_ID)以及可供选择性自定义的本地项目路径;或者
CREATION_PATH = "from-scratch",以及所选框架、内容语言、派生的命名值和已确定的项目位置。
阶段 2:搭建项目并启动开发服务器
目标:立即运行网站,让用户在规划功能和设计时就有内容可预览
搭建好的项目是一个临时的品牌加载屏幕——它会显示 Power Pages 动画“正在构建你的网站”体验,其中包含环绕元素、状态消息和功能卡片。它的唯一目的是快速启动开发服务器,让用户在你规划和构建时有内容可看。在阶段 5(实施)期间,整个搭建项目——包括 theme.css、Layout、Home 页面和所有占位组件——都会被完全替换为用户的实际网站:用户选择的字体、配色方案、页面、组件和导航。不要在加载屏幕的基础上继续构建;请将其彻底替换。
有关完整的框架 → 构建工具 → 路由器 → 输出路径映射,请参阅
${PLUGIN_ROOT}/references/framework-conventions.md。
操作:
2.1 复制模板
${PLUGIN_ROOT}在运行时已解析为插件的绝对路径。请直接在 Glob/Read 路径中使用它——不要搜索插件目录。
将匹配的资源模板中的所有文件读取并复制到项目目录:
| 框架 | 资源目录 |
|---|---|
| React | ${PLUGIN_ROOT}/skills/create-site/assets/react/ |
| Vue | ${PLUGIN_ROOT}/skills/create-site/assets/vue/ |
| Angular | ${PLUGIN_ROOT}/skills/create-site/assets/angular/ |
| Astro | ${PLUGIN_ROOT}/skills/create-site/assets/astro/ |
使用 Glob 查找资源目录中的所有文件,使用 Read 读取每个文件,然后使用 Write 写入项目目录,并保留相对路径结构。
还要复制搭建项目 CSS 中引用的共享加载器图标(url('/power-pages-icon.png')):
使用 Read 读取二进制文件 ${PLUGIN_ROOT}/skills/create-site/assets/shared/power-pages-icon.png,并使用 Write 将其写入 <PROJECT_ROOT>/public/power-pages-icon.png。(所有四种受支持的框架都会将 public/ 目录作为网站根目录提供,因此每种框架都能使用相同的 /power-pages-icon.png URL。)
初始化实时状态文件,让加载器在挂载时立即显示真实消息。使用 Write 写入 <PROJECT_ROOT>/public/scaffold-status.json:
{ "message": "Planning your site", "awaitingInput": false }
完整约定请参阅实时预览状态协议——从现在开始,每次 AskUserQuestion 之前以及阶段 5 的每个实施步骤之前,都要更新此文件。
2.2 替换占位符
复制后,替换所有文件中的所有 __PLACEHOLDER__ 标记。在每个文件上使用 Edit,并设置 replace_all: true。
- 名称/slug/描述占位符:使用阶段 1 中的实际值(
__SITE_NAME__、__SITE_SLUG__、__SITE_DESCRIPTION__)。 - 文档语言占位符:将
__SITE_LOCALE__替换为SITE_LOCALE,将__SITE_DIRECTION__替换为SITE_DIRECTION。
**注意:**搭建项目的加载屏幕使用硬编码的 Power Pages 品牌颜色——没有需要替换的颜色占位符(如
__PRIMARY_COLOR__)。用户选择的配色方案将在阶段 5 完全替换搭建项目时重新应用。
2.3 重命名 gitignore
在项目根目录中将 gitignore 重命名为 .gitignore(存储时不带点前缀,以避免干扰插件仓库中的 git)。
2.4 安装依赖项
在初始化 git 之前运行 npm install,以便将 package-lock.json 包含在初始提交中:
cd "<PROJECT_ROOT>"
npm install
Astro 需要 Node 22.12 或更高版本。 Astro 7 在任何更低版本上都会退出并显示
Node.js vX is not supported by Astro!,因此选择 Astro 框架时,请先运行node --version,并让用户升级后再继续。
2.5 初始化 Git 仓库
初始化 git 仓库并进行首次提交。这样会将所有模板文件和 package-lock.json 一并纳入一个干净的基线:
cd "<PROJECT_ROOT>"
git init
git add -A
git commit -m "Initial scaffold: __SITE_NAME__ (__FRAMEWORK__)"
从现在开始,每完成一个重要里程碑就提交一次,以便在出现任何破坏性更改时都能回滚。
2.6 启动开发服务器
现在必须执行此步骤——在开始任何规划或自定义之前。 开发服务器可在规划功能和设计时,为用户提供实时预览:
cd "<PROJECT_ROOT>"
npm run dev
使用 Bash 在后台运行 npm run dev,并设置 run_in_background: true。记下本地 URL(Vite 通常为 http://localhost:5173,Angular 通常为 http://localhost:4200,Astro 通常为 http://localhost:4321)。
2.7 使用 Playwright 验证并分享 URL
开发服务器启动后,立即验证脚手架是否正常运行:
- 使用
mcp__plugin_power-pages_playwright__browser_navigate打开开发服务器 URL - 使用
mcp__plugin_power-pages_playwright__browser_snapshot验证页面是否正确加载(仅使用无障碍快照——截图留到第 5 阶段的设计检查时使用) - 将开发服务器 URL 分享给用户,以便他们在自己的浏览器中预览网站(例如:“你的网站正在
http://localhost:5173运行——在浏览器中打开它,就能跟着我一起构建。”)
关卡:在以下所有条件都满足之前,不要进入第 3 阶段:
- 模板文件已复制,且占位符已替换
- Git 仓库已初始化,并已提交初始脚手架
npm install已成功完成- 开发服务器正在后台运行(
npm run dev)- Playwright 已打开网站,并通过
browser_snapshot验证网站可以加载- 已将开发服务器 URL 分享给用户
如果上述任一事项尚未完成,请现在完成后再继续。
输出:已验证脚手架的开发服务器正在运行,且已将 URL 分享给用户
第 3 阶段:组件规划
目标:确定网站需要哪些页面、组件和设计元素,同时让用户预览正在运行的脚手架
🚦 关卡(计划 · create-site:3.requirements): 四个子提示词(功能多选、审美风格、整体氛围、品牌)以及用户提供品牌输入时的品牌跟进问题,将共同确定第 4 阶段的计划和第 5 阶段的实现。在以下操作列表的第 2 步触发。
触发条件: 进入第 3 阶段;脚手架加载器已启动。 提问原因: 错误的功能集、审美风格或品牌会被写入渲染后的计划中——第 4.7 阶段的关卡仍能发现大多数问题,但延后发现会造成浪费。 取消后的处理: 无——脚手架加载器文件只是临时产物,将在第 5 阶段整体替换。
操作:
-
显示“等待输入”横幅,让用户即使在浏览器加载器全屏显示时,也能注意到终端提示。
Write<PROJECT_ROOT>/public/scaffold-status.json:{ "message": "Planning your site", "awaitingInput": true, "inputPrompt": "Features, aesthetic, mood, and brand - please answer in the terminal." }用户回答后(包括任何品牌跟进问题),立即再次
Write同一文件,将"awaitingInput": false设为横幅消失。 -
使用
AskUserQuestion收集功能和设计需求:问题 标题 选项 需要哪些功能?(可多选) 功能 (根据第 1 阶段提供的网站名称、用途和受众,生成 3-4 个符合上下文的选项) 你想要什么审美风格? 审美风格 极简干净(推荐)、大胆鲜明、暗黑沉郁、温暖自然 整体氛围如何? 氛围 专业可信(推荐)、创意活泼、技术精准、优雅高端 网站是否需要匹配现有品牌? 品牌 否,打造全新形象(推荐)、匹配我的现有网站、使用我的品牌颜色或 Logo 品牌跟进问题。 将答案记录为
BRAND_SOURCE(fresh、website或assets)。若选择匹配我的现有网站,使用AskUserQuestion询问 URL(自由文本——使用单个通用选项,让用户通过“其他”输入);仅接受http://或https://URL,否则再次询问。若选择使用我的品牌颜色或 Logo,以同样方式询问十六进制颜色值和/或 Logo 文件的绝对路径。在模板自定义路径中,跳过此子提示词(见第 1.5 阶段第 20 步)。功能选项不会硬编码。 根据第 1 阶段的回答推断相关功能。例如:
- “HR Dashboard” + 内部用户 → 员工目录、请假申请、公告、组织架构图
- “Contoso Portal” + 外部用户 → 联系表单、服务目录、知识库、常见问题
- “Partner Hub” + 内部用户 → 文档库、合作伙伴目录、交易跟踪器、通知
始终生成适合特定站点的选项,切勿重复使用固定列表。
如果包含身份验证功能选项, 请用通用方式描述,例如“用于跟踪申请状态的登录/注册”或类似描述。功能说明中不要提及特定身份提供方(例如“Entra ID”“SAML”“Google”)——
/power-pages:setup-auth技能会询问用户希望使用哪个提供方。 -
AI 组件规划——根据第 1 阶段的回答(站点名称、用途、受众)和上面选择的功能,提出站点可能会使用哪些 Power Pages 生成式 AI 摘要 API。站点本身不依赖这些 API——页面会预留插槽,并且无需 AI 也能运行;用户准备好后,
/add-ai-webapi会填充这些插槽。使用支持多选的AskUserQuestion,让用户选择是否启用:问题 标题 选项 站点应具备哪些 AI 摘要功能?(多选——每项都可稍后通过 /add-ai-webapi添加)AI 摘要 (生成 2-4 个结合上下文的选项,并加上“暂不选择”) 选项不会硬编码。 根据第 1 阶段的回答和上面选择的功能,推断相关 AI 摘要功能。示例:
- “HR Dashboard” + 请假申请功能 → “请假申请数据摘要”、“知识库搜索摘要”
- “Contoso Portal” + 知识库 → “全站搜索摘要”、“文章数据摘要”
- “Customer Self-Service” + 支持案例 / 事件 → “支持案例数据摘要(Microsoft 提供的配方)”、“所附知识文章数据摘要”
将标准
incident表视为与其他 Dataverse 表相同——如果站点处理支持案例,请为其提出数据摘要,但不要强行使用 Microsoft 提供的配方($select=description,title+ portal-comments 展开),除非这确实符合用户体验。自定义案例类表,或标准 incident 的其他方面,都可作为常规的数据摘要选项。始终包含 暂不选择,以便用户稍后决定。不要在此技能中集成这些 API——只需记录用户的选择,以便第 4 阶段的计划可以提及这些选择,并让第 8 阶段将/add-ai-webapi作为建议的后续步骤。在内存中将选择记录为
AI_SUMMARY_PICKS——包含以下一项或多项的列表:search-summary、data-summarization。 -
将选择映射到目标页面。 对于
AI_SUMMARY_PICKS中的每一项,确定哪个页面将承载 AI 界面,并将映射存储为AI_SUMMARY_PLACEMENTS。第 4 阶段会向用户展示此映射,第 5 阶段会据此预留插槽。使用第 2 步中的功能选择,并将此映射视为 Claude 在第 7 步中提出页面列表的输入:如果某项选择没有合适的目标页面,就在计划中添加一个页面,为摘要提供展示位置:选择项 默认目标页面 如果计划中没有匹配的页面 search-summary搜索 / 搜索结果页面(例如, SearchResults、Search)在计划中添加搜索页面,为摘要提供展示位置 data-summarization用户指定的表的详情页(例如,产品对应 ProductDetail,支持案例对应CaseDetail)——如果有歧义,请询问用户提议添加详情页;如果用户拒绝,则改用列表 / 仪表板页面 AI_SUMMARY_PLACEMENTS结构:每个 placement 一条记录,例如[{ pick: "data-summarization", targetPage: "CaseDetail", marker: "POWERPAGES:AI-SLOT kind=data-summarization" }]。marker字符串是 Phase 5 写入页面源码的注释标签,作为保留锚点,供/add-ai-webapi之后查找。保持结构统一——每个 placement 一个 marker,始终使用同一个标签,这样后续 skill 的 explore 步骤就能确定性地 grep 到它们。 -
阅读设计参考:
${PLUGIN_ROOT}/references/design-aesthetics.md、${PLUGIN_ROOT}/references/page-blueprints.md和${PLUGIN_ROOT}/references/bidirectional-design.md。 -
编写体验简报(design-aesthetics.md 第 1 节)——受众与任务、主要与次要操作、主要疑虑、证明策略、设计论点、hero 概念和标志性时刻。先确定品牌来源(第 2 节):对于
website,用那里的 Playwright 代码片段从 URL 中提取品牌;对于assets,围绕提供的颜色构建调色板;对于fresh,从美学 x 情绪映射表(第 11 节)中匹配的单元格开始。记录展示字体和正文字体、色彩方向、几何形态和动效方向。字体选择必须支持SITE_LOCALE所使用的完整书写系统;要验证字形覆盖和整形,而不是仅因为某款纯拉丁字体符合美学就选择它,并使用兼容的、感知书写系统的后备字体栈。 -
分析需求并确定所需组件。将每个页面的内容按
page-blueprints.md中的叙事节拍依次规划,每个 section 一行并注明其用途。如果第 4 步的AI_SUMMARY_PLACEMENTS暗示了计划中尚未包含的页面(例如,support-case 表上的 data-summarization pick 对应的CaseDetail页面),现在就将其加入页面列表。以表格形式向用户展示组件计划:| Component Type | Count | Details | |---------------------|-------|---------| | Pages | 4 | Home, About, Services, Contact | | Shared Components | 3 | Navbar, Footer, ContactForm | | Design Elements | 5 | Schibsted Grotesk + Public Sans, 15 color tokens, product-moment hero, status-timeline signature moment, paper-grain backgrounds | | Routes | 4 | /, /about, /services, /contact |为每个计划中的可见或可交互组件构建
BIDIRECTIONAL_REVIEW_DATA, 包括并非共享源组件的页面局部控件。使用共享的双向标准, 将每个条目分类为direction-neutral、direction-aware、direction-fixed或unknown-third-party。记录本地化原因、适用状态、 适用的desktop/narrow视口以及计划中的检查。将任何涉及 内联放置、文本方向、水平移动、顺序、方向含义、混合书写系统内容 或渲染在正常组件子树之外的内容,都视为潜在的双向表面。这是预期的审查范围,不是项目清单。Phase 5 必须将其与实际源码 和渲染后的应用进行核对,补充实现过程中发现的组件或状态。 不要单独编写组件清单 JSON 文件。
-
根据简报和品牌来源,选择最终颜色 token——design-aesthetics.md 第 3 节中的每个颜色角色。在值进入计划之前,现在就用 WCAG AA 检查每一对文本和背景。这些值会在实现阶段(Phase 5)完全替换脚手架加载屏幕时,全新写入新的
theme.css。
输出:确认要创建的页面列表(含叙事节拍)、组件、设计元素和路由,以及体验简报和颜色 token
Phase 4:计划审批
目标:将实现计划渲染为 HTML 文档,在用户的默认浏览器中打开,并在开始实现前获得批准。
为什么用 HTML 而不是聊天消息:结构化 HTML 计划(类似由
/integrate-backend、/add-server-logic和/add-cloud-flow生成的计划)让用户可以浏览各 section、比较色板并预览排版——这些在终端里都无法实现。他们浏览器中的脚手架加载器也可能是全屏的,因此在新标签页中展示计划能将其放在用户真正可阅读的位置。
4.1 阅读设计参考资料
如果 ${PLUGIN_ROOT}/references/design-aesthetics.md 和 ${PLUGIN_ROOT}/references/page-blueprints.md 尚未纳入上下文,请阅读它们。你在下方填写的每个字段都必须能追溯到第 3 阶段的体验简报。
规划中的 AI 就绪状态。 如果第 3 阶段的
AI_SUMMARY_PLACEMENTS非空,请在对应的PAGES_DATA条目的description或content中体现每个预留位置,例如:“预留 AI 摘要卡片位置;稍后由/add-ai-webapi填充。页面上线时不包含 AI 功能。” 这样可以如实管理用户预期:网站不依赖租户启用生成式 AI 功能,也不会向最终用户显示“运行 /add-ai-webapi”之类的占位内容。如果AI_SUMMARY_PLACEMENTS为空,请勿在规划中提及 AI。
4.2 构建规划数据
组装一个包含以下键的 JSON 对象。规划模板会拒绝缺少必需键的数据,因此请全部包含。
| 键 | 类型 | 内容 |
|---|---|---|
SITE_NAME | string | 第 1 阶段中的站点/品牌名称,保留原样 |
PLAN_TITLE | string | 按 SITE_LOCALE 本地化的 "Implementation Plan" |
FRAMEWORK | string | React / Vue / Angular / Astro |
SITE_LANGUAGE | string | 第 1 阶段中易读的内容语言名称;条件允许时使用该语言书写 |
SITE_LOCALE | string | 标准 BCP-47 内容区域设置 |
SITE_DIRECTION | string | ltr 或 rtl |
AESTHETIC | string | 选定的美学风格(例如 Minimal & Clean) |
MOOD | string | 选定的情绪基调(例如 Professional & Trustworthy) |
SUMMARY | string | 一段本地化文字,说明网站的定位及服务对象 |
PLAN_LABELS | object | 下文定义的完整本地化模板标签字典 |
DESIGN_DIRECTION_DATA | object | 体验简报:{ thesis, brandSource, audience, primaryAction, secondaryAction, principalDoubt, proofStrategy, heroConcept, signatureMoment }——所有值均为字符串。brandSource 说明设计令牌的来源(例如 "Fresh identity"、"Matched to https://contoso.com") |
TYPOGRAPHY_DATA | object | { primary: { name, sample, reason }, secondary: { name, sample, reason } }——primary 为正文字体,secondary 为展示字体;name 必须来自 design-aesthetics.md 中已验证字体表所列字体系列(或品牌自有的 Google Font) |
PALETTE_DATA | array | [{ var, hex, description }]——每个条目对应 design-aesthetics.md 第 3 节中的一个颜色令牌 |
MOTION_DATA | array | [{ label, description }]——首屏入场动画、标志性时刻、滚动显现、交互状态、路由切换 |
BACKGROUNDS_DATA | array | [{ label, description }]——主视觉氛围、分区底色、纹理、图案 |
PAGES_DATA | array | [{ name, route, description, content: [...], components: [...] }]——content 按顺序列出页面的叙事要点,每个分区一行并说明其目的;components 列出使用的共享组件名称 |
COMPONENTS_DATA | array | [{ name, purpose, usedBy: [...] }]——共享组件及使用它们的页面名称 |
ROUTES_DATA | array | [{ path, page }]——路由器将注册的所有路由 |
BIDIRECTIONAL_REVIEW_DATA | array | 每个计划中的可见或可交互组件,格式为 { component, classification, reason, states: [...], viewports: ["desktop" or "narrow"], checks: [...] };面向制作方的所有字符串都必须本地化 |
REVIEW_DATA | object | { agentChecks: [...], makerReview: [...] },遵循下方的责任约定 |
DEPLOYMENT_DATA | array | [{ title, description, recommended?: boolean }]——必须且只能将一个选项标记为 recommended: true |
PLAN_LABELS 必须包含以下所有键。英文值用于描述各标签的含义;请将每个值都翻译为 SITE_LOCALE。请原样保留 {siteName} 和 {count},因为渲染器会在运行时替换这些占位符。 | ||
| 请勿遗漏任何键,也不要让个别标签回退为英文。 |
{
"navigation": {
"group": "Plan",
"overview": "Overview",
"design": "Design",
"pages": "Pages & Components",
"deployment": "Deployment & Review"
},
"overview": {
"title": "Overview",
"description": "Implementation plan for {siteName}",
"stats": {
"pages": "Pages",
"components": "Shared Components",
"routes": "Routes"
},
"nextSteps": {
"title": "What happens next",
"design": {
"title": "Apply design tokens",
"description": "Fonts, palette, motion, and backgrounds written into your theme"
},
"components": {
"title": "Build shared components",
"descriptionOne": "{count} reusable component used across pages",
"descriptionOther": "{count} reusable components used across pages"
},
"pages": {
"title": "Create pages",
"descriptionOne": "{count} page with routing and navigation",
"descriptionOther": "{count} pages with routing and navigation"
},
"critique": {
"title": "Critique the design",
"description": "Desktop and mobile screenshots scored and refined against the design rubric"
},
"verify": {
"title": "Verify & accessibility",
"description": "axe-core audit, Playwright checks, and user review"
}
}
},
"design": {
"title": "Design",
"description": "Design direction, typography, palette, motion, and background treatments",
"direction": {
"title": "Design direction",
"heroConcept": "Hero concept",
"signatureMoment": "Signature moment",
"primaryAction": "Primary action",
"secondaryAction": "Secondary action",
"principalDoubt": "Principal doubt",
"proofStrategy": "Proof strategy",
"brandSource": "Brand source",
"audience": "Audience and job"
},
"typography": "Typography",
"palette": "Color palette",
"motion": "Motion & animation",
"backgrounds": "Background treatment",
"primaryRole": "Primary — body & UI",
"secondaryRole": "Secondary — headings"
},
"pages": {
"title": "Pages & Components",
"description": "Pages to build, their content outline, and the shared components they rely on",
"pages": "Pages",
"components": "Shared components",
"routing": "Routing",
"path": "Path",
"page": "Page",
"content": "Content",
"componentsUsed": "Components used",
"usedBy": "Used by",
"noComponents": "No shared components planned yet."
},
"bidirectional": {
"title": "Bidirectional review scope",
"description": "Planned component, state, and viewport coverage; reconciled with the actual implementation before handoff",
"classification": "Classification",
"reason": "Reason",
"states": "Applicable states",
"viewports": "Applicable viewports",
"checks": "Planned checks",
"classifications": {
"directionNeutral": "Direction-neutral",
"directionAware": "Direction-aware",
"directionFixed": "Direction-fixed",
"unknownThirdParty": "Unknown or third-party"
},
"viewport": {
"desktop": "Desktop",
"narrow": "Narrow or mobile"
}
},
"deployment": {
"title": "Deployment & Review",
"description": "Verification checklist and deployment options",
"verify": "Before handoff — verify",
"agentChecks": "Agent verification",
"agentChecksDescription": "The agent will run and report these checks before handoff.",
"makerReview": "Maker review",
"makerReviewDescription": "Review these judgment-based items in the live preview.",
"options": "Deployment options",
"recommended": "Recommended"
},
"common": {
"noneSpecified": "None specified."
},
"footer": {
"aiWarning": "AI-generated content may be incorrect"
}
}
**所有面向制作方的计划数据都必须使用所选内容语言。**这包括 PLAN_TITLE、SUMMARY、PLAN_LABELS、描述性的 AESTHETIC 和 MOOD 名称、字体选择理由和示例、调色板描述、动效与背景的标签和描述、页面显示名称/描述/内容大纲、组件显示名称/用途、路由页面标签、审核项,以及部署标题/描述。
技术值保持不变:框架和软件包名称、文件路径、路由、命令、区域设置标签、CSS 变量、十六进制颜色、标识符、API 名称,以及品牌/产品名称。当页面或组件的 name 同时也是计划中的代码标识符时,请保持不变;仅将面向用户的显示名称本地化。请使用完整的译文短语,不要按英文语序拼接翻译后的片段。
将 REVIEW_DATA 构建为两个非空的本地化字符串数组:
{
"agentChecks": [
"The root document uses the canonical site locale and resolved writing direction",
"The bidirectional-readiness audit reports no errors; layout uses logical CSS and every physical exception is documented and verified",
"Every visible or interactive component is classified, and the planned review scope is reconciled with the actual source and rendered application",
"Every applicable state and viewport of direction-aware, direction-fixed, and unknown or third-party components has verification evidence",
"Localized form labels, placeholders, hints, helper text, validation messages, prefixes, suffixes, and overlays follow the active UI direction; only classified machine-oriented values remain LTR",
"Representative routes work in the selected direction and a pseudo-opposite direction at desktop and narrow widths without console errors, broken reading order, or broken focus order",
"Unknown-direction content uses bdi or dir=auto, while URLs, email addresses, code, paths, GUIDs, and identifiers are explicitly isolated",
"Dates, numbers, currency, percentages, and relative time use locale-aware Intl formatting",
"Typography covers the selected script and expanded or opposite-direction text does not clip or overlap",
"Directional components, interactions, icons, and assets behave according to their reviewed mirror, preserve, or replace classification",
"Every route has zero critical or serious axe-core accessibility violations"
],
"makerReview": [
"Language, terminology, tone, regional wording, and regulated content are appropriate",
"Visual hierarchy and reading flow feel natural in the selected direction and the opposite-direction preview",
"Typography remains readable and preserves the intended brand character",
"Icon and image direction choices are culturally and semantically appropriate",
"Text wrapping, expansion, and narrow-screen layouts remain visually acceptable"
]
}
请将这些内容翻译为完整的审核标准,而不是照搬英文措辞。两组内容的职责不同:
agentChecks是 Agent 必须在第 5–7 阶段执行并报告的客观事项。它们不是给制作方的指示。makerReview包含自动化无法判定的语言、文化、品牌和视觉方面的判断。制作方需在第 7 阶段于实时预览中审核这些内容。
不要将主观的制作方判断移入 agentChecks,也不要要求制作方重复进行确定性的源码、浏览器或无障碍检查。
4.3 渲染 HTML 计划
在 <PROJECT_ROOT>/docs/ 下选择一个输出路径。默认使用 create-site-plan.html;如果该文件已存在,请选择一个有描述性的变体,例如 create-site-plan-v2.html(渲染脚本不会覆盖已有文件)。
node "${PLUGIN_ROOT}/scripts/render-createsite-plan.js" --output "<PROJECT_ROOT>/docs/create-site-plan.html" --data - <<'PLAN'
<plan JSON>
PLAN
如示例所示,通过 stdin 传入计划 JSON,这样无需写入临时文件,且其中的内容(例如用户输入的品牌 URL)不会由 shell 解析;带引号的 'PLAN' 分隔符会关闭展开。
脚本成功时会输出 {"status":"ok","output":"<path>"}。请捕获并使用该实际输出路径,以便执行下一步。
4.4 在默认浏览器中打开计划
使用适用于当前环境的平台文件打开程序,在默认浏览器中打开 <OUTPUT_PATH>。例如,在 macOS 上使用 open,在 Linux 上使用 xdg-open,在 Windows 上使用相应的默认浏览器打开程序。
4.5 在终端中简要总结
终端消息应保持简短——完整计划现已放在 HTML 文件中。请包含:
- 一句话确认计划已渲染,并说明输出路径。
- 3–5 行项目符号摘要:设计理念、标志性时刻、框架、内容语言、页面和组件数量,以及主要调色板颜色。
- 提示:「请查看已打开的浏览器标签页,了解设计方向、页面、色板、字体示例和部署选项。」
请勿在终端中输出完整计划内容——这会违背使用 HTML 视图的目的。
4.6 显示「等待输入」横幅
请求用户批准时,用户可能仍在查看全屏脚手架加载界面。请 Write <PROJECT_ROOT>/public/scaffold-status.json:
{ "message": "Ready to build", "awaitingInput": true, "inputPrompt": "Plan approval needed — review the plan in your browser and answer in the terminal." }
用户回复后,立即再次 Write 同一文件,并将 "awaitingInput": false。
4.7 请求批准
🚦 关卡(计划 · create-site:4.7.plan-approval): 在第 5 阶段开始用真实页面、组件和设计令牌替换脚手架之前,对渲染后的 HTML 计划进行最终确认。
触发条件: 第 4.3 阶段已渲染
docs/create-site-plan.html;第 4.4 阶段已在浏览器中打开该文件。 请求原因: 第 5 阶段会重写整个脚手架(theme.css、Layout、Home 页面、组件、路由)——撤销这些更改会影响实现阶段中的每次提交。 取消后的状态: 不会造成破坏——可以连同项目目录一起删除脚手架本身;不会触发 Dataverse / 部署。
使用 AskUserQuestion:
| 问题 | 标题 | 选项 |
|---|---|---|
| 这个计划看起来不错吗? | 计划 | 批准并开始构建(推荐),我想进行修改 |
- 如果选择“批准”:继续第 5 阶段。
- 如果选择“我想进行修改”:询问他们想修改什么,更新 JSON,并渲染到一个新文件名(渲染脚本不会覆盖文件)。在浏览器中重新打开新文件,并重复第 4.5–4.7 步。
输出:已批准的实现计划,以及与项目一同提交的 HTML 副本,供用户在实现期间及之后参考。
第 5 阶段:实现
目标:应用设计理念,从一开始就构建所有页面、组件和设计元素,然后通过视觉设计评审验证结果
前提条件: 开发服务器必须已运行,并通过 Playwright 验证(在第 2 阶段完成)。如果尚未运行,请返回并完成第 2 阶段。
设计参考: 基于
${PLUGIN_ROOT}/references/design-aesthetics.md(设计系统)和${PLUGIN_ROOT}/references/page-blueprints.md(页面叙事、主视觉模式、文案)进行构建。如果这些内容尚未包含在上下文中,请现在阅读。从一开始,所有页面和组件都应采用所选的字体、颜色令牌、图像、动效和背景——不要先使用中性样式构建,之后再重新设计。
操作:
5.1 为所有工作创建待办事项
编写任何代码之前,使用 TaskCreate 为每项工作创建一个待办事项,让用户清楚了解将要构建的内容:
- 每个页面创建一个待办事项——例如,“创建联系页面(
/contact)”、“创建仪表板页面(/dashboard)” - 每个共享组件创建一个待办事项——例如,“创建 ContactForm 组件”、“创建 DataTable 组件”
- 为路由创建一个待办事项——“使用所有新路由更新路由器”
- 为导航创建一个待办事项——“使用导航链接更新 Layout/Header”
- 为设计基础创建一个待办事项 - “应用设计令牌(主题令牌、字体、动效、背景)”
- 为设计评审创建一个待办事项 - “进行设计评审(桌面端和移动端)”
每个待办事项都应包含清晰的 subject、activeForm 和 description,其中应注明文件路径及页面/组件的用途。然后按顺序处理待办事项,并将每项状态从 in_progress 标记为 completed。
5.2 替换脚手架并进行构建
脚手架只是一个临时加载屏幕——必须在此阶段将其完全替换。不要在其基础上继续构建,也不要尝试将加载动画改造成真实页面。使用用户选定的设计,从头开始。
在加载器中播报进度:执行以下每个步骤之前,更新
<PROJECT_ROOT>/public/scaffold-status.json,这样用户——可能仍在观看 Home 页面的加载器——就能看到实际进度,而不是硬编码的占位循环。使用简短的现在分词message(例如,"Creating Navbar component"、"Creating Contact page")。可在消息本身中加入有用的分组背景信息。加载器会在约 1.5 秒内获取更新。第 4 步替换 Home 页面后,更新将不再产生效果。
-
设计基础 - 从头彻底重写
theme.css(Angular 项目则为styles.css),加入 design-aesthetics.md 第 3 节中的完整令牌集、基础元素样式、第 8 节中的交互状态样式、prefers-reduced-motion区块、背景处理方式,以及逻辑 CSS 属性。所选字体栈必须涵盖SITE_LOCALE解析出的文字系统。默认情况下不要使用物理方向的左/右布局,即使网站最初仅支持从左到右的文字也不例外。将选定的 Google Fonts 添加到入口 HTML 的字体<link>中(index.html,Astro 项目则为Layout.astro),与脚手架中的 DM Sans + Outfit 并列;加载器会一直使用脚手架中的字体,直到第 4 步。彻底删除脚手架的加载屏幕 CSS。完成此步骤后提交。开始前,将加载器状态设为{ "message": "Applying design tokens" }。 -
布局 - 重写 Layout 组件(Astro 项目还需重写 Header/Footer),构建体现所选设计的适当导航、页眉和页脚,并为已批准计划中的每个路由添加链接,以便在首次印象评审前定稿页眉。用真实的布局结构替换脚手架中仅传递内容的 Layout。将状态设为
{ "message": "Rewriting Layout" }。 -
共享组件 - 构建可复用组件(Navbar、Footer、ContactForm 等),供页面使用;每个组件都必须包含 design-aesthetics.md 状态表中适用于它的所有状态。为每个组件设置状态:
{ "message": "Creating <Component> component" }。 -
页面 - 为每个请求的页面创建路由组件,彻底替换脚手架的 Home 页面和 About 占位页。先构建 Home 页面,并优先构建其主视觉区,遵循需求简介中的主视觉概念。Home 页面构建完成后,立即对
/进行首次印象评审(参见5.7中的“运行评审”),并修复主视觉区,直至通过,最多进行三轮——之后构建的所有页面都会沿用这一基础。按各页面计划中的叙事节奏顺序构建页面区块。每个页面组件都必须在挂载时更新document.title,以反映当前页面(例如"Contact — Contoso Portal")。使用框架惯用的生命周期钩子:useEffect(React)、onMounted(Vue)、ngOnInit(Angular),或在 Astro 中使用 frontmatter 里的<title>标签。格式:"<Page Name> — <Site Name>",主页仅使用"<Site Name>"。编写文件前,为每个页面设置状态:{ "message": "Creating <Page> page" }。替换 Home 页面本身后,加载器便会消失——之后无需再更新状态。 -
路由器 - 注册所有新路由(脚手架中只有
/和/about——添加所有请求的路由) -
导航 - 确认每个 Layout/Header 链接都指向已注册的路由,并使用
aria-current="page"标记当前项目 -
入口 HTML - 从
index.html中字体<link>移除脚手架的 DM Sans + Outfit(Astro 项目则从Layout.astro中移除),确保只加载选定的字体系列;并将<meta name="theme-color">设为--color-bg的值 -
预留 AI 摘要位置 —— 仅当 Phase 3 中的
AI_SUMMARY_PLACEMENTS非空时执行。对于每个位置,在目标页面源代码的预定插入点插入一个注释标记。不要添加可见的占位 UI、存根组件或额外路由——只需添加一个可通过 grep 搜索的锚点,供/add-ai-webapi之后查找并替换。语法取决于框架:框架 标记语法 React (JSX) {/* POWERPAGES:AI-SLOT kind=<pick> */}放在组件的 return JSX 内Vue (SFC 模板) `` 放在 <template>内Angular (HTML 模板) `` 放在组件模板内 Astro `` 放在组件的 HTML 内 其中
<pick>为search-summary、data-summarization之一——必须与位置记录中的内容完全一致。页面内的位置:
data-summarization(记录详情):紧接页面标题之后、详情内容之前——Copilot 风格的摘要卡片自然会出现在阅读顺序中的这个位置。data-summarization(列表页):紧接列表 / 表格之前、页面标题及任何筛选栏之后。search-summary:紧接搜索结果列表之前、搜索输入框之后——摘要段落会先于关键词命中项显示。
每个位置只放置一个标记,且必须与
AI_SUMMARY_PLACEMENTS记录中marker字段的定义完全一致。不要添加占位组件(<CopilotSummaryCard />等)、CSS 类或空的<aside>元素——该位置只需一条注释。网站交付时应当仿佛完全没有考虑过 AI;后续技能才会执行实际工作。
内容语言要求:
- 使用
SITE_LANGUAGE原生撰写内容;不要先制作英文网站,再机械地翻译。 - 本地化所有用户可见的 SPA 文本:导航、标题、正文、按钮、链接、表单、验证信息、加载 / 错误 / 空状态、文档标题、元数据、图片替代文本和 ARIA 标签。
- 保留代码标识符、软件包名称、CSS 类、Dataverse 逻辑名称、API 端点和环境变量,不作翻译。
- 默认将路由路径保留为稳定的语义化英文 ASCII 标识符(例如
/services),同时使用SITE_LANGUAGE编写导航标签和页面标题。创建单语言网站时不要翻译路径;稳定的路由可避免破坏书签、分析、集成、测试以及后续的/add-localization配置。只有在制作者明确要求采用永久单语言 SEO 策略时,才使用本地化路径。 - 对于每种语言环境,都使用逻辑 CSS 属性,并保留有意义的 DOM 阅读 / 焦点顺序。不要反转数组或使用
row-reverse来模拟 RTL。初始即为 RTL 的网站也必须在 RTL 模式下进行视觉验证。 - 将每个表单字段视为一个完整的方向性表面:本地化标签、占位符、提示、辅助文本、验证消息、前缀、后缀、图标和打开的菜单,都应遵循当前 UI 方向,并使用逻辑对齐。原生自由文本输入框和文本区域应使用自适应
dir:内容为空时使用当前 UI 方向,有内容时使用auto,清空后再次使用当前 UI 方向。在适合提交方向元数据的地方保留或添加dirname。只有电子邮件地址、电话号码、URL、代码、路径、GUID 和标识符等经分类的机器定向值才保持 LTR;添加共享标准要求的相邻/* bidi-fixed: <specific reason>; verify=ltr,rtl */注释或等效 HTML 指令。字段周围的 UI 仍须适应当前方向。 - 使用
<bdi>或dir="auto"包裹独立插入的方向未知内容(姓名、评论、标题、搜索查询)。URL、电子邮件、代码、文件路径、GUID 和其他机器值应明确隔离,通常使用 LTR。自由文本原生控件不得永久使用dir="auto";应根据值是否为空及当前 UI 方向设置其原生方向。 - 使用
IntlAPI 格式化日期、数字、货币、百分比和相对时间,不要通过拼接与语言环境相关的标点或符号来格式化。 - 将方向性图标和素材分类为保持不变、镜像或替换。只有语义上遵循阅读进程的控件才应镜像。
- 只有在相邻位置添加
/* bidi-physical: <specific reason>; verify=ltr,rtl */指令,并且成功通过两种方向的浏览器验证后,才可保留必要的物理属性声明。 重要:构建真正可用的 UI,并贯彻设计理念——不要创建占位用的“即将推出”页面,也不要使用毫无设计的通用标记。每个页面和组件从创建之初就要体现这一理念。本阶段结束后,脚手架加载画面必须彻底消失,不得留下 Power Pages 品牌动画的任何痕迹。
5.3 有目的地选用视觉素材
遵循 design-aesthetics.md 第 6 节中的视觉素材优先顺序:首先将网站自身的 UI 作为产品体验呈现,其次使用定制的内嵌 SVG,最后才是摄影。不要使用占位服务(例如 placeholder.com、placehold.co)、失效的 <img> 标签或空白图片槽。
查找摄影素材:
- 使用
WebSearch在 Unsplash 上搜索该版块所需的具体主题(例如,site:unsplash.com city clerk helping resident at counter)——主题要贴合受众和工作内容,绝不要使用泛泛的“商务会议”。 - 选用具体照片,并使用带尺寸参数的直链:
https://images.unsplash.com/photo-{id}?w={width}&h={height}&fit=crop(卡片使用w=800,主视觉和通栏背景使用w=1600)。 - 所选照片应采用统一的艺术风格——光线和色温相近——并为所有照片应用相同的裁切比例和调色处理。
- 为每个
<img>提供描述性的alt文本(装饰性图片则使用alt="")、明确的width和height,并为首屏以下的图片设置loading="lazy"。
5.4 Git 提交检查点
每完成一个页面或组件,都要单独提交,以便在出现破坏性变更时回退。每个页面和每个组件都必须各自提交——不要将多个页面或组件合并为一次提交。
git add -A
git commit -m "<short description of what was added/changed>"
每次工具调用都会重新发送整个对话,因此应在不合并提交的前提下减少调用次数:在同一轮中编写一个组件的文件(例如 Navbar.tsx 和 Navbar.css);如果一轮中编写了多个组件,则在同一条命令中分别提交(git add src/components/Navbar.* && git commit -m "Add Navbar component" && git add src/components/Footer.* && git commit -m "Add Footer component")。
提交时机:
- 应用设计基础设置后(设计变量、字体、动效)
- 创建每个页面后(例如,“添加首页”“添加联系页面”)
- 修复首页的第一印象评审问题后
- 创建每个共享组件后(例如,“添加导航栏组件”“添加页脚组件”)
- 更新路由和导航后
- 每轮设计评审修复后
- 尝试任何高风险或实验性操作之前
如果出现问题,请回退到最近一次正常的提交:
git revert HEAD
5.5 实时验证
每次重大变更(新页面或组件)后,都要通过 Playwright 浏览网站,确认页面结构和内容:
- 使用
mcp__plugin_power-pages_playwright__browser_navigate重新加载或跳转到更新后的页面 - 使用
mcp__plugin_power-pages_playwright__browser_snapshot验证页面结构和内容是否正确 - 如果快照显示异常,请先修复,再继续
视觉评估——在桌面端和移动端宽度下截取屏幕截图——只在第一印象评审(5.2 第 4 步)和评审环节(5.7)中进行。截图会保留在对话中,并在每次后续调用时重新发送,因此构建期间的逐页检查应使用 browser_snapshot,而不是截图脚本。用户通过第 2.7 阶段分享的开发服务器 URL,在自己的浏览器中预览。
所有页面和组件完成后,运行确定性就绪状态审计:
node "${PLUGIN_ROOT}/scripts/audit-bidirectional-readiness.js" --projectRoot "<PROJECT_ROOT>"
命令会输出结构化 JSON;存在确定性错误时会以非零状态退出。在这种预期的失败路径中,也要解析 JSON。修复每条 error 发现项。在实际运行的网站中检查每条几何布局、滚动、视觉顺序和内容扩展相关发现项。对于有意采用物理方向的产品要求,保留该声明,添加经过验证的相邻 bidi-physical 指令,并在 LTR 和 RTL 下检查该组件。对于有意固定、面向机器的文本,添加相邻的 bidi-fixed 指令,并在两个方向下检查周围 UI。即使目前还没有第二种真实语言环境,也要使用伪相反方向内容检查换行、导航、表单、混合姓名/标识符、图标、日历及窄屏/移动端布局。对于每个自由输入的原生输入框或文本区域,都要在两种 UI 方向下检查空、RTL 值、LTR 值、RTL 值清除后以及 LTR 值清除后的状态。每个清除状态都必须先输入对应的值,再将其清除;空状态和清除后的状态遵循 UI 语言环境,已填入内容的状态则遵循输入值的方向。
根据已完成的源码和渲染路由,对 BIDIRECTIONAL_REVIEW_DATA 进行核对。添加规划期间未知的所有已实现可见组件或交互组件,并添加实现过程中发现的所有适用状态或视口。对每个站点,都要检查所选方向和伪相反方向:LTR 站点要覆盖伪 RTL,RTL 站点要覆盖伪 LTR。方向中立组件需要检查继承情况;方向感知组件需要在两个方向下检查所有适用状态;方向固定组件需要提供语义理由,并检查周围 UI;未知/第三方组件需要进行渲染检查,包括其打开的对话框、菜单、自动完成面板、工具提示、teleport、portal、覆盖层、Shadow DOM 或 iframe。
构建 ${PLUGIN_ROOT}/references/rendered-bidirectional-verification.md 中描述的临时渲染验证规范。根据核对后的组件范围和实际选择器生成此规范;不要将其作为组件清单提交。为重要但不稳定的验证界面添加 data-bidi-id 属性。纳入所有适用状态和视口、复合表单部件、挂载到 body 的覆盖层、明确的焦点序列、不重叠组件对,以及特定方向的计算样式预期。如果两种真实 LTR 和 RTL 语言环境都存在,则使用它们;否则添加仅用于浏览器的伪相反语言环境。必须通过应用激活真实语言环境,并验证代表性的本地化内容;只有伪语言环境可以使用仅限浏览器的 set-document 操作。如果添加了运行时本地化,则每个真实的非默认语言环境都必须包含 default -> locale -> default 和 locale -> default -> locale 转换序列,并检查路由、表单状态、应用状态和焦点是否保留。伪语言环境不参与应用切换转换。每个真实语言环境(包括默认语言环境)都需要可重复使用的应用激活操作;往返切换时,use-current 无法恢复语言环境。对于新添加的运行时语言环境,使用 add-localization 定义的本地化验证事务:通过常规选择器临时仅公开记录的目标语言环境,针对环回开发服务器运行 Playwright,成功时保持其可用,失败时恢复为 fail-closed 可用状态。验证事务存在期间,不要完成创建或提供部署。裸 preserve 选择器仅适用于表单控件;对于标签页、面板、计数器和其他应用状态,应使用明确的文本、属性或属性值保留条目。
保留每项适用的 REVIEW_DATA.agentChecks 的证据:审核结果、测试过的路由/视口、使用的方向、混合内容测试样本、字体/文本扩展情况、组件/资源分类、控制台结果,以及任何修复措施。不得仅凭制作者的目视检查,就将由 Agent 负责的标准标记为已完成。
5.6 清理实时状态文件
移除脚手架加载器后,public/scaffold-status.json 就成了多余文件,会随部署的网站一起发布。请从 <PROJECT_ROOT>/public/ 中删除该文件,并将此删除操作与最终实现一并提交。
5.7 设计评审
根据截图,按照 ${PLUGIN_ROOT}/references/design-critique.md 评审网站(首次评审时读取一次)。每次工具调用都会重新发送整个对话,因此评审流程经过精简,可尽量减少调用次数:一次脚本调用即可捕获所有路由在两种宽度下的画面并运行自动检查,再用一次调用并行打开所有截图。逐页进行单步浏览器调用(导航、调整大小、截图、评估)会让相同的评审耗费多得多的调用。
运行评审(用于 5.2 步骤 4 中的第一印象评审,以及每一轮设计评审):
-
按 design-critique.md 的 Capture 部分所示,通过 stdin 发送请求进行捕获——开发服务器 URL 和路由都不应出现在命令行中。无需安装任何内容:脚本使用插件固定的 Playwright 版本。
node "${PLUGIN_ROOT}/scripts/capture-design-review.js" --input - <<'REQUEST' {"url": "<DEV_SERVER_URL>", "routes": ["/", "/about"]} REQUEST -
在一次调用中打开
summary.images中的所有路径,并从 JSON 中读取自动检查结果。 -
按照 design-critique.md 的说明进行评判,并撰写其定义的精简评分卡和修复清单。
-
按影响从高到低修复评分卡中的所有问题,并提交更改。仅在下一轮开始时重新捕获——每轮捕获一次,在该轮所有修复完成后进行,而不是每次单独修复后都捕获。
评审流程:评审每条路由,然后修复并再次评审;只捕获发生变化的路由和 /,直到满足以下任一条件:
- 没有关键门槛未通过,且每个类别的评分均为 3 分或更高——评审完成。
- 已进行三轮评审,且没有关键门槛未通过——评审完成;仍低于 3 分的类别及其原因需记录在评分卡中,以便 Phase 7 摘要展示给用户。
- 已进行三轮评审,且仍有关键门槛未通过——进入下方的门槛处理流程;关键门槛绝不能悄无声息地通过。保留最终评分卡以供 Phase 7 摘要使用,然后向同一脚本发送
{"cleanup": "<outputDir>"},以相同方式删除每轮的截图。
🚦 门槛(进度 · create-site:5.7.critique-blocked): 经过三轮设计评审后,仍有关键设计门槛未通过。
触发条件: Phase 5.7 第三轮结束时,
design-critique.md中至少有一个关键门槛未通过。 询问原因: 关键门槛意味着首屏、移动端布局、无障碍流程或真实性证明存在问题;若不作处理就继续,会让问题延续到审计和部署后的网站中。 取消后的状态: 无——网站文件和提交仍保留在磁盘上。
使用 AskUserQuestion,在问题中列出每个未通过的关键门槛及其证据:
| 问题 | 标题 | 选项 |
|---|---|---|
| (未通过的门槛及证据) - 我应该如何继续? | 设计门槛 | 继续修复(推荐)、继续并将其记录为已知问题、在此停止 |
- 继续修复:针对未通过的门槛最多再进行三轮评审;如果仍未通过,则返回此门槛处理流程。
- 继续并将其记录为已知问题:将其作为已知问题添加到评分卡,并继续完成 5.7;Phase 7 摘要会列出该问题。
- 在此停止:停止此技能;项目及其提交仍保留在磁盘上。
门槛:在所有自定义工作完成且 5.7 评审流程完成之前,不得进入 Phase 6。 所有要求的页面和功能都已实现,设计令牌位于主题文件中,所选 Google Fonts 通过字体检查,没有关键门槛未通过(除非用户选择忽略该门槛继续),并且已记录评分卡。
输出:所有页面、组件和设计元素均已实现、评审并验证
5.8 提供其他语言选项
在提供子工作流之前,查询集中式的本地化可用性:
node "${PLUGIN_ROOT}/scripts/lib/localization-config.js" mode-availability --framework "<react|vue|angular|astro>"
如果 availableModes 为空,请根据返回的原因说明所选框架暂时无法进行本地化,设置 LOCALIZATION_REQUESTED=false,并且不要询问是否添加其他语言。目前 Astro 属于这种情况。不要在 create-site 中硬编码单独的框架可用性列表。
询问现在是否要添加更多语言:
您现在要添加更多语言吗? 该网站目前仅支持
<SITE_LANGUAGE>。此操作只会为 SPA 添加本地化;Dataverse 和 Power Pages 的系统消息仍为英文。
使用 AskUserQuestion,并且必须使用以下原文和选项:
| 问题 | 标题 | 选项 |
|---|---|---|
| 您现在要添加更多语言吗? 该网站目前仅支持 <SITE_LANGUAGE>。此操作只会为 SPA 添加本地化;Dataverse 和 Power Pages 的系统消息仍为英文。 | 其他语言 | 是 — 现在添加更多语言、否 — 网站仅使用 <SITE_LANGUAGE> |
将答案记录为 LOCALIZATION_REQUESTED=true|false。即使 $ARGUMENTS 中提到了本地化,也要在此时询问,这样制作者的回答就能立即决定是否启动子工作流。
当 LOCALIZATION_REQUESTED=true 时,调用:
/power-pages:add-localization <PROJECT_ROOT> [FROM_CREATE_SITE]
请等待技能完成后再进行无障碍验证。子技能负责处理区域设置和软件包问题、生成翻译、连接语言选择器、进行实现审查、构建和本地化检查。[FROM_CREATE_SITE] 上下文可避免重复询问部署;控制权会返回此处,由 create-site 执行无障碍验证、最终审查和现有的部署提示。
当 LOCALIZATION_REQUESTED=false 时,跳过子工作流。
5.9 验证已批准的网站语言
使用已批准的语言上下文,明确运行 create-site 验证器。只能使用 resolve-locale 返回的规范 SITE_LOCALE 和经过验证的 SITE_DIRECTION 枚举;切勿传入制作者最初的自由文本答案。将命令的工作目录设为 PROJECT_ROOT 后运行:
node "${PLUGIN_ROOT}/skills/create-site/scripts/validate-site.js" \
--expectedLocale "<SITE_LOCALE>" \
--expectedDirection "<SITE_DIRECTION>"
将退出码 2 视为阻塞错误。修复报告中指出的文档或本地化默认值不匹配问题,然后重新运行命令,直到通过。自动技能验证器运行时不带这些参数,但仍会检查检测到的文档或本地化上下文是否有效且一致。显式调用可验证其是否也与本次 create-site 会话中批准的区域设置和方向相符,且无需临时或持久化元数据。
关卡:在完成第 5.7 阶段的设计评审、完成所有请求的本地化,并通过已批准的网站语言验证之前,请勿进入第 6 阶段。完成的设计必须使用支持文字脚本的字体排版和方向中立的逻辑布局。
输出:已实现并验证所有页面、组件、设计元素及请求的本地化
第 6 阶段:无障碍验证
目标:使用 axe-core 自动化测试验证网站是否符合 WCAG 2.2 AA 标准,并修复任何违规问题
前提条件: 必须已完整实现所有页面和组件(第 5 阶段已完成)。开发服务器必须正在运行。
操作:
6.1 前提条件
开发服务器必须正在运行。无需安装任何内容:审计使用插件固定版本的 Playwright 和系统已安装的浏览器(Edge 或 Chrome),并会在运行前验证 axe-core 脚本的哈希是否与固定值一致。
6.2 对每个页面运行 axe-core 审计
通过 Bash 运行审计脚本,将开发服务器 URL 和每个站点路由作为 JSON 请求发送到标准输入,避免 shell 解析它们:
node "${PLUGIN_ROOT}/scripts/axe-audit.js" --input - <<'REQUEST'
{"url": "<DEV_SERVER_URL>", "routes": ["/", "/about", "/services", "/contact"]}
REQUEST
解析返回的每个路由结果组成的 JSON 数组。每个结果包含 violations(含 id、impact、description、helpUrl 和受影响的 nodes)、passes 计数和 incomplete 计数。退出码非零表示发现了至少一个 critical 或 serious 违规,或者某个路由无法审计——其结果会包含 error 而非违规项,因此请修复原因(路由损坏或开发服务器未运行)并重新审计。
解析 JSON 输出并记录所有违规项。
6.2b 运行渲染后的双向审计
阅读 ${PLUGIN_ROOT}/references/rendered-bidirectional-verification.md。如果
add-localization 返回了成功报告,请先验证并复用该报告:
node "${PLUGIN_ROOT}/scripts/audit-rendered-bidirectional-readiness.js" \
--projectRoot "<PROJECT_ROOT>" \
--reuse-report "<ADD_LOCALIZATION_REPORT_PATH>"
仅当子报告来自已通过的完整 standard 或
extensive 运行,且源代码、资源、依赖项、构建配置和语言区域可用性
仍与报告一致时,此操作才会成功。永不复用 targeted 修复
报告。仅就绪状态元数据不会导致报告失效。如果复用成功,不要编写
规格或重复执行矩阵。如果报告已过期——例如无障碍修复更改了相关源代码——
请根据当前实现编写组件/状态/视口规格并运行:
node "${PLUGIN_ROOT}/scripts/audit-rendered-bidirectional-readiness.js" \
--url "<DEV_SERVER_URL>" \
--projectRoot "<PROJECT_ROOT>" \
--spec "<TEMP_SPEC_PATH>" \
--evidence-dir "<PROJECT_ROOT>/docs/bidirectional-evidence/<RUN_ID>" \
--output "<PROJECT_ROOT>/docs/bidirectional-evidence/<RUN_ID>/report.json"
该命令会输出 JSON,并在存在阻塞性渲染结果时以 1 退出;
在此预期情况下,请解析标准输出。退出码 2 表示运行器/规格失败,
必须先修复才能继续。报告写入后删除临时规格。
修复所有渲染的 error 并重新运行受影响的完整矩阵。审查结果必须依据截图
进行处置;它们不会自动通过。审计必须涵盖计算后的 lang/dir、组件和复合字段
方向、裁切、页面溢出、视口越界、声明的非重叠、焦点顺序、
门户/覆盖层、未知/第三方界面,以及适用时的运行时往返保留。
可见且不透明的外部界面会造成阻塞,除非将其替换,或使其在受影响的语言区域中有意不可用。
采用渲染验证参考中的分组执行模型:
默认 maxConcurrency: 3;除非存在确定性重置方式,否则使用 reload
隔离状态;只有在提供明确重置操作时才使用 resettable;对于全局或破坏性状态,使用
isolated。彼此独立的路由/视口/语言区域分组可以并发运行;语言区域切换序列必须串行运行。
GATE:如果 create-site 报告中存在影响可用语言区域或可用站点体验的 error,或存在未分类的审查结果,则不得继续进入阶段 7。 每个审查项都必须有证据和 建议的解决方案、基于判断的通过结论,或供创建者审查的可用性限制处置方案。 只有当 add-localization 阻塞项所影响的语言区域在每个激活边界均可验证为不可用时, 才能继续;将该子报告作为待处理证据保留,并使用真实语言区域加伪造的相反方向 审计其余可用体验。截图记录的是渲染结果;它不能推翻确定性失败。
对每条审查结果,记录其属于已解决、接受为基于判断的通过,还是建议作为可用性限制处理。可用性限制
必须注明受影响的组件/页面、用户影响以及报告或截图证据。绝不能允许创建者覆盖错误结果。如果
add-localization 子报告返回 pending-remediation,请在继续处理可用站点体验前,
确认所有受影响的语言区域仍被排除在选择器、检测、元数据和生产输出之外。
6.3 修复无障碍违规
对于发现的每项违规,找出源文件并进行修复:
| 违规 | 修复方法 |
|---|---|
图片缺少 alt 文本 | 为 <img> 标签添加描述性 alt 属性 |
| 颜色对比度不足 | 调整 CSS 颜色变量,使对比度达到 4.5:1(普通文本)或 3:1(大号文本) |
| 表单缺少标签 | 添加 <label> 元素或 aria-label 属性 |
| 缺少地标区域 | 使用 <main>、<nav>、<header>、<footer> 包裹内容 |
| 标题级别跳跃 | 修正标题层级(h1 → h2 → h3,不得跳级) |
| 链接缺少文本 | 为链接添加描述性文本或 aria-label |
缺少或错误的 lang/dir 属性 | 在根 <html> 元素上设置 lang="<SITE_LOCALE>" 和 dir="<SITE_DIRECTION>" |
| 焦点指示器不充分 | 为交互元素添加可见的 outline 样式 |
修复每组相关违规后,提交更改:
git add -A
git commit -m "Fix accessibility: <violation description>"
6.4 修复后重新验证
完成所有修复后,重新运行审计脚本(与 6.2 中的命令相同),确认违规已解决:
- 如果出现新的违规(例如,某项修复引入了回归问题),重复执行 6.3–6.4
- 持续执行,直到脚本以代码 0 退出(已审计每个路由,且
critical和serious违规均为零) - 如果某项修复更改了颜色、间距或布局,则对受影响的路由进行一轮复查,并确认评分卡仍然成立。复查也可能更改源文件;如果发生这种情况,请重新审计这些路由。只有在最后一次审计和最后一轮复查均通过且无需进一步修复时,Phase 6 才算完成
向用户展示汇总表:
| Page | Route | Violations Found | Violations Fixed | Status |
|------|-------|-----------------|-----------------|--------|
| Home | / | 3 | 3 | Pass |
| About | /about | 1 | 1 | Pass |
| Contact | /contact | 2 | 2 | Pass |
| **Total** | | **6** | **6** | **All passing** |
GATE:在所有页面通过 axe-core 审计且
critical和serious违规均为零之前,请勿进入 Phase 7。 也应尽可能修复轻微和中等违规,但它们不会阻止继续。
将最终 axe-core 结果记录到相应的
REVIEW_DATA.agentChecks 项中。无障碍工作仍由 Agent 负责;不会要求 maker
重复执行自动化审计。
输出:通过无障碍验证的网站,axe-core critical/serious 违规为零
Phase 7:复查与用户测试
目标:确保网站符合用户预期,且所有页面均可正常使用
🚦 Gate(plan · create-site:7.review): 实时网站复查——这是部署提示出现前请求更改的最后机会。取消分支允许用户继续迭代。此 Gate 在下方操作列表的第 5 步触发。
触发条件: Phase 7 已通过 Playwright 验证所有页面均可渲染。 提问原因: 用户将失去在部署前发现 UI 问题的机会;有问题的页面可能会被推送。 取消后: 无变化——网站文件保留在磁盘上。
操作:
-
通过一次调用确认每个路由仍能正常加载——使用 5.7 中的捕获请求,包含每个路由并设置
"checksOnly": true——并修复列在summary.pageErrors、summary.overflow或summary.captureErrors下的任何路由。视觉复查已在 Phase 5.7 完成 -
展示已构建内容的概要,并附上 Phase 5.7 的设计评分卡:
| Component Type | Count | Details | |---------------------|-------|---------| | Pages | 4 | Home (/), About (/about), Services (/services), Contact (/contact) | | Shared Components | 3 | Navbar, Footer, ContactForm | | Design Elements | 5 | Schibsted Grotesk + Public Sans, 15 color tokens, product-moment hero, status-timeline signature moment, paper-grain backgrounds | | Git Commits | 9 | scaffold + 8 feature and critique commits |接下来用一句话说明设计理念,并展示评分卡 (类别、分数、一行依据),包括任何评分低于 3 的类别及其原因。同时包含
SITE_LANGUAGE、SITE_LOCALE和SITE_DIRECTION。展示经过协调的双向复查范围,列出每个组件的分类以及已测试的适用状态和视口。 分别报告方向感知、方向固定以及未知/第三方证据;不要将实施前的计划视为已验证实施组件的证据。
将范围中的每个条目链接到相应的已渲染报告用例和任何已捕获的屏幕截图。显示通过、待复查和失败用例的总数。不要将临时规范内部信息作为项目配置公开。 然后单独报告
REVIEW_DATA.agentChecks中的标准,并附上第 5–6 阶段收集的证据。每项标准都必须报告为 Passed 或 Blocked,绝不能报告为制作者验证。请求批准前,先修复所有受阻的技术标准。将
REVIEW_DATA.makerReview作为制作者的实时预览检查清单。请制作者评估语言正确性、术语、语气、地区用语、受监管内容、视觉层级和阅读流畅度、品牌字体、方向性图像/图标以及文本扩展情况。自动化检查无法批准这些依赖主观判断的标准。最后,列出网站中仍然存在的示例内容——在
src中搜索SAMPLE CONTENT标记,并说明每个标记所在的文件以及它代表的内容,以便用户在本次审核中提供真实内容。 -
向用户提供开发服务器 URL,并列出所有可用路由
-
将双向文本支持状态分类为:
- Ready:没有未解决的静态或渲染问题。
- Ready with proposed limitations:仅当所有剩余问题均为审核级别,且网站仍然可用、可读、可访问,并且每项问题都有明确的影响说明和证据时,才可使用此状态。
- Blocked:当确定性错误影响某个可用语言区域或可用的网站体验时,使用此状态。返回第 5–6 阶段;不要要求制作者接受缺陷。
- Ready for available locales; localization pending:当所有剩余错误仅属于尚未添加的本地化语言区域,且该语言区域已从所有启用边界中排除时,使用此状态。显示不可用的语言区域及其阻塞原因,不要提供该语言区域供批准或启用。
-
使用
AskUserQuestion请用户进行审核:"The site is ready for review at
<dev server URL>. Please check it out in your browser. Would you like any changes?" 对于 Ready,提供 Accept changes 和 Request revisions。对于存在拟议可用性限制的情况,提供 Fix before handoff、Accept with documented limitations 和 Request revisions。接受仅适用于审核级别的限制;在最终摘要中记录批准时间戳、影响和证据路径。如果已配置本地化,则在.powerpages-localization.json中最终确定相同的处理意见,更新所有选择器、检测、元数据和静态输出可用性边界,然后重新运行本地化验证器、项目构建,以及受影响的真实语言区域启用测试,再继续操作。 -
如果用户要求修改,请应用修改,并在返回此审核关卡前,重复适用的静态、渲染、无障碍和浏览器检查。对受影响的每个路由重新运行 axe-core 审核(6.2),因为新增控件、表单字段或重新组织的版块可能会引入违规,即使外观没有变化。如果修改影响外观,还要对受影响的路由再次进行设计审核。
7.1 最终网站完整性关卡
用户批准源代码修改并完成所有常规验证后,运行:
node "${PLUGIN_ROOT}/scripts/validate-site-integrity.js" --projectRoot "<PROJECT_ROOT>"
继续之前,修复所有阻塞错误。检查并报告相关双向文本方向及扩展内容中的审核发现。如果审核导致进一步的源代码修改,请重新运行此关卡。此关卡成功退出前,不得进入部署阶段。
输出:已获用户批准、可供部署的网站
第 8 阶段:部署与后续步骤
目标:部署网站并建议改进方向
此阶段为必需阶段。切勿在询问是否部署之前结束会话。
🚦 关卡(plan · create-site:8.deploy): 部署提示 — 用户选择“是”时调用
/deploy-site。跳过后,网站文件会留在磁盘上,供用户稍后部署。此步骤在下方操作列表的第 3 步触发。触发条件: 进入第 8 阶段;第 7 阶段审核已获批准。 询问原因: 自动部署会使用 PAC CLI 当前指向的环境 — 首次部署到错误环境后,很难恢复。 取消后保留: 无 — 网站文件仍留在磁盘上;不会触发部署。
操作:
-
记录技能使用情况:
参考:
${PLUGIN_ROOT}/references/skill-tracking-reference.md按照参考文档中的技能跟踪说明,记录此技能的使用情况。使用
--skillName "CreateSite"。注意:首次创建的网站可能不存在.powerpages-site— 脚本会静默退出。 -
再次搜索
src中的SAMPLE CONTENT标记。如果仍有标记,在询问前逐一列出每个标记及其所在文件,说明这些标记是虚构的占位内容,在公开发布前应予替换,在问题中注明数量,并将 暂时跳过 设为推荐选项。 -
使用
AskUserQuestion,选项为:立即部署(推荐)、暂时跳过;如果仍有示例内容,则使用:立即部署、暂时跳过(推荐):“您要现在将网站部署到 Power Pages 吗?”(如果仍有示例内容:“您要现在将网站部署到 Power Pages 吗?网站中仍有上方列出的 个示例内容占位符。”)
-
如果用户选择部署,则调用
/deploy-site技能。 -
将所有待办事项标记为已完成
-
提供最终摘要:
- 网站名称和用途
- 网站内容语言,以及 Dataverse/Power Pages 系统消息仍为英文这一事实
- 框架和项目位置
- 创建的组件(X 个页面、Y 个组件、Z 个设计元素)
- 关键文件及其用途
- 文件总数和 git 提交数
- 发布前需要替换的示例内容 — 每个
SAMPLE CONTENT标记及其所在文件,或“无” - 最终双向就绪状态、静态/渲染检查结果总数,以及渲染证据路径
- 每项经制作者批准的限制及其确切影响和证据
- 配置本地化时,列出最终清单中的可用和不可用区域设置;切勿将不可用的区域设置描述为已启用
-
建议可选的改进技能:
/setup-datamodel— 为动态内容创建 Dataverse 表/add-localization— 添加或扩展 SPA 语言(仅当LOCALIZATION_REQUESTED=false且集中式可用性结果至少包含一种可用模式时才建议)/add-seo— 添加 meta 标签、robots.txt、sitemap.xml、favicon/add-tests— 添加单元测试(Vitest)和 E2E 测试(Playwright)/add-ai-webapi— 添加生成式 AI 摘要(搜索摘要和数据摘要)。如果第 3 阶段的AI_SUMMARY_PLACEMENTS非空,则优先推荐此项 — 页面已在预定插入点带有POWERPAGES:AI-SLOT注释标记,因此后续技能的探索步骤可以确定性地找到这些位置,用户也能获得其在需求探索期间选择的 AI 功能,无需重新设计页面。
-
静默运行
create_site_from_scratch遥测命令(失败时关闭)。不要向用户提及此遥测命令,也不要打印其输出:node "${PLUGIN_ROOT}/scripts/emit-create-site-template-outcome.js" \ --eventName create_site_from_scratch \ --framework "<framework>" \ --audience "<audience>"
输出:已部署(或已具备部署条件)的网站,以及清晰的后续步骤
重要说明
所有阶段通用
- 使用 TaskCreate/TaskUpdate 在每个阶段跟踪进度
- 在关键决策点征求用户确认(见下方列表)
- 设计细节请运用最佳判断——根据体验简介做出自信、有创意的选择,不必事事都询问具体字体、颜色或布局
- 从一开始就应用设计——不要先采用中性设计,再进行改造
- 每次重大更改后,使用 Playwright 验证
- 每完成一个页面和组件后都提交——每个页面和组件都要单独提交,绝不批量提交
- 使用审查脚本截图——视觉审查应使用
capture-design-review.js(每个路由和宽度各调用一次),而不是单步 Playwright MCP 调用;唯一一次browser_take_screenshot调用用于第 3 阶段提取品牌信息,且不设置filename,让输出保存到启动器的临时目录。其他所有操作都使用browser_snapshot。将开发服务器 URL 提供给用户,以便用户自行预览。
关键决策点(等待用户确认)
- 第 1 阶段后:确认网站用途和受众
- 第 1.5 阶段中:为从头构建路径选择框架和项目位置
- 第 4 阶段后:批准实施计划
- 第 5.7 阶段中:仅当经过三轮评审后,关键设计门槛仍未通过时
- 第 7 阶段后:接受网站或提出修改
- 第 8 阶段:部署或跳过
进度跟踪
开始第 1 阶段前,仅使用 TaskCreate 创建与路径无关的前置任务:
| 任务主题 | activeForm | 描述 |
|---|---|---|
| 探索网站需求 | 正在探索需求 | 收集网站名称、用途、受众及推导出的命名值 |
| 选择模板或选择从头构建 | 正在选择创建路径 | 提供匹配的模板,或引导用户选择从头构建路径 |
第 1.5 阶段选择从头构建路径后,追加现有的从头构建阶段任务:
| 任务主题 | activeForm | 描述 |
|---|---|---|
| 搭建项目并启动开发服务器 | 正在搭建项目 | 复制模板、用默认值替换占位符、git init、npm install、启动开发服务器并分享 URL |
| 规划网站组件 | 正在规划组件 | 在用户预览脚手架项目期间,确定页面、组件、设计方向和路由 |
| 批准实施计划 | 正在获取计划批准 | 展示涵盖设计和页面的实施计划,并获得用户批准 |
| 实施页面和组件 | 正在构建网站 | 应用设计令牌,创建所有页面、组件、路由和导航,并使用已批准的内容语言;运行设计评审流程,然后询问是否要添加更多语言 |
| 使用 axe-core 验证无障碍性 | 正在验证无障碍性 | 在每个页面运行 axe-core,修复所有严重和较严重的问题,并重复验证,直至通过 |
| 与用户审查 | 正在审查网站 | 浏览所有页面、分享 URL、获取用户反馈并应用修改 |
| 部署并收尾 | 正在部署网站 | 询问是否部署、展示摘要并建议后续步骤 |
第 1.5 阶段选择模板路径后,立即追加安装前任务,以便用户查看每项环境检查:
| 任务主题 | 进行时形式 | 描述 |
|---|---|---|
| 选择本地模板目录 | 正在选择项目位置 | 询问应将本地模板项目克隆到何处,并要求目标位置是新建或空目录 |
| 解析目标环境 | 正在解析环境 | 在进行任何环境预检之前,解析当前 PAC/Azure 目标环境和令牌 |
| 确认目标环境 | 正在确认环境 | 询问解析出的环境是否是用户希望用于安装模板的环境 |
| 验证 CLI 租户是否一致 | 正在检查 CLI 租户 | 安装前,验证 PAC CLI 和 Azure CLI 是否已通过同一租户进行身份验证 |
| 验证 JavaScript 解除阻止要求 | 正在检查 JavaScript 设置 | 检查 blockedattachments 中是否有 .js,并在获得同意后,仅移除 js,然后再上传站点 |
| 验证 Dataverse 语言要求 | 正在检查语言可用性 | 调用 RetrieveAvailableLanguages,并要求所选模板的 requiredDataverseLanguages 中列出的每个 LCID 均可用 |
| 确认模板安装 | 正在确认模板安装 | 显示所选模板和目标环境,然后询问用户是否最终同意安装 |
重新安装策略选择普通导入、更新或无论如何都导入的路径后,追加:
当 TEMPLATE_SOLUTIONS_TO_IMPORT 恰好包含一个条目时,使用任务主题 导入模板支持解决方案,进行时形式为 正在导入支持解决方案。包含两个或更多条目时,使用表格中的复数形式。
| 任务主题 | 进行时形式 | 描述 |
|---|---|---|
| 导入模板支持解决方案 | 正在导入模板支持解决方案 | 按确定性顺序导入每个必需的非托管支持解决方案;清单中标记为 publishChanges 的解决方案会在导入过程中发布更改 |
| 克隆、生成并上传模板站点 | 正在创建模板站点 | 将打包的 SPA 源代码克隆到所选本地目录,安装依赖项,生成并验证配置的编译输出,然后上传生成的代码站点 |
| 应用模板种子数据 | 正在应用种子数据 | 在只读规划和任何财务货币决策之后,与站点创建并行插入可选的模板种子记录;失败不会阻止激活 |
| 显示未激活的模板站点 | 正在显示模板站点 | 站点创建和种子数据处理汇合后,使用 pac pages clone 写入 .powerpages-site/website.yml 的 Website Record ID,并告知用户上传的站点尚未激活 |
| 激活模板站点 | 正在激活模板站点 | 使用解析出的站点名称和 Website Record ID 调用 activate-site |
| 显示已上线的模板站点 | 正在显示已上线站点 | 在浏览器中打开已激活的站点 URL,并邀请用户继续自定义 |
如果用户选择自定义已上线的模板,追加:
| 任务主题 | 进行时形式 | 描述 |
|---|---|---|
| 规划模板自定义 | 正在规划自定义内容 | 询问用户希望更改哪些内容,并规划针对现有克隆项目的编辑 |
当所有支持解决方案均已安装,且版本相同或更新时,追加相同列表,但不包含 导入模板支持解决方案。打包 SPA 的克隆/上传、站点发现、种子数据、激活和实时预览任务仍会运行。
每项任务开始时通过 TaskUpdate 将状态标记为 in_progress,完成后标记为 completed。这样用户可以了解进度,并确保工作流具有确定性,同时避免在未来非从头开始的分支中永久跳过任务。
质量标准
每个站点在完成前都必须符合以下标准:
- 通过 Phase 5.7 设计评审:无关键门槛未通过,且每个评分项得分均为 3 分或以上;若经过三轮评审后仍低于 3 分,则记录该评分及原因(参见
design-critique.md);用户选择继续推进而未通过的关键门槛,需记录为已知问题 - 在主题文件中一次性定义设计令牌(颜色角色、字体、间距、圆角、阴影、动效),并在各处统一使用
- 通过字体检查,确认所选 Google Fonts 已加载
SITE_LOCALE的字形覆盖完整且字形塑形正确- 所有请求的页面和功能均已实现(非占位内容)
- 所有路由均正常工作,导航完整
- 根文档使用已批准的
SITE_LOCALE和SITE_DIRECTION - 通过 axe-core 验证无障碍功能——所有页面均无严重或高危违规
- 在关键里程碑创建 Git 提交
- 通过 Playwright 验证
- 用户已审阅并批准
- 已提供部署选项
示例工作流
用户请求
“为我们的顾问创建一个合作伙伴门户”
Phase 1:需求探索
- 名称:合作伙伴门户
- 用途:公司门户
- 受众:内部(合作伙伴、顾问)
Phase 1.5:模板分支决策
- 创建方式:从零开始
- 框架:React
- 位置:当前目录下的新文件夹
partner-portal
Phase 2:搭建脚手架并启动
- 已复制 React 模板,并替换默认占位内容
- 已初始化 Git、安装 npm,并在
http://localhost:5173启动开发服务器 - 已通过 Playwright 验证脚手架可以加载
- 已将 URL 分享给用户——用户可以立即预览
Phase 3:组件规划
- 功能:顾问名录、项目跟踪器、文档库、公告
- 美学:简约整洁
- 风格:专业可信
- 品牌:全新品牌形象
- 体验简介:主要操作是“查找顾问”;主要疑虑是“此名录是最新的吗?”;通过实时数据生成的醒目“今日已更新”标记提供佐证
- 设计理念:平静、精准、自信而不张扬——采用温暖的纸感表面,以 Schibsted Grotesk 作为标题字体、Public Sans 作为正文字体,使用一种深青色作为操作色,并设计以任务为先、展示实时项目数量的欢迎主视觉区
- 标志性时刻:名录筛选条件即时生效,顾问卡片随之动态重排
- 已展示并获批包含各页面叙事节奏的组件表
Phase 4:计划审批
- 将计划数据整理为单个 JSON 对象,其中包含
DESIGN_DIRECTION_DATA - 通过
render-createsite-plan.js渲染到docs/create-site-plan.html - 在用户的默认浏览器中打开
- 在终端中显示简短摘要,并提示浏览器标签页的位置
- 用户通过 AskUserQuestion 批准
Phase 5:实施
- 为每个页面、组件、路由、导航、设计基础和评审环节创建待办事项
- 按顺序构建:设计令牌 → 布局 → 共享组件 → 首页(先做主视觉区,再进行第一印象评审)→ 其余页面 → 路由器 → 导航
- 第一轮设计评审中,叙事得分为 2(首页有三个相同的卡片区块),细节得分为 2(图标描边宽度不一致);两项问题均已修复,第二轮中所有类别得分均达到 3 分或以上
- 每完成一项主要内容和每轮评审后都创建 Git 提交
Phase 6:无障碍验证
- 通过
browser_evaluate在全部 4 个页面中注入并运行 axe-core - 发现 5 项违规:2 处缺少替代文本、1 处对比度不足、1 处缺少 lang 属性、1 处跳过标题级别
- 已在源代码中修复全部违规并创建提交
- 重新运行后确认所有页面均无严重或高危违规
Phase 7:评审
- 已展示摘要表和设计评分卡
- 用户在
http://localhost:5173审阅,并要求微调颜色 - 已应用调整并重新验证
Phase 8:部署
- 用户选择部署 → 调用了
/deploy-site - 已提供最终摘要和后续步骤建议
从 Phase 1:需求探索 开始