mcpField
E
experience-cms-content-type-generate

experience-cms-content-type-generate

github.com/forcedotcom开发1.1k710 次安装1 天前更新
代码仓库

关于 experience-cms-content-type-generate

面向 Salesforce 的技能,用于发现、创建、验证和部署 ContentTypeBundle。它只创建新类型,并与组织中的现有类型协调,不修改其定义。

主要功能

  1. 01查找本地和组织中的内容类型
  2. 02将现有类型与组织版本协调
  3. 03生成 Bundle 架构和元数据
  4. 04通过部署试运行验证
  5. 05自动修复架构错误
  6. 06经用户批准后部署

适用场景

  1. 01创建新的 Salesforce CMS 内容类型
  2. 02检查本地类型与组织版本是否一致
  3. 03部署前验证新 Bundle

MCP 相关

使用 metadata-grounding 发现类型,并通过 content-readonly 检查工作区内容类型;还依赖 Salesforce CLI 执行检索、验证和部署。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill experience-cms-content-type-generate

安装后在 Claude Code 中输入/experience-cms-content-type-generate确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
discovery-prompts.md
schema-example.json
references/
agent-checklist.md
anti-patterns.md
deployment-errors.md
discovery-details.md
discovery-query-rules.md
edit-fields-loop.md
pre-deploy-checklist.md
retrieve-and-reconcile.md
schema-rules.md
schema-summary-format.md

详细介绍

experience-cms-content-type-generate

此技能的作用

  • 发现 — 检查本地 <sfdx-source>/contentTypes/,并查询 metadata-grounding,查找现有的 OOTB/自定义 ContentTypeBundle。
  • 协调 — 对于已存在的已选定/已提供 FQN,从组织中检索(仅限自定义类型),并与本地内容协调。返回与组织相符的 {fqn, schema}。
  • 创建 — 生成通过验证器检查的 ContentTypeBundle(schema.json、.contentTypeBundle-meta.xml)。
  • 验证 — 针对 CLI 默认组织(或已选定组织)运行 sf project deploy start --dry-run。
  • 自动修复 — 修复架构问题并重新验证(最多尝试 3 次)。
  • 部署 — 询问是否部署;用户确认后部署。

新类型仅支持创建,现有类型仅支持协调。除非用户同意以组织中的副本替换本地副本,否则不会修改、重命名或删除现有 Bundle;也不会编写内容记录。如果消息要求在现有类型中添加/重命名/移除字段(动词 add/append/remove/drop/rename/change/modify/update 针对某个 FQN 或已命名类型),请在 1a 之前打印一行:This skill is create-only for new types and reconcile-only for existing. To modify "<fqn|name>", edit its schema.json and redeploy, or use the CMS UI. — 然后继续发现流程,让用户进入该类型的摘要(7.5)。不要悄悄继续执行,假装已完成修改。

调用约定

由用户直接调用,或由另一个技能委派调用(例如 experience-cms-content-generate)。分为两部分:调用方提供的输入参数,以及在每个终止状态下输出的返回结果。

输入参数(均为可选)
参数类型作用
fqn字符串(namespace__DeveloperName)完全跳过发现过程。直接使用此 FQN 跳转到步骤 1e(检索并核对)。跳过 intent 和 1d 中的选择提示。调用方已确切知道要解析的类型时使用。
intent字符串用户的原始消息。用于步骤 1c 中的发现关键词,以及步骤 1d 中“匹配……”的措辞。由自然语言用户提示触发此技能时,默认使用此参数。
suppressCreateContentPrompt布尔值,默认 false禁止在步骤 8 末尾询问“是否要立即使用此类型创建内容?”。已自行驱动内容创建流程的调用方 MUST 传入 true。用户直接调用时保持 false,以便自然地向用户提供下一步操作。
spaceId字符串步骤 1c 中工作区内容类型检查(get_content_types_for_workspace)的工作区范围。调用方已解析出内容空间时传入。与 baseType 互斥——见下文。
folderId字符串同一项检查的文件夹范围,可替代 spaceId。与 baseType 互斥——见下文。
baseType字符串,默认 "CONTENT"get_content_types_for_workspace 的 baseType 参数。与 spaceId/folderId 互斥——见下文。

工作区范围解析——互斥约定。 spaceId / folderId / baseType 仅缩小步骤 1c 中工作区内容类型检查的范围,不影响所采用的发现路径。传入 spaceId 或 folderId 的调用方 MUST NOT 同时传入 baseType——范围本身已经决定了符合条件的类型;此技能不接受在同一次调用中同时传入两者。每次运行只适用以下四种组合之一——调用形式及原因见 references/discovery-details.md#1c continued:

  1. 仅 spaceId → get_content_types_for_workspace({ spaceId })。
  2. 仅 folderId → get_content_types_for_workspace({ folderId })。
  3. 仅 baseType → get_content_types_for_workspace({ baseType })。
  4. 三者均未传入 → get_content_types_for_workspace({ baseType: "CONTENT" })(默认值)。

常见调用形式:

  • 用户直接调用 → intent、suppressCreateContentPrompt 为 false。执行完整的发现、检索并核对流程,以及末尾提示。不传入 spaceId/folderId/baseType——采用上述组合 4。
  • 委托调用 → { intent | fqn, suppressCreateContentPrompt: true }。执行完整的发现、检索并核对流程;返回 {success, fqn, schema}(检索用于确认该类型已部署到组织中——仅存在于本地的类型会解析为 not_deployed,而不会返回单纯的成功结果)。禁止末尾提示。
  • 委托调用且已知工作区 → { intent, suppressCreateContentPrompt: true, spaceId: "<contentSpaceOrFolderId>" }——组合 1。调用方已解析出工作区(例如,从 get_or_create_cms_workspace_and_web_app_channel 获取 contentSpaceOrFolderId)并希望将步骤 1c 的检查限定在该范围时传入。此调用中 DO NOT 同时传入 baseType。如果调用方在内容类型发现之后才解析工作区(如当前的 experience-cms-content-generate),委托调用时就没有可传入的 spaceId——这属于组合 4,是有效且预期的调用形式。
返回结果

每个终止状态都会发出结构化结果。message 字段会作为最终单行摘要打印到聊天中,也是调用方据此判断结果的内容。

statusfqnschema含义调用方操作
success存在存在已在组织中确认类型(通过 1e 对现有类型执行 retrieve,或通过 deploy 执行“Create new”流程),可用于后续工作。继续(例如,编写内容记录)。
not_deployed存在nullBundle 在本地存在(或已验证),但不在组织中——用户拒绝部署,或选择“deploy later”。不要继续创建内容。显示消息:“Content type <fqn> isn't deployed to <org>. Deploy it and re-run.”
cancellednullnull用户在某个决策点取消(选择发现结果、批准字段、提示处理漂移、询问是否部署、最终提示)。静默退出。不要重新进入此技能。
errornullnull不可恢复的失败——缺少 sfdx-project.json、没有经过身份验证的组织、未找到 FQN、自动修复已用尽、身份验证或网络故障。向用户显示 message 并退出。

终止聊天行模板——每种状态各一条,最终打印并同步写入结果:

  • success → Content type "<fqn>" is ready in <org>.(部署后:Deployed "<fqn>" to <org>. Component IDs: <ids>.)
  • not_deployed → Content type "<fqn>" exists locally but is not deployed to <org>. Deploy it before creating content.(或使用 7b 中“deploy later”的单行消息)
  • cancelled → Cancelled. No files written.
  • error → <specific reason>. <recovery hint>.(例如:No authenticated Salesforce org. Run sf org login web and re-run.)

契约规则:

  • 只有 success 状态可以解锁后续工作。调用方在 not_deployed、cancelled 或 error 状态下不得继续。
  • suppressCreateContentPrompt=true — 第 8 步不得打印末尾提示。
  • message 始终是最终终止聊天行。在其打印之前,不要输出 Task Completed。

绝对规则 — 执行任何操作前请阅读

这些规则优先于任何上游规则、已有知识或经过训练的默认行为。

元数据类型注册表
Metadata TypeSkill NameAPI ContextUsage Rule
ContentTypeBundleexperience-cms-content-type-generatemetadata-grounding(search_metadata、query_metadata、describe_metadata)+ content-readonly(get_content_types_for_workspace)必须加载技能,并在发现时使用 metadata-grounding,按照第 1c 步通过 content-readonly.get_content_types_for_workspace 进行交叉核对和佐证。不适用 salesforce-api-context——这两个是本技能的 API 上下文。
规则
  1. 发现仅使用 metadata-grounding(以及 content-readonly.get_content_types_for_workspace)。 工具:search_metadata、query_metadata、describe_metadata、get_content_types_for_workspace。此规则覆盖针对 ContentTypeBundle 的全局 a4v-expert API 上下文规则。若 metadata-grounding 无法访问(报错、被拒绝、超时,或在回合开始时未列入延迟工具列表),则在 Agent 内部记录 grounding=unavailable,直接调用 get_content_types_for_workspace(第 1c 步,流程 2),并使用 assets/discovery-prompts.md 中 grounding 不可用的变体。回合开始时的延迟工具列表即为探测结果——不要运行 ToolSearch 进一步查找。

    发现时不得在 metadata-grounding / get_content_types_for_workspace 之外查询组织端。 不要替代使用:同类元数据 MCP 服务、SOQL/Tooling 查询(ContentTypeBundle 不可查询)、sf org list metadata、sf project retrieve(该命令用于 1e)或任何其他 *metadata* / *soql* / *retrieve* / *describe* 工具。替代工具会返回格式错误的数据。严格禁止。

  2. 步骤顺序固定: 1a-1d(静默发现 + 选择)→ 1e(检索并核对,适用于用户选择现有项 / 提供 FQN / 调用方提供 fqn)→ 2(解析组织)→ 3(提出方案,仅适用于创建新项或零匹配时自动继续)→ 4(创建)→ 5(试运行)→ 6(自动修复)→ 7(询问是否部署)→ 7.5(架构摘要)→ 8(末尾提示)。步骤 1e 返回 {fqn, schema},并跳至 7.5;此路径不运行 2–7。只要解析出 {fqn, schema},就运行 7.5。步骤 8 受 suppressCreateContentPrompt 控制。直接调用且零匹配时,1d 自动继续至 2 → 3。

  3. 每条“创建新项”路径都必须执行步骤 5 和 7。 步骤 5 前的退出路径:(a)Use existing / Provide an FQN / 调用方提供 fqn → 经由 1e 路由,该步骤返回 {fqn, schema} 且不写入任何文件;(b)在任何选择环节选择 Cancel。步骤 4 与步骤 7 的解析之间不得输出 Task Completed。1e 中漂移分支的“将本地部署到组织”也必须经由 5 和 7 路由。

  4. Task Completed 必须是最后一个操作——输出后,技能即结束。部署 JSON 中的 deployUrl 仅供参考,不是操作提示。之后禁止执行:open <deployUrl>、xdg-open、sf org open、sf project deploy report、打开浏览器标签页、回显 URL、有关“下一步”的说明文字。

  5. 步骤 1e 的检索会破坏性地覆盖 schema.json;发生漂移时必须询问用户。 sf project retrieve start --metadata ContentTypeBundle:<Name> 会覆盖本地 schema.json。每次 1e 检索前,都要将其快照保存到 localSchemaBefore(绝不保存 meta.xml)。漂移 = 对 localSchemaBefore 与检索后 schema.json 进行解析后的 JSON 比较;左侧始终是 localSchemaBefore,绝不能将检索后的文件与自身比较。发生漂移时,必须提示用户处理漂移(在聊天中显示差异,然后调用 ask_user_tool:Deploy local to org / Overwrite local with org / Cancel),绝不能静默协调。输出结果前,Cancel 和 Deploy local to org 都必须从 localSchemaBefore 恢复 schema.json。完整的快照/恢复流程及漂移提示模板 → references/retrieve-and-reconcile.md。

  6. 步骤 1d——以表格显示前 5 项,然后询问。 行数始终为 min(combined.length, 5);combined 是本地匹配项和每条 grounding 行的合并结果,并已去重(Location 为 local, org),不得以“不相关”为由删除。排序时,OOTB 优先(sfdc_cms__* 或 grounding 的 isOOTB),自定义项(c__*)按 grounding 排名随后列出。第 1 行的 FQN 用作 Use existing: 选项的名称。有 1 行或更多行时,先显示表格(FQN | Description | Location),然后调用 ask_user_tool:Use existing: <row-1 FQN> / Provide an FQN / Create new: <newName> / Cancel(<newName> 不得与 combined 中的任何 FQN 冲突)。combined 为空时,不显示表格,只调用 ask_user_tool:Create new: <contentTypeName> / Cancel(在委托调用或提供了 fqn 时,额外提供 Provide an FQN)。表格中绝不混用“没有匹配项”之类的措辞;不加引言;不得使用 #/Name/Label 列;绝不能声称某个类型“不在组织中”——只有 1e 的检索结果才具有权威性。完整提示模板 → assets/discovery-prompts.md。

完整的 Agent 检查清单和陷阱列表 → references/agent-checklist.md。

文件路径(严格)

  • Bundle 目录:<sfdx-source>/contentTypes/<ContentTypeName>/——不是 contentTypeBundles/<ContentTypeName>/。
  • 仅两个文件:schema.json 和 <ContentTypeName>.contentTypeBundle-meta.xml。

输出规范

用户会阅读聊天内容。本技能的大部分机制只供你使用,而非面向用户。

不要输出:状态行、任务进度清单、规划说明(我现在将……、根据本技能的……)、反模式推理、豁免说明、“操作被拒绝;改用……继续”等备注,或建议用户自行运行验证/部署。

只输出以下内容:1d 发现摘要、1e 漂移提示、3b 拟议字段表格 + ask_user_tool、步骤 4 中两行“文件已创建”确认、步骤 5 中一行验证结果、7a 部署询问、7b/7e 一行摘要、7.5 架构摘要表(无论何时解析出 {fqn, schema}),以及步骤 8 提示(当 suppressCreateContentPrompt 为 false/未设置时)。

**在步骤 7 完成、7.5 摘要已输出且步骤 8 的门控已评估之前,不要输出 Task Completed、“完成”或“一切就绪”。**过早结束会悄然导致摘要和部署询问被跳过。

Agent 检查清单和触发条件

完整的强制进度检查清单和触发条件列表位于 references/agent-checklist.md。在 Agent 内部复制检查清单,并且只在实际完成相应操作后才勾选。不要将其输出到聊天中。

工作流(创建)

1. 发现现有类型(静默——此步骤不向用户提问)

1a. 解析项目上下文(Agent 内部)

读取 sfdx-project.json。取 packageDirectories[0].path 并追加 /main/default → <sfdx-source>。Bundle 位于 <sfdx-source>/contentTypes/。如果缺少 sfdx-project.json,则根据 § 调用约定输出 error 结果,消息为 This is not an SFDX project — open the project root and re-run.,打印该消息并停止。

**显式 FQN 快速通道(直接用户)。**调用方未提供 fqn,但消息中明确包含 namespace__DeveloperName 格式的标记(例如 sfdc_cms__news、c__PressRelease)→ 将其记录为 fqn,并直接转到步骤 1e,跳过 1b/1c/1d。这与委托调用中的 {fqn} 格式相对应。

**残余意图捕获(Agent 内部)。**扫描消息,查找由 and also/and then/then 连接的第二个分句,或针对内容记录的第二个祈使动词(例如 create <type> and also create a <thing> about X)。如果存在,将其暂存为 residualIntent——步骤 8 的“Yes”分支会将其作为 intent 转发。在步骤 8 之前不要输出或处理它。

1b. 本地发现(静默)

使用目录列表功能(list_files/针对 <sfdx-source>/contentTypes/*/schema.json 的 Glob/IDE list_directory),而不是内容搜索/grep 工具——内容搜索会漏掉仅出现在文件夹名称中的匹配项。读取每个子文件夹中的 schema.json(title + description)。按意图进行语义匹配:根据内容领域进行判断,而不是匹配字面字符串——即使没有字面属性匹配,名为 MarketPlace 的文件夹也符合 marketplace 请求。将所有语义匹配的本地 Bundle 返回到 combined(步骤 1d 的排序和最多 5 行限制会处理其余部分)。零匹配 → 返回零(直接调用时自动继续)。

理由、反模式和工具选择详情 → references/discovery-details.md#1b。

1c. 组织发现(静默——分派 metadata-grounding.search_metadata,由 content-readonly.get_content_types_for_workspace 提供支持)

**分派门控:1c 是工具调用,而非思考步骤。**即使 1b 找到本地匹配项,也不要跳过——组织中可能仍有同名 Bundle(“Name already exists”源自此处)。无条件执行;只有真正的服务中断才可豁免相应工具。绝不要询问“是否应该搜索?”。 search_metadata 的 query 参数只能包含内容领域名词——用 3–5 个英文单词描述内容的主题(新闻、文章、产品、新闻稿)。不能是 FQN、命名空间提示,也不能是消息副本。metadataType: "ContentTypeBundle" 已表明类型。绝不分派包含 sfdc_cms、c__、__、content type、bundle、metadata 或 cms 的查询;如果包含,则重新构建查询。完整规则集和具体调用格式表(也从 experience-cms-content-generate 引用,作为防止规则偏移的安全网)→ references/discovery-query-rules.md。

服务目标:metadata-grounding(RULE 1)。limit=5,按 OOTB 优先排序。

1c 中不要分派 query_metadata。 search_metadata 会返回 1d 表格所需的所有信息(FQN、描述、OOTB 标记)。query_metadata 仅在 1e 中获取 OOTB 架构时才是必要的——按需分派,且只针对选定的那个 FQN。逐行扇出会造成 N 次无谓往返。

工作区内容类型检查——每次运行都分派,可与 grounding 并行或代替 grounding。 调用参数遵循互斥约定(§ 调用约定);绝不能同时传入 baseType 和 spaceId/folderId。流程 1(grounding 可用)——同时分派 get_content_types_for_workspace;组织候选集合 = 两个 FQN 集合的交集(允许为空,不要扩大范围或重试)。流程 2(grounding 不可用)——直接分派该工具,将其作为唯一的组织信号;对其结果行应用步骤 1b 的语义匹配,并记录 groundingFallback=workspaceTypes,供 1d 的 TRUTH GATE 使用。两种情况下,该工具都不能替代 1e 中的检索。调用格式、不可用情况的处理方式及原因 → references/discovery-details.md#1c continued。

1d. 始终呈现发现结果——包括“没有匹配项”

发现结果是聊天中首先展示给用户的信号。始终告知用户检查了什么以及发现了什么。所有情况(A/B/C)都要通过 ask_user_tool 提供选项列表,并等待用户在下一轮回复。

真实性门槛——1d 的措辞必须与 1c 中实际发生的情况一致。 不要声称本轮没有执行的检查;也不要透露实际上没有发生的跳过操作。分三种情况:

  • 已分派 search_metadata(流程 1,无论 get_content_types_for_workspace 的结果如何)→ 已通过 grounding 检查组织。无需说明。
  • search_metadata 不可用,但已分派 get_content_types_for_workspace(流程 2 / groundingFallback=workspaceTypes)→ 已检查组织,只是没有通过 grounding 检查。按照 assets/discovery-prompts.md,追加 (已检查此工作区支持的内容类型 — metadata-grounding 不可用)。
  • 两者都不可用 → 确实未检查组织。按照 assets/discovery-prompts.md,追加 (已跳过组织检查 — grounding 不可用)。

参见 references/discovery-details.md#1d。

Provide an FQN 门控——计算 showFqnOption。 满足以下任一条件时显示:(1)存在任意结果(本地结果或 grounding 结果 ≥1)——用户可能想要的匹配项不在第 1 行;第 2–5 行及超出上限的结果会在 FQN 选项的括号说明中展示;或者(2)调用是委派的(suppressCreateContentPrompt === true 或调用方提供了 fqn)——即使没有匹配项,在委派调用的变体中也始终提供该选项。

否则(直接调用且零匹配项)→ showFqnOption = false;跳过选择提示,打印信息行,并自动继续步骤 2。在直接用户要求创建新内容且没有任何匹配项时提供 FQN 输入,会改变用户的目标。 直接调用未匹配结果的意图校验门。 自动继续之前,从消息中提取至少一个可识别的内容领域名词(真实词语、命名实体或复合领域词汇——不能是乱码,也不能在剔除 content type/bundle/CMS/schema/metadata 等机制性名词后只剩填充词)。≥1 个可识别名词 → 继续执行步骤 2 → 3,步骤 3 拟定的名称只能由这些词元组成。若只有乱码或填充词 → 不要自动继续;询问 Your request "<original message>" doesn't name a content domain. What kind of content type would you like to create (e.g. news, blog, press release, product)?,并提供 Cancel + 自由文本;自由文本会重新开始 1b,选择 Cancel → cancelled。当 1c 重建的查询为空时,也应用同一校验门——不要发送空白的 search_metadata。严格禁止根据消息中不存在的词元编造名称。完整规则和示例 → references/discovery-details.md#1d。

提示词模板(有匹配项、零匹配时直接自动继续、零匹配时委派、“Provide an FQN” 后续询问)→ assets/discovery-prompts.md。逐字复制。

1d 输出格式——两个独立输出,绝不合并(报告最多的 UX 缺陷):

  1. 先输出聊天 Markdown — Top <N> matching content types: 标题 + FQN | Description | Location 表格(assets/discovery-prompts.md 中的输出 1)。普通聊天文本,不是工具调用。
  2. 然后调用 ask_user_tool,其 question 必须严格为 Found matches. Pick one:(逐字照抄,只用一个简短句子),并提供 4 个固定选项:Use existing: <row-1 FQN> / Provide an FQN / Create new: <newName> / Cancel。

严格禁止: 在表格前或用其他内容代替表格,写任何前言或说明句;将表格、表格列或 (checked supported… / org check skipped…) 后缀放进 question 字段(该字段会呈现为没有 Markdown 的一整行文本,导致报告中的一大段文本问题);改写或“优化”question(它必须逐字为 Found matches. Pick one:);为每一行添加一个 Use existing: 选项(选项固定为 4 个——其他行通过 Provide an FQN 选择)。所有结构化内容都放在输出 1 的聊天 Markdown 中;question 字段必须保持为一个纯文本句子。→ assets/discovery-prompts.md,逐字复制。

用户回复后的路由:

用户选择下一步操作
Use existing: <Name>转到步骤 1e,并设置 fqn = <namespace>__<Name>(对于文件夹未命名空间化的本地匹配项,默认命名空间为 c)。
Provide an FQN(仅当 showFqnOption = true 时提供)按照 assets/discovery-prompts.md 中的内容询问后续问题。用户回复后 → 转到步骤 1e,并使用该 FQN。
Create new: <contentTypeName>继续执行步骤 2 → 步骤 3。
Cancel按照 § Invocation contract 发出 cancelled 结果,输出 Cancelled. No files written.,然后输出 Task Completed。

严格禁止在同一条消息中输出选项列表并宣布“正在继续创建新的”。由用户来选择,而不是由你选择。

1e. 检索并协调

当 1d 找到匹配项/提供 FQN 时,或调用方以 {fqn} 调用时进入。目标:返回与组织匹配的 {fqn, schema}。

命名空间检查:

  • 自定义 FQN(c__*,非平台命名空间)→ 运行 sf project retrieve start --metadata ContentTypeBundle:<DeveloperName> --target-org <alias> --json。自定义项始终执行检索。
  • OOTB FQN(sfdc_cms__*)→ 跳过 sf project retrieve start(不会返回可用内容)。此分支需要实时的 metadata-grounding schema,无论 FQN 是通过哪种流程找到的——get_content_types_for_workspace(流程 2)永远不会返回 schema,只返回 {fqn, name, description}。如果 1c 已记录 grounding=unavailable,请勿尝试 query_metadata——直接转到下方的错误处理。否则,立即仅针对选定的 FQN 调度 query_metadata({ metadataType: "ContentTypeBundle", id: "<grounding row id from 1c>" }),并从其响应中解析 schema(若未返回 schema 负载,则回退到 describe_metadata)。如果响应出错/为空/不包含 schema,则显示 Can't resolve OOTB FQN "<fqn>" without a real schema from metadata-grounding. Retry when grounding is back, or provide a custom FQN. 并以 error 退出。绝不要根据训练数据编造 OOTB schema——“sfdc_cms__news 通常有 title/body/summary……”这种想法正是导致 3–7 出错的原因。完整规则 → references/retrieve-and-reconcile.md § Namespace gate。

协调——将检索到的 schema 与 <sfdx-source>/contentTypes/<DeveloperName>/schema.json 进行比较。完整对照表、漂移模板和路由规则 → references/retrieve-and-reconcile.md。

路由结果:

  • 本地不存在,检索到 → 写入本地,返回 {fqn, schema=retrieved}。
  • 两者都有且匹配 → 返回 {fqn, schema=local}。
  • 两者都有但不一致 → 显示漂移提示(Deploy local to org → 步骤 5/7;Overwrite local with org → 替换文件并返回;Cancel → 退出)。
  • 仅本地存在 → 先打印步骤 7.5 的摘要(让用户看到将要部署的内容),然后提供部署(5/7)或取消选项。
  • 两者都不存在 → 重新调度 1d 选择,并提供 Try a different FQN 选项。

成功时,返回 {fqn, schema} 并继续执行 7.5 → 8。 不提议字段,也不重新验证——已协调的 schema 是唯一事实来源。新建时仅创建;已有项仅协调。

1d 中的“Create new”是唯一会继续到步骤 2 的路径。

2. 解析目标组织

优先使用 SF CLI 默认值(通过 sf config set target-org=<alias> 设置的别名,或 sfdx-project.json 中的 defaultUsername)。解析为 <orgAlias>,并在每次 sf 调用中传入 --target-org <orgAlias>。设置了默认值时,此步骤不显示任何内容。

没有默认值时——回退处理: 运行 sf org list --json(不显示任何内容):

  • 没有已认证的组织 → 输出 error,消息为 No authenticated Salesforce org found. Run sf org login web and re-run.,打印并停止。
  • 恰好一个 → 静默将其用作 <orgAlias>。不要修改 sf config。
  • 两个或更多 → 调度 ask_user_tool,问题为 No default Salesforce org is set. Pick the org to use for this run:,每个组织对应一个选项,标签为 <alias> (<username>) [<devhub|sandbox|scratch|prod>],并提供 Cancel。选定后,仅在本次运行中将其用作 <orgAlias>(不要运行 sf config set target-org=…)。选择 Cancel → 输出 cancelled,打印 Cancelled. No files written.,并执行 Task Completed。

所选别名适用于本次运行中后续的所有 sf 调用(1e 检索、5 试运行、7 部署)。

3. 提议字段并征得用户批准

前提条件:1c 已调度 search_metadata(或记录了 grounding=unavailable),且上一轮中的 1d 已解析为 Create new: <contentTypeName>。其他 1d 选项会转到其他步骤:Use existing / Provide an FQN → 1e;Cancel → 输出 cancelled。

如果你刚通过 ask_user_tool 显示了选项列表,而用户还没有回复,请停止——本轮到此结束。

3a. 确定字段属性(Agent 内部)。 设置 apiName(驼峰命名)、title(易读名称)、lightning:type、required。默认保持最简:除非用户提出要求,否则不设置 lightning:textIndexed、lightning:localizable、长度/范围边界、enum、const、placeholderText。

内容类型名称必须能从用户的消息中推导出来。 <contentTypeName> 及其 title 必须由消息中实际出现的词构成(可以调整为 PascalCase 或调整词序——laptop review → LaptopReview)。严格禁止凭空编造、从无关的打开标签页或之前会话的上下文中推导。如果 1d 的合理性检查通过,但名词集较少(只有一个词),就使用该词——不要添加修饰。提议的名称中若包含消息中不存在的词,就属于幻觉反模式;请停止并重新执行 1d 的检查。

在 Agent 内部将 userEditedFields 初始化为 false。此变量控制步骤 6 的分支。仅当用户在 3b 循环中输入自由文本编辑内容时才将其设为 true(参见 references/edit-fields-loop.md)。Edit fields → Approve as shown 且未输入任何编辑内容时,该值仍为 false——提议未经修改即获接受。

3b. 展示提议的字段——先在聊天中显示 Markdown 表格,然后在同一轮调用 ask_user_tool。 不要将表格合并到工具提示中——界面会移除格式,用户无法在充分了解的情况下批准。

消息 1(聊天中可见的纯文本):

Proposed fields for `<contentTypeName>` at `<sfdx-source>/contentTypes/<contentTypeName>/`:

| # | API name | Type | Required | Constraints | Title |
|---|---|---|---|---|---|
| 1 | `title` | `lightning__textType` | yes | maxLength: 200 | Title |
| 2 | `body`  | `lightning__richTextType` | yes | — | Body |

格式约束遵循 7.5(参见 references/schema-summary-format.md):对于用户要求或提议的每个字段约束,使用逗号分隔的 key: value 对——例如 maxLength、minLength、minimum、maximum、enum、const、lightning:localizable、lightning:allowedUrlSchemes 等。如果字段除 required 外没有其他约束,则使用 —。

消息 2(同一轮):调用 ask_user_tool,问题为 Approve these fields, edit them, or cancel?,选项为 Approve、Edit fields、Cancel。

  • Approve → 步骤 4。
  • Edit fields → 按照下方循环操作。循环直至获批。
  • Cancel → 输出 cancelled,打印 Cancelled. No files written.,并标记 Task Completed。

字段编辑循环——每轮都重新打印当前表格(聊天中可见),然后调用 ask_user_tool,提供 Approve as shown / Cancel 选项,并接受自由文本编辑指令。工具返回的自由文本就是编辑指令——请解析并应用,然后继续循环;不要停止。 完整模板及防止提前停止的指导 → references/edit-fields-loop.md。

4. 创建文件(静默——最多用两行确认)

前提条件:步骤 3b 在紧接的上一轮中返回了用户的 Approve。未经批准不得写入文件。

创建 <sfdx-source>/contentTypes/<contentTypeName>/。使用已加载的 assets/schema-example.json(遵循 § Schema rules——不要重新获取同级的 schema.json)。根据已批准的字段生成 schema.json 和 <contentTypeName>.contentTypeBundle-meta.xml,其中必须严格包含以下四行,仅替换 <masterLabel>:

<?xml version="1.0" encoding="UTF-8"?>
<ContentTypeBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel><contentTypeName></masterLabel>
</ContentTypeBundle>

不要添加 <displayName>、<description>、<contentTypeFields>、<fieldType> 或任何旧版 SOAP 元素——这些会导致验证器失败。每个字段中禁止使用的键(type、format、default、空的 lightning:uiOptions)→ references/schema-rules.md。

写入后,严格打印以下两行:

Created <sfdx-source>/contentTypes/<contentTypeName>/schema.json
Created <sfdx-source>/contentTypes/<contentTypeName>/<contentTypeName>.contentTypeBundle-meta.xml

然后继续步骤 5。调用 5a 前,先运行 references/pre-deploy-checklist.md 中的部署前 schema 检查清单(Agent 内部执行,不要打印)。原地修复并重新检查。

5. 验证(必须)

步骤 5 无条件执行。只有步骤 1d 以 Use existing 结束时才跳过。grounding=unavailable 不会跳过步骤 5(这是不同的身份验证路径)。不要建议用户运行 dry-run 命令——由你来运行。

5a. 运行(聊天中不加前言):

sf project deploy start --source-dir <sfdx-source>/contentTypes/<contentTypeName> --target-org <orgAlias> --dry-run --json

5b. 结果处理:

  • result.status === "Succeeded" → 打印一行:Validated against <org>: Succeeded (dry-run — nothing deployed yet).。这不是终止状态——你的下一步操作是步骤 7a 中的 ask_user_tool。
  • result.status === "Failed" 且包含 componentFailures → 执行步骤 6(自动修复)。
  • result.status === "Failed" 且 componentFailures 数量为零 → CLI/身份验证问题。显示原始响应,并请用户检查 sf org list、源路径和 sfdx-project.json。不要将其计入 3 次尝试之一。
6. 修复验证器错误 — 根据 userEditedFields 静默自动修复或先询问

根据 userEditedFields(在 3a 中设置)分支处理。区分依据是问题架构由谁编写:

  • false → 用户原样接受了 Agent 的提议。验证失败是 Agent 的错误。静默自动修复。
  • true → 用户在 3b 中输入了编辑内容。失败可能反映用户意图(例如,整数使用 enum、文本使用 default),只有用户才能解决。显示计划的修复,并先询问。

两个分支都遵循扫描整个架构的原则,并将 Agent 驱动的修复限制在 3 次尝试以内。每次尝试都要修复整个架构中验证器可能标记的所有问题,而不是一次只处理一个字段 — 验证器每次只报告 1–2 个错误;不要陷入每次尝试只处理一个字段的做法。错误到修复的映射(包括安全与可能改变意图的 Kind)→ references/deployment-errors.md。

6a. 静默自动修复(userEditedFields === false)

  1. 读取每个 componentFailures[].problem。
  2. 扫描 properties,查找同类问题。例如,一个 placeholderText missing → 检查所有 lightning:uiOptions;一个 format 被拒绝 → 从每个字段中移除 type/format/default;一个不允许的特定类型键 → 检查该 lightning:type 的每个字段。
  3. 一次性重写,然后重新运行步骤 5 一次。计为 3 次尝试中的一次。

如果 3 次尝试后仍然失败:显示最后一个错误,并转到 6b 的 Let me edit 后续流程。若放弃/退出且未成功通过验证,则使用最后一个验证器错误作为 message 发出 error,然后停止。

6b. 修复前先询问(userEditedFields === true)— 显示计划的修复

用户编写的内容被验证器拒绝了。不要静默改写用户的意图。

消息 1(聊天中可见的纯文本,不在 ask_user_tool 中)— 列出每个 componentFailures[].problem,并在 references/deployment-errors.md 中查找修复方法:

Validation failed on your edited schema. Planned fixes:

  ✗ <field>: <one-line error description>
      Planned: <fix> (<impact>)
  ✗ <field2>: …
      Planned: …

包含本轮的所有错误,并按字段分组。如果修复会移除/更改用户编写的约束(enum、const、lightning:localizable、数值边界、类型替换),括号中必须说明具体后果 — “失去数值排序”、“不再限制为枚举值”、“文本将无法本地化” — 不要写“更改类型”。Kind=safe 修复(移除 default、移除 type/format、allowedUrlSchemes: ["https"])须原样写入 safe: validator hygiene, no intent change。

消息 2(同一轮)— 使用问题 How would you like to proceed? 和选项 Apply these fixes and continue / Let me edit — I'll fix it, then re-validate / Cancel 调用 ask_user_tool。

处理方式:

  • Apply these fixes and continue → 严格按显示的修复执行,一次性重写,然后重新运行步骤 5 一次。计为 3 次尝试中的一次。成功 → 7a。失败 → 返回 6b 顶部,并使用新错误(userEditedFields 保持为 true)。
  • Let me edit → 打印步骤 4 中双行确认的文件路径,并原样打印验证器错误;使用问题 Reply when you're done editing. 和选项 Re-validate / Cancel 调用 ask_user_tool。Re-validate → 重新运行步骤 5(用户驱动的循环不计入 3 次尝试)。Cancel → 发出 cancelled,打印 Cancelled. Your edits remain at <sfdx-source>/contentTypes/<contentTypeName>/,然后输出 Task Completed。不要删除文件。
  • Cancel → 发出 cancelled,打印 Cancelled. No files written.,然后输出 Task Completed。

如果 Apply these fixes 用尽 3 次尝试:显示最后一个错误,并转到 Let me edit 后续流程(不要再次提供 Apply these fixes)。如果用户随后选择 Cancel,则使用最后一个验证器错误作为 message 发出 error,然后停止。

7. 部署(是/否——等待用户下一轮回复)

步骤 5 成功后即可进入步骤 7;但这并不代表可以跳过步骤 7。执行任何部署或终端标记前,先通过 ask_user_tool 询问用户。

7a. 调用 ask_user_tool:

  • 问题:Validation succeeded. Deploy "<contentTypeName>" to <org> now?
  • 选项:Yes - deploy now / No - I'll deploy later

7b. 用户选择否:发出 not_deployed 结果并停止。打印一行 —

Validated. Nothing deployed. To deploy later: sf project deploy start --source-dir <sfdx-source>/contentTypes/<contentTypeName> --target-org <orgAlias>

返回结果:{ status: "not_deployed", fqn: "<namespace>__<contentTypeName>", schema: null, message: "<the line above>" }。不要运行步骤 7.5 或步骤 8——该类型尚未存在于组织中。

7c. 用户选择是:运行

sf project deploy start --source-dir <sfdx-source>/contentTypes/<contentTypeName> --target-org <orgAlias> --json

7d. 部署出错:

  • 如果存在 componentFailures(少见——通常发生在验证与部署之间进行了手动编辑,或组织端状态发生了暂时性变化):根据当前的 userEditedFields 标志,转到步骤 6 中相应的分支。由于到达 7d 前必定已至少有一轮面向用户的交互(步骤 7a 中的 Yes - deploy now),因此该标志可权威地表明最后由谁编写了文件。重新验证成功后,再次询问步骤 7a 中的部署问题。如果无法恢复(自动修复已用尽后取消,或用户在 6b 中取消),则根据 § 调用约定发出 error 结果,将最后一条验证器错误作为 message,然后停止。
  • 如果 componentFailures 为零(认证/网络问题):根据 § 调用约定发出 error 结果,将原始响应摘要作为 message,然后停止。

7e. 成功:打印一行——Deployed <contentTypeName> to <org>. Component IDs: <ids>. 这是一个 success 终止状态——任务结束时要发出的结果为 { status: "success", fqn: "<namespace>__<contentTypeName>", schema: <the just-deployed schema>, message: "<the line above>" }。继续执行步骤 7.5。从此处到步骤 7.5 之间,不要运行任何其他命令(见规则 4)。

7.5. 架构摘要(所有已解析架构路径均执行)

每当 {fqn, schema} 已解析时都运行,无论来源如何,也不受 suppressCreateContentPrompt 影响——此步骤仅提供信息,不会开启新一轮交互。每次运行只执行一次——在 Agent 内部跟踪 summaryPrinted;1e 的“仅本地”分支会在部署或取消前打印摘要,因此后续 7e 不得再次触发。

在以下情况下运行:1e 返回的 {fqn, schema} 中,schema 不为 null;7e 成功;1e 的差异提示中“Deploy local to org”分支到达 7e;或 1e 的“仅本地”分支在部署或取消前。应用 summaryPrinted 门控。

以下情况不要运行:用户选择 Cancel、7b(稍后部署)、任何未解析出架构的错误路径,或 summaryPrinted === true。

以聊天文本打印(不要在 ask_user_tool 中打印):

Content type "<fqn>" is ready. Schema:

| # | API name | Type | Required | Constraints | Title |
|---|---|---|---|---|---|
| 1 | `title` | `lightning__textType` | yes | maxLength: 200 | Title |
| 2 | `body`  | `lightning__richTextType` | yes | — | Body |

列定义、约束规则、排序/截断(最多 20 个属性)、禁止打印的内容列表 → references/schema-summary-format.md。

立即继续执行步骤 8(无需单独开启新一轮交互,也无需中间提示)。

8. 末尾的“创建内容?”提示(由 suppressCreateContentPrompt 控制)

仅从 success 到达此步骤 — 第 8 步仅控制是否在此之后显示额外提示。门控条件:suppressCreateContentPrompt === true → 完全跳过,发出 success + Task Completed(该提示会重复调用方已在驱动的轮次)。否则,在以下情况下运行:成功来自 1e 返回 {fqn, schema}、7e 成功,或 1e 的漂移提示“将本地部署到组织”到达 7e。不要在 Cancel(cancelled)、7b(not_deployed)或任何错误路径(error)上运行。

使用问题 Would you like to create content using "<fqn>" now? 和选项 Yes — create content now / No — I'm done 调度 ask_user_tool。

  • Yes → 将任务交给 experience-cms-content-generate,参数为 { fqn, schema, suppressCreateContentPrompt: true };如果 1a 捕获了意图残留(例如,create a news type and also create an article about our Q4 launch → 转发 create an article about our Q4 launch),也传入 intent: <residualIntent>。如果没有意图残留,则省略 intent。不要循环返回。发出 success + Task Completed。
  • No → 打印 Done. FQN: <fqn>.,发出 success、Task Completed。

第 8 步完成后,不要运行任何命令(规则 4)。

Schema 规则(以验证器为准)

CMS 部署验证器是事实依据。完整规则集 → references/schema-rules.md(根级、字段级键、lightning:uiOptions 注意事项、各类型接受的键及特殊情况)。在第 4 步和第 6 步之前加载。

也要在第 4 步之前加载 assets/schema-example.json — 规范形状参考,包含每个受支持 lightning:type 的默认最小形式。不要仅因为第 1b/1e 步已将同级目录中的 schema.json 加载到上下文中,就将其用作参考 — 同级文件包含用户特定的约束(maxLength、lightning:localizable、索引),在缺乏依据时使用会导致这些约束泄漏。

不可妥协的要求:根级必须有 unevaluatedProperties: false(仅根级),不得有 $schema;字段级仅允许 title / description / lightning:type / lightning:uiOptions 及该类型接受的键;绝不使用 type / format / default / 空的 lightning:uiOptions;仅使用 sfdc_cms:metadataContent 混入项。

反模式

完整反模式表(违规行为 / 自我辩解 / 正确做法) → references/anti-patterns.md。如果你发现自己正想着其中列出的某种“错误想法”,立即停止 — 这个想法就是错误本身。

备注

  • 文件夹名称、<masterLabel> 和 bundle 的 title 应保持一致(PascalCase 文件夹、人类可读的标签和标题)。
  • 避免使用 lightning__fileType 和 lightning__contentReferenceType — 目前不受支持。
  • Eval 数据集位于 packages/adk-eval/eval/domains/experience-cms-content-type-generate/,不在 tests/evals/ 中。

参考文件索引

  • references/agent-checklist.md — 必须遵循的进度检查清单和警示点。
  • references/schema-rules.md — 验证器规则集(bundle 根级 + 字段级 + lightning:uiOptions + 各类型接受的键 + 验证器特殊情况)。
  • references/pre-deploy-checklist.md — 第 5 步之前的 Agent 内部 schema 合理性检查。
  • references/deployment-errors.md — 验证器错误 → 修复方法映射(第 6 步)。
  • references/anti-patterns.md — 错误想法目录。
  • references/discovery-details.md — 1b/1c/1d 规则的依据。
  • references/discovery-query-rules.md — 构造 search_metadata 查询的唯一事实来源。本文件此处第 1c 步引用此规则,父技能 experience-cms-content-generate 也会引用(作为防止规则漂移的保障)。
  • references/retrieve-and-reconcile.md — 第 1e 步的协调表和漂移路由。
  • references/edit-fields-loop.md — 第 3b 步的编辑循环模板和路由表。
  • references/schema-summary-format.md — 第 7.5 步的列、排序和截断规则。
  • assets/discovery-prompts.md — 第 1d 步的选项列表模板及无匹配项时的变体。
  • assets/schema-example.json — 参考 schema,涵盖每个受支持的 lightning:type。

评论

支持 Markdown